<!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>[194546] 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/194546">194546</a></dd>
<dt>Author</dt> <dd>bburg@apple.com</dd>
<dt>Date</dt> <dd>2016-01-04 12:37:10 -0800 (Mon, 04 Jan 2016)</dd>
</dl>

<h3>Log Message</h3>
<pre>Web Inspector: add a DebugUI context menu item for saving inspector protocol traffic to file
https://bugs.webkit.org/show_bug.cgi?id=152671

Reviewed by Timothy Hatcher.

Add a new tracer that captures all messages, and debug context menu
items to control whether to capture protocol traffic and export it.
In later patches, a reciprocal &quot;Import...&quot; context menu item will
allow opening saved protocol traces and viewing them in a debug UI
content view for debugging/visualizing protocol traffic.

* UserInterface/Base/Main.js:
* UserInterface/Debug/CapturingProtocolTracer.js: Copied from Source/WebInspectorUI/UserInterface/Protocol/ProtocolTracer.js.

    This tracer saves everything into a flat array. JSON protocol
    messages are saved as escaped strings, in case they are not
    valid JSON. We want to be able to debug such scenarios.

(WebInspector.CapturingProtocolTracer):
(WebInspector.CapturingProtocolTracer.prototype.get trace):
(WebInspector.CapturingProtocolTracer.prototype.logFrontendException):
(WebInspector.CapturingProtocolTracer.prototype.logProtocolError):
(WebInspector.CapturingProtocolTracer.prototype.logFrontendRequest):
(WebInspector.CapturingProtocolTracer.prototype.logDidHandleResponse):
(WebInspector.CapturingProtocolTracer.prototype.logDidHandleEvent):
(WebInspector.CapturingProtocolTracer.prototype._stringifyMessage):
(WebInspector.CapturingProtocolTracer.prototype._processEntry):

* UserInterface/Debug/ProtocolTrace.js: Added.

    This is a dumb container that holds protocol trace data. It will
    be responsible for deserializing saved trace files in later work.

(WebInspector.ProtocolTrace):
(WebInspector.ProtocolTrace.prototype.addEntry):
(WebInspector.ProtocolTrace.prototype.get saveData):
* UserInterface/Main.html:
* UserInterface/Protocol/InspectorBackend.js:
(InspectorBackendClass):

    Simplify the implementation. Now there are one or two tracers
    at any given time. The default tracer handles legacy logging
    behavior and always exists. The custom tracer is installed when
    the &quot;Capture Protocol Traffic&quot; context menu item is toggled.

    Dispatch to the array of active tracers at each trace point.
    Tracers now get the actual JSON message instead of a stringified
    version passed as an argument.

(InspectorBackendClass.prototype.set dumpInspectorProtocolMessages):
(InspectorBackendClass.prototype.get dumpInspectorProtocolMessages):
(InspectorBackendClass.prototype.set dumpInspectorTimeStats):
(InspectorBackendClass.prototype.set customTracer):
(InspectorBackendClass.prototype.get activeTracers):
(InspectorBackendClass.prototype._startOrStopAutomaticTracing):
(InspectorBackendClass.prototype._sendMessageToBackend):
(InspectorBackendClass.prototype._dispatchResponse):
(InspectorBackendClass.prototype._dispatchEvent):
(InspectorBackendClass.prototype.set activeTracer): Deleted.
(InspectorBackendClass.prototype.get activeTracer): Deleted.
* UserInterface/Protocol/LoggingProtocolTracer.js:
(WebInspector.LoggingProtocolTracer.prototype._processEntry):
(WebInspector.LoggingProtocolTracer):
(WebInspector.LoggingProtocolTracer.prototype.logFrontendRequest):
(WebInspector.LoggingProtocolTracer.prototype.logWillHandleResponse):
(WebInspector.LoggingProtocolTracer.prototype.logDidHandleResponse):
(WebInspector.LoggingProtocolTracer.prototype.logWillHandleEvent):
(WebInspector.LoggingProtocolTracer.prototype.logDidHandleEvent):
* UserInterface/Protocol/ProtocolTracer.js:
(WebInspector.ProtocolTracer.prototype.logFrontendException):
(WebInspector.ProtocolTracer.prototype.logProtocolError):
(WebInspector.ProtocolTracer.prototype.logFrontendRequest):
(WebInspector.ProtocolTracer.prototype.logWillHandleResponse):
(WebInspector.ProtocolTracer.prototype.logDidHandleResponse):
(WebInspector.ProtocolTracer.prototype.logWillHandleEvent):
(WebInspector.ProtocolTracer.prototype.logDidHandleEvent):</pre>

