<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN"
"http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head><meta http-equiv="content-type" content="text/html; charset=utf-8" />
<title>[210367] trunk</title>
</head>
<body>

<style type="text/css"><!--
#msg dl.meta { border: 1px #006 solid; background: #369; padding: 6px; color: #fff; }
#msg dl.meta dt { float: left; width: 6em; font-weight: bold; }
#msg dt:after { content:':';}
#msg dl, #msg dt, #msg ul, #msg li, #header, #footer, #logmsg { font-family: verdana,arial,helvetica,sans-serif; font-size: 10pt;  }
#msg dl a { font-weight: bold}
#msg dl a:link    { color:#fc3; }
#msg dl a:active  { color:#ff0; }
#msg dl a:visited { color:#cc6; }
h3 { font-family: verdana,arial,helvetica,sans-serif; font-size: 10pt; font-weight: bold; }
#msg pre { overflow: auto; background: #ffc; border: 1px #fa0 solid; padding: 6px; }
#logmsg { background: #ffc; border: 1px #fa0 solid; padding: 1em 1em 0 1em; }
#logmsg p, #logmsg pre, #logmsg blockquote { margin: 0 0 1em 0; }
#logmsg p, #logmsg li, #logmsg dt, #logmsg dd { line-height: 14pt; }
#logmsg h1, #logmsg h2, #logmsg h3, #logmsg h4, #logmsg h5, #logmsg h6 { margin: .5em 0; }
#logmsg h1:first-child, #logmsg h2:first-child, #logmsg h3:first-child, #logmsg h4:first-child, #logmsg h5:first-child, #logmsg h6:first-child { margin-top: 0; }
#logmsg ul, #logmsg ol { padding: 0; list-style-position: inside; margin: 0 0 0 1em; }
#logmsg ul { text-indent: -1em; padding-left: 1em; }#logmsg ol { text-indent: -1.5em; padding-left: 1.5em; }
#logmsg > ul, #logmsg > ol { margin: 0 0 1em 0; }
#logmsg pre { background: #eee; padding: 1em; }
#logmsg blockquote { border: 1px solid #fa0; border-left-width: 10px; padding: 1em 1em 0 1em; background: white;}
#logmsg dl { margin: 0; }
#logmsg dt { font-weight: bold; }
#logmsg dd { margin: 0; padding: 0 0 0.5em 0; }
#logmsg dd:before { content:'\00bb';}
#logmsg table { border-spacing: 0px; border-collapse: collapse; border-top: 4px solid #fa0; border-bottom: 1px solid #fa0; background: #fff; }
#logmsg table th { text-align: left; font-weight: normal; padding: 0.2em 0.5em; border-top: 1px dotted #fa0; }
#logmsg table td { text-align: right; border-top: 1px dotted #fa0; padding: 0.2em 0.5em; }
#logmsg table thead th { text-align: center; border-bottom: 1px solid #fa0; }
#logmsg table th.Corner { text-align: left; }
#logmsg hr { border: none 0; border-top: 2px dashed #fa0; height: 1px; }
#header, #footer { color: #fff; background: #636; border: 1px #300 solid; padding: 6px; }
#patch { width: 100%; }
#patch h4 {font-family: verdana,arial,helvetica,sans-serif;font-size:10pt;padding:8px;background:#369;color:#fff;margin:0;}
#patch .propset h4, #patch .binary h4 {margin:0;}
#patch pre {padding:0;line-height:1.2em;margin:0;}
#patch .diff {width:100%;background:#eee;padding: 0 0 10px 0;overflow:auto;}
#patch .propset .diff, #patch .binary .diff  {padding:10px 0;}
#patch span {display:block;padding:0 10px;}
#patch .modfile, #patch .addfile, #patch .delfile, #patch .propset, #patch .binary, #patch .copfile {border:1px solid #ccc;margin:10px 0;}
#patch ins {background:#dfd;text-decoration:none;display:block;padding:0 10px;}
#patch del {background:#fdd;text-decoration:none;display:block;padding:0 10px;}
#patch .lines, .info {color:#888;background:#fff;}
--></style>
<div id="msg">
<dl class="meta">
<dt>Revision</dt> <dd><a href="http://trac.webkit.org/projects/webkit/changeset/210367">210367</a></dd>
<dt>Author</dt> <dd>bburg@apple.com</dd>
<dt>Date</dt> <dd>2017-01-05 12:07:44 -0800 (Thu, 05 Jan 2017)</dd>
</dl>

<h3>Log Message</h3>
<pre>Web Inspector: Test.html should support globals reportInternalError, reportUnhandledRejection, reportUncaughtException
https://bugs.webkit.org/show_bug.cgi?id=161358
&lt;rdar://problem/28066446&gt;

Reviewed by Joseph Pecoraro.

Source/WebInspectorUI:

We have a hodgepodge of redundant code that reports uncaught exceptions in the inspector page.
There is better handling of uncaught exceptions in the inspected page, such as including stack traces.

This patch consolidates a lot of this code and makes it possible to report
unhandled promise rejections, top-level uncaught exceptions, and exceptions
caught in a try-catch block. The formatting and sanitization code for all of
these things is shared and consistent. Finally, some tests have been added to
catch regressions in unhandled rejection/uncaught exception reporting.

* UserInterface/Test/FrontendTestHarness.js:
(FrontendTestHarness): Explicitly set initial flag state here so it's easy to find all flags.

(FrontendTestHarness.prototype.redirectConsoleToTestOutput):
Extract this code to sanitize stack frames and put it in TestHarness. It is used
by other methods that need to print stack frames.

(FrontendTestHarness.prototype.reportUnhandledRejection):
(FrontendTestHarness.prototype.reportUncaughtException):
Added. Sanitize stack trace data so it is deterministic. Log the message to the
original window.console but don't exit early. Sometimes the test page is not
fully loaded if we throw an exception quite early in the test() method, and there's
no harm in not early returning. If we do early return in this case, then a test that
uses reportUncaughtException on purpose may not complete because the call to completeTest()
would be skipped by returning early.

(FrontendTestHarness.prototype.reportUncaughtExceptionFromEvent):
Renamed from reportUncaughtException since the signature of that method suggests
it should have a single exception argument rather than lots of data arguments.

* UserInterface/Test/Test.js: Add globals.

* UserInterface/Test/TestHarness.js:
(TestHarness): Document class flags.
(TestHarness.sanitizeURL):
(TestHarness.sanitizeStackFrame):
(TestHarness.prototype.sanitizeStack):
Extract this code from other parts of the test harness. Make sanitizeStack
an instance method so that there is only one place that needs to check the
'suppressStackTraces' flag.

* UserInterface/Test/TestSuite.js:
(TestSuite.prototype.logThrownObject):
(TestSuite):
(AsyncTestSuite.prototype.runTestCases):
(AsyncTestSuite):
(SyncTestSuite.prototype.runTestCases):
(SyncTestSuite):
(TestSuite.messageFromThrownObject): Deleted.
Inline some helpers with only one use-site and consolidate redundant code
for adding an exception and message to the test results.

LayoutTests:

Improve uncaught exception reporting and add some tests to document
new and existing behavior.

* http/tests/inspector/resources/inspector-test.js:
(runTest.runTestMethodInFrontend):
(runTest): Outsource reporting of an uncaught exception while injecting
a method into the frontend. By doing this, we can make the report using
the actual exception object since it doesn't go through window.onerror.

* inspector/unit-tests/async-test-suite-expected.txt:
* inspector/unit-tests/async-test-suite.html:
* inspector/unit-tests/sync-test-suite-expected.txt:
* inspector/unit-tests/sync-test-suite.html:
Rebaseline and force suppression of stack traces, which are not deterministic
across commits due to logging specific lines and columns in TestCombined.js.

