<!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>[202875] trunk/Source/WebInspectorUI</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/202875">202875</a></dd>
<dt>Author</dt> <dd>bburg@apple.com</dd>
<dt>Date</dt> <dd>2016-07-06 13:22:46 -0700 (Wed, 06 Jul 2016)</dd>
</dl>

<h3>Log Message</h3>
<pre>Web Inspector: Uncaught Exception reporter should include the currently dispatching protocol event or request/response if applicable
https://bugs.webkit.org/show_bug.cgi?id=159320
&lt;rdar://problem/27117754&gt;

Reviewed by Timothy Hatcher and Joseph Pecoraro.

Keep track of the currently dispatched protocol response or protocol event and make
them available to the uncaught exception reporter. If an internal exception is reported
while dispatching an event or response, dump the protocol message(s) into the pre-filled
bug report.

* UserInterface/Debug/UncaughtExceptionReporter.js:
(stringifyAndTruncateObject): Added.
Rearrange the code that generates the pre-filled report so it's easier to add optional sections.

* UserInterface/Protocol/InspectorBackend.js:
(InspectorBackendClass):
(InspectorBackendClass.prototype.get currentDispatchState): Expose the dispatching state.
(InspectorBackendClass.prototype._sendCommandToBackendWithCallback):
(InspectorBackendClass.prototype._sendCommandToBackendExpectingPromise):
Store the originating command request with the pendingResponse data so that we can examine
the originating request if the response causes an error. This will cause request message objects
to be garbage-collected after their responses are dispatched rather than when the request is sent.
But, I don't forsee this being a performance problem since we should always get a command response
and pending command responses do not typically accumulate except when the inspector first loads.

(InspectorBackendClass.prototype._dispatchResponse): Save the response being dispatched.
(InspectorBackendClass.prototype._dispatchResponseToCallback): Simplify exception reporting.
(InspectorBackendClass.prototype._dispatchEvent): Save the event being dispatched.</pre>

<h3>Modified Paths</h3>
<ul>
<li><a href="#trunkSourceWebInspectorUIChangeLog">trunk/Source/WebInspectorUI/ChangeLog</a></li>
<li><a href="#trunkSourceWebInspectorUIUserInterfaceDebugUncaughtExceptionReporterjs">trunk/Source/WebInspectorUI/UserInterface/Debug/UncaughtExceptionReporter.js</a></li>
<li><a href="#trunkSourceWebInspectorUIUserInterfaceProtocolInspectorBackendjs">trunk/Source/WebInspectorUI/UserInterface/Protocol/InspectorBackend.js</a></li>
</ul>