<h3>Modified Paths</h3>
<ul>
<li><a href="#trunkSourceWebInspectorUIChangeLog">trunk/Source/WebInspectorUI/ChangeLog</a></li>
<li><a href="#trunkSourceWebInspectorUIUserInterfaceBaseMainjs">trunk/Source/WebInspectorUI/UserInterface/Base/Main.js</a></li>
<li><a href="#trunkSourceWebInspectorUIUserInterfaceMainhtml">trunk/Source/WebInspectorUI/UserInterface/Main.html</a></li>
<li><a href="#trunkSourceWebInspectorUIUserInterfaceProtocolInspectorBackendjs">trunk/Source/WebInspectorUI/UserInterface/Protocol/InspectorBackend.js</a></li>
<li><a href="#trunkSourceWebInspectorUIUserInterfaceProtocolLoggingProtocolTracerjs">trunk/Source/WebInspectorUI/UserInterface/Protocol/LoggingProtocolTracer.js</a></li>
<li><a href="#trunkSourceWebInspectorUIUserInterfaceProtocolProtocolTracerjs">trunk/Source/WebInspectorUI/UserInterface/Protocol/ProtocolTracer.js</a></li>
</ul>

<h3>Added Paths</h3>
<ul>
<li><a href="#trunkSourceWebInspectorUIUserInterfaceDebugCapturingProtocolTracerjs">trunk/Source/WebInspectorUI/UserInterface/Debug/CapturingProtocolTracer.js</a></li>
<li><a href="#trunkSourceWebInspectorUIUserInterfaceDebugProtocolTracejs">trunk/Source/WebInspectorUI/UserInterface/Debug/ProtocolTrace.js</a></li>
</ul>