* inspector/unit-tests/globals-uncaught-exception-from-timer-callback-expected.txt: Added.
* inspector/unit-tests/globals-uncaught-exception-from-timer-callback.html: Added.
* inspector/unit-tests/globals-uncaught-exception-in-test-function-expected.txt: Added.
* inspector/unit-tests/globals-uncaught-exception-in-test-function.html: Added.
* inspector/unit-tests/globals-uncaught-exception-in-test-suite-expected.txt: Added.
* inspector/unit-tests/globals-uncaught-exception-in-test-suite.html: Added.
* inspector/unit-tests/globals-unhandled-rejection-in-test-function-expected.txt: Added.
* inspector/unit-tests/globals-unhandled-rejection-in-test-function.html: Added.
* inspector/unit-tests/globals-unhandled-rejection-in-test-suite-expected.txt: Added.
* inspector/unit-tests/globals-unhandled-rejection-in-test-suite.html: Added.
* inspector/unit-tests/globals-unhandled-rejection-in-timer-callback-expected.txt: Added.
* inspector/unit-tests/globals-unhandled-rejection-in-timer-callback.html: Added.</pre>

<h3>Modified Paths</h3>
<ul>
<li><a href="#trunkLayoutTestsChangeLog">trunk/LayoutTests/ChangeLog</a></li>
<li><a href="#trunkLayoutTestshttptestsinspectorresourcesinspectortestjs">trunk/LayoutTests/http/tests/inspector/resources/inspector-test.js</a></li>
<li><a href="#trunkLayoutTestsinspectorunittestsasynctestsuiteexpectedtxt">trunk/LayoutTests/inspector/unit-tests/async-test-suite-expected.txt</a></li>
<li><a href="#trunkLayoutTestsinspectorunittestsasynctestsuitehtml">trunk/LayoutTests/inspector/unit-tests/async-test-suite.html</a></li>
<li><a href="#trunkLayoutTestsinspectorunittestssynctestsuiteexpectedtxt">trunk/LayoutTests/inspector/unit-tests/sync-test-suite-expected.txt</a></li>
<li><a href="#trunkLayoutTestsinspectorunittestssynctestsuitehtml">trunk/LayoutTests/inspector/unit-tests/sync-test-suite.html</a></li>
<li><a href="#trunkSourceWebInspectorUIChangeLog">trunk/Source/WebInspectorUI/ChangeLog</a></li>
<li><a href="#trunkSourceWebInspectorUIUserInterfaceTestFrontendTestHarnessjs">trunk/Source/WebInspectorUI/UserInterface/Test/FrontendTestHarness.js</a></li>
<li><a href="#trunkSourceWebInspectorUIUserInterfaceTestTestjs">trunk/Source/WebInspectorUI/UserInterface/Test/Test.js</a></li>
<li><a href="#trunkSourceWebInspectorUIUserInterfaceTestTestHarnessjs">trunk/Source/WebInspectorUI/UserInterface/Test/TestHarness.js</a></li>
<li><a href="#trunkSourceWebInspectorUIUserInterfaceTestTestSuitejs">trunk/Source/WebInspectorUI/UserInterface/Test/TestSuite.js</a></li>
</ul>

<h3>Added Paths</h3>
<ul>
<li><a href="#trunkLayoutTestsinspectorunittestsglobalsuncaughtexceptionfromtimercallbackexpectedtxt">trunk/LayoutTests/inspector/unit-tests/globals-uncaught-exception-from-timer-callback-expected.txt</a></li>
<li><a href="#trunkLayoutTestsinspectorunittestsglobalsuncaughtexceptionfromtimercallbackhtml">trunk/LayoutTests/inspector/unit-tests/globals-uncaught-exception-from-timer-callback.html</a></li>
<li><a href="#trunkLayoutTestsinspectorunittestsglobalsuncaughtexceptionintestfunctionexpectedtxt">trunk/LayoutTests/inspector/unit-tests/globals-uncaught-exception-in-test-function-expected.txt</a></li>
<li><a href="#trunkLayoutTestsinspectorunittestsglobalsuncaughtexceptionintestfunctionhtml">trunk/LayoutTests/inspector/unit-tests/globals-uncaught-exception-in-test-function.html</a></li>
<li><a href="#trunkLayoutTestsinspectorunittestsglobalsuncaughtexceptionintestsuiteexpectedtxt">trunk/LayoutTests/inspector/unit-tests/globals-uncaught-exception-in-test-suite-expected.txt</a></li>
<li><a href="#trunkLayoutTestsinspectorunittestsglobalsuncaughtexceptionintestsuitehtml">trunk/LayoutTests/inspector/unit-tests/globals-uncaught-exception-in-test-suite.html</a></li>
<li><a href="#trunkLayoutTestsinspectorunittestsglobalsunhandledrejectionintestfunctionexpectedtxt">trunk/LayoutTests/inspector/unit-tests/globals-unhandled-rejection-in-test-function-expected.txt</a></li>
<li><a href="#trunkLayoutTestsinspectorunittestsglobalsunhandledrejectionintestfunctionhtml">trunk/LayoutTests/inspector/unit-tests/globals-unhandled-rejection-in-test-function.html</a></li>
<li><a href="#trunkLayoutTestsinspectorunittestsglobalsunhandledrejectionintestsuiteexpectedtxt">trunk/LayoutTests/inspector/unit-tests/globals-unhandled-rejection-in-test-suite-expected.txt</a></li>
<li><a href="#trunkLayoutTestsinspectorunittestsglobalsunhandledrejectionintestsuitehtml">trunk/LayoutTests/inspector/unit-tests/globals-unhandled-rejection-in-test-suite.html</a></li>
<li><a href="#trunkLayoutTestsinspectorunittestsglobalsunhandledrejectionintimercallbackexpectedtxt">trunk/LayoutTests/inspector/unit-tests/globals-unhandled-rejection-in-timer-callback-expected.txt</a></li>
<li><a href="#trunkLayoutTestsinspectorunittestsglobalsunhandledrejectionintimercallbackhtml">trunk/LayoutTests/inspector/unit-tests/globals-unhandled-rejection-in-timer-callback.html</a></li>
</ul>