</div>
<div id="patch">
<h3>Diff</h3>
<a id="trunkSourceWebInspectorUIChangeLog"></a>
<div class="modfile"><h4>Modified: trunk/Source/WebInspectorUI/ChangeLog (202874 => 202875)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/WebInspectorUI/ChangeLog        2016-07-06 20:14:38 UTC (rev 202874)
+++ trunk/Source/WebInspectorUI/ChangeLog        2016-07-06 20:22:46 UTC (rev 202875)
</span><span class="lines">@@ -1,3 +1,35 @@
</span><ins>+2016-07-06  Brian Burg  &lt;bburg@apple.com&gt;
+
+        Web Inspector: Uncaught Exception reporter should include the currently dispatching protocol event or request/response if applicable
+        https://bugs.webkit.org/show_bug.cgi?id=159320
+        &lt;rdar://problem/27117754&gt;
+
+        Reviewed by Timothy Hatcher and Joseph Pecoraro.
+
+        Keep track of the currently dispatched protocol response or protocol event and make
+        them available to the uncaught exception reporter. If an internal exception is reported
+        while dispatching an event or response, dump the protocol message(s) into the pre-filled
+        bug report.
+
+        * UserInterface/Debug/UncaughtExceptionReporter.js:
+        (stringifyAndTruncateObject): Added.
+        Rearrange the code that generates the pre-filled report so it's easier to add optional sections.
+
+        * UserInterface/Protocol/InspectorBackend.js:
+        (InspectorBackendClass):
+        (InspectorBackendClass.prototype.get currentDispatchState): Expose the dispatching state.
+        (InspectorBackendClass.prototype._sendCommandToBackendWithCallback):
+        (InspectorBackendClass.prototype._sendCommandToBackendExpectingPromise):
+        Store the originating command request with the pendingResponse data so that we can examine
+        the originating request if the response causes an error. This will cause request message objects
+        to be garbage-collected after their responses are dispatched rather than when the request is sent.
+        But, I don't forsee this being a performance problem since we should always get a command response
+        and pending command responses do not typically accumulate except when the inspector first loads.
+
+        (InspectorBackendClass.prototype._dispatchResponse): Save the response being dispatched.
+        (InspectorBackendClass.prototype._dispatchResponseToCallback): Simplify exception reporting.
+        (InspectorBackendClass.prototype._dispatchEvent): Save the event being dispatched.
+
</ins><span class="cx"> 2016-07-05  Timothy Hatcher  &lt;timothy@apple.com&gt;
</span><span class="cx"> 
</span><span class="cx">         Web Inspector: Switch last uses of -webkit-linear-gradient() to linear-gradient()
</span></span></pre></div>
<a id="trunkSourceWebInspectorUIUserInterfaceDebugUncaughtExceptionReporterjs"></a>
<div class="modfile"><h4>Modified: trunk/Source/WebInspectorUI/UserInterface/Debug/UncaughtExceptionReporter.js (202874 => 202875)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/WebInspectorUI/UserInterface/Debug/UncaughtExceptionReporter.js        2016-07-06 20:14:38 UTC (rev 202874)
+++ trunk/Source/WebInspectorUI/UserInterface/Debug/UncaughtExceptionReporter.js        2016-07-06 20:22:46 UTC (rev 202875)
</span><span class="lines">@@ -181,16 +181,46 @@
</span><span class="cx">         inspectedPageURL = WebInspector.frameResourceManager.mainFrame.url;
</span><span class="cx">     } catch (e) { }
</span><span class="cx"> 
</span><ins>+    let topLevelItems = [
+        `Inspected URL:        ${inspectedPageURL || &quot;(unknown)&quot;}`,
+        `Loading completed:    ${!!loadCompleted}`,
+        `Frontend User Agent:  ${window.navigator.userAgent}`,
+    ];
+
+    function stringifyAndTruncateObject(object) {
+        let string = JSON.stringify(object);
+        return string.length &gt; 500 ? string.substr(0, 500) + &quot;…&quot; : string;
+    }
+
+    if (InspectorBackend &amp;&amp; InspectorBackend.currentDispatchState) {
+        let state = InspectorBackend.currentDispatchState;
+        if (state.event) {
+            topLevelItems.push(&quot;Dispatch Source:      Protocol Event&quot;);
+            topLevelItems.push(&quot;&quot;);
+            topLevelItems.push(&quot;Protocol Event:&quot;);
+            topLevelItems.push(stringifyAndTruncateObject(state.event));
+        }
+        if (state.response) {
+            topLevelItems.push(&quot;Dispatch Source:      Protocol Command Response&quot;);
+            topLevelItems.push(&quot;&quot;);
+            topLevelItems.push(&quot;Protocol Command Response:&quot;);
+            topLevelItems.push(stringifyAndTruncateObject(state.response));
+        }
+        if (state.request) {
+            topLevelItems.push(&quot;&quot;);
+            topLevelItems.push(&quot;Protocol Command Request:&quot;);
+            topLevelItems.push(stringifyAndTruncateObject(state.request));
+        }
+    }
+
</ins><span class="cx">     let formattedErrorDetails = window.__uncaughtExceptions.map((entry) =&gt; formattedEntry(entry));
</span><span class="cx">     let detailsForBugReport = formattedErrorDetails.map((line) =&gt; ` - ${line}`).join(&quot;\n&quot;);
</span><ins>+    topLevelItems.push(&quot;&quot;);
+    topLevelItems.push(&quot;Uncaught Exceptions:&quot;);
+    topLevelItems.push(detailsForBugReport);
+
</ins><span class="cx">     let encodedBugDescription = encodeURIComponent(`-------
</span><del>-Auto-generated details:
-
-Inspected URL:        ${inspectedPageURL || &quot;(unknown)&quot;}
-Loading completed:    ${!!loadCompleted}
-Frontend User Agent:  ${window.navigator.userAgent}
-Uncaught exceptions:
-${detailsForBugReport}
</del><ins>+${topLevelItems.join(&quot;\n&quot;)}
</ins><span class="cx"> -------
</span><span class="cx"> 
</span><span class="cx"> * STEPS TO REPRODUCE
</span></span></pre></div>
<a id="trunkSourceWebInspectorUIUserInterfaceProtocolInspectorBackendjs"></a>
<div class="modfile"><h4>Modified: trunk/Source/WebInspectorUI/UserInterface/Protocol/InspectorBackend.js (202874 => 202875)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/WebInspectorUI/UserInterface/Protocol/InspectorBackend.js        2016-07-06 20:14:38 UTC (rev 202874)
+++ trunk/Source/WebInspectorUI/UserInterface/Protocol/InspectorBackend.js        2016-07-06 20:22:46 UTC (rev 202875)
</span><span class="lines">@@ -43,6 +43,12 @@
</span><span class="cx">         this._defaultTracer = new WebInspector.LoggingProtocolTracer;
</span><span class="cx">         this._activeTracers = [this._defaultTracer];
</span><span class="cx"> 
</span><ins>+        this._currentDispatchState = {
+            event: null,
+            request: null,
+            response: null,
+        };
+
</ins><span class="cx">         this._dumpInspectorTimeStats = false;
</span><span class="cx"> 
</span><span class="cx">         let setting = WebInspector.autoLogProtocolMessagesSetting = new WebInspector.Setting(&quot;auto-collect-protocol-messages&quot;, false);
</span><span class="lines">@@ -205,7 +211,7 @@
</span><span class="cx">         if (!isEmptyObject(parameters))
</span><span class="cx">             messageObject[&quot;params&quot;] = parameters;
</span><span class="cx"> 
</span><del>-        let responseData = {command, callback};
</del><ins>+        let responseData = {command, request: messageObject, callback};
</ins><span class="cx"> 
</span><span class="cx">         if (this.activeTracer)
</span><span class="cx">             responseData.sendRequestTimestamp = timestamp();
</span><span class="lines">@@ -226,7 +232,7 @@
</span><span class="cx">         if (!isEmptyObject(parameters))
</span><span class="cx">             messageObject[&quot;params&quot;] = parameters;
</span><span class="cx"> 
</span><del>-        let responseData = {command};
</del><ins>+        let responseData = {command, request: messageObject};
</ins><span class="cx"> 
</span><span class="cx">         if (this.activeTracer)
</span><span class="cx">             responseData.sendRequestTimestamp = timestamp();
</span><span class="lines">@@ -262,19 +268,25 @@
</span><span class="cx">         console.assert(this._pendingResponses.has(sequenceId), sequenceId, this._pendingResponses);
</span><span class="cx"> 
</span><span class="cx">         let responseData = this._pendingResponses.take(sequenceId) || {};
</span><del>-        let {command, callback, promise} = responseData;
</del><ins>+        let {request, command, callback, promise} = responseData;
</ins><span class="cx"> 
</span><span class="cx">         let processingStartTimestamp = timestamp();
</span><span class="cx">         for (let tracer of this.activeTracers)
</span><span class="cx">             tracer.logWillHandleResponse(messageObject);
</span><span class="cx"> 
</span><ins>+        this._currentDispatchState.request = request;
+        this._currentDispatchState.response = messageObject;
+
</ins><span class="cx">         if (typeof callback === &quot;function&quot;)
</span><del>-            this._dispatchResponseToCallback(command, messageObject, callback);
</del><ins>+            this._dispatchResponseToCallback(command, request, messageObject, callback);
</ins><span class="cx">         else if (typeof promise === &quot;object&quot;)
</span><span class="cx">             this._dispatchResponseToPromise(command, messageObject, promise);
</span><span class="cx">         else
</span><span class="cx">             console.error(&quot;Received a command response without a corresponding callback or promise.&quot;, messageObject, command);
</span><span class="cx"> 
</span><ins>+        this._currentDispatchState.request = null;
+        this._currentDispatchState.response = null;
+
</ins><span class="cx">         let processingTime = (timestamp() - processingStartTimestamp).toFixed(3);
</span><span class="cx">         let roundTripTime = (processingStartTimestamp - responseData.sendRequestTimestamp).toFixed(3);
</span><span class="cx"> 
</span><span class="lines">@@ -285,23 +297,20 @@
</span><span class="cx">             this._flushPendingScripts();
</span><span class="cx">     }
</span><span class="cx"> 
</span><del>-    _dispatchResponseToCallback(command, messageObject, callback)
</del><ins>+    _dispatchResponseToCallback(command, requestObject, responseObject, callback)
</ins><span class="cx">     {
</span><span class="cx">         let callbackArguments = [];
</span><del>-        callbackArguments.push(messageObject[&quot;error&quot;] ? messageObject[&quot;error&quot;].message : null);
</del><ins>+        callbackArguments.push(responseObject[&quot;error&quot;] ? responseObject[&quot;error&quot;].message : null);
</ins><span class="cx"> 
</span><del>-        if (messageObject[&quot;result&quot;]) {
-            for (var parameterName of command.replySignature)
-                callbackArguments.push(messageObject[&quot;result&quot;][parameterName]);
</del><ins>+        if (responseObject[&quot;result&quot;]) {
+            for (let parameterName of command.replySignature)
+                callbackArguments.push(responseObject[&quot;result&quot;][parameterName]);
</ins><span class="cx">         }
</span><span class="cx"> 
</span><span class="cx">         try {
</span><span class="cx">             callback.apply(null, callbackArguments);
</span><span class="cx">         } catch (e) {
</span><del>-            WebInspector.reportInternalError(e, {
-                &quot;cause&quot;: `An uncaught exception was thrown while dispatching response callback for command ${command.qualifiedName}.`,
-                &quot;protocol-message&quot;: JSON.stringify(messageObject),
-            });
</del><ins>+            WebInspector.reportInternalError(e, {&quot;cause&quot;: `An uncaught exception was thrown while dispatching response callback for command ${command.qualifiedName}.`});
</ins><span class="cx">         }
</span><span class="cx">     }
</span><span class="cx"> 
</span><span class="lines">@@ -343,6 +352,8 @@
</span><span class="cx">         for (let tracer of this.activeTracers)
</span><span class="cx">             tracer.logWillHandleEvent(messageObject);
</span><span class="cx"> 
</span><ins>+        this._currentDispatchState.event = messageObject;
+
</ins><span class="cx">         try {
</span><span class="cx">             agent.dispatchEvent(eventName, eventArguments);
</span><span class="cx">         } catch (e) {
</span><span class="lines">@@ -349,12 +360,11 @@
</span><span class="cx">             for (let tracer of this.activeTracers)
</span><span class="cx">                 tracer.logFrontendException(messageObject, e);
</span><span class="cx"> 
</span><del>-            WebInspector.reportInternalError(e, {
-                &quot;cause&quot;: `An uncaught exception was thrown while handling event: ${qualifiedName}`,
-                &quot;protocol-message&quot;: JSON.stringify(messageObject),
-            });
</del><ins>+            WebInspector.reportInternalError(e, {&quot;cause&quot;: `An uncaught exception was thrown while handling event: ${qualifiedName}`});
</ins><span class="cx">         }
</span><span class="cx"> 
</span><ins>+        this._currentDispatchState.event = null;
+
</ins><span class="cx">         let processingDuration = (timestamp() - processingStartTimestamp).toFixed(3);
</span><span class="cx">         for (let tracer of this.activeTracers)
</span><span class="cx">             tracer.logDidHandleEvent(messageObject, {dispatch: processingDuration});
</span><span class="lines">@@ -400,6 +410,8 @@
</span><span class="cx">         return this._active;
</span><span class="cx">     }
</span><span class="cx"> 
</span><ins>+    get currentDispatchState() { return this._currentDispatchState; }
+
</ins><span class="cx">     set dispatcher(value)
</span><span class="cx">     {
</span><span class="cx">         this._dispatcher = value;
</span></span></pre>
</div>
</div>

</body>
</html>