</div>
<div id="patch">
<h3>Diff</h3>
<a id="trunkSourceWebInspectorUIChangeLog"></a>
<div class="modfile"><h4>Modified: trunk/Source/WebInspectorUI/ChangeLog (194545 => 194546)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/WebInspectorUI/ChangeLog        2016-01-04 19:51:45 UTC (rev 194545)
+++ trunk/Source/WebInspectorUI/ChangeLog        2016-01-04 20:37:10 UTC (rev 194546)
</span><span class="lines">@@ -1,3 +1,82 @@
</span><ins>+2016-01-04  Brian Burg  &lt;bburg@apple.com&gt;
+
+        Web Inspector: add a DebugUI context menu item for saving inspector protocol traffic to file
+        https://bugs.webkit.org/show_bug.cgi?id=152671
+
+        Reviewed by Timothy Hatcher.
+
+        Add a new tracer that captures all messages, and debug context menu
+        items to control whether to capture protocol traffic and export it.
+        In later patches, a reciprocal &quot;Import...&quot; context menu item will
+        allow opening saved protocol traces and viewing them in a debug UI
+        content view for debugging/visualizing protocol traffic.
+
+        * UserInterface/Base/Main.js:
+        * UserInterface/Debug/CapturingProtocolTracer.js: Copied from Source/WebInspectorUI/UserInterface/Protocol/ProtocolTracer.js.
+
+            This tracer saves everything into a flat array. JSON protocol
+            messages are saved as escaped strings, in case they are not
+            valid JSON. We want to be able to debug such scenarios.
+
+        (WebInspector.CapturingProtocolTracer):
+        (WebInspector.CapturingProtocolTracer.prototype.get trace):
+        (WebInspector.CapturingProtocolTracer.prototype.logFrontendException):
+        (WebInspector.CapturingProtocolTracer.prototype.logProtocolError):
+        (WebInspector.CapturingProtocolTracer.prototype.logFrontendRequest):
+        (WebInspector.CapturingProtocolTracer.prototype.logDidHandleResponse):
+        (WebInspector.CapturingProtocolTracer.prototype.logDidHandleEvent):
+        (WebInspector.CapturingProtocolTracer.prototype._stringifyMessage):
+        (WebInspector.CapturingProtocolTracer.prototype._processEntry):
+
+        * UserInterface/Debug/ProtocolTrace.js: Added.
+
+            This is a dumb container that holds protocol trace data. It will
+            be responsible for deserializing saved trace files in later work.
+
+        (WebInspector.ProtocolTrace):
+        (WebInspector.ProtocolTrace.prototype.addEntry):
+        (WebInspector.ProtocolTrace.prototype.get saveData):
+        * UserInterface/Main.html:
+        * UserInterface/Protocol/InspectorBackend.js:
+        (InspectorBackendClass):
+
+            Simplify the implementation. Now there are one or two tracers
+            at any given time. The default tracer handles legacy logging
+            behavior and always exists. The custom tracer is installed when
+            the &quot;Capture Protocol Traffic&quot; context menu item is toggled.
+
+            Dispatch to the array of active tracers at each trace point.
+            Tracers now get the actual JSON message instead of a stringified
+            version passed as an argument.
+
+        (InspectorBackendClass.prototype.set dumpInspectorProtocolMessages):
+        (InspectorBackendClass.prototype.get dumpInspectorProtocolMessages):
+        (InspectorBackendClass.prototype.set dumpInspectorTimeStats):
+        (InspectorBackendClass.prototype.set customTracer):
+        (InspectorBackendClass.prototype.get activeTracers):
+        (InspectorBackendClass.prototype._startOrStopAutomaticTracing):
+        (InspectorBackendClass.prototype._sendMessageToBackend):
+        (InspectorBackendClass.prototype._dispatchResponse):
+        (InspectorBackendClass.prototype._dispatchEvent):
+        (InspectorBackendClass.prototype.set activeTracer): Deleted.
+        (InspectorBackendClass.prototype.get activeTracer): Deleted.
+        * UserInterface/Protocol/LoggingProtocolTracer.js:
+        (WebInspector.LoggingProtocolTracer.prototype._processEntry):
+        (WebInspector.LoggingProtocolTracer):
+        (WebInspector.LoggingProtocolTracer.prototype.logFrontendRequest):
+        (WebInspector.LoggingProtocolTracer.prototype.logWillHandleResponse):
+        (WebInspector.LoggingProtocolTracer.prototype.logDidHandleResponse):
+        (WebInspector.LoggingProtocolTracer.prototype.logWillHandleEvent):
+        (WebInspector.LoggingProtocolTracer.prototype.logDidHandleEvent):
+        * UserInterface/Protocol/ProtocolTracer.js:
+        (WebInspector.ProtocolTracer.prototype.logFrontendException):
+        (WebInspector.ProtocolTracer.prototype.logProtocolError):
+        (WebInspector.ProtocolTracer.prototype.logFrontendRequest):
+        (WebInspector.ProtocolTracer.prototype.logWillHandleResponse):
+        (WebInspector.ProtocolTracer.prototype.logDidHandleResponse):
+        (WebInspector.ProtocolTracer.prototype.logWillHandleEvent):
+        (WebInspector.ProtocolTracer.prototype.logDidHandleEvent):
+
</ins><span class="cx"> 2016-01-04  Devin Rousso  &lt;dcrousso+webkit@gmail.com&gt;
</span><span class="cx"> 
</span><span class="cx">         Web Inspector: Comma separated values in the Visual sidebar are appended with )
</span></span></pre></div>
<a id="trunkSourceWebInspectorUIUserInterfaceBaseMainjs"></a>
<div class="modfile"><h4>Modified: trunk/Source/WebInspectorUI/UserInterface/Base/Main.js (194545 => 194546)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/WebInspectorUI/UserInterface/Base/Main.js        2016-01-04 19:51:45 UTC (rev 194545)
+++ trunk/Source/WebInspectorUI/UserInterface/Base/Main.js        2016-01-04 20:37:10 UTC (rev 194546)
</span><span class="lines">@@ -1,5 +1,5 @@
</span><span class="cx"> /*
</span><del>- * Copyright (C) 2013-2014 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">@@ -1378,6 +1378,23 @@
</span><span class="cx">         proposedContextMenu.appendItem(WebInspector.unlocalizedString(&quot;Reload Web Inspector&quot;), () =&gt; {
</span><span class="cx">             window.location.reload();
</span><span class="cx">         });
</span><ins>+
+        let protocolSubMenu = proposedContextMenu.appendSubMenuItem(WebInspector.unlocalizedString(&quot;Protocol Debugging&quot;), null, false);
+        let isCapturingTraffic = InspectorBackend.activeTracer instanceof WebInspector.CapturingProtocolTracer;
+
+        protocolSubMenu.appendCheckboxItem(WebInspector.unlocalizedString(&quot;Capture Trace&quot;), () =&gt; {
+            if (isCapturingTraffic)
+                InspectorBackend.activeTracer = null;
+            else
+                InspectorBackend.activeTracer = new WebInspector.CapturingProtocolTracer;
+        }, isCapturingTraffic);
+
+        protocolSubMenu.appendSeparator();
+
+        protocolSubMenu.appendItem(WebInspector.unlocalizedString(&quot;Export Trace\u2014&quot;), () =&gt; {
+            const forceSaveAs = true;
+            WebInspector.saveDataToFile(InspectorBackend.activeTracer.trace.saveData, forceSaveAs);
+        }, !isCapturingTraffic);
</ins><span class="cx">     } else {
</span><span class="cx">         const onlyExisting = true;
</span><span class="cx">         proposedContextMenu = WebInspector.ContextMenu.createFromEvent(event, onlyExisting);
</span></span></pre></div>
<a id="trunkSourceWebInspectorUIUserInterfaceDebugCapturingProtocolTracerjsfromrev194545trunkSourceWebInspectorUIUserInterfaceProtocolProtocolTracerjs"></a>
<div class="copfile"><h4>Copied: trunk/Source/WebInspectorUI/UserInterface/Debug/CapturingProtocolTracer.js (from rev 194545, trunk/Source/WebInspectorUI/UserInterface/Protocol/ProtocolTracer.js) (0 => 194546)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/WebInspectorUI/UserInterface/Debug/CapturingProtocolTracer.js                                (rev 0)
+++ trunk/Source/WebInspectorUI/UserInterface/Debug/CapturingProtocolTracer.js        2016-01-04 20:37:10 UTC (rev 194546)
</span><span class="lines">@@ -0,0 +1,89 @@
</span><ins>+/*
+ * Copyright (C) 2016 Apple Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+ * THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+WebInspector.CapturingProtocolTracer = class CapturingProtocolTracer extends WebInspector.ProtocolTracer
+{
+    constructor()
+    {
+        super();
+
+        this._trace = new WebInspector.ProtocolTrace;
+    }
+
+    // Public
+
+    get trace()
+    {
+        return this._trace;
+    }
+
+    logFrontendException(message, exception)
+    {
+        this._processEntry({type: &quot;exception&quot;, message: this._stringifyMessage(message), exception});
+    }
+
+    logProtocolError(message, error)
+    {
+        this._processEntry({type: &quot;error&quot;, message: this._stringifyMessage(message), error});
+    }
+
+    logFrontendRequest(message)
+    {
+        this._processEntry({type: &quot;request&quot;, message: this._stringifyMessage(message)});
+    }
+
+    logDidHandleResponse(message, timings = null)
+    {
+        let entry = {type: &quot;response&quot;, message: this._stringifyMessage(message)};
+        if (timings)
+            entry.timings = Object.shallowCopy(timings);
+
+        this._processEntry(entry);
+    }
+
+    logDidHandleEvent(message, timings = null)
+    {
+        let entry = {type: &quot;event&quot;, message: this._stringifyMessage(message)};
+        if (timings)
+            entry.timings = Object.shallowCopy(timings);
+
+        this._processEntry(entry);
+    }
+
+    _stringifyMessage(message)
+    {
+        try {
+            return JSON.stringify(message);
+        } catch (e) {
+            console.error(&quot;couldn't stringify object:&quot;, message, e);
+            return {};
+        }
+    }
+
+    _processEntry(entry)
+    {
+        this._trace.addEntry(entry);
+    }
+};
</ins></span></pre></div>
<a id="trunkSourceWebInspectorUIUserInterfaceDebugProtocolTracejs"></a>
<div class="addfile"><h4>Added: trunk/Source/WebInspectorUI/UserInterface/Debug/ProtocolTrace.js (0 => 194546)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/WebInspectorUI/UserInterface/Debug/ProtocolTrace.js                                (rev 0)
+++ trunk/Source/WebInspectorUI/UserInterface/Debug/ProtocolTrace.js        2016-01-04 20:37:10 UTC (rev 194546)
</span><span class="lines">@@ -0,0 +1,56 @@
</span><ins>+/*
+ * Copyright (C) 2016 Apple Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+ * THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+WebInspector.ProtocolTrace = class ProtocolTrace extends WebInspector.Object
+{
+    constructor()
+    {
+        super();
+
+        this._entries = [];
+    }
+
+    // Public
+
+    addEntry(entry) {
+        this._entries.push(entry);
+    }
+
+    get saveData() {
+        let now = new Date();
+        let YYYY = now.getFullYear();
+        let MM = now.getMonth() + 1;
+        let DD = now.getDate();
+        let hh = now.getHours();
+        let mm = now.getMinutes();
+        let ss = now.getSeconds();
+
+        // This follows the file name of screen shots on OS X (en-US):
+        // &quot;Protocol Trace 2015-12-31 at 12.43.04.json&quot;.
+        // When the Intl API is implemented, we can do a better job.
+        let filename = WebInspector.unlocalizedString(`Protocol Trace at ${YYYY}-${MM}-${DD} ${hh}.${mm}.${ss}.json`);
+        return {url: &quot;web-inspector:///&quot; + encodeURIComponent(filename), content: JSON.stringify(this._entries)};
+    }
+};
</ins></span></pre></div>
<a id="trunkSourceWebInspectorUIUserInterfaceMainhtml"></a>
<div class="modfile"><h4>Modified: trunk/Source/WebInspectorUI/UserInterface/Main.html (194545 => 194546)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/WebInspectorUI/UserInterface/Main.html        2016-01-04 19:51:45 UTC (rev 194545)
+++ trunk/Source/WebInspectorUI/UserInterface/Main.html        2016-01-04 20:37:10 UTC (rev 194546)
</span><span class="lines">@@ -643,6 +643,8 @@
</span><span class="cx">     &lt;script src=&quot;Base/Main.js&quot;&gt;&lt;/script&gt;
</span><span class="cx"> 
</span><span class="cx">     &lt;script src=&quot;Debug/Bootstrap.js&quot;&gt;&lt;/script&gt;
</span><ins>+    &lt;script src=&quot;Debug/CapturingProtocolTracer.js&quot;&gt;&lt;/script&gt;
+    &lt;script src=&quot;Debug/ProtocolTrace.js&quot;&gt;&lt;/script&gt;
</ins><span class="cx"> 
</span><span class="cx">     &lt;script&gt;
</span><span class="cx">         WebInspector.loaded();
</span></span></pre></div>
<a id="trunkSourceWebInspectorUIUserInterfaceProtocolInspectorBackendjs"></a>
<div class="modfile"><h4>Modified: trunk/Source/WebInspectorUI/UserInterface/Protocol/InspectorBackend.js (194545 => 194546)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/WebInspectorUI/UserInterface/Protocol/InspectorBackend.js        2016-01-04 19:51:45 UTC (rev 194545)
+++ trunk/Source/WebInspectorUI/UserInterface/Protocol/InspectorBackend.js        2016-01-04 20:37:10 UTC (rev 194546)
</span><span class="lines">@@ -1,6 +1,6 @@
</span><span class="cx"> /*
</span><span class="cx">  * Copyright (C) 2011 Google Inc. All rights reserved.
</span><del>- * Copyright (C) 2013, 2015 Apple Inc. All rights reserved.
</del><ins>+ * Copyright (C) 2013, 2015, 2016 Apple Inc. All rights reserved.
</ins><span class="cx">  * Copyright (C) 2014 University of Washington.
</span><span class="cx">  *
</span><span class="cx">  * Redistribution and use in source and binary forms, with or without
</span><span class="lines">@@ -38,9 +38,11 @@
</span><span class="cx">         this._pendingResponses = new Map;
</span><span class="cx">         this._agents = {};
</span><span class="cx">         this._deferredScripts = [];
</span><del>-        this._activeTracer = null;
-        this._automaticTracer = null;
</del><span class="cx"> 
</span><ins>+        this._customTracer = null;
+        this._defaultTracer = new WebInspector.LoggingProtocolTracer;
+        this._activeTracers = [this._defaultTracer];
+
</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">@@ -59,16 +61,12 @@
</span><span class="cx">         let setting = WebInspector.autoLogProtocolMessagesSetting;
</span><span class="cx">         setting.value = value;
</span><span class="cx"> 
</span><del>-        if (this.activeTracer !== this._automaticTracer)
-            return;
-
-        if (this.activeTracer)
-            this.activeTracer.dumpMessagesToConsole = value;
</del><ins>+        this._defaultTracer.dumpMessagesToConsole = value;
</ins><span class="cx">     }
</span><span class="cx"> 
</span><span class="cx">     get dumpInspectorProtocolMessages()
</span><span class="cx">     {
</span><del>-        return !!this._automaticTracer;
</del><ins>+        return WebInspector.autoLogProtocolMessagesSetting.value;
</ins><span class="cx">     }
</span><span class="cx"> 
</span><span class="cx">     set dumpInspectorTimeStats(value)
</span><span class="lines">@@ -76,11 +74,7 @@
</span><span class="cx">         if (!this.dumpInspectorProtocolMessages)
</span><span class="cx">             this.dumpInspectorProtocolMessages = true;
</span><span class="cx"> 
</span><del>-        if (this.activeTracer !== this._automaticTracer)
-            return;
-
-        if (this.activeTracer)
-            this.activeTracer.dumpTimingDataToConsole = value;
</del><ins>+        this._defaultTracer.dumpTimingDataToConsole = value;
</ins><span class="cx">     }
</span><span class="cx"> 
</span><span class="cx">     get dumpInspectorTimeStats()
</span><span class="lines">@@ -88,40 +82,36 @@
</span><span class="cx">         return this._dumpInspectorTimeStats;
</span><span class="cx">     }
</span><span class="cx"> 
</span><del>-    set activeTracer(tracer)
</del><ins>+    set customTracer(tracer)
</ins><span class="cx">     {
</span><del>-        console.assert(!tracer || tracer instanceof WebInspector.ProtocolTracer);
</del><ins>+        console.assert(!tracer || tracer instanceof WebInspector.ProtocolTracer, tracer);
+        console.assert(!tracer || tracer !== this._defaultTracer, tracer);
</ins><span class="cx"> 
</span><span class="cx">         // Bail early if no state change is to be made.
</span><del>-        if (!tracer &amp;&amp; !this._activeTracer)
</del><ins>+        if (!tracer &amp;&amp; !this._customTracer)
</ins><span class="cx">             return;
</span><span class="cx"> 
</span><del>-        if (tracer === this._activeTracer)
</del><ins>+        if (tracer === this._customTracer)
</ins><span class="cx">             return;
</span><span class="cx"> 
</span><del>-        // Don't allow an automatic tracer to dislodge a custom tracer.
-        if (this._activeTracer &amp;&amp; tracer === this._automaticTracer)
</del><ins>+        if (tracer === this._defaultTracer)
</ins><span class="cx">             return;
</span><span class="cx"> 
</span><del>-        if (this.activeTracer)
-            this.activeTracer.logFinished();
</del><ins>+        if (this._customTracer)
+            this._customTracer.logFinished();
</ins><span class="cx"> 
</span><del>-        if (this._activeTracer === this._automaticTracer)
-            this._automaticTracer = null;
</del><ins>+        this._customTracer = tracer;
+        this._activeTracers = [this._defaultTracer];
</ins><span class="cx"> 
</span><del>-        this._activeTracer = tracer;
-        if (this.activeTracer)
-            this.activeTracer.logStarted();
-        else {
-            // If the custom tracer was removed and automatic tracing is enabled,
-            // then create a new automatic tracer and install it in its place.
-            this._startOrStopAutomaticTracing();
</del><ins>+        if (this._customTracer) {
+            this._customTracer.logStarted();
+            this._activeTracers.push(this._customTracer);
</ins><span class="cx">         }
</span><span class="cx">     }
</span><span class="cx"> 
</span><del>-    get activeTracer()
</del><ins>+    get activeTracers()
</ins><span class="cx">     {
</span><del>-        return this._activeTracer || null;
</del><ins>+        return this._activeTracers;
</ins><span class="cx">     }
</span><span class="cx"> 
</span><span class="cx">     registerCommand(qualifiedName, callSignature, replySignature)
</span><span class="lines">@@ -187,24 +177,8 @@
</span><span class="cx"> 
</span><span class="cx">     _startOrStopAutomaticTracing()
</span><span class="cx">     {
</span><del>-        let setting = WebInspector.autoLogProtocolMessagesSetting;
-
-        // Bail if there is no state transition to be made.
-        if (!(setting.value ^ !!this.activeTracer))
-            return;
-
-        if (!setting.value) {
-            if (this.activeTracer === this._automaticTracer)
-                this.activeTracer = null;
-
-            this._automaticTracer = null;
-        } else {
-            this._automaticTracer = new WebInspector.LoggingProtocolTracer;
-            this._automaticTracer.dumpMessagesToConsole = this.dumpInspectorProtocolMessages;
-            this._automaticTracer.dumpTimingDataToConsole = this.dumpTimingDataToConsole;
-            // This will be ignored if a custom tracer is installed.
-            this.activeTracer = this._automaticTracer;
-        }
</del><ins>+        this._defaultTracer.dumpMessagesToConsole = this.dumpInspectorProtocolMessages;
+        this._defaultTracer.dumpTimingDataToConsole = this.dumpTimingDataToConsole;
</ins><span class="cx">     }
</span><span class="cx"> 
</span><span class="cx">     _agentForDomain(domainName)
</span><span class="lines">@@ -267,11 +241,10 @@
</span><span class="cx"> 
</span><span class="cx">     _sendMessageToBackend(messageObject)
</span><span class="cx">     {
</span><del>-        let stringifiedMessage = JSON.stringify(messageObject);
-        if (this.activeTracer)
-            this.activeTracer.logFrontendRequest(stringifiedMessage);
</del><ins>+        for (let tracer of this.activeTracers)
+            tracer.logFrontendRequest(messageObject);
</ins><span class="cx"> 
</span><del>-        InspectorFrontendHost.sendMessageToBackend(stringifiedMessage);
</del><ins>+        InspectorFrontendHost.sendMessageToBackend(JSON.stringify(messageObject));
</ins><span class="cx">     }
</span><span class="cx"> 
</span><span class="cx">     _dispatchResponse(messageObject)
</span><span class="lines">@@ -286,14 +259,12 @@
</span><span class="cx">         let sequenceId = messageObject[&quot;id&quot;];
</span><span class="cx">         console.assert(this._pendingResponses.has(sequenceId), sequenceId, this._pendingResponses);
</span><span class="cx"> 
</span><del>-        let responseData = this._pendingResponses.take(sequenceId);
</del><ins>+        let responseData = this._pendingResponses.take(sequenceId) || {};
</ins><span class="cx">         let {command, callback, promise} = responseData;
</span><span class="cx"> 
</span><del>-        let processingStartTimestamp;
-        if (this.activeTracer) {
-            processingStartTimestamp = timestamp();
-            this.activeTracer.logWillHandleResponse(JSON.stringify(messageObject));
-        }
</del><ins>+        let processingStartTimestamp = timestamp();
+        for (let tracer of this.activeTracers)
+            tracer.logWillHandleResponse(messageObject);
</ins><span class="cx"> 
</span><span class="cx">         if (typeof callback === &quot;function&quot;)
</span><span class="cx">             this._dispatchResponseToCallback(command, messageObject, callback);
</span><span class="lines">@@ -302,12 +273,12 @@
</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><del>-        if (this.activeTracer) {
-            let processingTime = (timestamp() - processingStartTimestamp).toFixed(3);
-            let roundTripTime = (processingStartTimestamp - responseData.sendRequestTimestamp).toFixed(3);
-            this.activeTracer.logDidHandleResponse(JSON.stringify(messageObject), {rtt: roundTripTime, dispatch: processingTime});
-        }
</del><ins>+        let processingTime = (timestamp() - processingStartTimestamp).toFixed(3);
+        let roundTripTime = (processingStartTimestamp - responseData.sendRequestTimestamp).toFixed(3);
</ins><span class="cx"> 
</span><ins>+        for (let tracer of this.activeTracers)
+            tracer.logDidHandleResponse(messageObject, {rtt: roundTripTime, dispatch: processingTime});
+
</ins><span class="cx">         if (this._deferredScripts.length &amp;&amp; !this._pendingResponses.size)
</span><span class="cx">             this._flushPendingScripts();
</span><span class="cx">     }
</span><span class="lines">@@ -363,24 +334,21 @@
</span><span class="cx">         if (messageObject[&quot;params&quot;])
</span><span class="cx">             eventArguments = event.parameterNames.map((name) =&gt; messageObject[&quot;params&quot;][name]);
</span><span class="cx"> 
</span><del>-        let processingStartTimestamp;
-        if (this.activeTracer) {
-            processingStartTimestamp = timestamp();
-            this.activeTracer.logWillHandleEvent(JSON.stringify(messageObject));
-        }
</del><ins>+        let processingStartTimestamp = timestamp();
+        for (let tracer of this.activeTracers)
+            tracer.logWillHandleEvent(messageObject);
</ins><span class="cx"> 
</span><span class="cx">         try {
</span><span class="cx">             agent.dispatchEvent(eventName, eventArguments);
</span><span class="cx">         } catch (e) {
</span><span class="cx">             console.error(&quot;Uncaught exception in inspector page while handling event &quot; + qualifiedName, e);
</span><del>-            if (this.activeTracer)
-                this.activeTracer.logFrontendException(JSON.stringify(messageObject), e);
</del><ins>+            for (let tracer of this.activeTracers)
+                tracer.logFrontendException(messageObject, e);
</ins><span class="cx">         }
</span><span class="cx"> 
</span><del>-        if (this.activeTracer) {
-            let processingTime = (timestamp() - processingStartTimestamp).toFixed(3);
-            this.activeTracer.logDidHandleEvent(JSON.stringify(messageObject), {dispatch: processingTime});
-        }
</del><ins>+        let processingDuration = (timestamp() - processingStartTimestamp).toFixed(3);
+        for (let tracer of this.activeTracers)
+            tracer.logDidHandleEvent(messageObject, {dispatch: processingDuration});
</ins><span class="cx">     }
</span><span class="cx"> 
</span><span class="cx">     _reportProtocolError(messageObject)
</span></span></pre></div>
<a id="trunkSourceWebInspectorUIUserInterfaceProtocolLoggingProtocolTracerjs"></a>
<div class="modfile"><h4>Modified: trunk/Source/WebInspectorUI/UserInterface/Protocol/LoggingProtocolTracer.js (194545 => 194546)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/WebInspectorUI/UserInterface/Protocol/LoggingProtocolTracer.js        2016-01-04 19:51:45 UTC (rev 194545)
+++ trunk/Source/WebInspectorUI/UserInterface/Protocol/LoggingProtocolTracer.js        2016-01-04 20:37:10 UTC (rev 194546)
</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">@@ -68,23 +68,17 @@
</span><span class="cx"> 
</span><span class="cx">     logFrontendRequest(message)
</span><span class="cx">     {
</span><del>-        console.assert(typeof message === &quot;string&quot;, &quot;Must stringify messages to avoid leaking all JSON protocol messages.&quot;)
-
</del><span class="cx">         this._processEntry({type: &quot;request&quot;, message});
</span><span class="cx">     }
</span><span class="cx"> 
</span><span class="cx">     logWillHandleResponse(message)
</span><span class="cx">     {
</span><del>-        console.assert(typeof message === &quot;string&quot;, &quot;Must stringify messages to avoid leaking all JSON protocol messages.&quot;)
-
</del><span class="cx">         let entry = {type: &quot;response&quot;, message};
</span><span class="cx">         this._processEntry(entry);
</span><span class="cx">     }
</span><span class="cx"> 
</span><span class="cx">     logDidHandleResponse(message, timings = null)
</span><span class="cx">     {
</span><del>-        console.assert(typeof message === &quot;string&quot;, &quot;Must stringify messages to avoid leaking all JSON protocol messages.&quot;)
-
</del><span class="cx">         let entry = {type: &quot;response&quot;, message};
</span><span class="cx">         if (timings)
</span><span class="cx">             entry.timings = Object.shallowCopy(timings);
</span><span class="lines">@@ -94,16 +88,12 @@
</span><span class="cx"> 
</span><span class="cx">     logWillHandleEvent(message)
</span><span class="cx">     {
</span><del>-        console.assert(typeof message === &quot;string&quot;, &quot;Must stringify messages to avoid leaking all JSON protocol messages.&quot;)
-
</del><span class="cx">         let entry = {type: &quot;event&quot;, message};
</span><span class="cx">         this._processEntry(entry);
</span><span class="cx">     }
</span><span class="cx"> 
</span><span class="cx">     logDidHandleEvent(message, timings = null)
</span><span class="cx">     {
</span><del>-        console.assert(typeof message === &quot;string&quot;, &quot;Must stringify messages to avoid leaking all JSON protocol messages.&quot;)
-
</del><span class="cx">         let entry = {type: &quot;event&quot;, message};
</span><span class="cx">         if (timings)
</span><span class="cx">             entry.timings = Object.shallowCopy(timings);
</span><span class="lines">@@ -119,6 +109,6 @@
</span><span class="cx">             else if (entry.timings.dispatch)
</span><span class="cx">                 this._logToConsole(`time-stats: Handling: ${entry.timings.dispatch || NaN}ms`);
</span><span class="cx">         } else if (this._dumpMessagesToConsole &amp;&amp; !entry.timings)
</span><del>-            this._logToConsole(`${entry.type}: ${entry.message}`);
</del><ins>+            this._logToConsole(`${entry.type}: ${JSON.stringify(entry.message)}`);
</ins><span class="cx">     }
</span><span class="cx"> };
</span></span></pre></div>
<a id="trunkSourceWebInspectorUIUserInterfaceProtocolProtocolTracerjs"></a>
<div class="modfile"><h4>Modified: trunk/Source/WebInspectorUI/UserInterface/Protocol/ProtocolTracer.js (194545 => 194546)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/WebInspectorUI/UserInterface/Protocol/ProtocolTracer.js        2016-01-04 19:51:45 UTC (rev 194545)
+++ trunk/Source/WebInspectorUI/UserInterface/Protocol/ProtocolTracer.js        2016-01-04 20:37:10 UTC (rev 194546)
</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">@@ -34,50 +34,36 @@
</span><span class="cx"> 
</span><span class="cx">     logFrontendException(message, exception)
</span><span class="cx">     {
</span><del>-        console.assert(typeof message === &quot;string&quot;, &quot;Must stringify messages to avoid leaking all JSON protocol messages.&quot;)
-
</del><span class="cx">         // To be overridden by subclasses.
</span><span class="cx">     }
</span><span class="cx"> 
</span><span class="cx">     logProtocolError(message, error)
</span><span class="cx">     {
</span><del>-        console.assert(typeof message === &quot;string&quot;, &quot;Must stringify messages to avoid leaking all JSON protocol messages.&quot;)
-
</del><span class="cx">         // To be overridden by subclasses.
</span><span class="cx">     }
</span><span class="cx"> 
</span><span class="cx">     logFrontendRequest(message)
</span><span class="cx">     {
</span><del>-        console.assert(typeof message === &quot;string&quot;, &quot;Must stringify messages to avoid leaking all JSON protocol messages.&quot;)
-
</del><span class="cx">         // To be overridden by subclasses.
</span><span class="cx">     }
</span><span class="cx"> 
</span><span class="cx">     logWillHandleResponse(message)
</span><span class="cx">     {
</span><del>-        console.assert(typeof message === &quot;string&quot;, &quot;Must stringify messages to avoid leaking all JSON protocol messages.&quot;)
-
</del><span class="cx">         // To be overridden by subclasses.
</span><span class="cx">     }
</span><span class="cx"> 
</span><span class="cx">     logDidHandleResponse(message, timings = null)
</span><span class="cx">     {
</span><del>-        console.assert(typeof message === &quot;string&quot;, &quot;Must stringify messages to avoid leaking all JSON protocol messages.&quot;)
-
</del><span class="cx">         // To be overridden by subclasses.
</span><span class="cx">     }
</span><span class="cx"> 
</span><span class="cx">     logWillHandleEvent(message)
</span><span class="cx">     {
</span><del>-        console.assert(typeof message === &quot;string&quot;, &quot;Must stringify messages to avoid leaking all JSON protocol messages.&quot;)
-
</del><span class="cx">         // To be overridden by subclasses.
</span><span class="cx">     }
</span><span class="cx"> 
</span><span class="cx">     logDidHandleEvent(message, timings = null)
</span><span class="cx">     {
</span><del>-        console.assert(typeof message === &quot;string&quot;, &quot;Must stringify messages to avoid leaking all JSON protocol messages.&quot;)
-
</del><span class="cx">         // To be overridden by subclasses.
</span><span class="cx">     }
</span><span class="cx"> 
</span></span></pre>
</div>
</div>

</body>
</html>