</div>
<div id="patch">
<h3>Diff</h3>
<a id="trunkLayoutTestsChangeLog"></a>
<div class="modfile"><h4>Modified: trunk/LayoutTests/ChangeLog (210366 => 210367)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/LayoutTests/ChangeLog        2017-01-05 19:51:38 UTC (rev 210366)
+++ trunk/LayoutTests/ChangeLog        2017-01-05 20:07:44 UTC (rev 210367)
</span><span class="lines">@@ -1,3 +1,40 @@
</span><ins>+2017-01-04  Brian Burg  &lt;bburg@apple.com&gt;
+
+        Web Inspector: Test.html should support globals reportInternalError, reportUnhandledRejection, reportUncaughtException
+        https://bugs.webkit.org/show_bug.cgi?id=161358
+        &lt;rdar://problem/28066446&gt;
+
+        Reviewed by Joseph Pecoraro.
+
+        Improve uncaught exception reporting and add some tests to document
+        new and existing behavior.
+
+        * http/tests/inspector/resources/inspector-test.js:
+        (runTest.runTestMethodInFrontend):
+        (runTest): Outsource reporting of an uncaught exception while injecting
+        a method into the frontend. By doing this, we can make the report using
+        the actual exception object since it doesn't go through window.onerror.
+
+        * inspector/unit-tests/async-test-suite-expected.txt:
+        * inspector/unit-tests/async-test-suite.html:
+        * inspector/unit-tests/sync-test-suite-expected.txt:
+        * inspector/unit-tests/sync-test-suite.html:
+        Rebaseline and force suppression of stack traces, which are not deterministic
+        across commits due to logging specific lines and columns in TestCombined.js.
+
+        * inspector/unit-tests/globals-uncaught-exception-from-timer-callback-expected.txt: Added.
+        * inspector/unit-tests/globals-uncaught-exception-from-timer-callback.html: Added.
+        * inspector/unit-tests/globals-uncaught-exception-in-test-function-expected.txt: Added.
+        * inspector/unit-tests/globals-uncaught-exception-in-test-function.html: Added.
+        * inspector/unit-tests/globals-uncaught-exception-in-test-suite-expected.txt: Added.
+        * inspector/unit-tests/globals-uncaught-exception-in-test-suite.html: Added.
+        * inspector/unit-tests/globals-unhandled-rejection-in-test-function-expected.txt: Added.
+        * inspector/unit-tests/globals-unhandled-rejection-in-test-function.html: Added.
+        * inspector/unit-tests/globals-unhandled-rejection-in-test-suite-expected.txt: Added.
+        * inspector/unit-tests/globals-unhandled-rejection-in-test-suite.html: Added.
+        * inspector/unit-tests/globals-unhandled-rejection-in-timer-callback-expected.txt: Added.
+        * inspector/unit-tests/globals-unhandled-rejection-in-timer-callback.html: Added.
+
</ins><span class="cx"> 2017-01-05  Andreas Kling  &lt;akling@apple.com&gt;
</span><span class="cx"> 
</span><span class="cx">         Skip fast/scrolling/page-cache-back-overflow-scroll-restore.html on iOS simulator.
</span></span></pre></div>
<a id="trunkLayoutTestshttptestsinspectorresourcesinspectortestjs"></a>
<div class="modfile"><h4>Modified: trunk/LayoutTests/http/tests/inspector/resources/inspector-test.js (210366 => 210367)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/LayoutTests/http/tests/inspector/resources/inspector-test.js        2017-01-05 19:51:38 UTC (rev 210366)
+++ trunk/LayoutTests/http/tests/inspector/resources/inspector-test.js        2017-01-05 20:07:44 UTC (rev 210367)
</span><span class="lines">@@ -88,9 +88,9 @@
</span><span class="cx">         try {
</span><span class="cx">             testFunction();
</span><span class="cx">         } catch (e) {
</span><del>-            // FIXME: the redirected console methods do not forward additional arguments.
-            console.error(&quot;Exception during test execution: &quot; + e, e.stack || &quot;(no stack trace)&quot;);
-            InspectorTest.completeTest();
</del><ins>+            // Using this instead of window.onerror will preserve the stack trace.
+            e.code = testFunction.toString();
+            InspectorTest.reportUncaughtException(e);
</ins><span class="cx">         }
</span><span class="cx">     }
</span><span class="cx"> 
</span></span></pre></div>
<a id="trunkLayoutTestsinspectorunittestsasynctestsuiteexpectedtxt"></a>
<div class="modfile"><h4>Modified: trunk/LayoutTests/inspector/unit-tests/async-test-suite-expected.txt (210366 => 210367)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/LayoutTests/inspector/unit-tests/async-test-suite-expected.txt        2017-01-05 19:51:38 UTC (rev 210366)
+++ trunk/LayoutTests/inspector/unit-tests/async-test-suite-expected.txt        2017-01-05 20:07:44 UTC (rev 210367)
</span><span class="lines">@@ -26,6 +26,7 @@
</span><span class="cx"> -- Running test case: DummyTest3
</span><span class="cx"> -- Running test case: FailingTest4
</span><span class="cx"> !! EXCEPTION: [object Object]
</span><ins>+Stack Trace: (suppressed)
</ins><span class="cx"> PASS: Promise from sequentialExecutionSuite.runTestCases() should reject when a test case fails.
</span><span class="cx"> PASS: Promise from sequentialExecutionSuite.runTestCases() should reject without altering its result value.
</span><span class="cx"> PASS: sequentialExecutionSuite should have executed four tests.
</span><span class="lines">@@ -37,6 +38,7 @@
</span><span class="cx"> -- Running test case: PassingTest5
</span><span class="cx"> -- Running test case: FailingTest6
</span><span class="cx"> !! EXCEPTION: {&quot;token&quot;:666}
</span><ins>+Stack Trace: (suppressed)
</ins><span class="cx"> PASS: Promise from abortOnFailureSuite.runTestCases() should reject when a test case fails.
</span><span class="cx"> PASS: Promise from abortOnFailureSuite.runTestCases() should reject without altering its result value.
</span><span class="cx"> PASS: abortOnFailureSuite should have executed two tests.
</span><span class="lines">@@ -58,11 +60,13 @@
</span><span class="cx"> == Running test suite: AsyncTestSuite.SetupException
</span><span class="cx"> -- Running test setup.
</span><span class="cx"> !! EXCEPTION: 
</span><ins>+Stack Trace: (suppressed)
</ins><span class="cx"> PASS: Promise from setupExceptionTestSuite.runTestCases() should reject.
</span><span class="cx"> 
</span><span class="cx"> == Running test suite: AsyncTestSuite.SetupFailure
</span><span class="cx"> -- Running test setup.
</span><span class="cx"> !! EXCEPTION: undefined
</span><ins>+Stack Trace: (suppressed)
</ins><span class="cx"> PASS: Promise from setupFailureTestSuite.runTestCases() should reject.
</span><span class="cx"> 
</span><span class="cx"> == Running test suite: AsyncTestSuite.TeardownException
</span><span class="lines">@@ -69,6 +73,7 @@
</span><span class="cx"> -- Running test case: TestWithExceptionDuringTeardown
</span><span class="cx"> -- Running test teardown.
</span><span class="cx"> !! EXCEPTION: 
</span><ins>+Stack Trace: (suppressed)
</ins><span class="cx"> PASS: Promise from teardownExceptionTestSuite.runTestCases() should reject.
</span><span class="cx"> 
</span><span class="cx"> == Running test suite: AsyncTestSuite.TeardownFailure
</span><span class="lines">@@ -75,5 +80,6 @@
</span><span class="cx"> -- Running test case: TestWithExceptionDuringTeardown
</span><span class="cx"> -- Running test teardown.
</span><span class="cx"> !! EXCEPTION: undefined
</span><ins>+Stack Trace: (suppressed)
</ins><span class="cx"> PASS: Promise from teardownFailureTestSuite.runTestCases() should reject.
</span><span class="cx"> 
</span></span></pre></div>
<a id="trunkLayoutTestsinspectorunittestsasynctestsuitehtml"></a>
<div class="modfile"><h4>Modified: trunk/LayoutTests/inspector/unit-tests/async-test-suite.html (210366 => 210367)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/LayoutTests/inspector/unit-tests/async-test-suite.html        2017-01-05 19:51:38 UTC (rev 210366)
+++ trunk/LayoutTests/inspector/unit-tests/async-test-suite.html        2017-01-05 20:07:44 UTC (rev 210367)
</span><span class="lines">@@ -5,6 +5,8 @@
</span><span class="cx"> &lt;script&gt;
</span><span class="cx"> function test()
</span><span class="cx"> {
</span><ins>+    ProtocolTest.suppressStackTraces = true;
+
</ins><span class="cx">     try {
</span><span class="cx">         let result = new AsyncTestSuite(this);
</span><span class="cx">         ProtocolTest.log(&quot;FAIL: instantiating AsyncTestSuite requires name argument.&quot;);
</span></span></pre></div>
<a id="trunkLayoutTestsinspectorunittestsglobalsuncaughtexceptionfromtimercallbackexpectedtxt"></a>
<div class="addfile"><h4>Added: trunk/LayoutTests/inspector/unit-tests/globals-uncaught-exception-from-timer-callback-expected.txt (0 => 210367)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/LayoutTests/inspector/unit-tests/globals-uncaught-exception-from-timer-callback-expected.txt                                (rev 0)
+++ trunk/LayoutTests/inspector/unit-tests/globals-uncaught-exception-from-timer-callback-expected.txt        2017-01-05 20:07:44 UTC (rev 210367)
</span><span class="lines">@@ -0,0 +1,5 @@
</span><ins>+Test that the uncaught exception hook will immediately terminate the test and print the associated exception and stack trace.
+
+Uncaught exception in Inspector page: Error: This is an exception thrown in the inspector page. [global:20:24]
+
+
</ins></span></pre></div>
<a id="trunkLayoutTestsinspectorunittestsglobalsuncaughtexceptionfromtimercallbackhtml"></a>
<div class="addfile"><h4>Added: trunk/LayoutTests/inspector/unit-tests/globals-uncaught-exception-from-timer-callback.html (0 => 210367)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/LayoutTests/inspector/unit-tests/globals-uncaught-exception-from-timer-callback.html                                (rev 0)
+++ trunk/LayoutTests/inspector/unit-tests/globals-uncaught-exception-from-timer-callback.html        2017-01-05 20:07:44 UTC (rev 210367)
</span><span class="lines">@@ -0,0 +1,19 @@
</span><ins>+&lt;!doctype html&gt;
+&lt;html&gt;
+&lt;head&gt;
+&lt;script src=&quot;../../http/tests/inspector/resources/inspector-test.js&quot;&gt;&lt;/script&gt;
+&lt;script&gt;
+function test()
+{
+    InspectorTest.suppressStackTraces = true;
+
+    setTimeout(() =&gt; {
+        throw new Error(&quot;This is an exception thrown in the inspector page.&quot;);
+    });
+}
+&lt;/script&gt;
+&lt;/head&gt;
+&lt;body onload=&quot;runTest()&quot;&gt;
+&lt;p&gt;Test that the uncaught exception hook will immediately terminate the test and print the associated exception and stack trace.&lt;/p&gt;
+&lt;/body&gt;
+&lt;/html&gt;
</ins></span></pre></div>
<a id="trunkLayoutTestsinspectorunittestsglobalsuncaughtexceptionintestfunctionexpectedtxt"></a>
<div class="addfile"><h4>Added: trunk/LayoutTests/inspector/unit-tests/globals-uncaught-exception-in-test-function-expected.txt (0 => 210367)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/LayoutTests/inspector/unit-tests/globals-uncaught-exception-in-test-function-expected.txt                                (rev 0)
+++ trunk/LayoutTests/inspector/unit-tests/globals-uncaught-exception-in-test-function-expected.txt        2017-01-05 20:07:44 UTC (rev 210367)
</span><span class="lines">@@ -0,0 +1,15 @@
</span><ins>+Test that the uncaught exception hook will immediately terminate the test and print the associated exception and stack trace.
+
+Uncaught exception in Inspector page: This is an exception thrown in the inspector page.
+
+Stack Trace:
+(suppressed)
+
+Evaluated Code:
+function test()
+{
+    InspectorTest.suppressStackTraces = true;
+
+    throw new Error(&quot;This is an exception thrown in the inspector page.&quot;);
+}
+
</ins></span></pre></div>
<a id="trunkLayoutTestsinspectorunittestsglobalsuncaughtexceptionintestfunctionhtml"></a>
<div class="addfile"><h4>Added: trunk/LayoutTests/inspector/unit-tests/globals-uncaught-exception-in-test-function.html (0 => 210367)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/LayoutTests/inspector/unit-tests/globals-uncaught-exception-in-test-function.html                                (rev 0)
+++ trunk/LayoutTests/inspector/unit-tests/globals-uncaught-exception-in-test-function.html        2017-01-05 20:07:44 UTC (rev 210367)
</span><span class="lines">@@ -0,0 +1,17 @@
</span><ins>+&lt;!doctype html&gt;
+&lt;html&gt;
+&lt;head&gt;
+&lt;script src=&quot;../../http/tests/inspector/resources/inspector-test.js&quot;&gt;&lt;/script&gt;
+&lt;script&gt;
+function test()
+{
+    InspectorTest.suppressStackTraces = true;
+
+    throw new Error(&quot;This is an exception thrown in the inspector page.&quot;);
+}
+&lt;/script&gt;
+&lt;/head&gt;
+&lt;body onload=&quot;runTest()&quot;&gt;
+&lt;p&gt;Test that the uncaught exception hook will immediately terminate the test and print the associated exception and stack trace.&lt;/p&gt;
+&lt;/body&gt;
+&lt;/html&gt;
</ins></span></pre></div>
<a id="trunkLayoutTestsinspectorunittestsglobalsuncaughtexceptionintestsuiteexpectedtxt"></a>
<div class="addfile"><h4>Added: trunk/LayoutTests/inspector/unit-tests/globals-uncaught-exception-in-test-suite-expected.txt (0 => 210367)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/LayoutTests/inspector/unit-tests/globals-uncaught-exception-in-test-suite-expected.txt                                (rev 0)
+++ trunk/LayoutTests/inspector/unit-tests/globals-uncaught-exception-in-test-suite-expected.txt        2017-01-05 20:07:44 UTC (rev 210367)
</span><span class="lines">@@ -0,0 +1,8 @@
</span><ins>+Test that the unhandled promise rejection hook will immediately terminate the test and print the associated exception and stack trace.
+
+
+== Running test suite: UncaughtExceptionInsideTestCase
+-- Running test case: CatchRejectedPromise
+!! EXCEPTION: This promise is rejected with an Error object.
+Stack Trace: (suppressed)
+
</ins></span></pre></div>
<a id="trunkLayoutTestsinspectorunittestsglobalsuncaughtexceptionintestsuitehtml"></a>
<div class="addfile"><h4>Added: trunk/LayoutTests/inspector/unit-tests/globals-uncaught-exception-in-test-suite.html (0 => 210367)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/LayoutTests/inspector/unit-tests/globals-uncaught-exception-in-test-suite.html                                (rev 0)
+++ trunk/LayoutTests/inspector/unit-tests/globals-uncaught-exception-in-test-suite.html        2017-01-05 20:07:44 UTC (rev 210367)
</span><span class="lines">@@ -0,0 +1,26 @@
</span><ins>+&lt;!doctype html&gt;
+&lt;html&gt;
+&lt;head&gt;
+&lt;script src=&quot;../../http/tests/inspector/resources/inspector-test.js&quot;&gt;&lt;/script&gt;
+&lt;script&gt;
+function test()
+{
+    InspectorTest.suppressStackTraces = true;
+
+    let suite = InspectorTest.createAsyncSuite(&quot;UncaughtExceptionInsideTestCase&quot;);
+    suite.addTestCase({
+        name: &quot;CatchRejectedPromise&quot;,
+        description: &quot;window.reportUnhandledRejection should dump the unhandled exception and quit testing.&quot;,
+        test(resolve, reject) {
+            throw new Error(&quot;This promise is rejected with an Error object.&quot;);
+        }
+    })
+
+    suite.runTestCasesAndFinish();
+}
+&lt;/script&gt;
+&lt;/head&gt;
+&lt;body onload=&quot;runTest()&quot;&gt;
+&lt;p&gt;Test that the unhandled promise rejection hook will immediately terminate the test and print the associated exception and stack trace.&lt;/p&gt;
+&lt;/body&gt;
+&lt;/html&gt;
</ins></span></pre></div>
<a id="trunkLayoutTestsinspectorunittestsglobalsunhandledrejectionintestfunctionexpectedtxt"></a>
<div class="addfile"><h4>Added: trunk/LayoutTests/inspector/unit-tests/globals-unhandled-rejection-in-test-function-expected.txt (0 => 210367)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/LayoutTests/inspector/unit-tests/globals-unhandled-rejection-in-test-function-expected.txt                                (rev 0)
+++ trunk/LayoutTests/inspector/unit-tests/globals-unhandled-rejection-in-test-function-expected.txt        2017-01-05 20:07:44 UTC (rev 210367)
</span><span class="lines">@@ -0,0 +1,7 @@
</span><ins>+Test that the unhandled hook will immediately terminate the test and print the associated exception and stack trace.
+
+Unhandled promise rejection in inspector page: This is an exception thrown in the inspector page.
+
+Stack Trace: (suppressed)
+
+
</ins></span></pre></div>
<a id="trunkLayoutTestsinspectorunittestsglobalsunhandledrejectionintestfunctionhtml"></a>
<div class="addfile"><h4>Added: trunk/LayoutTests/inspector/unit-tests/globals-unhandled-rejection-in-test-function.html (0 => 210367)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/LayoutTests/inspector/unit-tests/globals-unhandled-rejection-in-test-function.html                                (rev 0)
+++ trunk/LayoutTests/inspector/unit-tests/globals-unhandled-rejection-in-test-function.html        2017-01-05 20:07:44 UTC (rev 210367)
</span><span class="lines">@@ -0,0 +1,17 @@
</span><ins>+&lt;!doctype html&gt;
+&lt;html&gt;
+&lt;head&gt;
+&lt;script src=&quot;../../http/tests/inspector/resources/inspector-test.js&quot;&gt;&lt;/script&gt;
+&lt;script&gt;
+function test()
+{
+    InspectorTest.suppressStackTraces = true;
+
+    reportUnhandledRejection(new Error(&quot;This is an exception thrown in the inspector page.&quot;));
+}
+&lt;/script&gt;
+&lt;/head&gt;
+&lt;body onload=&quot;runTest()&quot;&gt;
+&lt;p&gt;Test that the unhandled hook will immediately terminate the test and print the associated exception and stack trace.&lt;/p&gt;
+&lt;/body&gt;
+&lt;/html&gt;
</ins></span></pre></div>
<a id="trunkLayoutTestsinspectorunittestsglobalsunhandledrejectionintestsuiteexpectedtxt"></a>
<div class="addfile"><h4>Added: trunk/LayoutTests/inspector/unit-tests/globals-unhandled-rejection-in-test-suite-expected.txt (0 => 210367)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/LayoutTests/inspector/unit-tests/globals-unhandled-rejection-in-test-suite-expected.txt                                (rev 0)
+++ trunk/LayoutTests/inspector/unit-tests/globals-unhandled-rejection-in-test-suite-expected.txt        2017-01-05 20:07:44 UTC (rev 210367)
</span><span class="lines">@@ -0,0 +1,10 @@
</span><ins>+Test that the unhandled promise rejection hook will immediately terminate the test and print the associated exception and stack trace.
+
+
+== Running test suite: ReportUnhandledRejection
+-- Running test case: CatchRejectedPromise
+Unhandled promise rejection in inspector page: This promise is rejected with an Error object.
+
+Stack Trace: (suppressed)
+
+
</ins></span></pre></div>
<a id="trunkLayoutTestsinspectorunittestsglobalsunhandledrejectionintestsuitehtml"></a>
<div class="addfile"><h4>Added: trunk/LayoutTests/inspector/unit-tests/globals-unhandled-rejection-in-test-suite.html (0 => 210367)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/LayoutTests/inspector/unit-tests/globals-unhandled-rejection-in-test-suite.html                                (rev 0)
+++ trunk/LayoutTests/inspector/unit-tests/globals-unhandled-rejection-in-test-suite.html        2017-01-05 20:07:44 UTC (rev 210367)
</span><span class="lines">@@ -0,0 +1,27 @@
</span><ins>+&lt;!doctype html&gt;
+&lt;html&gt;
+&lt;head&gt;
+&lt;script src=&quot;../../http/tests/inspector/resources/inspector-test.js&quot;&gt;&lt;/script&gt;
+&lt;script&gt;
+function test()
+{
+    InspectorTest.suppressStackTraces = true;
+
+    let suite = InspectorTest.createAsyncSuite(&quot;ReportUnhandledRejection&quot;);
+    suite.addTestCase({
+        name: &quot;CatchRejectedPromise&quot;,
+        description: &quot;window.reportUnhandledRejection should dump the unhandled exception and quit testing.&quot;,
+        test(resolve, reject) {
+            let rejectedPromise = Promise.reject(new Error(&quot;This promise is rejected with an Error object.&quot;));
+            rejectedPromise.catch(reportUnhandledRejection);
+        }
+    })
+
+    suite.runTestCasesAndFinish();
+}
+&lt;/script&gt;
+&lt;/head&gt;
+&lt;body onload=&quot;runTest()&quot;&gt;
+&lt;p&gt;Test that the unhandled promise rejection hook will immediately terminate the test and print the associated exception and stack trace.&lt;/p&gt;
+&lt;/body&gt;
+&lt;/html&gt;
</ins></span></pre></div>
<a id="trunkLayoutTestsinspectorunittestsglobalsunhandledrejectionintimercallbackexpectedtxt"></a>
<div class="addfile"><h4>Added: trunk/LayoutTests/inspector/unit-tests/globals-unhandled-rejection-in-timer-callback-expected.txt (0 => 210367)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/LayoutTests/inspector/unit-tests/globals-unhandled-rejection-in-timer-callback-expected.txt                                (rev 0)
+++ trunk/LayoutTests/inspector/unit-tests/globals-unhandled-rejection-in-timer-callback-expected.txt        2017-01-05 20:07:44 UTC (rev 210367)
</span><span class="lines">@@ -0,0 +1,5 @@
</span><ins>+Test that the uncaught exception hook will immediately terminate the test and print the associated exception and stack trace.
+
+Unhandled promise rejection in inspector page: This is an exception thrown in the inspector page.
+
+
</ins></span></pre></div>
<a id="trunkLayoutTestsinspectorunittestsglobalsunhandledrejectionintimercallbackhtml"></a>
<div class="addfile"><h4>Added: trunk/LayoutTests/inspector/unit-tests/globals-unhandled-rejection-in-timer-callback.html (0 => 210367)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/LayoutTests/inspector/unit-tests/globals-unhandled-rejection-in-timer-callback.html                                (rev 0)
+++ trunk/LayoutTests/inspector/unit-tests/globals-unhandled-rejection-in-timer-callback.html        2017-01-05 20:07:44 UTC (rev 210367)
</span><span class="lines">@@ -0,0 +1,19 @@
</span><ins>+&lt;!doctype html&gt;
+&lt;html&gt;
+&lt;head&gt;
+&lt;script src=&quot;../../http/tests/inspector/resources/inspector-test.js&quot;&gt;&lt;/script&gt;
+&lt;script&gt;
+function test()
+{
+    InspectorTest.suppressStackTraces = true;
+
+    setTimeout(() =&gt; {
+        reportUnhandledRejection(new Error(&quot;This is an exception thrown in the inspector page.&quot;));
+    });
+}
+&lt;/script&gt;
+&lt;/head&gt;
+&lt;body onload=&quot;runTest()&quot;&gt;
+&lt;p&gt;Test that the uncaught exception hook will immediately terminate the test and print the associated exception and stack trace.&lt;/p&gt;
+&lt;/body&gt;
+&lt;/html&gt;
</ins></span></pre></div>
<a id="trunkLayoutTestsinspectorunittestssynctestsuiteexpectedtxt"></a>
<div class="modfile"><h4>Modified: trunk/LayoutTests/inspector/unit-tests/sync-test-suite-expected.txt (210366 => 210367)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/LayoutTests/inspector/unit-tests/sync-test-suite-expected.txt        2017-01-05 19:51:38 UTC (rev 210366)
+++ trunk/LayoutTests/inspector/unit-tests/sync-test-suite-expected.txt        2017-01-05 20:07:44 UTC (rev 210367)
</span><span class="lines">@@ -26,6 +26,7 @@
</span><span class="cx"> -- Running test case: DummyTest3
</span><span class="cx"> -- Running test case: FailingTest4
</span><span class="cx"> !! EXCEPTION: [object Object]
</span><ins>+Stack Trace: (suppressed)
</ins><span class="cx"> PASS: Return value of sequentialExecutionSuite.runTestCases() should be false when a test case fails.
</span><span class="cx"> PASS: sequentialExecutionSuite should have executed four tests.
</span><span class="cx"> PASS: sequentialExecutionSuite should have passed three tests.
</span><span class="lines">@@ -54,18 +55,20 @@
</span><span class="cx"> == Running test suite: SyncTestSuite.SetupException
</span><span class="cx"> -- Running test setup.
</span><span class="cx"> !! EXCEPTION: 
</span><ins>+Stack Trace: (suppressed)
</ins><span class="cx"> 
</span><span class="cx"> == Running test suite: SyncTestSuite.SetupFailure
</span><span class="cx"> -- Running test setup.
</span><del>-!! EXCEPTION
</del><ins>+!! SETUP FAILED
</ins><span class="cx"> 
</span><span class="cx"> == Running test suite: SyncTestSuite.TeardownException
</span><span class="cx"> -- Running test case: TestWithExceptionDuringTeardown
</span><span class="cx"> -- Running test teardown.
</span><span class="cx"> !! EXCEPTION: 
</span><ins>+Stack Trace: (suppressed)
</ins><span class="cx"> 
</span><span class="cx"> == Running test suite: SyncTestSuite.TeardownFailure
</span><span class="cx"> -- Running test case: TestWithExceptionDuringTeardown
</span><span class="cx"> -- Running test teardown.
</span><del>-!! EXCEPTION:
</del><ins>+!! TEARDOWN FAILED
</ins><span class="cx"> 
</span></span></pre></div>
<a id="trunkLayoutTestsinspectorunittestssynctestsuitehtml"></a>
<div class="modfile"><h4>Modified: trunk/LayoutTests/inspector/unit-tests/sync-test-suite.html (210366 => 210367)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/LayoutTests/inspector/unit-tests/sync-test-suite.html        2017-01-05 19:51:38 UTC (rev 210366)
+++ trunk/LayoutTests/inspector/unit-tests/sync-test-suite.html        2017-01-05 20:07:44 UTC (rev 210367)
</span><span class="lines">@@ -5,6 +5,8 @@
</span><span class="cx"> &lt;script&gt;
</span><span class="cx"> function test()
</span><span class="cx"> {
</span><ins>+    ProtocolTest.suppressStackTraces = true;
+
</ins><span class="cx">     try {
</span><span class="cx">         let result = new SyncTestSuite(this);
</span><span class="cx">         ProtocolTest.log(&quot;FAIL: instantiating SyncTestSuite requires name argument.&quot;);
</span></span></pre></div>
<a id="trunkSourceWebInspectorUIChangeLog"></a>
<div class="modfile"><h4>Modified: trunk/Source/WebInspectorUI/ChangeLog (210366 => 210367)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/WebInspectorUI/ChangeLog        2017-01-05 19:51:38 UTC (rev 210366)
+++ trunk/Source/WebInspectorUI/ChangeLog        2017-01-05 20:07:44 UTC (rev 210367)
</span><span class="lines">@@ -1,3 +1,62 @@
</span><ins>+2017-01-04  Brian Burg  &lt;bburg@apple.com&gt;
+
+        Web Inspector: Test.html should support globals reportInternalError, reportUnhandledRejection, reportUncaughtException
+        https://bugs.webkit.org/show_bug.cgi?id=161358
+        &lt;rdar://problem/28066446&gt;
+
+        Reviewed by Joseph Pecoraro.
+
+        We have a hodgepodge of redundant code that reports uncaught exceptions in the inspector page.
+        There is better handling of uncaught exceptions in the inspected page, such as including stack traces.
+
+        This patch consolidates a lot of this code and makes it possible to report
+        unhandled promise rejections, top-level uncaught exceptions, and exceptions
+        caught in a try-catch block. The formatting and sanitization code for all of
+        these things is shared and consistent. Finally, some tests have been added to
+        catch regressions in unhandled rejection/uncaught exception reporting.
+
+        * UserInterface/Test/FrontendTestHarness.js:
+        (FrontendTestHarness): Explicitly set initial flag state here so it's easy to find all flags.
+
+        (FrontendTestHarness.prototype.redirectConsoleToTestOutput):
+        Extract this code to sanitize stack frames and put it in TestHarness. It is used
+        by other methods that need to print stack frames.
+
+        (FrontendTestHarness.prototype.reportUnhandledRejection):
+        (FrontendTestHarness.prototype.reportUncaughtException):
+        Added. Sanitize stack trace data so it is deterministic. Log the message to the
+        original window.console but don't exit early. Sometimes the test page is not
+        fully loaded if we throw an exception quite early in the test() method, and there's
+        no harm in not early returning. If we do early return in this case, then a test that
+        uses reportUncaughtException on purpose may not complete because the call to completeTest()
+        would be skipped by returning early.
+
+        (FrontendTestHarness.prototype.reportUncaughtExceptionFromEvent):
+        Renamed from reportUncaughtException since the signature of that method suggests
+        it should have a single exception argument rather than lots of data arguments.
+
+        * UserInterface/Test/Test.js: Add globals.
+
+        * UserInterface/Test/TestHarness.js:
+        (TestHarness): Document class flags.
+        (TestHarness.sanitizeURL):
+        (TestHarness.sanitizeStackFrame):
+        (TestHarness.prototype.sanitizeStack):
+        Extract this code from other parts of the test harness. Make sanitizeStack
+        an instance method so that there is only one place that needs to check the
+        'suppressStackTraces' flag.
+
+        * UserInterface/Test/TestSuite.js:
+        (TestSuite.prototype.logThrownObject):
+        (TestSuite):
+        (AsyncTestSuite.prototype.runTestCases):
+        (AsyncTestSuite):
+        (SyncTestSuite.prototype.runTestCases):
+        (SyncTestSuite):
+        (TestSuite.messageFromThrownObject): Deleted.
+        Inline some helpers with only one use-site and consolidate redundant code
+        for adding an exception and message to the test results.
+
</ins><span class="cx"> 2017-01-04  Nikita Vasilyev  &lt;nvasilyev@apple.com&gt;
</span><span class="cx"> 
</span><span class="cx">         Web Inspector: application cache details not shown in Storage Tab
</span></span></pre></div>
<a id="trunkSourceWebInspectorUIUserInterfaceTestFrontendTestHarnessjs"></a>
<div class="modfile"><h4>Modified: trunk/Source/WebInspectorUI/UserInterface/Test/FrontendTestHarness.js (210366 => 210367)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/WebInspectorUI/UserInterface/Test/FrontendTestHarness.js        2017-01-05 19:51:38 UTC (rev 210366)
+++ trunk/Source/WebInspectorUI/UserInterface/Test/FrontendTestHarness.js        2017-01-05 20:07:44 UTC (rev 210367)
</span><span class="lines">@@ -1,5 +1,5 @@
</span><span class="cx"> /*
</span><del>- * Copyright (C) 2013-2015 Apple Inc. All rights reserved.
</del><ins>+ * Copyright (C) 2013-2016 Apple Inc. All rights reserved.
</ins><span class="cx">  *
</span><span class="cx">  * Redistribution and use in source and binary forms, with or without
</span><span class="cx">  * modification, are permitted provided that the following conditions
</span><span class="lines">@@ -31,6 +31,9 @@
</span><span class="cx"> 
</span><span class="cx">         this._results = [];
</span><span class="cx">         this._shouldResendResults = true;
</span><ins>+
+        // Options that are set per-test for debugging purposes.
+        this.dumpActivityToSystemConsole = false;
</ins><span class="cx">     }
</span><span class="cx"> 
</span><span class="cx">     // TestHarness Overrides
</span><span class="lines">@@ -151,25 +154,7 @@
</span><span class="cx">                 } catch (e) {
</span><span class="cx">                     // Skip the first frame which is added by this function.
</span><span class="cx">                     let frames = e.stack.split(&quot;\n&quot;).slice(1);
</span><del>-                    let sanitizedFrames = frames.map((frame, i) =&gt; {
-                        // Most frames are of the form &quot;functionName@file:///foo/bar/File.js:345&quot;.
-                        // But, some frames do not have a functionName. Get rid of the file path.
-                        let nameAndURLSeparator = frame.indexOf(&quot;@&quot;);
-                        let frameName = (nameAndURLSeparator &gt; 0) ? frame.substr(0, nameAndURLSeparator) : &quot;(anonymous)&quot;;
-
-                        let lastPathSeparator = Math.max(frame.lastIndexOf(&quot;/&quot;), frame.lastIndexOf(&quot;\\&quot;));
-                        let frameLocation = (lastPathSeparator &gt; 0) ? frame.substr(lastPathSeparator + 1) : frame;
-                        if (!frameLocation.length)
-                            frameLocation = &quot;unknown&quot;;
-
-                        // Clean up the location so it is bracketed or in parenthesis.
-                        if (frame.indexOf(&quot;[native code]&quot;) !== -1)
-                            frameLocation = &quot;[native code]&quot;;
-                        else
-                            frameLocation = &quot;(&quot; + frameLocation + &quot;)&quot;;
-
-                        return `#${i}: ${frameName} ${frameLocation}`;
-                    });
</del><ins>+                    let sanitizedFrames = frames.map(TestHarness.sanitizeStackFrame);
</ins><span class="cx">                     self.addResult(&quot;TRACE: &quot; + Array.from(arguments).join(&quot; &quot;));
</span><span class="cx">                     self.addResult(sanitizedFrames.join(&quot;\n&quot;));
</span><span class="cx">                 }
</span><span class="lines">@@ -189,23 +174,63 @@
</span><span class="cx">         window.console = redirectedMethods;
</span><span class="cx">     }
</span><span class="cx"> 
</span><del>-    reportUncaughtException(message, url, lineNumber, columnNumber)
</del><ins>+    reportUnhandledRejection(error)
</ins><span class="cx">     {
</span><del>-        let result = `Uncaught exception in inspector page: ${message} [${url}:${lineNumber}:${columnNumber}]`;
</del><ins>+        let message = error.message;
+        let stack = error.stack;
+        let result = `Unhandled promise rejection in inspector page: ${message}\n`;
+        if (stack) {
+            let sanitizedStack = this.sanitizeStack(stack);
+            result += `\nStack Trace: ${sanitizedStack}\n`;
+        }
</ins><span class="cx"> 
</span><span class="cx">         // If the connection to the test page is not set up, then just dump to console and give up.
</span><span class="cx">         // Errors encountered this early can be debugged by loading Test.html in a normal browser page.
</span><del>-        if (this._originalConsole &amp;&amp; !this._testPageHasLoaded()) {
</del><ins>+        if (this._originalConsole &amp;&amp; !this._testPageHasLoaded())
</ins><span class="cx">             this._originalConsole[&quot;error&quot;](result);
</span><del>-            return false;
-        }
</del><span class="cx"> 
</span><span class="cx">         this.addResult(result);
</span><span class="cx">         this.completeTest();
</span><ins>+
</ins><span class="cx">         // Stop default handler so we can empty InspectorBackend's message queue.
</span><span class="cx">         return true;
</span><span class="cx">     }
</span><span class="cx"> 
</span><ins>+    reportUncaughtExceptionFromEvent(message, url, lineNumber, columnNumber)
+    {
+        // An exception thrown from a timer callback does not report a URL.
+        if (url === &quot;undefined&quot;)
+            url = &quot;global&quot;;
+
+        return this.reportUncaughtException({message, url, lineNumber, columnNumber});
+    }
+
+    reportUncaughtException({message, url, lineNumber, columnNumber, stack, code})
+    {
+        let result;
+        let sanitizedURL = TestHarness.sanitizeURL(url);
+        let sanitizedStack = this.sanitizeStack(stack);
+        if (url || lineNumber || columnNumber)
+            result = `Uncaught exception in Inspector page: ${message} [${sanitizedURL}:${lineNumber}:${columnNumber}]\n`;
+        else
+            result = `Uncaught exception in Inspector page: ${message}\n`;
+
+        if (stack)
+            result += `\nStack Trace:\n${sanitizedStack}\n`;
+        if (code)
+            result += `\nEvaluated Code:\n${code}`;
+
+        // If the connection to the test page is not set up, then just dump to console and give up.
+        // Errors encountered this early can be debugged by loading Test.html in a normal browser page.
+        if (this._originalConsole &amp;&amp; !this._testPageHasLoaded())
+            this._originalConsole[&quot;error&quot;](result);
+
+        this.addResult(result);
+        this.completeTest();
+        // Stop default handler so we can empty InspectorBackend's message queue.
+        return true;
+    }
+
</ins><span class="cx">     // Private
</span><span class="cx"> 
</span><span class="cx">     _testPageHasLoaded()
</span></span></pre></div>
<a id="trunkSourceWebInspectorUIUserInterfaceTestTestjs"></a>
<div class="modfile"><h4>Modified: trunk/Source/WebInspectorUI/UserInterface/Test/Test.js (210366 => 210367)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/WebInspectorUI/UserInterface/Test/Test.js        2017-01-05 19:51:38 UTC (rev 210366)
+++ trunk/Source/WebInspectorUI/UserInterface/Test/Test.js        2017-01-05 20:07:44 UTC (rev 210367)
</span><span class="lines">@@ -115,3 +115,6 @@
</span><span class="cx"> InspectorTest.redirectConsoleToTestOutput();
</span><span class="cx"> 
</span><span class="cx"> WebInspector.reportInternalError = (e) =&gt; { console.error(e); }
</span><ins>+
+window.reportUnhandledRejection = InspectorTest.reportUnhandledRejection.bind(InspectorTest);
+window.onerror = InspectorTest.reportUncaughtExceptionFromEvent.bind(InspectorTest);
</ins></span></pre></div>
<a id="trunkSourceWebInspectorUIUserInterfaceTestTestHarnessjs"></a>
<div class="modfile"><h4>Modified: trunk/Source/WebInspectorUI/UserInterface/Test/TestHarness.js (210366 => 210367)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/WebInspectorUI/UserInterface/Test/TestHarness.js        2017-01-05 19:51:38 UTC (rev 210366)
+++ trunk/Source/WebInspectorUI/UserInterface/Test/TestHarness.js        2017-01-05 20:07:44 UTC (rev 210367)
</span><span class="lines">@@ -1,5 +1,5 @@
</span><span class="cx"> /*
</span><del>- * Copyright (C) 2015 Apple Inc. All rights reserved.
</del><ins>+ * Copyright (C) 2015, 2016 Apple Inc. All rights reserved.
</ins><span class="cx">  *
</span><span class="cx">  * Redistribution and use in source and binary forms, with or without
</span><span class="cx">  * modification, are permitted provided that the following conditions
</span><span class="lines">@@ -32,6 +32,12 @@
</span><span class="cx">         this._logCount = 0;
</span><span class="cx">         this._failureObjects = new Map;
</span><span class="cx">         this._failureObjectIdentifier = 1;
</span><ins>+
+        // Options that are set per-test for debugging purposes.
+        this.forceDebugLogging = false;
+
+        // Options that are set per-test to ensure deterministic output.
+        this.suppressStackTraces = false;
</ins><span class="cx">     }
</span><span class="cx"> 
</span><span class="cx">     completeTest()
</span><span class="lines">@@ -183,6 +189,55 @@
</span><span class="cx">         return (typeof message !== &quot;string&quot;) ? JSON.stringify(message) : message;
</span><span class="cx">     }
</span><span class="cx"> 
</span><ins>+    static sanitizeURL(url)
+    {
+        if (!url)
+            return &quot;(unknown)&quot;;
+
+        let lastPathSeparator = Math.max(url.lastIndexOf(&quot;/&quot;), url.lastIndexOf(&quot;\\&quot;));
+        let location = (lastPathSeparator &gt; 0) ? url.substr(lastPathSeparator + 1) : url;
+        if (!location.length)
+            location = &quot;(unknown)&quot;;
+
+        // Clean up the location so it is bracketed or in parenthesis.
+        if (url.indexOf(&quot;[native code]&quot;) !== -1)
+            location = &quot;[native code]&quot;;
+
+        return location;
+    }
+
+    static sanitizeStackFrame(frame, i)
+    {
+        // Most frames are of the form &quot;functionName@file:///foo/bar/File.js:345&quot;.
+        // But, some frames do not have a functionName. Get rid of the file path.
+        let nameAndURLSeparator = frame.indexOf(&quot;@&quot;);
+        let frameName = (nameAndURLSeparator &gt; 0) ? frame.substr(0, nameAndURLSeparator) : &quot;(anonymous)&quot;;
+
+        let lastPathSeparator = Math.max(frame.lastIndexOf(&quot;/&quot;), frame.lastIndexOf(&quot;\\&quot;));
+        let frameLocation = (lastPathSeparator &gt; 0) ? frame.substr(lastPathSeparator + 1) : frame;
+        if (!frameLocation.length)
+            frameLocation = &quot;unknown&quot;;
+
+        // Clean up the location so it is bracketed or in parenthesis.
+        if (frame.indexOf(&quot;[native code]&quot;) !== -1)
+            frameLocation = &quot;[native code]&quot;;
+        else
+            frameLocation = &quot;(&quot; + frameLocation + &quot;)&quot;;
+
+        return `#${i}: ${frameName} ${frameLocation}`;
+    }
+
+    sanitizeStack(stack)
+    {
+        if (this.suppressStackTraces)
+            return &quot;(suppressed)&quot;;
+
+        if (!stack || typeof stack !== &quot;string&quot;)
+            return &quot;(unknown)&quot;;
+
+        return stack.split(&quot;\n&quot;).map(TestHarness.sanitizeStackFrame).join(&quot;\n&quot;);
+    }
+
</ins><span class="cx">     // Private
</span><span class="cx"> 
</span><span class="cx">     _expect(type, condition, message, ...values)
</span></span></pre></div>
<a id="trunkSourceWebInspectorUIUserInterfaceTestTestSuitejs"></a>
<div class="modfile"><h4>Modified: trunk/Source/WebInspectorUI/UserInterface/Test/TestSuite.js (210366 => 210367)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/WebInspectorUI/UserInterface/Test/TestSuite.js        2017-01-05 19:51:38 UTC (rev 210366)
+++ trunk/Source/WebInspectorUI/UserInterface/Test/TestSuite.js        2017-01-05 20:07:44 UTC (rev 210367)
</span><span class="lines">@@ -87,16 +87,28 @@
</span><span class="cx">         this.testcases.push(testcase);
</span><span class="cx">     }
</span><span class="cx"> 
</span><del>-    static messageFromThrownObject(e)
</del><ins>+    // Protected
+
+    logThrownObject(e)
</ins><span class="cx">     {
</span><span class="cx">         let message = e;
</span><del>-        if (e instanceof Error)
</del><ins>+        let stack = &quot;(unknown)&quot;;
+        if (e instanceof Error) {
</ins><span class="cx">             message = e.message;
</span><ins>+            if (e.stack)
+                stack = e.stack;
+        }
</ins><span class="cx"> 
</span><span class="cx">         if (typeof message !== &quot;string&quot;)
</span><span class="cx">             message = JSON.stringify(message);
</span><span class="cx"> 
</span><del>-        return message;
</del><ins>+        let sanitizedStack = this._harness.sanitizeStack(stack);
+
+        let result = `!! EXCEPTION: ${message}`;
+        if (stack)
+            result += `\nStack Trace: ${sanitizedStack}`;
+
+        this._harness.log(result);
</ins><span class="cx">     }
</span><span class="cx"> };
</span><span class="cx"> 
</span><span class="lines">@@ -154,8 +166,7 @@
</span><span class="cx"> 
</span><span class="cx">         return result.catch((e) =&gt; {
</span><span class="cx">             this.failCount++;
</span><del>-            let message = TestSuite.messageFromThrownObject(e);
-            this._harness.log(`!! EXCEPTION: ${message}`);
</del><ins>+            this.logThrownObject(e);
</ins><span class="cx"> 
</span><span class="cx">             throw e; // Reject this promise by re-throwing the error.
</span><span class="cx">         });
</span><span class="lines">@@ -196,12 +207,11 @@
</span><span class="cx">                 try {
</span><span class="cx">                     let result = testcase.setup.call(null);
</span><span class="cx">                     if (result === false) {
</span><del>-                        this._harness.log(&quot;!! EXCEPTION&quot;);
</del><ins>+                        this._harness.log(&quot;!! SETUP FAILED&quot;);
</ins><span class="cx">                         return false;
</span><span class="cx">                     }
</span><span class="cx">                 } catch (e) {
</span><del>-                    let message = TestSuite.messageFromThrownObject(e);
-                    this._harness.log(`!! EXCEPTION: ${message}`);
</del><ins>+                    this.logThrownObject(e);
</ins><span class="cx">                     return false;
</span><span class="cx">                 }
</span><span class="cx">             }
</span><span class="lines">@@ -216,8 +226,7 @@
</span><span class="cx">                 }
</span><span class="cx">             } catch (e) {
</span><span class="cx">                 this.failCount++;
</span><del>-                let message = TestSuite.messageFromThrownObject(e);
-                this._harness.log(`!! EXCEPTION: ${message}`);
</del><ins>+                this.logThrownObject(e);
</ins><span class="cx">                 return false;
</span><span class="cx">             }
</span><span class="cx"> 
</span><span class="lines">@@ -227,12 +236,11 @@
</span><span class="cx">                 try {
</span><span class="cx">                     let result = testcase.teardown.call(null);
</span><span class="cx">                     if (result === false) {
</span><del>-                        this._harness.log(&quot;!! EXCEPTION:&quot;);
</del><ins>+                        this._harness.log(&quot;!! TEARDOWN FAILED&quot;);
</ins><span class="cx">                         return false;
</span><span class="cx">                     }
</span><span class="cx">                 } catch (e) {
</span><del>-                    let message = TestSuite.messageFromThrownObject(e);
-                    this._harness.log(`!! EXCEPTION: ${message}`);
</del><ins>+                    this.logThrownObject(e);
</ins><span class="cx">                     return false;
</span><span class="cx">                 }
</span><span class="cx">             }
</span></span></pre>
</div>
</div>

</body>
</html>