<!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>[198353] 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/198353">198353</a></dd>
<dt>Author</dt> <dd>joepeck@webkit.org</dd>
<dt>Date</dt> <dd>2016-03-17 14:02:07 -0700 (Thu, 17 Mar 2016)</dd>
</dl>

<h3>Log Message</h3>
<pre>Web Inspector: HeapSnapshots are slow and use too much memory
https://bugs.webkit.org/show_bug.cgi?id=155571

Reviewed by Timothy Hatcher.

Source/WebInspectorUI:

This is the first inclusion of Workers into Web Inspector. In this case
the Main side merely needs to make requests of the Worker and get back
objects that it can interact with more.

New file heirarchies:

    UserInterface/Proxies
        - new Proxy classes in the Main page.
        - treat like Model classes, but not quite model.

    UserInterface/Workers/HeapSnapshotWorker
        - new Worker classes for Workers. No WebInspector namespace.
        - no minification of these resources, they are simply copied.

Remote procedure call interface between the Main/Worker page happens
through the WorkerProxy and Worker classes. There are simple ways
to perform factory style methods and call methods on objects, and
get the result in a callback. Similiar to frontend &lt;-&gt; backend agent
communication:

    HeapSnapshotWorkerProxy: (Main world)
        - creates the worker
        - performAction(&quot;actionName&quot;, arguments, callback)
        - callMethod(objectId, &quot;methodName&quot;, arguments, callback)
        - handle message =&gt; dispatch event or invoke callback

    HeapSnapshotWorker: (Worker world)
        - sendEvent(&quot;eventName&quot;, eventData)
        - handle message =&gt; dispatch action or method on object

Proxy object methods are boilerplate calls to performAction/callMethod
with deserialization of responses. The rest of the frontend can just
treat Proxy objects as Model objects with some data and async methods.

Because the Node/Edge data is so small, objects are cheaply created
when needed and not cached. This means that there may be duplicate
HeapSnapshotNode's for the same node. For example if different Views
both request instancesWithClassName(&quot;Foo&quot;). This is fine, as none
of our Views really care about object uniqueness, they are only
interested in the data or querying for more data.

* Scripts/combine-resources.pl:
* Scripts/copy-user-interface-resources.pl:
Copy the Workers directory to the resources directory.
Its code is only meant to be loaded by Workers, so it
shouldn't be included in the Main page.

* UserInterface/Main.html:
* UserInterface/Test.html:
* UserInterface/Models/HeapSnapshot.js: Removed.
* UserInterface/Models/HeapSnapshotDiff.js: Removed.
* UserInterface/Models/HeapSnapshotEdge.js: Removed.
* UserInterface/Models/HeapSnapshotNode.js: Removed.
Replace the old simple Model classes with Proxy classes that interact
with the Worker.

* UserInterface/Models/HeapAllocationsInstrument.js:
(WebInspector.HeapAllocationsInstrument.prototype._takeHeapSnapshot):
(WebInspector.HeapAllocationsInstrument):
* UserInterface/Models/HeapAllocationsTimelineRecord.js:
(WebInspector.HeapAllocationsTimelineRecord):
* UserInterface/Models/HeapSnapshotRootPath.js:
(WebInspector.HeapSnapshotRootPath):
(WebInspector.HeapSnapshotRootPath.prototype.appendEdge):
* UserInterface/Protocol/HeapObserver.js:
(WebInspector.HeapObserver.prototype.trackingStart):
(WebInspector.HeapObserver.prototype.trackingComplete):
* UserInterface/Views/ContentView.js:
(WebInspector.ContentView.createFromRepresentedObject):
(WebInspector.ContentView.isViewable):
* UserInterface/Views/HeapAllocationsTimelineView.js:
(WebInspector.HeapAllocationsTimelineView.prototype.showHeapSnapshotDiff):
(WebInspector.HeapAllocationsTimelineView.prototype._takeHeapSnapshotClicked):
(WebInspector.HeapAllocationsTimelineView.prototype._dataGridNodeSelected):
(WebInspector.HeapAllocationsTimelineView):
* UserInterface/Views/HeapSnapshotClassDataGridNode.js:
(WebInspector.HeapSnapshotClassDataGridNode.prototype._populate):
* UserInterface/Views/HeapSnapshotClusterContentView.js:
* UserInterface/Views/HeapSnapshotInstanceDataGridNode.js:
(WebInspector.HeapSnapshotInstanceDataGridNode):
(WebInspector.HeapSnapshotInstanceDataGridNode.logHeapSnapshotNode.node.shortestGCRootPath.):
(WebInspector.HeapSnapshotInstanceDataGridNode.logHeapSnapshotNode):
(WebInspector.HeapSnapshotInstanceDataGridNode.prototype._mouseoverHandler.appendPath):
(WebInspector.HeapSnapshotInstanceDataGridNode.prototype._mouseoverHandler.stringifyEdge):
(WebInspector.HeapSnapshotInstanceDataGridNode.prototype._mouseoverHandler):
* UserInterface/Views/HeapSnapshotInstancesContentView.js:
(WebInspector.HeapSnapshotInstancesContentView):
* UserInterface/Views/HeapSnapshotInstancesDataGridTree.js:
(WebInspector.HeapSnapshotInstancesDataGridTree):
* UserInterface/Views/HeapSnapshotSummaryContentView.js:
(WebInspector.HeapSnapshotSummaryContentView):
Update existing code to expect the new Proxy objects or create
the new HeapSnapshot using workers.

* UserInterface/Proxies/HeapSnapshotDiffProxy.js: Added.
(WebInspector.HeapSnapshotDiffProxy):
(WebInspector.HeapSnapshotDiffProxy.deserialize):
(WebInspector.HeapSnapshotDiffProxy.prototype.get snapshot1):
(WebInspector.HeapSnapshotDiffProxy.prototype.get snapshot2):
(WebInspector.HeapSnapshotDiffProxy.prototype.get totalSize):
(WebInspector.HeapSnapshotDiffProxy.prototype.get totalObjectCount):
(WebInspector.HeapSnapshotDiffProxy.prototype.get categories):
(WebInspector.HeapSnapshotDiffProxy.prototype.allocationBucketCounts):
(WebInspector.HeapSnapshotDiffProxy.prototype.instancesWithClassName):
(WebInspector.HeapSnapshotDiffProxy.prototype.nodeWithIdentifier):
A HeapSnapshotDiffProxy looks like a HeapSnapshotProxy and responds to
the same methods, but has the extra snapshot1/2 pointers.

* UserInterface/Proxies/HeapSnapshotEdgeProxy.js:
(WebInspector.HeapSnapshotEdgeProxy):
(WebInspector.HeapSnapshotEdgeProxy.deserialize):
Edge data. No methods are proxied at this point.

* UserInterface/Proxies/HeapSnapshotNodeProxy.js: Added.
(WebInspector.HeapSnapshotNodeProxy):
(WebInspector.HeapSnapshotNodeProxy.deserialize):
(WebInspector.HeapSnapshotNodeProxy.prototype.shortestGCRootPath):
(WebInspector.HeapSnapshotNodeProxy.prototype.dominatedNodes):
(WebInspector.HeapSnapshotNodeProxy.prototype.retainedNodes):
(WebInspector.HeapSnapshotNodeProxy.prototype.retainers):
Node data and methods to query for node relationships.

* UserInterface/Proxies/HeapSnapshotProxy.js: Added.
(WebInspector.HeapSnapshotProxy):
(WebInspector.HeapSnapshotProxy.deserialize):
(WebInspector.HeapSnapshotProxy.prototype.get proxyObjectId):
(WebInspector.HeapSnapshotProxy.prototype.get identifier):
(WebInspector.HeapSnapshotProxy.prototype.get totalSize):
(WebInspector.HeapSnapshotProxy.prototype.get totalObjectCount):
(WebInspector.HeapSnapshotProxy.prototype.get categories):
(WebInspector.HeapSnapshotProxy.prototype.allocationBucketCounts):
(WebInspector.HeapSnapshotProxy.prototype.instancesWithClassName):
(WebInspector.HeapSnapshotProxy.prototype.nodeWithIdentifier):
Snapshot data and methods to query for nodes.

* UserInterface/Proxies/HeapSnapshotWorkerProxy.js: Added.
(WebInspector.HeapSnapshotWorkerProxy):
(WebInspector.HeapSnapshotWorkerProxy.singleton):
(WebInspector.HeapSnapshotWorkerProxy.prototype.createSnapshot):
(WebInspector.HeapSnapshotWorkerProxy.prototype.createSnapshotDiff):
(WebInspector.HeapSnapshotWorkerProxy.prototype.performAction):
(WebInspector.HeapSnapshotWorkerProxy.prototype.callMethod):
(WebInspector.HeapSnapshotWorkerProxy.prototype._postMessage):
(WebInspector.HeapSnapshotWorkerProxy.prototype._handleMessage):
Singleton factory for the worker and proxied communication with the worker.
Provide means for invoking &quot;factory actions&quot; and &quot;object methods&quot;.

* UserInterface/Workers/HeapSnapshot/HeapSnapshotWorker.js: Added.
(HeapSnapshotWorker):
(HeapSnapshotWorker.prototype.createSnapshot):
(HeapSnapshotWorker.prototype.createSnapshotDiff):
(HeapSnapshotWorker.prototype.sendEvent):
(HeapSnapshotWorker.prototype._handleMessage):
Main worker code. Handle dispatching actions and methods.

* UserInterface/Workers/HeapSnapshot/HeapSnapshot.js: Added.
(HeapSnapshot):
(HeapSnapshot.buildCategories):
(HeapSnapshot.allocationBucketCounts):
(HeapSnapshot.instancesWithClassName):
(HeapSnapshot.prototype.allocationBucketCounts):
(HeapSnapshot.prototype.instancesWithClassName):
(HeapSnapshot.prototype.nodeWithIdentifier):
(HeapSnapshot.prototype.shortestGCRootPath):
(HeapSnapshot.prototype.dominatedNodes):
(HeapSnapshot.prototype.retainedNodes):
(HeapSnapshot.prototype.retainers):
(HeapSnapshot.prototype.serialize):
(HeapSnapshot.prototype.serializeNode):
(HeapSnapshot.prototype.serializeEdge):
(HeapSnapshot.prototype._buildOutgoingEdges):
(HeapSnapshot.prototype._buildIncomingEdges):
(HeapSnapshot.prototype._buildPostOrderIndexes):
(HeapSnapshot.prototype._buildDominatorIndexes):
(HeapSnapshot.prototype._buildRetainedSizes):
(HeapSnapshot.prototype._gcRootPathes.visitNode):
(HeapSnapshot.prototype._gcRootPathes):
(HeapSnapshotDiff):
(HeapSnapshotDiff.prototype.allocationBucketCounts):
(HeapSnapshotDiff.prototype.instancesWithClassName):
(HeapSnapshotDiff.prototype.nodeWithIdentifier):
(HeapSnapshotDiff.prototype.shortestGCRootPath):
(HeapSnapshotDiff.prototype.dominatedNodes):
(HeapSnapshotDiff.prototype.retainedNodes):
(HeapSnapshotDiff.prototype.retainers):
(HeapSnapshotDiff.prototype.serialize):
New HeapSnapshot data processing implementation. Instead of creating
a new object per Node or per Edge create data arrays containing data
per-Node. Operate on these lists of data instead of creating many objects.

LayoutTests:

* inspector/heap/getPreview.html:
* inspector/heap/getRemoteObject.html:
* inspector/heap/snapshot.html:
Update tests to use the new HeapSnapshotWorker frontend code.

* inspector/unit-tests/heap-snapshot-expected.txt: Added.
* inspector/unit-tests/heap-snapshot.html: Added.
Verify the data processing in and worker communication work HeapSnapshotWorker
produces expected values when compared with the simple HeapSnapshot/Node/Edge
implentation.</pre>

<h3>Modified Paths</h3>
<ul>
<li><a href="#trunkLayoutTestsChangeLog">trunk/LayoutTests/ChangeLog</a></li>
<li><a href="#trunkLayoutTestsinspectorheapgetPreviewhtml">trunk/LayoutTests/inspector/heap/getPreview.html</a></li>
<li><a href="#trunkLayoutTestsinspectorheapgetRemoteObjecthtml">trunk/LayoutTests/inspector/heap/getRemoteObject.html</a></li>
<li><a href="#trunkLayoutTestsinspectorheapsnapshothtml">trunk/LayoutTests/inspector/heap/snapshot.html</a></li>
<li><a href="#trunkSourceWebInspectorUIChangeLog">trunk/Source/WebInspectorUI/ChangeLog</a></li>
<li><a href="#trunkSourceWebInspectorUIScriptscombineresourcespl">trunk/Source/WebInspectorUI/Scripts/combine-resources.pl</a></li>
<li><a href="#trunkSourceWebInspectorUIScriptscopyuserinterfaceresourcespl">trunk/Source/WebInspectorUI/Scripts/copy-user-interface-resources.pl</a></li>
<li><a href="#trunkSourceWebInspectorUIUserInterfaceMainhtml">trunk/Source/WebInspectorUI/UserInterface/Main.html</a></li>
<li><a href="#trunkSourceWebInspectorUIUserInterfaceModelsHeapAllocationsInstrumentjs">trunk/Source/WebInspectorUI/UserInterface/Models/HeapAllocationsInstrument.js</a></li>
<li><a href="#trunkSourceWebInspectorUIUserInterfaceModelsHeapAllocationsTimelineRecordjs">trunk/Source/WebInspectorUI/UserInterface/Models/HeapAllocationsTimelineRecord.js</a></li>
<li><a href="#trunkSourceWebInspectorUIUserInterfaceModelsHeapSnapshotRootPathjs">trunk/Source/WebInspectorUI/UserInterface/Models/HeapSnapshotRootPath.js</a></li>
<li><a href="#trunkSourceWebInspectorUIUserInterfaceProtocolHeapObserverjs">trunk/Source/WebInspectorUI/UserInterface/Protocol/HeapObserver.js</a></li>
<li><a href="#trunkSourceWebInspectorUIUserInterfaceTesthtml">trunk/Source/WebInspectorUI/UserInterface/Test.html</a></li>
<li><a href="#trunkSourceWebInspectorUIUserInterfaceViewsContentViewjs">trunk/Source/WebInspectorUI/UserInterface/Views/ContentView.js</a></li>
<li><a href="#trunkSourceWebInspectorUIUserInterfaceViewsHeapAllocationsTimelineViewjs">trunk/Source/WebInspectorUI/UserInterface/Views/HeapAllocationsTimelineView.js</a></li>
<li><a href="#trunkSourceWebInspectorUIUserInterfaceViewsHeapSnapshotClassDataGridNodejs">trunk/Source/WebInspectorUI/UserInterface/Views/HeapSnapshotClassDataGridNode.js</a></li>
<li><a href="#trunkSourceWebInspectorUIUserInterfaceViewsHeapSnapshotClusterContentViewjs">trunk/Source/WebInspectorUI/UserInterface/Views/HeapSnapshotClusterContentView.js</a></li>
<li><a href="#trunkSourceWebInspectorUIUserInterfaceViewsHeapSnapshotInstanceDataGridNodejs">trunk/Source/WebInspectorUI/UserInterface/Views/HeapSnapshotInstanceDataGridNode.js</a></li>
<li><a href="#trunkSourceWebInspectorUIUserInterfaceViewsHeapSnapshotInstancesContentViewjs">trunk/Source/WebInspectorUI/UserInterface/Views/HeapSnapshotInstancesContentView.js</a></li>
<li><a href="#trunkSourceWebInspectorUIUserInterfaceViewsHeapSnapshotInstancesDataGridTreejs">trunk/Source/WebInspectorUI/UserInterface/Views/HeapSnapshotInstancesDataGridTree.js</a></li>
<li><a href="#trunkSourceWebInspectorUIUserInterfaceViewsHeapSnapshotSummaryContentViewjs">trunk/Source/WebInspectorUI/UserInterface/Views/HeapSnapshotSummaryContentView.js</a></li>
</ul>

<h3>Added Paths</h3>
<ul>
<li><a href="#trunkLayoutTestsinspectorunittestsheapsnapshotexpectedtxt">trunk/LayoutTests/inspector/unit-tests/heap-snapshot-expected.txt</a></li>
<li><a href="#trunkLayoutTestsinspectorunittestsheapsnapshothtml">trunk/LayoutTests/inspector/unit-tests/heap-snapshot.html</a></li>
<li>trunk/Source/WebInspectorUI/UserInterface/Proxies/</li>
<li><a href="#trunkSourceWebInspectorUIUserInterfaceProxiesHeapSnapshotDiffProxyjs">trunk/Source/WebInspectorUI/UserInterface/Proxies/HeapSnapshotDiffProxy.js</a></li>
<li><a href="#trunkSourceWebInspectorUIUserInterfaceProxiesHeapSnapshotEdgeProxyjs">trunk/Source/WebInspectorUI/UserInterface/Proxies/HeapSnapshotEdgeProxy.js</a></li>
<li><a href="#trunkSourceWebInspectorUIUserInterfaceProxiesHeapSnapshotNodeProxyjs">trunk/Source/WebInspectorUI/UserInterface/Proxies/HeapSnapshotNodeProxy.js</a></li>
<li><a href="#trunkSourceWebInspectorUIUserInterfaceProxiesHeapSnapshotProxyjs">trunk/Source/WebInspectorUI/UserInterface/Proxies/HeapSnapshotProxy.js</a></li>
<li><a href="#trunkSourceWebInspectorUIUserInterfaceProxiesHeapSnapshotWorkerProxyjs">trunk/Source/WebInspectorUI/UserInterface/Proxies/HeapSnapshotWorkerProxy.js</a></li>
<li>trunk/Source/WebInspectorUI/UserInterface/Workers/</li>
<li>trunk/Source/WebInspectorUI/UserInterface/Workers/HeapSnapshot/</li>
<li><a href="#trunkSourceWebInspectorUIUserInterfaceWorkersHeapSnapshotHeapSnapshotjs">trunk/Source/WebInspectorUI/UserInterface/Workers/HeapSnapshot/HeapSnapshot.js</a></li>
<li><a href="#trunkSourceWebInspectorUIUserInterfaceWorkersHeapSnapshotHeapSnapshotWorkerjs">trunk/Source/WebInspectorUI/UserInterface/Workers/HeapSnapshot/HeapSnapshotWorker.js</a></li>
</ul>

<h3>Removed Paths</h3>
<ul>
<li><a href="#trunkSourceWebInspectorUIUserInterfaceModelsHeapSnapshotjs">trunk/Source/WebInspectorUI/UserInterface/Models/HeapSnapshot.js</a></li>
<li><a href="#trunkSourceWebInspectorUIUserInterfaceModelsHeapSnapshotDiffjs">trunk/Source/WebInspectorUI/UserInterface/Models/HeapSnapshotDiff.js</a></li>
<li><a href="#trunkSourceWebInspectorUIUserInterfaceModelsHeapSnapshotEdgejs">trunk/Source/WebInspectorUI/UserInterface/Models/HeapSnapshotEdge.js</a></li>
<li><a href="#trunkSourceWebInspectorUIUserInterfaceModelsHeapSnapshotNodejs">trunk/Source/WebInspectorUI/UserInterface/Models/HeapSnapshotNode.js</a></li>
</ul>

</div>
<div id="patch">
<h3>Diff</h3>
<a id="trunkLayoutTestsChangeLog"></a>
<div class="modfile"><h4>Modified: trunk/LayoutTests/ChangeLog (198352 => 198353)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/LayoutTests/ChangeLog        2016-03-17 21:01:21 UTC (rev 198352)
+++ trunk/LayoutTests/ChangeLog        2016-03-17 21:02:07 UTC (rev 198353)
</span><span class="lines">@@ -1,3 +1,21 @@
</span><ins>+2016-03-17  Joseph Pecoraro  &lt;pecoraro@apple.com&gt;
+
+        Web Inspector: HeapSnapshots are slow and use too much memory
+        https://bugs.webkit.org/show_bug.cgi?id=155571
+
+        Reviewed by Timothy Hatcher.
+
+        * inspector/heap/getPreview.html:
+        * inspector/heap/getRemoteObject.html:
+        * inspector/heap/snapshot.html:
+        Update tests to use the new HeapSnapshotWorker frontend code.
+
+        * inspector/unit-tests/heap-snapshot-expected.txt: Added.
+        * inspector/unit-tests/heap-snapshot.html: Added.
+        Verify the data processing in and worker communication work HeapSnapshotWorker
+        produces expected values when compared with the simple HeapSnapshot/Node/Edge
+        implentation.
+
</ins><span class="cx"> 2016-03-17  Ryan Haddad  &lt;ryanhaddad@apple.com&gt;
</span><span class="cx"> 
</span><span class="cx">         Marking http/tests/security/aboutBlank/window-open-self-about-blank.html as flaky on ios-sim-debug
</span></span></pre></div>
<a id="trunkLayoutTestsinspectorheapgetPreviewhtml"></a>
<div class="modfile"><h4>Modified: trunk/LayoutTests/inspector/heap/getPreview.html (198352 => 198353)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/LayoutTests/inspector/heap/getPreview.html        2016-03-17 21:01:21 UTC (rev 198352)
+++ trunk/LayoutTests/inspector/heap/getPreview.html        2016-03-17 21:02:07 UTC (rev 198353)
</span><span class="lines">@@ -52,15 +52,17 @@
</span><span class="cx">                 InspectorTest.evaluateInPage(&quot;triggerCreateStringObject()&quot;);
</span><span class="cx">                 HeapAgent.snapshot((error, timestamp, snapshotStringData) =&gt; { // Newly created objects.
</span><span class="cx">                     InspectorTest.expectThat(!error, &quot;Should not have an error creating a snapshot.&quot;);
</span><del>-                    let payload = JSON.parse(snapshotStringData);
-                    let snapshot = WebInspector.HeapSnapshot.fromPayload(payload);
-
-                    let strings = snapshot.instancesWithClassName(&quot;string&quot;);
-                    let heapSnapshotNode = strings.reduce((result, x) =&gt; result.id &lt; x.id ? x : result, strings[0]);
-                    HeapAgent.getPreview(heapSnapshotNode.id, (error, string, functionDetails, objectPreviewPayload) =&gt; {
-                        InspectorTest.expectThat(!error, &quot;Should not have an error getting preview.&quot;);
-                        InspectorTest.log(&quot;STRING: &quot; + string);
-                        resolve();
</del><ins>+                    let workerProxy = WebInspector.HeapSnapshotWorkerProxy.singleton();
+                    workerProxy.createSnapshot(snapshotStringData, ({objectId, snapshot: serializedSnapshot}) =&gt; {
+                        let snapshot = WebInspector.HeapSnapshotProxy.deserialize(objectId, serializedSnapshot);
+                        snapshot.instancesWithClassName(&quot;string&quot;, (strings) =&gt; {
+                            let heapSnapshotNode = strings.reduce((result, x) =&gt; result.id &lt; x.id ? x : result, strings[0]);
+                            HeapAgent.getPreview(heapSnapshotNode.id, (error, string, functionDetails, objectPreviewPayload) =&gt; {
+                                InspectorTest.expectThat(!error, &quot;Should not have an error getting preview.&quot;);
+                                InspectorTest.log(&quot;STRING: &quot; + string);
+                                resolve();
+                            });
+                        });
</ins><span class="cx">                     });
</span><span class="cx">                 });
</span><span class="cx">             });
</span><span class="lines">@@ -75,15 +77,17 @@
</span><span class="cx">                 InspectorTest.evaluateInPage(&quot;triggerCreateFunctionObject()&quot;);
</span><span class="cx">                 HeapAgent.snapshot((error, timestamp, snapshotStringData) =&gt; { // Newly created objects.
</span><span class="cx">                     InspectorTest.expectThat(!error, &quot;Should not have an error creating a snapshot.&quot;);
</span><del>-                    let payload = JSON.parse(snapshotStringData);
-                    let snapshot = WebInspector.HeapSnapshot.fromPayload(payload);
-
-                    let functions = snapshot.instancesWithClassName(&quot;Function&quot;);
-                    let heapSnapshotNode = functions.reduce((result, x) =&gt; result.id &lt; x.id ? x : result, functions[0]);
-                    HeapAgent.getPreview(heapSnapshotNode.id, (error, string, functionDetails, objectPreviewPayload) =&gt; {
-                        InspectorTest.expectThat(!error, &quot;Should not have an error getting preview.&quot;);
-                        InspectorTest.log(&quot;FUNCTION DETAILS: &quot; + JSON.stringify(functionDetails, jsonFilter, 4));
-                        resolve();
</del><ins>+                    let workerProxy = WebInspector.HeapSnapshotWorkerProxy.singleton();
+                    workerProxy.createSnapshot(snapshotStringData, ({objectId, snapshot: serializedSnapshot}) =&gt; {
+                        let snapshot = WebInspector.HeapSnapshotProxy.deserialize(objectId, serializedSnapshot);
+                        snapshot.instancesWithClassName(&quot;Function&quot;, (functions) =&gt; {
+                            let heapSnapshotNode = functions.reduce((result, x) =&gt; result.id &lt; x.id ? x : result, functions[0]);
+                            HeapAgent.getPreview(heapSnapshotNode.id, (error, string, functionDetails, objectPreviewPayload) =&gt; {
+                                InspectorTest.expectThat(!error, &quot;Should not have an error getting preview.&quot;);
+                                InspectorTest.log(&quot;FUNCTION DETAILS: &quot; + JSON.stringify(functionDetails, jsonFilter, 4));
+                                resolve();
+                            });
+                        });
</ins><span class="cx">                     });
</span><span class="cx">                 });
</span><span class="cx">             });
</span><span class="lines">@@ -98,15 +102,17 @@
</span><span class="cx">                 InspectorTest.evaluateInPage(&quot;triggerCreateMapObject()&quot;);
</span><span class="cx">                 HeapAgent.snapshot((error, timestamp, snapshotStringData) =&gt; { // Newly created objects.
</span><span class="cx">                     InspectorTest.expectThat(!error, &quot;Should not have an error creating a snapshot.&quot;);
</span><del>-                    let payload = JSON.parse(snapshotStringData);
-                    let snapshot = WebInspector.HeapSnapshot.fromPayload(payload);
-
-                    let maps = snapshot.instancesWithClassName(&quot;Map&quot;);
-                    let heapSnapshotNode = maps.reduce((result, x) =&gt; result.id &lt; x.id ? x : result, maps[0]);
-                    HeapAgent.getPreview(heapSnapshotNode.id, (error, string, functionDetails, objectPreviewPayload) =&gt; {
-                        InspectorTest.expectThat(!error, &quot;Should not have an error getting preview.&quot;);
-                        InspectorTest.log(&quot;OBJECT PREVIEW: &quot; + JSON.stringify(objectPreviewPayload, jsonFilter, 4));
-                        resolve();
</del><ins>+                    let workerProxy = WebInspector.HeapSnapshotWorkerProxy.singleton();
+                    workerProxy.createSnapshot(snapshotStringData, ({objectId, snapshot: serializedSnapshot}) =&gt; {
+                        let snapshot = WebInspector.HeapSnapshotProxy.deserialize(objectId, serializedSnapshot);
+                        snapshot.instancesWithClassName(&quot;Map&quot;, (maps) =&gt; {
+                            let heapSnapshotNode = maps.reduce((result, x) =&gt; result.id &lt; x.id ? x : result, maps[0]);
+                            HeapAgent.getPreview(heapSnapshotNode.id, (error, string, functionDetails, objectPreviewPayload) =&gt; {
+                                InspectorTest.expectThat(!error, &quot;Should not have an error getting preview.&quot;);
+                                InspectorTest.log(&quot;OBJECT PREVIEW: &quot; + JSON.stringify(objectPreviewPayload, jsonFilter, 4));
+                                resolve();
+                            });
+                        });
</ins><span class="cx">                     });
</span><span class="cx">                 });
</span><span class="cx">             });
</span><span class="lines">@@ -136,18 +142,21 @@
</span><span class="cx">                 InspectorTest.evaluateInPage(&quot;triggerCreateMapObject()&quot;);
</span><span class="cx">                 HeapAgent.snapshot((error, timestamp, snapshotStringData) =&gt; { // Newly created objects.
</span><span class="cx">                     InspectorTest.expectThat(!error, &quot;Should not have an error creating a snapshot.&quot;);
</span><del>-                    let payload = JSON.parse(snapshotStringData);
-                    let snapshot = WebInspector.HeapSnapshot.fromPayload(payload);
</del><ins>+                    let workerProxy = WebInspector.HeapSnapshotWorkerProxy.singleton();
+                    workerProxy.createSnapshot(snapshotStringData, ({objectId, snapshot: serializedSnapshot}) =&gt; {
+                        let snapshot = WebInspector.HeapSnapshotProxy.deserialize(objectId, serializedSnapshot);
</ins><span class="cx"> 
</span><del>-                    InspectorTest.evaluateInPage(&quot;triggerDeleteMapObject()&quot;);
-                    HeapAgent.gc();
</del><ins>+                        InspectorTest.evaluateInPage(&quot;triggerDeleteMapObject()&quot;);
+                        HeapAgent.gc();
</ins><span class="cx"> 
</span><del>-                    let maps = snapshot.instancesWithClassName(&quot;Map&quot;);
-                    let heapSnapshotNode = maps.reduce((result, x) =&gt; result.id &lt; x.id ? x : result, maps[0]);
-                    HeapAgent.getPreview(heapSnapshotNode.id, (error, string, functionDetails, objectPreviewPayload) =&gt; {
-                        InspectorTest.expectThat(error, &quot;Should get an error when object has been collected.&quot;);
-                        InspectorTest.pass(error);
-                        resolve();
</del><ins>+                        snapshot.instancesWithClassName(&quot;Map&quot;, (maps) =&gt; {
+                            let heapSnapshotNode = maps.reduce((result, x) =&gt; result.id &lt; x.id ? x : result, maps[0]);
+                            HeapAgent.getPreview(heapSnapshotNode.id, (error, string, functionDetails, objectPreviewPayload) =&gt; {
+                                InspectorTest.expectThat(error, &quot;Should get an error when object has been collected.&quot;);
+                                InspectorTest.pass(error);
+                                resolve();
+                            });
+                        });
</ins><span class="cx">                     });
</span><span class="cx">                 });
</span><span class="cx">             });
</span></span></pre></div>
<a id="trunkLayoutTestsinspectorheapgetRemoteObjecthtml"></a>
<div class="modfile"><h4>Modified: trunk/LayoutTests/inspector/heap/getRemoteObject.html (198352 => 198353)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/LayoutTests/inspector/heap/getRemoteObject.html        2016-03-17 21:01:21 UTC (rev 198352)
+++ trunk/LayoutTests/inspector/heap/getRemoteObject.html        2016-03-17 21:02:07 UTC (rev 198353)
</span><span class="lines">@@ -33,16 +33,20 @@
</span><span class="cx">         test: (resolve, reject) =&gt; {
</span><span class="cx">             HeapAgent.snapshot((error, timestamp, snapshotStringData) =&gt; {
</span><span class="cx">                 InspectorTest.expectThat(!error, &quot;Should not have an error creating a snapshot.&quot;);
</span><del>-                let payload = JSON.parse(snapshotStringData);
-                let snapshot = WebInspector.HeapSnapshot.fromPayload(payload);
-                let heapSnapshotNode = snapshot.instancesWithClassName(&quot;Window&quot;)[0];
-                InspectorTest.expectThat(heapSnapshotNode, &quot;Should should include at least one 'Window' instance.&quot;);
</del><ins>+                let workerProxy = WebInspector.HeapSnapshotWorkerProxy.singleton();
+                workerProxy.createSnapshot(snapshotStringData, ({objectId, snapshot: serializedSnapshot}) =&gt; {
+                    let snapshot = WebInspector.HeapSnapshotProxy.deserialize(objectId, serializedSnapshot);
+                    snapshot.instancesWithClassName(&quot;Window&quot;, (windows) =&gt; {
+                        let heapSnapshotNode = windows[0];
+                        InspectorTest.expectThat(heapSnapshotNode, &quot;Should should include at least one 'Window' instance.&quot;);
</ins><span class="cx"> 
</span><del>-                HeapAgent.getRemoteObject(heapSnapshotNode.id, &quot;test&quot;, (error, remoteObjectPayload) =&gt; {
-                    InspectorTest.expectThat(!error, &quot;Should not have an error getting remote object.&quot;);
-                    let remoteObject = WebInspector.RemoteObject.fromPayload(remoteObjectPayload);
-                    InspectorTest.log(remoteObject.description);
-                    resolve();
</del><ins>+                        HeapAgent.getRemoteObject(heapSnapshotNode.id, &quot;test&quot;, (error, remoteObjectPayload) =&gt; {
+                            InspectorTest.expectThat(!error, &quot;Should not have an error getting remote object.&quot;);
+                            let remoteObject = WebInspector.RemoteObject.fromPayload(remoteObjectPayload);
+                            InspectorTest.log(remoteObject.description);
+                            resolve();
+                        });
+                    });
</ins><span class="cx">                 });
</span><span class="cx">             });
</span><span class="cx">         }
</span><span class="lines">@@ -71,19 +75,22 @@
</span><span class="cx">                 InspectorTest.evaluateInPage(&quot;triggerCreateMapObject()&quot;);
</span><span class="cx">                 HeapAgent.snapshot((error, timestamp, snapshotStringData) =&gt; { // Newly created objects.
</span><span class="cx">                     InspectorTest.expectThat(!error, &quot;Should not have an error creating a snapshot.&quot;);
</span><del>-                    let payload = JSON.parse(snapshotStringData);
-                    let snapshot = WebInspector.HeapSnapshot.fromPayload(payload);
-                    let maps = snapshot.instancesWithClassName(&quot;Map&quot;);
-                    InspectorTest.expectThat(maps.length, &quot;Should should include at least one 'Map' instance.&quot;);
</del><ins>+                    let workerProxy = WebInspector.HeapSnapshotWorkerProxy.singleton();
+                    workerProxy.createSnapshot(snapshotStringData, ({objectId, snapshot: serializedSnapshot}) =&gt; {
+                        let snapshot = WebInspector.HeapSnapshotProxy.deserialize(objectId, serializedSnapshot);
+                        snapshot.instancesWithClassName(&quot;Map&quot;, (maps) =&gt; {
+                            InspectorTest.expectThat(maps.length, &quot;Should should include at least one 'Map' instance.&quot;);
</ins><span class="cx"> 
</span><del>-                    InspectorTest.evaluateInPage(&quot;triggerDeleteMapObject()&quot;);
-                    HeapAgent.gc();
</del><ins>+                            InspectorTest.evaluateInPage(&quot;triggerDeleteMapObject()&quot;);
+                            HeapAgent.gc();
</ins><span class="cx"> 
</span><del>-                    let heapSnapshotNode = maps.reduce((result, x) =&gt; result.id &lt; x.id ? x : result, maps[0]);
-                    HeapAgent.getRemoteObject(heapSnapshotNode.id, &quot;test&quot;, (error, remoteObjectPayload) =&gt; {
-                        InspectorTest.expectThat(error, &quot;Should get an error when object has been collected.&quot;);
-                        InspectorTest.pass(error);
-                        resolve();
</del><ins>+                            let heapSnapshotNode = maps.reduce((result, x) =&gt; result.id &lt; x.id ? x : result, maps[0]);
+                            HeapAgent.getRemoteObject(heapSnapshotNode.id, &quot;test&quot;, (error, remoteObjectPayload) =&gt; {
+                                InspectorTest.expectThat(error, &quot;Should get an error when object has been collected.&quot;);
+                                InspectorTest.pass(error);
+                                resolve();
+                            });
+                        });
</ins><span class="cx">                     });
</span><span class="cx">                 });
</span><span class="cx">             });
</span></span></pre></div>
<a id="trunkLayoutTestsinspectorheapsnapshothtml"></a>
<div class="modfile"><h4>Modified: trunk/LayoutTests/inspector/heap/snapshot.html (198352 => 198353)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/LayoutTests/inspector/heap/snapshot.html        2016-03-17 21:01:21 UTC (rev 198352)
+++ trunk/LayoutTests/inspector/heap/snapshot.html        2016-03-17 21:02:07 UTC (rev 198353)
</span><span class="lines">@@ -13,13 +13,17 @@
</span><span class="cx">         test: (resolve, reject) =&gt; {
</span><span class="cx">             HeapAgent.snapshot((error, timestamp, snapshotStringData) =&gt; {
</span><span class="cx">                 InspectorTest.expectThat(!error, &quot;Should not have an error creating a snapshot.&quot;);
</span><del>-                let payload = JSON.parse(snapshotStringData);
-                let snapshot = WebInspector.HeapSnapshot.fromPayload(payload);
-                InspectorTest.expectThat(snapshot.totalSize &gt; 1024, &quot;Snapshot size should be greater than 1kb.&quot;);
-                InspectorTest.expectThat(snapshot.totalObjectCount &gt; 100, &quot;Snapshot object count should be greater than 100.&quot;);
-                InspectorTest.expectThat(snapshot.categories.get(&quot;Window&quot;), &quot;Snapshot should include a class category for 'Window'.&quot;);
-                InspectorTest.expectThat(snapshot.instancesWithClassName(&quot;Window&quot;).length &gt; 0, &quot;Snapshot should include at least one 'Window' instance.&quot;);
-                resolve();
</del><ins>+                let workerProxy = WebInspector.HeapSnapshotWorkerProxy.singleton();
+                workerProxy.createSnapshot(snapshotStringData, ({objectId, snapshot: serializedSnapshot}) =&gt; {
+                    let snapshot = WebInspector.HeapSnapshotProxy.deserialize(objectId, serializedSnapshot);
+                    InspectorTest.expectThat(snapshot.totalSize &gt; 1024, &quot;Snapshot size should be greater than 1kb.&quot;);
+                    InspectorTest.expectThat(snapshot.totalObjectCount &gt; 100, &quot;Snapshot object count should be greater than 100.&quot;);
+                    InspectorTest.expectThat(snapshot.categories.get(&quot;Window&quot;), &quot;Snapshot should include a class category for 'Window'.&quot;);
+                    snapshot.instancesWithClassName(&quot;Window&quot;, (windows) =&gt; {
+                        InspectorTest.expectThat(windows.length &gt; 0, &quot;Snapshot should include at least one 'Window' instance.&quot;);
+                        resolve();
+                    });
+                });
</ins><span class="cx">             });
</span><span class="cx">         }
</span><span class="cx">     });
</span></span></pre></div>
<a id="trunkLayoutTestsinspectorunittestsheapsnapshotexpectedtxt"></a>
<div class="addfile"><h4>Added: trunk/LayoutTests/inspector/unit-tests/heap-snapshot-expected.txt (0 => 198353)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/LayoutTests/inspector/unit-tests/heap-snapshot-expected.txt                                (rev 0)
+++ trunk/LayoutTests/inspector/unit-tests/heap-snapshot-expected.txt        2016-03-17 21:02:07 UTC (rev 198353)
</span><span class="lines">@@ -0,0 +1,42 @@
</span><ins>+Testing HeapSnapshot Worker and Proxy objects.
+
+
+== Running test suite: HeapSnapshot
+-- Running test case: HeapSnapshotProxy data
+PASS: Should not have an error creating a snapshot.
+PASS: Snapshots totalSize should match.
+PASS: Snapshots totalObjectCount should match.
+
+-- Running test case: HeapSnapshotProxy.prototype.instancesWithClassName
+PASS: Should be at least 1 Window.
+PASS: Window object count is expected.
+PASS: Every className should be 'Window'.
+PASS: Should be at least 1 Function.
+PASS: Function object count is expected.
+PASS: Every className should be 'Function'.
+PASS: Should be at least 1 string.
+PASS: string count is expected.
+PASS: Every className should be 'string'.
+
+-- Running test case: HeapSnapshotProxy.prototype.nodeWithIdentifier and HeapSnapshotNodeProxy data
+PASS: Node className should be 'Window'.
+PASS: Node identifier should match.
+PASS: Node size should match.
+PASS: Node internal state should match.
+PASS: Node gcRoot state should match.
+PASS: Node retainedSize should at least be the size.
+
+-- Running test case: HeapSnapshotProxy.prototype.allocationBucketCounts
+PASS: Result should have 3 buckets, for small/medium/large.
+PASS: Small count should match.
+PASS: Medium count should match.
+PASS: Large count should match.
+
+-- Running test case: HeapSnapshotNodeProxy.prototype.retainedNodes
+PASS: Number of retained nodes should match.
+PASS: Node values should match.
+
+-- Running test case: HeapSnapshotNodeProxy.prototype.retainers
+PASS: Number of retainer nodes should match.
+PASS: Node values should match.
+
</ins></span></pre></div>
<a id="trunkLayoutTestsinspectorunittestsheapsnapshothtml"></a>
<div class="addfile"><h4>Added: trunk/LayoutTests/inspector/unit-tests/heap-snapshot.html (0 => 198353)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/LayoutTests/inspector/unit-tests/heap-snapshot.html                                (rev 0)
+++ trunk/LayoutTests/inspector/unit-tests/heap-snapshot.html        2016-03-17 21:02:07 UTC (rev 198353)
</span><span class="lines">@@ -0,0 +1,262 @@
</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()
+{
+    // Simple HeapSnapshot representation.
+
+    WebInspector.TestHeapSnapshotNode = class TestHeapSnapshotNode
+    {
+        constructor(identifier, className, size, internal)
+        {
+            this.id = identifier;
+            this.className = className;
+            this.size = size; 
+            this.internal = internal;
+            this.gcRoot = false;
+            this.outgoingEdges = [];
+            this.incomingEdges = [];
+        }
+    }
+
+    WebInspector.TestHeapSnapshotEdge = class TestHeapSnapshotEdge
+    {
+        constructor(from, to, type, data)
+        {
+            this.from = from;
+            this.to = to;
+            this.type = type;
+            this.data = data;
+        }
+    };
+
+    WebInspector.TestHeapSnapshot = class TestHeapSnapshot
+    {
+        constructor(rootNode, nodes, nodeMap)
+        {
+            console.assert(rootNode instanceof WebInspector.TestHeapSnapshotNode);
+            console.assert(nodes.every((n) =&gt; n instanceof WebInspector.TestHeapSnapshotNode));
+
+            this.rootNode = rootNode;
+            this.nodes = nodes;
+            this.nodeMap = nodeMap;
+            this.totalSize = nodes.reduce((sum, node) =&gt; sum += node.size, 0);
+            this.totalObjectCount = nodes.length;
+        }
+
+        static fromPayload(payload)
+        {
+            let {version, nodes, nodeClassNames, edges, edgeTypes, edgeNames} = payload;
+            console.assert(version === 1, &quot;Only know how to handle JavaScriptCore Heap Snapshot Format Version 1&quot;);
+
+            let nodeMap = new Map;
+
+            // Turn nodes into real nodes.
+            let processedNodes = [];
+            for (let i = 0, length = nodes.length; i &lt; length;) {
+                let id = nodes[i++];
+                let size = nodes[i++];
+                let classNameIndex = nodes[i++];
+                let internal = nodes[i++];
+
+                let node = new WebInspector.TestHeapSnapshotNode(id, nodeClassNames[classNameIndex], size, !!internal);
+                nodeMap.set(id, node);
+                processedNodes.push(node);
+            }
+
+            // Turn edges into real edges and set them on the nodes.
+            for (let i = 0, length = edges.length; i &lt; length;) {
+                let fromIdentifier = edges[i++];
+                let toIdentifier = edges[i++];
+                let edgeTypeIndex = edges[i++];
+                let data = edges[i++];
+
+                let from = nodeMap.get(fromIdentifier);
+                let to = nodeMap.get(toIdentifier);
+                let type = edgeTypes[edgeTypeIndex];
+                if (type === &quot;Property&quot; || type === &quot;Variable&quot;)
+                    data = edgeNames[data];
+
+                let edge = new WebInspector.TestHeapSnapshotEdge(from, to, type, data);
+                from.outgoingEdges.push(edge);
+                to.incomingEdges.push(edge);
+            }
+
+            // Root node.
+            let rootNode = nodeMap.get(0);
+            console.assert(rootNode, &quot;Node with identifier 0 is the synthetic &lt;root&gt; node.&quot;);
+            console.assert(rootNode.outgoingEdges.length &gt; 0, &quot;This had better have children!&quot;);
+            console.assert(rootNode.incomingEdges.length === 0, &quot;This had better not have back references!&quot;);
+
+            // Mark GC roots.
+            let rootNodeEdges = rootNode.outgoingEdges;
+            for (let i = 0, length = rootNodeEdges.length; i &lt; length; ++i)
+                rootNodeEdges[i].to.gcRoot = true;
+
+            return new WebInspector.TestHeapSnapshot(rootNode, processedNodes, nodeMap);
+        }
+
+        // Public
+
+        instancesWithClassName(className)
+        {
+            let results = [];
+            for (let i = 0; i &lt; this.nodes.length; ++i) {
+                let node = this.nodes[i];
+                if (node.className === className)
+                    results.push(node);
+            }
+            return results;
+        }
+    };
+
+    // ------
+
+    let suite = InspectorTest.createAsyncSuite(&quot;HeapSnapshot&quot;);
+
+    let snapshot = null;
+    let snapshotNodeForWindowObject = null;
+    let testSnapshot = null;
+    let testSnapshotNodeForWindowObject = null;
+
+    function compareNodes(node1, node2) {
+        return node1.id === node2.id
+            &amp;&amp; node1.size === node2.size
+            &amp;&amp; node1.className === node2.className
+            &amp;&amp; node1.internal === node2.internal
+            &amp;&amp; node1.gcRoot === node2.gcRoot;
+    }
+
+    suite.addTestCase({
+        name: &quot;HeapSnapshotProxy data&quot;,
+        test: (resolve, reject) =&gt; {
+            HeapAgent.snapshot((error, timestamp, snapshotStringData) =&gt; {
+                InspectorTest.expectThat(!error, &quot;Should not have an error creating a snapshot.&quot;);
+                testSnapshot = WebInspector.TestHeapSnapshot.fromPayload(JSON.parse(snapshotStringData));
+                let workerProxy = WebInspector.HeapSnapshotWorkerProxy.singleton();
+                workerProxy.createSnapshot(snapshotStringData, ({objectId, snapshot: serializedSnapshot}) =&gt; {
+                    snapshot = WebInspector.HeapSnapshotProxy.deserialize(objectId, serializedSnapshot);
+                    InspectorTest.assert(testSnapshot, &quot;Created TestHeapSnapshot&quot;);
+                    InspectorTest.assert(snapshot, &quot;Created HeapSnapshotProxy&quot;);
+                    InspectorTest.expectThat(snapshot.totalSize === testSnapshot.totalSize, &quot;Snapshots totalSize should match.&quot;);
+                    InspectorTest.expectThat(snapshot.totalObjectCount === testSnapshot.totalObjectCount, &quot;Snapshots totalObjectCount should match.&quot;);
+                    resolve();
+                });
+            });
+        }
+    });
+
+    suite.addTestCase({
+        name: &quot;HeapSnapshotProxy.prototype.instancesWithClassName&quot;,
+        test: (resolve, reject) =&gt; {
+            let windowObjects = testSnapshot.instancesWithClassName(&quot;Window&quot;)
+            let windowObjectCount = windowObjects.length;
+            let functionObjectCount = testSnapshot.instancesWithClassName(&quot;Function&quot;).length;
+            let stringCount = testSnapshot.instancesWithClassName(&quot;string&quot;).length;
+
+            snapshot.instancesWithClassName(&quot;Window&quot;, (windows) =&gt; {
+                testSnapshotNodeForWindowObject = windowObjects[0]; // Used by later tests.
+                snapshotNodeForWindowObject = windows[0]; // Used by later tests.
+
+                InspectorTest.expectThat(windows.length &gt; 0, &quot;Should be at least 1 Window.&quot;);
+                InspectorTest.expectThat(windows.length === windowObjectCount, &quot;Window object count is expected.&quot;);
+                InspectorTest.expectThat(windows.every((node) =&gt; node.className === &quot;Window&quot;), &quot;Every className should be 'Window'.&quot;);
+            });
+
+            snapshot.instancesWithClassName(&quot;Function&quot;, (functions) =&gt; {
+                InspectorTest.expectThat(functions.length &gt; 0, &quot;Should be at least 1 Function.&quot;);
+                InspectorTest.expectThat(functions.length === functionObjectCount, &quot;Function object count is expected.&quot;);
+                InspectorTest.expectThat(functions.every((node) =&gt; node.className === &quot;Function&quot;), &quot;Every className should be 'Function'.&quot;);
+            });
+
+            snapshot.instancesWithClassName(&quot;string&quot;, (strings) =&gt; {
+                InspectorTest.expectThat(strings.length &gt; 0, &quot;Should be at least 1 string.&quot;);
+                InspectorTest.expectThat(strings.length === stringCount, &quot;string count is expected.&quot;);
+                InspectorTest.expectThat(strings.every((node) =&gt; node.className === &quot;string&quot;), &quot;Every className should be 'string'.&quot;);
+                resolve();
+            });
+        }
+    });
+
+    suite.addTestCase({
+        name: &quot;HeapSnapshotProxy.prototype.nodeWithIdentifier and HeapSnapshotNodeProxy data&quot;,
+        test: (resolve, reject) =&gt; {
+            snapshot.nodeWithIdentifier(testSnapshotNodeForWindowObject.id, (heapSnapshotNode) =&gt; {
+                InspectorTest.expectThat(heapSnapshotNode.className === &quot;Window&quot;, &quot;Node className should be 'Window'.&quot;);
+                InspectorTest.expectThat(heapSnapshotNode.id === testSnapshotNodeForWindowObject.id, &quot;Node identifier should match.&quot;)
+                InspectorTest.expectThat(heapSnapshotNode.size === testSnapshotNodeForWindowObject.size, &quot;Node size should match.&quot;);
+                InspectorTest.expectThat(heapSnapshotNode.internal === testSnapshotNodeForWindowObject.internal, &quot;Node internal state should match.&quot;);
+                InspectorTest.expectThat(heapSnapshotNode.gcRoot === testSnapshotNodeForWindowObject.gcRoot, &quot;Node gcRoot state should match.&quot;);
+                InspectorTest.expectThat(heapSnapshotNode.retainedSize &gt;= heapSnapshotNode.size, &quot;Node retainedSize should at least be the size.&quot;);
+                resolve();
+            });
+        }
+    });
+
+    suite.addTestCase({
+        name: &quot;HeapSnapshotProxy.prototype.allocationBucketCounts&quot;,
+        test: (resolve, reject) =&gt; {
+            let testSmall = 0, testMedium = 0, testLarge = 0;
+            const smallSize = 32, mediumSize = 128;
+            for (let {size} of testSnapshot.nodes) {
+                if (size &lt; smallSize)
+                    testSmall++;
+                else if (size &lt; mediumSize)
+                    testMedium++;
+                else
+                    testLarge++;
+            }
+
+            snapshot.allocationBucketCounts([smallSize, mediumSize], (results) =&gt; {
+                let [small, medium, large] = results;
+                InspectorTest.expectThat(results.length === 3, &quot;Result should have 3 buckets, for small/medium/large.&quot;);
+                InspectorTest.expectThat(small === testSmall, &quot;Small count should match.&quot;);
+                InspectorTest.expectThat(medium === testMedium, &quot;Medium count should match.&quot;);
+                InspectorTest.expectThat(large === testLarge, &quot;Large count should match.&quot;);
+                resolve();
+            });
+        }
+    });
+
+    suite.addTestCase({
+        name: &quot;HeapSnapshotNodeProxy.prototype.retainedNodes&quot;,
+        test: (resolve, reject) =&gt; {
+            let expectedNodes = testSnapshotNodeForWindowObject.outgoingEdges.map((edge) =&gt; edge.to);
+            expectedNodes.sort((a, b) =&gt; a.id - b.id);
+
+            snapshotNodeForWindowObject.retainedNodes((nodes) =&gt; {
+                nodes.sort((a, b) =&gt; a.id - b.id);
+                InspectorTest.assert(nodes.length &gt; 0, &quot;Test only makes since if there are retained nodes&quot;);
+                InspectorTest.expectThat(nodes.length === expectedNodes.length, &quot;Number of retained nodes should match.&quot;);
+                InspectorTest.expectThat(nodes.every((node, i) =&gt; compareNodes(node, expectedNodes[i])), &quot;Node values should match.&quot;);
+                resolve();
+            });
+        }
+    });
+
+    suite.addTestCase({
+        name: &quot;HeapSnapshotNodeProxy.prototype.retainers&quot;,
+        test: (resolve, reject) =&gt; {
+            let expectedNodes = testSnapshotNodeForWindowObject.incomingEdges.map((edge) =&gt; edge.from);
+            expectedNodes.sort((a, b) =&gt; a.id - b.id);
+
+            snapshotNodeForWindowObject.retainers((nodes) =&gt; {
+                nodes.sort((a, b) =&gt; a.id - b.id);
+                InspectorTest.assert(nodes.length &gt; 0, &quot;Test only makes since if there are retainer nodes&quot;);
+                InspectorTest.expectThat(nodes.length === expectedNodes.length, &quot;Number of retainer nodes should match.&quot;);
+                InspectorTest.expectThat(nodes.every((node, i) =&gt; compareNodes(node, expectedNodes[i])), &quot;Node values should match.&quot;);
+                resolve();
+            });
+        }
+    });
+
+    suite.runTestCasesAndFinish();
+}
+&lt;/script&gt;
+&lt;/head&gt;
+&lt;body onload=&quot;runTest()&quot;&gt;
+    &lt;p&gt;Testing HeapSnapshot Worker and Proxy objects.&lt;/p&gt;
+&lt;/body&gt;
+&lt;/html&gt;
</ins></span></pre></div>
<a id="trunkSourceWebInspectorUIChangeLog"></a>
<div class="modfile"><h4>Modified: trunk/Source/WebInspectorUI/ChangeLog (198352 => 198353)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/WebInspectorUI/ChangeLog        2016-03-17 21:01:21 UTC (rev 198352)
+++ trunk/Source/WebInspectorUI/ChangeLog        2016-03-17 21:02:07 UTC (rev 198353)
</span><span class="lines">@@ -1,3 +1,200 @@
</span><ins>+2016-03-17  Joseph Pecoraro  &lt;pecoraro@apple.com&gt;
+
+        Web Inspector: HeapSnapshots are slow and use too much memory
+        https://bugs.webkit.org/show_bug.cgi?id=155571
+
+        Reviewed by Timothy Hatcher.
+
+        This is the first inclusion of Workers into Web Inspector. In this case
+        the Main side merely needs to make requests of the Worker and get back
+        objects that it can interact with more.
+        
+        New file heirarchies:
+
+            UserInterface/Proxies
+                - new Proxy classes in the Main page.
+                - treat like Model classes, but not quite model.
+
+            UserInterface/Workers/HeapSnapshotWorker
+                - new Worker classes for Workers. No WebInspector namespace.
+                - no minification of these resources, they are simply copied.
+
+        Remote procedure call interface between the Main/Worker page happens
+        through the WorkerProxy and Worker classes. There are simple ways
+        to perform factory style methods and call methods on objects, and
+        get the result in a callback. Similiar to frontend &lt;-&gt; backend agent
+        communication:
+
+            HeapSnapshotWorkerProxy: (Main world)
+                - creates the worker
+                - performAction(&quot;actionName&quot;, arguments, callback)
+                - callMethod(objectId, &quot;methodName&quot;, arguments, callback)
+                - handle message =&gt; dispatch event or invoke callback
+
+            HeapSnapshotWorker: (Worker world)
+                - sendEvent(&quot;eventName&quot;, eventData)
+                - handle message =&gt; dispatch action or method on object
+        
+        Proxy object methods are boilerplate calls to performAction/callMethod
+        with deserialization of responses. The rest of the frontend can just
+        treat Proxy objects as Model objects with some data and async methods.
+
+        Because the Node/Edge data is so small, objects are cheaply created
+        when needed and not cached. This means that there may be duplicate
+        HeapSnapshotNode's for the same node. For example if different Views
+        both request instancesWithClassName(&quot;Foo&quot;). This is fine, as none
+        of our Views really care about object uniqueness, they are only
+        interested in the data or querying for more data.
+
+        * Scripts/combine-resources.pl:
+        * Scripts/copy-user-interface-resources.pl:
+        Copy the Workers directory to the resources directory.
+        Its code is only meant to be loaded by Workers, so it
+        shouldn't be included in the Main page.
+
+        * UserInterface/Main.html:
+        * UserInterface/Test.html:
+        * UserInterface/Models/HeapSnapshot.js: Removed.
+        * UserInterface/Models/HeapSnapshotDiff.js: Removed.
+        * UserInterface/Models/HeapSnapshotEdge.js: Removed.
+        * UserInterface/Models/HeapSnapshotNode.js: Removed.
+        Replace the old simple Model classes with Proxy classes that interact
+        with the Worker.
+
+        * UserInterface/Models/HeapAllocationsInstrument.js:
+        (WebInspector.HeapAllocationsInstrument.prototype._takeHeapSnapshot):
+        (WebInspector.HeapAllocationsInstrument):
+        * UserInterface/Models/HeapAllocationsTimelineRecord.js:
+        (WebInspector.HeapAllocationsTimelineRecord):
+        * UserInterface/Models/HeapSnapshotRootPath.js:
+        (WebInspector.HeapSnapshotRootPath):
+        (WebInspector.HeapSnapshotRootPath.prototype.appendEdge):
+        * UserInterface/Protocol/HeapObserver.js:
+        (WebInspector.HeapObserver.prototype.trackingStart):
+        (WebInspector.HeapObserver.prototype.trackingComplete):
+        * UserInterface/Views/ContentView.js:
+        (WebInspector.ContentView.createFromRepresentedObject):
+        (WebInspector.ContentView.isViewable):
+        * UserInterface/Views/HeapAllocationsTimelineView.js:
+        (WebInspector.HeapAllocationsTimelineView.prototype.showHeapSnapshotDiff):
+        (WebInspector.HeapAllocationsTimelineView.prototype._takeHeapSnapshotClicked):
+        (WebInspector.HeapAllocationsTimelineView.prototype._dataGridNodeSelected):
+        (WebInspector.HeapAllocationsTimelineView):
+        * UserInterface/Views/HeapSnapshotClassDataGridNode.js:
+        (WebInspector.HeapSnapshotClassDataGridNode.prototype._populate):
+        * UserInterface/Views/HeapSnapshotClusterContentView.js:
+        * UserInterface/Views/HeapSnapshotInstanceDataGridNode.js:
+        (WebInspector.HeapSnapshotInstanceDataGridNode):
+        (WebInspector.HeapSnapshotInstanceDataGridNode.logHeapSnapshotNode.node.shortestGCRootPath.):
+        (WebInspector.HeapSnapshotInstanceDataGridNode.logHeapSnapshotNode):
+        (WebInspector.HeapSnapshotInstanceDataGridNode.prototype._mouseoverHandler.appendPath):
+        (WebInspector.HeapSnapshotInstanceDataGridNode.prototype._mouseoverHandler.stringifyEdge):
+        (WebInspector.HeapSnapshotInstanceDataGridNode.prototype._mouseoverHandler):
+        * UserInterface/Views/HeapSnapshotInstancesContentView.js:
+        (WebInspector.HeapSnapshotInstancesContentView):
+        * UserInterface/Views/HeapSnapshotInstancesDataGridTree.js:
+        (WebInspector.HeapSnapshotInstancesDataGridTree):
+        * UserInterface/Views/HeapSnapshotSummaryContentView.js:
+        (WebInspector.HeapSnapshotSummaryContentView):
+        Update existing code to expect the new Proxy objects or create
+        the new HeapSnapshot using workers.
+
+        * UserInterface/Proxies/HeapSnapshotDiffProxy.js: Added.
+        (WebInspector.HeapSnapshotDiffProxy):
+        (WebInspector.HeapSnapshotDiffProxy.deserialize):
+        (WebInspector.HeapSnapshotDiffProxy.prototype.get snapshot1):
+        (WebInspector.HeapSnapshotDiffProxy.prototype.get snapshot2):
+        (WebInspector.HeapSnapshotDiffProxy.prototype.get totalSize):
+        (WebInspector.HeapSnapshotDiffProxy.prototype.get totalObjectCount):
+        (WebInspector.HeapSnapshotDiffProxy.prototype.get categories):
+        (WebInspector.HeapSnapshotDiffProxy.prototype.allocationBucketCounts):
+        (WebInspector.HeapSnapshotDiffProxy.prototype.instancesWithClassName):
+        (WebInspector.HeapSnapshotDiffProxy.prototype.nodeWithIdentifier):
+        A HeapSnapshotDiffProxy looks like a HeapSnapshotProxy and responds to
+        the same methods, but has the extra snapshot1/2 pointers.
+
+        * UserInterface/Proxies/HeapSnapshotEdgeProxy.js:
+        (WebInspector.HeapSnapshotEdgeProxy):
+        (WebInspector.HeapSnapshotEdgeProxy.deserialize):
+        Edge data. No methods are proxied at this point.
+
+        * UserInterface/Proxies/HeapSnapshotNodeProxy.js: Added.
+        (WebInspector.HeapSnapshotNodeProxy):
+        (WebInspector.HeapSnapshotNodeProxy.deserialize):
+        (WebInspector.HeapSnapshotNodeProxy.prototype.shortestGCRootPath):
+        (WebInspector.HeapSnapshotNodeProxy.prototype.dominatedNodes):
+        (WebInspector.HeapSnapshotNodeProxy.prototype.retainedNodes):
+        (WebInspector.HeapSnapshotNodeProxy.prototype.retainers):
+        Node data and methods to query for node relationships.
+
+        * UserInterface/Proxies/HeapSnapshotProxy.js: Added.
+        (WebInspector.HeapSnapshotProxy):
+        (WebInspector.HeapSnapshotProxy.deserialize):
+        (WebInspector.HeapSnapshotProxy.prototype.get proxyObjectId):
+        (WebInspector.HeapSnapshotProxy.prototype.get identifier):
+        (WebInspector.HeapSnapshotProxy.prototype.get totalSize):
+        (WebInspector.HeapSnapshotProxy.prototype.get totalObjectCount):
+        (WebInspector.HeapSnapshotProxy.prototype.get categories):
+        (WebInspector.HeapSnapshotProxy.prototype.allocationBucketCounts):
+        (WebInspector.HeapSnapshotProxy.prototype.instancesWithClassName):
+        (WebInspector.HeapSnapshotProxy.prototype.nodeWithIdentifier):
+        Snapshot data and methods to query for nodes.
+
+        * UserInterface/Proxies/HeapSnapshotWorkerProxy.js: Added.
+        (WebInspector.HeapSnapshotWorkerProxy):
+        (WebInspector.HeapSnapshotWorkerProxy.singleton):
+        (WebInspector.HeapSnapshotWorkerProxy.prototype.createSnapshot):
+        (WebInspector.HeapSnapshotWorkerProxy.prototype.createSnapshotDiff):
+        (WebInspector.HeapSnapshotWorkerProxy.prototype.performAction):
+        (WebInspector.HeapSnapshotWorkerProxy.prototype.callMethod):
+        (WebInspector.HeapSnapshotWorkerProxy.prototype._postMessage):
+        (WebInspector.HeapSnapshotWorkerProxy.prototype._handleMessage):
+        Singleton factory for the worker and proxied communication with the worker.
+        Provide means for invoking &quot;factory actions&quot; and &quot;object methods&quot;.
+
+        * UserInterface/Workers/HeapSnapshot/HeapSnapshotWorker.js: Added.
+        (HeapSnapshotWorker):
+        (HeapSnapshotWorker.prototype.createSnapshot):
+        (HeapSnapshotWorker.prototype.createSnapshotDiff):
+        (HeapSnapshotWorker.prototype.sendEvent):
+        (HeapSnapshotWorker.prototype._handleMessage):
+        Main worker code. Handle dispatching actions and methods.
+
+        * UserInterface/Workers/HeapSnapshot/HeapSnapshot.js: Added.
+        (HeapSnapshot):
+        (HeapSnapshot.buildCategories):
+        (HeapSnapshot.allocationBucketCounts):
+        (HeapSnapshot.instancesWithClassName):
+        (HeapSnapshot.prototype.allocationBucketCounts):
+        (HeapSnapshot.prototype.instancesWithClassName):
+        (HeapSnapshot.prototype.nodeWithIdentifier):
+        (HeapSnapshot.prototype.shortestGCRootPath):
+        (HeapSnapshot.prototype.dominatedNodes):
+        (HeapSnapshot.prototype.retainedNodes):
+        (HeapSnapshot.prototype.retainers):
+        (HeapSnapshot.prototype.serialize):
+        (HeapSnapshot.prototype.serializeNode):
+        (HeapSnapshot.prototype.serializeEdge):
+        (HeapSnapshot.prototype._buildOutgoingEdges):
+        (HeapSnapshot.prototype._buildIncomingEdges):
+        (HeapSnapshot.prototype._buildPostOrderIndexes):
+        (HeapSnapshot.prototype._buildDominatorIndexes):
+        (HeapSnapshot.prototype._buildRetainedSizes):
+        (HeapSnapshot.prototype._gcRootPathes.visitNode):
+        (HeapSnapshot.prototype._gcRootPathes):
+        (HeapSnapshotDiff):
+        (HeapSnapshotDiff.prototype.allocationBucketCounts):
+        (HeapSnapshotDiff.prototype.instancesWithClassName):
+        (HeapSnapshotDiff.prototype.nodeWithIdentifier):
+        (HeapSnapshotDiff.prototype.shortestGCRootPath):
+        (HeapSnapshotDiff.prototype.dominatedNodes):
+        (HeapSnapshotDiff.prototype.retainedNodes):
+        (HeapSnapshotDiff.prototype.retainers):
+        (HeapSnapshotDiff.prototype.serialize):
+        New HeapSnapshot data processing implementation. Instead of creating
+        a new object per Node or per Edge create data arrays containing data
+        per-Node. Operate on these lists of data instead of creating many objects.
+
</ins><span class="cx"> 2016-03-17  Nikita Vasilyev  &lt;nvasilyev@apple.com&gt;
</span><span class="cx"> 
</span><span class="cx">         Web Inspector: Large repaints when typing any character in console
</span></span></pre></div>
<a id="trunkSourceWebInspectorUIScriptscombineresourcespl"></a>
<div class="modfile"><h4>Modified: trunk/Source/WebInspectorUI/Scripts/combine-resources.pl (198352 => 198353)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/WebInspectorUI/Scripts/combine-resources.pl        2016-03-17 21:01:21 UTC (rev 198352)
+++ trunk/Source/WebInspectorUI/Scripts/combine-resources.pl        2016-03-17 21:02:07 UTC (rev 198353)
</span><span class="lines">@@ -103,7 +103,7 @@
</span><span class="cx">     }
</span><span class="cx"> }
</span><span class="cx"> 
</span><del>-my $inputDirectoryPattern = &quot;(?!External\/)[^\&quot;]*&quot;;
</del><ins>+my $inputDirectoryPattern = &quot;(?!External\/)(?!Workers\/)[^\&quot;]*&quot;;
</ins><span class="cx"> $inputDirectoryPattern = $inputDirectory . &quot;\/[^\&quot;]*&quot; if $inputDirectory;
</span><span class="cx"> 
</span><span class="cx"> concatenateFiles($outputStylesheetName, &quot;&lt;link rel=\&quot;stylesheet\&quot; href=\&quot;($inputDirectoryPattern)\&quot;&gt;&quot;, &quot;&lt;link rel=\&quot;stylesheet\&quot; href=\&quot;$outputStylesheetName\&quot;&gt;&quot;) if defined $outputStylesheetName;
</span></span></pre></div>
<a id="trunkSourceWebInspectorUIScriptscopyuserinterfaceresourcespl"></a>
<div class="modfile"><h4>Modified: trunk/Source/WebInspectorUI/Scripts/copy-user-interface-resources.pl (198352 => 198353)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/WebInspectorUI/Scripts/copy-user-interface-resources.pl        2016-03-17 21:01:21 UTC (rev 198352)
+++ trunk/Source/WebInspectorUI/Scripts/copy-user-interface-resources.pl        2016-03-17 21:02:07 UTC (rev 198353)
</span><span class="lines">@@ -125,6 +125,7 @@
</span><span class="cx"> my $uiRoot = File::Spec-&gt;catdir($ENV{'SRCROOT'}, 'UserInterface');
</span><span class="cx"> my $targetResourcePath = File::Spec-&gt;catdir($ENV{'TARGET_BUILD_DIR'}, $ENV{'UNLOCALIZED_RESOURCES_FOLDER_PATH'});
</span><span class="cx"> my $protocolDir = File::Spec-&gt;catdir($targetResourcePath, 'Protocol');
</span><ins>+my $workersDir = File::Spec-&gt;catdir($targetResourcePath, 'Workers');
</ins><span class="cx"> my $codeMirrorPath = File::Spec-&gt;catdir($uiRoot, 'External', 'CodeMirror');
</span><span class="cx"> my $esprimaPath = File::Spec-&gt;catdir($uiRoot, 'External', 'Esprima');
</span><span class="cx"> my $eslintPath = File::Spec-&gt;catdir($uiRoot, 'External', 'ESLint');
</span><span class="lines">@@ -238,8 +239,9 @@
</span><span class="cx">     # Remove ESLint until needed: &lt;https://webkit.org/b/136515&gt; Web Inspector: JavaScript source text editor should have a linter
</span><span class="cx">     unlink $targetESLintJS;
</span><span class="cx"> 
</span><del>-    # Copy the Legacy directory.
</del><ins>+    # Copy the Protocol/Legacy and Workers directories.
</ins><span class="cx">     ditto(File::Spec-&gt;catfile($uiRoot, 'Protocol', 'Legacy'), File::Spec-&gt;catfile($protocolDir, 'Legacy'));
</span><ins>+    ditto(File::Spec-&gt;catfile($uiRoot, 'Workers'), $workersDir);
</ins><span class="cx"> } else {
</span><span class="cx">     # Keep the files separate for engineering builds.
</span><span class="cx">     ditto($uiRoot, $targetResourcePath);
</span></span></pre></div>
<a id="trunkSourceWebInspectorUIUserInterfaceMainhtml"></a>
<div class="modfile"><h4>Modified: trunk/Source/WebInspectorUI/UserInterface/Main.html (198352 => 198353)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/WebInspectorUI/UserInterface/Main.html        2016-03-17 21:01:21 UTC (rev 198352)
+++ trunk/Source/WebInspectorUI/UserInterface/Main.html        2016-03-17 21:02:07 UTC (rev 198353)
</span><span class="lines">@@ -315,10 +315,6 @@
</span><span class="cx">     &lt;script src=&quot;Models/Gradient.js&quot;&gt;&lt;/script&gt;
</span><span class="cx">     &lt;script src=&quot;Models/HeapAllocationsInstrument.js&quot;&gt;&lt;/script&gt;
</span><span class="cx">     &lt;script src=&quot;Models/HeapAllocationsTimelineRecord.js&quot;&gt;&lt;/script&gt;
</span><del>-    &lt;script src=&quot;Models/HeapSnapshot.js&quot;&gt;&lt;/script&gt;
-    &lt;script src=&quot;Models/HeapSnapshotDiff.js&quot;&gt;&lt;/script&gt;
-    &lt;script src=&quot;Models/HeapSnapshotEdge.js&quot;&gt;&lt;/script&gt;
-    &lt;script src=&quot;Models/HeapSnapshotNode.js&quot;&gt;&lt;/script&gt;
</del><span class="cx">     &lt;script src=&quot;Models/HeapSnapshotRootPath.js&quot;&gt;&lt;/script&gt;
</span><span class="cx">     &lt;script src=&quot;Models/IndexedDatabase.js&quot;&gt;&lt;/script&gt;
</span><span class="cx">     &lt;script src=&quot;Models/IndexedDatabaseObjectStore.js&quot;&gt;&lt;/script&gt;
</span><span class="lines">@@ -379,6 +375,12 @@
</span><span class="cx">     &lt;script src=&quot;Models/TypeSet.js&quot;&gt;&lt;/script&gt;
</span><span class="cx">     &lt;script src=&quot;Models/WrappedPromise.js&quot;&gt;&lt;/script&gt;
</span><span class="cx"> 
</span><ins>+    &lt;script src=&quot;Proxies/HeapSnapshotDiffProxy.js&quot;&gt;&lt;/script&gt;
+    &lt;script src=&quot;Proxies/HeapSnapshotEdgeProxy.js&quot;&gt;&lt;/script&gt;
+    &lt;script src=&quot;Proxies/HeapSnapshotNodeProxy.js&quot;&gt;&lt;/script&gt;
+    &lt;script src=&quot;Proxies/HeapSnapshotProxy.js&quot;&gt;&lt;/script&gt;
+    &lt;script src=&quot;Proxies/HeapSnapshotWorkerProxy.js&quot;&gt;&lt;/script&gt;
+
</ins><span class="cx">     &lt;script src=&quot;Views/View.js&quot;&gt;&lt;/script&gt;
</span><span class="cx">     &lt;script src=&quot;Views/Dialog.js&quot;&gt;&lt;/script&gt;
</span><span class="cx"> 
</span></span></pre></div>
<a id="trunkSourceWebInspectorUIUserInterfaceModelsHeapAllocationsInstrumentjs"></a>
<div class="modfile"><h4>Modified: trunk/Source/WebInspectorUI/UserInterface/Models/HeapAllocationsInstrument.js (198352 => 198353)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/WebInspectorUI/UserInterface/Models/HeapAllocationsInstrument.js        2016-03-17 21:01:21 UTC (rev 198352)
+++ trunk/Source/WebInspectorUI/UserInterface/Models/HeapAllocationsInstrument.js        2016-03-17 21:02:07 UTC (rev 198353)
</span><span class="lines">@@ -74,9 +74,11 @@
</span><span class="cx">     _takeHeapSnapshot()
</span><span class="cx">     {
</span><span class="cx">         HeapAgent.snapshot(function(error, timestamp, snapshotStringData) {
</span><del>-            let payload = JSON.parse(snapshotStringData);
-            let snapshot = WebInspector.HeapSnapshot.fromPayload(payload);
-            WebInspector.timelineManager.heapSnapshotAdded(timestamp, snapshot);
</del><ins>+            let workerProxy = WebInspector.HeapSnapshotWorkerProxy.singleton();
+            workerProxy.createSnapshot(snapshotStringData, ({objectId, snapshot: serializedSnapshot}) =&gt; {
+                let snapshot = WebInspector.HeapSnapshotProxy.deserialize(objectId, serializedSnapshot);
+                WebInspector.timelineManager.heapSnapshotAdded(timestamp, snapshot);
+            });
</ins><span class="cx">         });
</span><span class="cx">     }
</span><span class="cx"> };
</span></span></pre></div>
<a id="trunkSourceWebInspectorUIUserInterfaceModelsHeapAllocationsTimelineRecordjs"></a>
<div class="modfile"><h4>Modified: trunk/Source/WebInspectorUI/UserInterface/Models/HeapAllocationsTimelineRecord.js (198352 => 198353)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/WebInspectorUI/UserInterface/Models/HeapAllocationsTimelineRecord.js        2016-03-17 21:01:21 UTC (rev 198352)
+++ trunk/Source/WebInspectorUI/UserInterface/Models/HeapAllocationsTimelineRecord.js        2016-03-17 21:02:07 UTC (rev 198353)
</span><span class="lines">@@ -30,7 +30,7 @@
</span><span class="cx">         super(WebInspector.TimelineRecord.Type.HeapAllocations, timestamp, timestamp);
</span><span class="cx"> 
</span><span class="cx">         console.assert(typeof timestamp === &quot;number&quot;);
</span><del>-        console.assert(heapSnapshot instanceof WebInspector.HeapSnapshot);
</del><ins>+        console.assert(heapSnapshot instanceof WebInspector.HeapSnapshotProxy);
</ins><span class="cx"> 
</span><span class="cx">         this._timestamp = timestamp;
</span><span class="cx">         this._heapSnapshot = heapSnapshot;
</span></span></pre></div>
<a id="trunkSourceWebInspectorUIUserInterfaceModelsHeapSnapshotjs"></a>
<div class="delfile"><h4>Deleted: trunk/Source/WebInspectorUI/UserInterface/Models/HeapSnapshot.js (198352 => 198353)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/WebInspectorUI/UserInterface/Models/HeapSnapshot.js        2016-03-17 21:01:21 UTC (rev 198352)
+++ trunk/Source/WebInspectorUI/UserInterface/Models/HeapSnapshot.js        2016-03-17 21:02:07 UTC (rev 198353)
</span><span class="lines">@@ -1,158 +0,0 @@
</span><del>-/*
- * 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.HeapSnapshotClassCategory = class HeapSnapshotClassCategory
-{
-    constructor(className)
-    {
-        this.className = className;
-        this.size = 0;
-        this.count = 0;
-        this.internalCount = 0;
-    }
-};
-
-WebInspector.HeapSnapshot = class HeapSnapshot extends WebInspector.Object
-{
-    constructor(rootNode, nodes, nodeMap)
-    {
-        super();
-
-        console.assert(!rootNode || rootNode instanceof WebInspector.HeapSnapshotNode);
-        console.assert(nodes instanceof Array);
-
-        this._rootNode = rootNode;
-        this._nodes = nodes;
-        this._nodeMap = nodeMap;
-
-        this._identifier = WebInspector.HeapSnapshot._nextAvailableSnapshotIdentifier++;
-        this._instances = nodes;
-
-        let categories = {};
-        for (let i = 0; i &lt; nodes.length; ++i) {
-            let {className, size, internal} = nodes[i];
-
-            let category = categories[className];
-            if (!category)
-                category = categories[className] = new WebInspector.HeapSnapshotClassCategory(className);
-
-            category.size += size;
-            category.count++;
-            if (internal)
-                category.internalCount++;
-        }
-        this._categories = Map.fromObject(categories);
-
-        this._totalSize = 0;
-        this._totalObjectCount = 0;
-        for (let {count, size} of this._categories.values()) {
-            this._totalSize += size;
-            this._totalObjectCount += count;
-        }
-    }
-
-    // Static
-
-    static fromPayload(payload)
-    {
-        let {version, nodes, nodeClassNames, edges, edgeTypes, edgeNames} = payload;
-        console.assert(version === 1, &quot;Only know how to handle JavaScriptCore Heap Snapshot Format Version 1&quot;);
-        console.assert(edgeTypes.every((type) =&gt; type in WebInspector.HeapSnapshotEdge.EdgeType), &quot;Unexpected edge type&quot;, edgeTypes);
-
-        let nodeMap = new Map;
-
-        // Turn nodes into real nodes.
-        let processedNodes = [];
-        for (let i = 0, length = nodes.length; i &lt; length;) {
-            let id = nodes[i++];
-            let size = nodes[i++];
-            let classNameIndex = nodes[i++];
-            let internal = nodes[i++];
-
-            let node = new WebInspector.HeapSnapshotNode(id, nodeClassNames[classNameIndex], size, !!internal);
-            nodeMap.set(id, node);
-            processedNodes.push(node);
-        }
-
-        // Turn edges into real edges and set them on the nodes.
-        for (let i = 0, length = edges.length; i &lt; length;) {
-            let fromIdentifier = edges[i++];
-            let toIdentifier = edges[i++];
-            let edgeTypeIndex = edges[i++];
-            let data = edges[i++];
-
-            let from = nodeMap.get(fromIdentifier);
-            let to = nodeMap.get(toIdentifier);
-            let type = edgeTypes[edgeTypeIndex];
-            if (type === WebInspector.HeapSnapshotEdge.EdgeType.Property || type === WebInspector.HeapSnapshotEdge.EdgeType.Variable)
-                data = edgeNames[data];
-
-            let edge = new WebInspector.HeapSnapshotEdge(from, to, type, data);
-            from.outgoingEdges.push(edge);
-            to.incomingEdges.push(edge);
-        }
-
-        // Root node.
-        let rootNode = nodeMap.get(0);
-        console.assert(rootNode, &quot;Node with identifier 0 is the synthetic &lt;root&gt; node.&quot;);
-        console.assert(rootNode.outgoingEdges.length &gt; 0, &quot;This had better have children!&quot;);
-        console.assert(rootNode.incomingEdges.length === 0, &quot;This had better not have back references!&quot;);
-
-        // Mark GC roots.
-        let rootNodeEdges = rootNode.outgoingEdges;
-        for (let i = 0, length = rootNodeEdges.length; i &lt; length; ++i)
-            rootNodeEdges[i].to.gcRoot = true;
-
-        return new WebInspector.HeapSnapshot(rootNode, processedNodes, nodeMap);
-    }
-
-    // Public
-
-    get rootNode() { return this._rootNode; }
-    get nodes() { return this._nodes; }
-    get identifier() { return this._identifier; }
-    get instances() { return this._instances; }
-    get categories() { return this._categories; }
-    get totalSize() { return this._totalSize; }
-    get totalObjectCount() { return this._totalObjectCount; }
-
-    instancesWithClassName(className)
-    {
-        let results = [];
-        for (let i = 0; i &lt; this._instances.length; ++i) {
-            let cell = this._instances[i];
-            if (cell.className === className)
-                results.push(cell);
-        }
-        return results;
-    }
-
-    nodeWithObjectIdentifier(id)
-    {
-        return this._nodeMap.get(id);
-    }
-};
-
-WebInspector.HeapSnapshot._nextAvailableSnapshotIdentifier = 1;
</del></span></pre></div>
<a id="trunkSourceWebInspectorUIUserInterfaceModelsHeapSnapshotDiffjs"></a>
<div class="delfile"><h4>Deleted: trunk/Source/WebInspectorUI/UserInterface/Models/HeapSnapshotDiff.js (198352 => 198353)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/WebInspectorUI/UserInterface/Models/HeapSnapshotDiff.js        2016-03-17 21:01:21 UTC (rev 198352)
+++ trunk/Source/WebInspectorUI/UserInterface/Models/HeapSnapshotDiff.js        2016-03-17 21:02:07 UTC (rev 198353)
</span><span class="lines">@@ -1,78 +0,0 @@
</span><del>-/*
- * 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.HeapSnapshotDiff = class HeapSnapshotDiff extends WebInspector.Object
-{
-    constructor(snapshot1, snapshot2)
-    {
-        super();
-
-        console.assert(snapshot1 instanceof WebInspector.HeapSnapshot);
-        console.assert(snapshot2 instanceof WebInspector.HeapSnapshot);
-
-        this._snapshot1 = snapshot1;
-        this._snapshot2 = snapshot2;
-
-        let known = new Map;
-        for (let instance of snapshot1.instances)
-            known.set(instance.id, instance);
-
-        let added = [];
-        for (let instance of snapshot2.instances) {
-            if (known.has(instance.id))
-                known.delete(instance.id);
-            else
-                added.push(instance);
-        }
-
-        let removed = [...known.values()];
-
-        this._addedInstances = added;
-        this._removedInstances = removed;
-    }
-
-    // Public
-
-    get snapshot1() { return this._snapshot1; }
-    get snapshot2() { return this._snapshot2; }
-    get addedInstances() { return this._addedInstances; }
-    get removedInstances() { return this._removedInstances; }
-
-    get sizeDifference()
-    {
-        return this._snapshot2.totalSize - this._snapshot1.totalSize;
-    }
-
-    get growth()
-    {
-        return this._addedInstances.reduce((sum, x) =&gt; sum += x.size, 0);
-    }
-
-    snapshotForDiff()
-    {
-        // FIXME: This only includes the newly added instances. Should we do anything with the removed instances?
-        return new WebInspector.HeapSnapshot(null, this._addedInstances, this._snapshot2.nodeMap);
-    }
-};
</del></span></pre></div>
<a id="trunkSourceWebInspectorUIUserInterfaceModelsHeapSnapshotEdgejs"></a>
<div class="delfile"><h4>Deleted: trunk/Source/WebInspectorUI/UserInterface/Models/HeapSnapshotEdge.js (198352 => 198353)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/WebInspectorUI/UserInterface/Models/HeapSnapshotEdge.js        2016-03-17 21:01:21 UTC (rev 198352)
+++ trunk/Source/WebInspectorUI/UserInterface/Models/HeapSnapshotEdge.js        2016-03-17 21:02:07 UTC (rev 198353)
</span><span class="lines">@@ -1,44 +0,0 @@
</span><del>-/*
- * 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.
- */
-
-// Directed edge between two HeapSnapshotNodes 'from' and 'to'.
-
-WebInspector.HeapSnapshotEdge = class HeapSnapshotEdge
-{
-    constructor(from, to, type, data)
-    {
-        this.from = from;
-        this.to = to;
-        this.type = type;
-        this.data = data;
-    }
-};
-
-WebInspector.HeapSnapshotEdge.EdgeType = {
-    Internal: &quot;Internal&quot;,       // No data.
-    Property: &quot;Property&quot;,       // data is string property name.
-    Index: &quot;Index&quot;,             // data is numeric index.
-    Variable: &quot;Variable&quot;,       // data is string variable name.
-};
</del></span></pre></div>
<a id="trunkSourceWebInspectorUIUserInterfaceModelsHeapSnapshotNodejs"></a>
<div class="delfile"><h4>Deleted: trunk/Source/WebInspectorUI/UserInterface/Models/HeapSnapshotNode.js (198352 => 198353)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/WebInspectorUI/UserInterface/Models/HeapSnapshotNode.js        2016-03-17 21:01:21 UTC (rev 198352)
+++ trunk/Source/WebInspectorUI/UserInterface/Models/HeapSnapshotNode.js        2016-03-17 21:02:07 UTC (rev 198353)
</span><span class="lines">@@ -1,96 +0,0 @@
</span><del>-/*
- * 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.HeapSnapshotNode = class HeapSnapshotNode
-{
-    constructor(identifier, className, size, internal)
-    {
-        this.id = identifier;
-        this.className = className;
-        this.size = size; 
-        this.internal = internal;
-        this.gcRoot = false;
-        this.outgoingEdges = [];
-        this.incomingEdges = [];
-    }
-
-    // Public
-
-    get shortestGCRootPath()
-    {
-        // Returns an array from this node to a gcRoot node.
-        // E.g. [Node, Edge, Node, Edge, Node].
-        // Internal nodes are avoided, so if the path is empty this
-        // node is either a gcRoot or only reachable via Internal nodes.
-
-        if (this._shortestGCRootPath !== undefined)
-            return this._shortestGCRootPath;
-
-        let paths = this._gcRootPaths();
-        paths.sort((a, b) =&gt; a.length - b.length);
-        this._shortestGCRootPath = paths[0] || null;
-
-        return this._shortestGCRootPath;
-    }
-
-    // Private
-
-    _gcRootPaths()
-    {
-        if (this.gcRoot)
-            return [];
-
-        let paths = [];
-        let currentPath = [];
-        let visitedSet = new Set;
-
-        function visitNode(node) {
-            if (node.gcRoot) {
-                let fullPath = currentPath.slice();
-                fullPath.push(node);
-                paths.push(fullPath);
-                return;
-            }
-
-            if (visitedSet.has(node))
-                return;
-            visitedSet.add(node);
-
-            currentPath.push(node);
-            for (let parentEdge of node.incomingEdges) {
-                if (parentEdge.from.internal)
-                    continue;
-                currentPath.push(parentEdge);
-                visitNode(parentEdge.from);
-                currentPath.pop();
-            }
-            currentPath.pop();
-        }
-
-        visitNode(this);
-
-        return paths;
-    }
-};
</del></span></pre></div>
<a id="trunkSourceWebInspectorUIUserInterfaceModelsHeapSnapshotRootPathjs"></a>
<div class="modfile"><h4>Modified: trunk/Source/WebInspectorUI/UserInterface/Models/HeapSnapshotRootPath.js (198352 => 198353)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/WebInspectorUI/UserInterface/Models/HeapSnapshotRootPath.js        2016-03-17 21:01:21 UTC (rev 198352)
+++ trunk/Source/WebInspectorUI/UserInterface/Models/HeapSnapshotRootPath.js        2016-03-17 21:02:07 UTC (rev 198353)
</span><span class="lines">@@ -29,7 +29,7 @@
</span><span class="cx">     {
</span><span class="cx">         super();
</span><span class="cx"> 
</span><del>-        console.assert(!node || node instanceof WebInspector.HeapSnapshotNode);
</del><ins>+        console.assert(!node || node instanceof WebInspector.HeapSnapshotNodeProxy);
</ins><span class="cx">         console.assert(!pathComponent || typeof pathComponent === &quot;string&quot;);
</span><span class="cx">         console.assert(!parent || parent instanceof WebInspector.HeapSnapshotRootPath);
</span><span class="cx"> 
</span><span class="lines">@@ -136,16 +136,16 @@
</span><span class="cx"> 
</span><span class="cx">     appendEdge(edge)
</span><span class="cx">     {
</span><del>-        console.assert(edge instanceof WebInspector.HeapSnapshotEdge);
</del><ins>+        console.assert(edge instanceof WebInspector.HeapSnapshotEdgeProxy);
</ins><span class="cx"> 
</span><span class="cx">         switch (edge.type) {
</span><del>-        case WebInspector.HeapSnapshotEdge.EdgeType.Internal:
</del><ins>+        case WebInspector.HeapSnapshotEdgeProxy.EdgeType.Internal:
</ins><span class="cx">             return this.appendInternal(edge.to);
</span><del>-        case WebInspector.HeapSnapshotEdge.EdgeType.Index:
</del><ins>+        case WebInspector.HeapSnapshotEdgeProxy.EdgeType.Index:
</ins><span class="cx">             return this.appendArrayIndex(edge.to, edge.data);
</span><del>-        case WebInspector.HeapSnapshotEdge.EdgeType.Property:
</del><ins>+        case WebInspector.HeapSnapshotEdgeProxy.EdgeType.Property:
</ins><span class="cx">             return this.appendPropertyName(edge.to, edge.data);
</span><del>-        case WebInspector.HeapSnapshotEdge.EdgeType.Variable:
</del><ins>+        case WebInspector.HeapSnapshotEdgeProxy.EdgeType.Variable:
</ins><span class="cx">             return this.appendVariableName(edge.to, edge.data);
</span><span class="cx">         }
</span><span class="cx"> 
</span></span></pre></div>
<a id="trunkSourceWebInspectorUIUserInterfaceProtocolHeapObserverjs"></a>
<div class="modfile"><h4>Modified: trunk/Source/WebInspectorUI/UserInterface/Protocol/HeapObserver.js (198352 => 198353)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/WebInspectorUI/UserInterface/Protocol/HeapObserver.js        2016-03-17 21:01:21 UTC (rev 198352)
+++ trunk/Source/WebInspectorUI/UserInterface/Protocol/HeapObserver.js        2016-03-17 21:02:07 UTC (rev 198353)
</span><span class="lines">@@ -32,17 +32,21 @@
</span><span class="cx">         WebInspector.heapManager.garbageCollected(collection);
</span><span class="cx">     }
</span><span class="cx"> 
</span><del>-    trackingStart(timestamp, snapshotData)
</del><ins>+    trackingStart(timestamp, snapshotStringData)
</ins><span class="cx">     {
</span><del>-        let payload = JSON.parse(snapshotData);
-        let snapshot = WebInspector.HeapSnapshot.fromPayload(payload);
-        WebInspector.timelineManager.heapTrackingStarted(timestamp, snapshot);
</del><ins>+        let workerProxy = WebInspector.HeapSnapshotWorkerProxy.singleton();
+        workerProxy.createSnapshot(snapshotStringData, ({objectId, snapshot: serializedSnapshot}) =&gt; {
+            let snapshot = WebInspector.HeapSnapshotProxy.deserialize(objectId, serializedSnapshot);
+            WebInspector.timelineManager.heapTrackingStarted(timestamp, snapshot);
+        });
</ins><span class="cx">     }
</span><span class="cx"> 
</span><del>-    trackingComplete(timestamp, snapshotData)
</del><ins>+    trackingComplete(timestamp, snapshotStringData)
</ins><span class="cx">     {
</span><del>-        let payload = JSON.parse(snapshotData);
-        let snapshot = WebInspector.HeapSnapshot.fromPayload(payload);
-        WebInspector.timelineManager.heapTrackingCompleted(timestamp, snapshot);
</del><ins>+        let workerProxy = WebInspector.HeapSnapshotWorkerProxy.singleton();
+        workerProxy.createSnapshot(snapshotStringData, ({objectId, snapshot: serializedSnapshot}) =&gt; {
+            let snapshot = WebInspector.HeapSnapshotProxy.deserialize(objectId, serializedSnapshot);
+            WebInspector.timelineManager.heapTrackingCompleted(timestamp, snapshot);
+        });
</ins><span class="cx">     }
</span><span class="cx"> };
</span></span></pre></div>
<a id="trunkSourceWebInspectorUIUserInterfaceProxiesHeapSnapshotDiffProxyjs"></a>
<div class="addfile"><h4>Added: trunk/Source/WebInspectorUI/UserInterface/Proxies/HeapSnapshotDiffProxy.js (0 => 198353)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/WebInspectorUI/UserInterface/Proxies/HeapSnapshotDiffProxy.js                                (rev 0)
+++ trunk/Source/WebInspectorUI/UserInterface/Proxies/HeapSnapshotDiffProxy.js        2016-03-17 21:02:07 UTC (rev 198353)
</span><span class="lines">@@ -0,0 +1,83 @@
</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.HeapSnapshotDiffProxy = class HeapSnapshotDiffProxy extends WebInspector.Object
+{
+    constructor(snapshotDiffObjectId, snapshot1, snapshot2, totalSize, totalObjectCount, categories)
+    {
+        super();
+
+        this._proxyObjectId = snapshotDiffObjectId;
+
+        console.assert(snapshot1 instanceof WebInspector.HeapSnapshotProxy);
+        console.assert(snapshot2 instanceof WebInspector.HeapSnapshotProxy);
+
+        this._snapshot1 = snapshot1;
+        this._snapshot2 = snapshot2;
+        this._totalSize = totalSize;
+        this._totalObjectCount = totalObjectCount;
+        this._categories = Map.fromObject(categories);
+    }
+
+    // Static
+
+    static deserialize(objectId, serializedSnapshotDiff)
+    {
+        let {snapshot1: serializedSnapshot1, snapshot2: serializedSnapshot2, totalSize, totalObjectCount, categories} = serializedSnapshotDiff;
+        // FIXME: The objectId for these snapshots is the snapshotDiff's objectId. Currently these
+        // snapshots are only used for static data so the proxing doesn't matter. However,
+        // should we serialize the objectId with the snapshot so we have the right objectId?
+        let snapshot1 = WebInspector.HeapSnapshotProxy.deserialize(objectId, serializedSnapshot1);
+        let snapshot2 = WebInspector.HeapSnapshotProxy.deserialize(objectId, serializedSnapshot2);
+        return new WebInspector.HeapSnapshotDiffProxy(objectId, snapshot1, snapshot2, totalSize, totalObjectCount, categories);
+    }
+
+    // Public
+
+    get snapshot1() { return this._snapshot1; }
+    get snapshot2() { return this._snapshot2; }
+    get totalSize() { return this._totalSize; }
+    get totalObjectCount() { return this._totalObjectCount; }
+    get categories() { return this._categories; }
+
+    allocationBucketCounts(bucketSizes, callback)
+    {
+        WebInspector.HeapSnapshotWorkerProxy.singleton().callMethod(this._proxyObjectId, &quot;allocationBucketCounts&quot;, bucketSizes, callback);
+    }
+
+    instancesWithClassName(className, callback)
+    {
+        WebInspector.HeapSnapshotWorkerProxy.singleton().callMethod(this._proxyObjectId, &quot;instancesWithClassName&quot;, className, (serializedNodes) =&gt; {
+            callback(serializedNodes.map(WebInspector.HeapSnapshotNodeProxy.deserialize.bind(null, this._proxyObjectId)));
+        });
+    }
+
+    nodeWithIdentifier(nodeIdentifier, callback)
+    {
+        WebInspector.HeapSnapshotWorkerProxy.singleton().callMethod(this._proxyObjectId, &quot;nodeWithIdentifier&quot;, nodeIdentifier, (serializedNode) =&gt; {
+            callback(WebInspector.HeapSnapshotNodeProxy.deserialize(this._proxyObjectId, serializedNode));
+        });
+    }
+};
</ins></span></pre></div>
<a id="trunkSourceWebInspectorUIUserInterfaceProxiesHeapSnapshotEdgeProxyjsfromrev198351trunkSourceWebInspectorUIUserInterfaceModelsHeapSnapshotEdgejs"></a>
<div class="copfile"><h4>Copied: trunk/Source/WebInspectorUI/UserInterface/Proxies/HeapSnapshotEdgeProxy.js (from rev 198351, trunk/Source/WebInspectorUI/UserInterface/Models/HeapSnapshotEdge.js) (0 => 198353)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/WebInspectorUI/UserInterface/Proxies/HeapSnapshotEdgeProxy.js                                (rev 0)
+++ trunk/Source/WebInspectorUI/UserInterface/Proxies/HeapSnapshotEdgeProxy.js        2016-03-17 21:02:07 UTC (rev 198353)
</span><span class="lines">@@ -0,0 +1,59 @@
</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.
+ */
+
+// Directed edge between two HeapSnapshotNodes 'from' and 'to'.
+
+WebInspector.HeapSnapshotEdgeProxy = class HeapSnapshotEdgeProxy
+{
+    constructor(objectId, fromIdentifier, toIdentifier, type, data)
+    {
+        this._proxyObjectId = objectId;
+
+        console.assert(type in WebInspector.HeapSnapshotEdgeProxy.EdgeType);
+
+        this.fromIdentifier = fromIdentifier;
+        this.toIdentifier = toIdentifier;
+        this.type = type;
+        this.data = data;
+
+        this.from = null;
+        this.to = null;
+    }
+
+    // Static
+
+    static deserialize(objectId, serializedEdge)
+    {
+        let {from, to, type, data} = serializedEdge;
+        return new WebInspector.HeapSnapshotEdgeProxy(objectId, from, to, type, data);
+    }
+};
+
+WebInspector.HeapSnapshotEdgeProxy.EdgeType = {
+    Internal: &quot;Internal&quot;,       // No data.
+    Property: &quot;Property&quot;,       // data is string property name.
+    Index: &quot;Index&quot;,             // data is numeric index.
+    Variable: &quot;Variable&quot;,       // data is string variable name.
+};
</ins></span></pre></div>
<a id="trunkSourceWebInspectorUIUserInterfaceProxiesHeapSnapshotNodeProxyjs"></a>
<div class="addfile"><h4>Added: trunk/Source/WebInspectorUI/UserInterface/Proxies/HeapSnapshotNodeProxy.js (0 => 198353)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/WebInspectorUI/UserInterface/Proxies/HeapSnapshotNodeProxy.js                                (rev 0)
+++ trunk/Source/WebInspectorUI/UserInterface/Proxies/HeapSnapshotNodeProxy.js        2016-03-17 21:02:07 UTC (rev 198353)
</span><span class="lines">@@ -0,0 +1,92 @@
</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.HeapSnapshotNodeProxy = class HeapSnapshotNodeProxy
+{
+    constructor(snapshotObjectId, identifier, className, size, retainedSize, internal, gcRoot)
+    {
+        this._proxyObjectId = snapshotObjectId;
+
+        this.id = identifier;
+        this.className = className;
+        this.size = size;
+        this.retainedSize = retainedSize;
+        this.internal = internal;
+        this.gcRoot = gcRoot;
+    }
+
+    // Static
+
+    static deserialize(objectId, serializedNode)
+    {
+        let {id, className, size, retainedSize, internal, gcRoot} = serializedNode;
+        return new WebInspector.HeapSnapshotNodeProxy(objectId, id, className, size, retainedSize, internal, gcRoot);
+    }
+
+    // Proxied
+
+    shortestGCRootPath(callback)
+    {
+        WebInspector.HeapSnapshotWorkerProxy.singleton().callMethod(this._proxyObjectId, &quot;shortestGCRootPath&quot;, this.id, (serializedPath) =&gt; {
+            let isNode = false;
+            let path = serializedPath.map((component) =&gt; {
+                isNode = !isNode;
+                if (isNode)
+                    return WebInspector.HeapSnapshotNodeProxy.deserialize(this._proxyObjectId, component);
+                return WebInspector.HeapSnapshotEdgeProxy.deserialize(this._proxyObjectId, component);
+            });
+
+            for (let i = 1; i &lt; path.length; i += 2) {
+                console.assert(path[i] instanceof WebInspector.HeapSnapshotEdgeProxy);
+                let edge = path[i];
+                edge.from = path[i - 1];
+                edge.to = path[i + 1];
+            }
+
+            callback(path);
+        });
+    }
+
+    dominatedNodes(callback)
+    {
+        WebInspector.HeapSnapshotWorkerProxy.singleton().callMethod(this._proxyObjectId, &quot;dominatedNodes&quot;, this.id, (serializedNodes) =&gt; {
+            callback(serializedNodes.map(WebInspector.HeapSnapshotNodeProxy.deserialize.bind(null, this._proxyObjectId)));
+        });
+    }
+
+    retainedNodes(callback)
+    {
+        WebInspector.HeapSnapshotWorkerProxy.singleton().callMethod(this._proxyObjectId, &quot;retainedNodes&quot;, this.id, (serializedNodes) =&gt; {
+            callback(serializedNodes.map(WebInspector.HeapSnapshotNodeProxy.deserialize.bind(null, this._proxyObjectId)));
+        });
+    }
+
+    retainers(callback)
+    {
+        WebInspector.HeapSnapshotWorkerProxy.singleton().callMethod(this._proxyObjectId, &quot;retainers&quot;, this.id, (serializedNodes) =&gt; {
+            callback(serializedNodes.map(WebInspector.HeapSnapshotNodeProxy.deserialize.bind(null, this._proxyObjectId)));
+        });
+    }
+};
</ins></span></pre></div>
<a id="trunkSourceWebInspectorUIUserInterfaceProxiesHeapSnapshotProxyjs"></a>
<div class="addfile"><h4>Added: trunk/Source/WebInspectorUI/UserInterface/Proxies/HeapSnapshotProxy.js (0 => 198353)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/WebInspectorUI/UserInterface/Proxies/HeapSnapshotProxy.js                                (rev 0)
+++ trunk/Source/WebInspectorUI/UserInterface/Proxies/HeapSnapshotProxy.js        2016-03-17 21:02:07 UTC (rev 198353)
</span><span class="lines">@@ -0,0 +1,74 @@
</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.HeapSnapshotProxy = class HeapSnapshotProxy extends WebInspector.Object
+{
+    constructor(snapshotObjectId, identifier, totalSize, totalObjectCount, categories)
+    {
+        super();
+
+        this._proxyObjectId = snapshotObjectId;
+
+        this._identifier = identifier;
+        this._totalSize = totalSize;
+        this._totalObjectCount = totalObjectCount;
+        this._categories = Map.fromObject(categories);
+    }
+
+    // Static
+
+    static deserialize(objectId, serializedSnapshot)
+    {
+        let {identifier, totalSize, totalObjectCount, categories} = serializedSnapshot;
+        return new WebInspector.HeapSnapshotProxy(objectId, identifier, totalSize, totalObjectCount, categories);
+    }
+
+    // Public
+
+    get proxyObjectId() { return this._proxyObjectId; }
+    get identifier() { return this._identifier; }
+    get totalSize() { return this._totalSize; }
+    get totalObjectCount() { return this._totalObjectCount; }
+    get categories() { return this._categories; }
+
+    allocationBucketCounts(bucketSizes, callback)
+    {
+        WebInspector.HeapSnapshotWorkerProxy.singleton().callMethod(this._proxyObjectId, &quot;allocationBucketCounts&quot;, bucketSizes, callback);
+    }
+
+    instancesWithClassName(className, callback)
+    {
+        WebInspector.HeapSnapshotWorkerProxy.singleton().callMethod(this._proxyObjectId, &quot;instancesWithClassName&quot;, className, (serializedNodes) =&gt; {
+            callback(serializedNodes.map(WebInspector.HeapSnapshotNodeProxy.deserialize.bind(null, this._proxyObjectId)));
+        });
+    }
+
+    nodeWithIdentifier(nodeIdentifier, callback)
+    {
+        WebInspector.HeapSnapshotWorkerProxy.singleton().callMethod(this._proxyObjectId, &quot;nodeWithIdentifier&quot;, nodeIdentifier, (serializedNode) =&gt; {
+            callback(WebInspector.HeapSnapshotNodeProxy.deserialize(this._proxyObjectId, serializedNode));
+        });
+    }
+};
</ins></span></pre></div>
<a id="trunkSourceWebInspectorUIUserInterfaceProxiesHeapSnapshotWorkerProxyjs"></a>
<div class="addfile"><h4>Added: trunk/Source/WebInspectorUI/UserInterface/Proxies/HeapSnapshotWorkerProxy.js (0 => 198353)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/WebInspectorUI/UserInterface/Proxies/HeapSnapshotWorkerProxy.js                                (rev 0)
+++ trunk/Source/WebInspectorUI/UserInterface/Proxies/HeapSnapshotWorkerProxy.js        2016-03-17 21:02:07 UTC (rev 198353)
</span><span class="lines">@@ -0,0 +1,116 @@
</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.HeapSnapshotWorkerProxy = class HeapSnapshotWorkerProxy extends WebInspector.Object
+{
+    constructor()
+    {
+        super();
+
+        this._heapSnapshotWorker = new Worker(&quot;Workers/HeapSnapshot/HeapSnapshotWorker.js&quot;);
+        this._heapSnapshotWorker.addEventListener(&quot;message&quot;, this._handleMessage.bind(this));
+
+        this._nextCallId = 1;
+        this._callbacks = new Map;
+    }
+
+    // Static
+
+    static singleton()
+    {
+        if (!HeapSnapshotWorkerProxy.instance)
+            HeapSnapshotWorkerProxy.instance = new HeapSnapshotWorkerProxy;
+        return HeapSnapshotWorkerProxy.instance;
+    }
+
+    // Actions
+
+    createSnapshot(snapshotStringData, callback)
+    {
+        this.performAction(&quot;createSnapshot&quot;, ...arguments);
+    }
+
+    createSnapshotDiff(objectId1, objectId2, callback)
+    {
+        this.performAction(&quot;createSnapshotDiff&quot;, ...arguments);
+    }
+
+    // Public
+
+    performAction(actionName)
+    {
+        let callId = this._nextCallId++;
+        let callback = arguments[arguments.length - 1];
+        let actionArguments = Array.prototype.slice.call(arguments, 1, arguments.length - 1);
+
+        console.assert(typeof actionName === &quot;string&quot;, &quot;performAction should always have an actionName&quot;);
+        console.assert(typeof callback === &quot;function&quot;, &quot;performAction should always have a callback&quot;);
+
+        this._callbacks.set(callId, callback);
+        this._postMessage({callId, actionName, actionArguments});
+    }
+
+    callMethod(objectId, methodName)
+    {
+        let callId = this._nextCallId++;
+        let callback = arguments[arguments.length - 1];
+        let methodArguments = Array.prototype.slice.call(arguments, 2, arguments.length - 1);
+
+        console.assert(typeof objectId === &quot;number&quot;, &quot;callMethod should always have an objectId&quot;);
+        console.assert(typeof methodName === &quot;string&quot;, &quot;callMethod should always have a methodName&quot;);
+        console.assert(typeof callback === &quot;function&quot;, &quot;callMethod should always have a callback&quot;);
+
+        this._callbacks.set(callId, callback);
+        this._postMessage({callId, objectId, methodName, methodArguments});
+    }
+
+    // Private
+
+    _postMessage()
+    {
+        this._heapSnapshotWorker.postMessage(...arguments);
+    }
+
+    _handleMessage(event)
+    {
+        let data = event.data;
+
+        // Event.
+        if (data.eventName) {
+            this.dispatchEventToListeners(data.eventName, data.eventData);
+            return;
+        }
+
+        // Action or Method Response.
+        if (data.callId) {
+            let callback = this._callbacks.get(data.callId);
+            this._callbacks.delete(data.callId);
+            callback(data.result);
+            return;
+        }
+
+        console.error(&quot;Unexpected HeapSnapshotWorker message&quot;, data);
+    }
+};
</ins></span></pre></div>
<a id="trunkSourceWebInspectorUIUserInterfaceTesthtml"></a>
<div class="modfile"><h4>Modified: trunk/Source/WebInspectorUI/UserInterface/Test.html (198352 => 198353)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/WebInspectorUI/UserInterface/Test.html        2016-03-17 21:01:21 UTC (rev 198352)
+++ trunk/Source/WebInspectorUI/UserInterface/Test.html        2016-03-17 21:02:07 UTC (rev 198353)
</span><span class="lines">@@ -114,10 +114,6 @@
</span><span class="cx">     &lt;script src=&quot;Models/Geometry.js&quot;&gt;&lt;/script&gt;
</span><span class="cx">     &lt;script src=&quot;Models/HeapAllocationsInstrument.js&quot;&gt;&lt;/script&gt;
</span><span class="cx">     &lt;script src=&quot;Models/HeapAllocationsTimelineRecord.js&quot;&gt;&lt;/script&gt;
</span><del>-    &lt;script src=&quot;Models/HeapSnapshot.js&quot;&gt;&lt;/script&gt;
-    &lt;script src=&quot;Models/HeapSnapshotDiff.js&quot;&gt;&lt;/script&gt;
-    &lt;script src=&quot;Models/HeapSnapshotEdge.js&quot;&gt;&lt;/script&gt;
-    &lt;script src=&quot;Models/HeapSnapshotNode.js&quot;&gt;&lt;/script&gt;
</del><span class="cx">     &lt;script src=&quot;Models/IndexedDatabase.js&quot;&gt;&lt;/script&gt;
</span><span class="cx">     &lt;script src=&quot;Models/IndexedDatabaseObjectStore.js&quot;&gt;&lt;/script&gt;
</span><span class="cx">     &lt;script src=&quot;Models/IndexedDatabaseObjectStoreIndex.js&quot;&gt;&lt;/script&gt;
</span><span class="lines">@@ -162,6 +158,12 @@
</span><span class="cx">     &lt;script src=&quot;Models/TimelineMarker.js&quot;&gt;&lt;/script&gt;
</span><span class="cx">     &lt;script src=&quot;Models/TimelineRecording.js&quot;&gt;&lt;/script&gt;
</span><span class="cx"> 
</span><ins>+    &lt;script src=&quot;Proxies/HeapSnapshotDiffProxy.js&quot;&gt;&lt;/script&gt;
+    &lt;script src=&quot;Proxies/HeapSnapshotEdgeProxy.js&quot;&gt;&lt;/script&gt;
+    &lt;script src=&quot;Proxies/HeapSnapshotNodeProxy.js&quot;&gt;&lt;/script&gt;
+    &lt;script src=&quot;Proxies/HeapSnapshotProxy.js&quot;&gt;&lt;/script&gt;
+    &lt;script src=&quot;Proxies/HeapSnapshotWorkerProxy.js&quot;&gt;&lt;/script&gt;
+
</ins><span class="cx">     &lt;script src=&quot;Controllers/CSSStyleManager.js&quot;&gt;&lt;/script&gt;
</span><span class="cx">     &lt;script src=&quot;Controllers/DOMTreeManager.js&quot;&gt;&lt;/script&gt;
</span><span class="cx">     &lt;script src=&quot;Controllers/DebuggerManager.js&quot;&gt;&lt;/script&gt;
</span></span></pre></div>
<a id="trunkSourceWebInspectorUIUserInterfaceViewsContentViewjs"></a>
<div class="modfile"><h4>Modified: trunk/Source/WebInspectorUI/UserInterface/Views/ContentView.js (198352 => 198353)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/WebInspectorUI/UserInterface/Views/ContentView.js        2016-03-17 21:01:21 UTC (rev 198352)
+++ trunk/Source/WebInspectorUI/UserInterface/Views/ContentView.js        2016-03-17 21:02:07 UTC (rev 198353)
</span><span class="lines">@@ -138,7 +138,7 @@
</span><span class="cx">         if (representedObject instanceof WebInspector.CallingContextTree)
</span><span class="cx">             return new WebInspector.ProfileView(representedObject, extraArguments);
</span><span class="cx"> 
</span><del>-        if (representedObject instanceof WebInspector.HeapSnapshot)
</del><ins>+        if (representedObject instanceof WebInspector.HeapSnapshotProxy || representedObject instanceof WebInspector.HeapSnapshotDiffProxy)
</ins><span class="cx">             return new WebInspector.HeapSnapshotClusterContentView(representedObject, extraArguments);
</span><span class="cx"> 
</span><span class="cx">         if (typeof representedObject === &quot;string&quot; || representedObject instanceof String)
</span><span class="lines">@@ -237,7 +237,7 @@
</span><span class="cx">             return true;
</span><span class="cx">         if (representedObject instanceof WebInspector.CallingContextTree)
</span><span class="cx">             return true;
</span><del>-        if (representedObject instanceof WebInspector.HeapSnapshot)
</del><ins>+        if (representedObject instanceof WebInspector.HeapSnapshotProxy || representedObject instanceof WebInspector.HeapSnapshotDiffProxy)
</ins><span class="cx">             return true;
</span><span class="cx">         if (typeof representedObject === &quot;string&quot; || representedObject instanceof String)
</span><span class="cx">             return true;
</span></span></pre></div>
<a id="trunkSourceWebInspectorUIUserInterfaceViewsHeapAllocationsTimelineViewjs"></a>
<div class="modfile"><h4>Modified: trunk/Source/WebInspectorUI/UserInterface/Views/HeapAllocationsTimelineView.js (198352 => 198353)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/WebInspectorUI/UserInterface/Views/HeapAllocationsTimelineView.js        2016-03-17 21:01:21 UTC (rev 198352)
+++ trunk/Source/WebInspectorUI/UserInterface/Views/HeapAllocationsTimelineView.js        2016-03-17 21:02:07 UTC (rev 198353)
</span><span class="lines">@@ -147,7 +147,7 @@
</span><span class="cx">         this._showingSnapshotList = false;
</span><span class="cx">         this._heapSnapshotDiff = heapSnapshotDiff;
</span><span class="cx"> 
</span><del>-        this._contentViewContainer.showContentViewForRepresentedObject(heapSnapshotDiff.snapshotForDiff());
</del><ins>+        this._contentViewContainer.showContentViewForRepresentedObject(heapSnapshotDiff);
</ins><span class="cx">     }
</span><span class="cx"> 
</span><span class="cx">     // Protected
</span><span class="lines">@@ -293,9 +293,11 @@
</span><span class="cx">     _takeHeapSnapshotClicked()
</span><span class="cx">     {
</span><span class="cx">         HeapAgent.snapshot(function(error, timestamp, snapshotStringData) {
</span><del>-            let payload = JSON.parse(snapshotStringData);
-            let snapshot = WebInspector.HeapSnapshot.fromPayload(payload);
-            WebInspector.timelineManager.heapSnapshotAdded(timestamp, snapshot);
</del><ins>+            let workerProxy = WebInspector.HeapSnapshotWorkerProxy.singleton();
+            workerProxy.createSnapshot(snapshotStringData, ({objectId, snapshot: serializedSnapshot}) =&gt; {
+                let snapshot = WebInspector.HeapSnapshotProxy.deserialize(objectId, serializedSnapshot);
+                WebInspector.timelineManager.heapSnapshotAdded(timestamp, snapshot);
+            });
</ins><span class="cx">         });
</span><span class="cx">     }
</span><span class="cx"> 
</span><span class="lines">@@ -363,8 +365,13 @@
</span><span class="cx">         }
</span><span class="cx"> 
</span><span class="cx">         // Selected Comparison.
</span><del>-        let diff = new WebInspector.HeapSnapshotDiff(this._baselineHeapSnapshotTimelineRecord.heapSnapshot, heapAllocationsTimelineRecord.heapSnapshot);
-        this.showHeapSnapshotDiff(diff);
</del><ins>+        let snapshot1 = this._baselineHeapSnapshotTimelineRecord.heapSnapshot;
+        let snapshot2 = heapAllocationsTimelineRecord.heapSnapshot;
+        let workerProxy = WebInspector.HeapSnapshotWorkerProxy.singleton();
+        workerProxy.createSnapshotDiff(snapshot1.proxyObjectId, snapshot2.proxyObjectId, ({objectId, snapshotDiff: serializedSnapshotDiff}) =&gt; {
+            let diff = WebInspector.HeapSnapshotDiffProxy.deserialize(objectId, serializedSnapshotDiff);
+            this.showHeapSnapshotDiff(diff);
+        });
</ins><span class="cx"> 
</span><span class="cx">         this._baselineDataGridNode.clearBaseline();
</span><span class="cx">         this._selectingComparisonHeapSnapshots = false;
</span></span></pre></div>
<a id="trunkSourceWebInspectorUIUserInterfaceViewsHeapSnapshotClassDataGridNodejs"></a>
<div class="modfile"><h4>Modified: trunk/Source/WebInspectorUI/UserInterface/Views/HeapSnapshotClassDataGridNode.js (198352 => 198353)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/WebInspectorUI/UserInterface/Views/HeapSnapshotClassDataGridNode.js        2016-03-17 21:01:21 UTC (rev 198352)
+++ trunk/Source/WebInspectorUI/UserInterface/Views/HeapSnapshotClassDataGridNode.js        2016-03-17 21:02:07 UTC (rev 198353)
</span><span class="lines">@@ -95,25 +95,25 @@
</span><span class="cx">     {
</span><span class="cx">         this.removeEventListener(&quot;populate&quot;, this._populate, this);
</span><span class="cx"> 
</span><del>-        let instances = this._tree.heapSnapshot.instancesWithClassName(this._data.className);
</del><ins>+        this._tree.heapSnapshot.instancesWithClassName(this._data.className, (instances) =&gt; {
+            // Batch.
+            if (instances.length &gt; WebInspector.HeapSnapshotClassDataGridNode.ChildrenBatchLimit) {
+                // FIXME: This should respect the this._tree.includeInternalObjects setting.
+                this._instances = instances;
+                this._batched = true;
+                this._updateBatchedSort();
+                this._fetchBatch(WebInspector.HeapSnapshotClassDataGridNode.ChildrenBatchLimit);
+                return;
+            }
</ins><span class="cx"> 
</span><del>-        // Batch.
-        if (instances.length &gt; WebInspector.HeapSnapshotClassDataGridNode.ChildrenBatchLimit) {
-            // FIXME: This should respect the this._tree.includeInternalObjects setting.
-            this._instances = instances;
-            this._batched = true;
-            this._updateBatchedSort();
-            this._fetchBatch(WebInspector.HeapSnapshotClassDataGridNode.ChildrenBatchLimit);
-            return;
-        }
</del><ins>+            for (let instance of instances) {
+                if (instance.internal &amp;&amp; !this._tree.includeInternalObjects)
+                    continue;
+                this.appendChild(new WebInspector.HeapSnapshotInstanceDataGridNode(instance, this._tree));
+            }
</ins><span class="cx"> 
</span><del>-        for (let instance of instances) {
-            if (instance.internal &amp;&amp; !this._tree.includeInternalObjects)
-                continue;
-            this.appendChild(new WebInspector.HeapSnapshotInstanceDataGridNode(instance, this._tree));
-        }
-
-        this.sort();
</del><ins>+            this.sort();
+        });
</ins><span class="cx">     }
</span><span class="cx"> 
</span><span class="cx">     _fetchBatch(batchSize)
</span></span></pre></div>
<a id="trunkSourceWebInspectorUIUserInterfaceViewsHeapSnapshotClusterContentViewjs"></a>
<div class="modfile"><h4>Modified: trunk/Source/WebInspectorUI/UserInterface/Views/HeapSnapshotClusterContentView.js (198352 => 198353)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/WebInspectorUI/UserInterface/Views/HeapSnapshotClusterContentView.js        2016-03-17 21:01:21 UTC (rev 198352)
+++ trunk/Source/WebInspectorUI/UserInterface/Views/HeapSnapshotClusterContentView.js        2016-03-17 21:02:07 UTC (rev 198353)
</span><span class="lines">@@ -29,7 +29,7 @@
</span><span class="cx">     {
</span><span class="cx">         super(heapSnapshot);
</span><span class="cx"> 
</span><del>-        console.assert(heapSnapshot instanceof WebInspector.HeapSnapshot);
</del><ins>+        console.assert(heapSnapshot instanceof WebInspector.HeapSnapshotProxy || heapSnapshot instanceof WebInspector.HeapSnapshotDiffProxy);
</ins><span class="cx"> 
</span><span class="cx">         this._heapSnapshot = heapSnapshot;
</span><span class="cx"> 
</span></span></pre></div>
<a id="trunkSourceWebInspectorUIUserInterfaceViewsHeapSnapshotInstanceDataGridNodejs"></a>
<div class="modfile"><h4>Modified: trunk/Source/WebInspectorUI/UserInterface/Views/HeapSnapshotInstanceDataGridNode.js (198352 => 198353)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/WebInspectorUI/UserInterface/Views/HeapSnapshotInstanceDataGridNode.js        2016-03-17 21:01:21 UTC (rev 198352)
+++ trunk/Source/WebInspectorUI/UserInterface/Views/HeapSnapshotInstanceDataGridNode.js        2016-03-17 21:02:07 UTC (rev 198353)
</span><span class="lines">@@ -29,7 +29,7 @@
</span><span class="cx">     {
</span><span class="cx">         super(node, false);
</span><span class="cx"> 
</span><del>-        console.assert(node instanceof WebInspector.HeapSnapshotNode);
</del><ins>+        console.assert(node instanceof WebInspector.HeapSnapshotNodeProxy);
</ins><span class="cx"> 
</span><span class="cx">         this._node = node;
</span><span class="cx">         this._tree = tree;
</span><span class="lines">@@ -45,44 +45,44 @@
</span><span class="cx">     static logHeapSnapshotNode(node)
</span><span class="cx">     {
</span><span class="cx">         let heapObjectIdentifier = node.id;
</span><del>-
</del><span class="cx">         let synthetic = true;
</span><span class="cx">         let text = WebInspector.UIString(&quot;Heap Snapshot Object (@%d)&quot;).format(heapObjectIdentifier);
</span><span class="cx"> 
</span><del>-        let gcRootPath = node.shortestGCRootPath;
-        if (gcRootPath) {
-            gcRootPath = gcRootPath.slice().reverse();
-            let windowIndex = gcRootPath.findIndex((x) =&gt; {
-                return x instanceof WebInspector.HeapSnapshotNode &amp;&amp; x.className === &quot;Window&quot;;
-            });
</del><ins>+        node.shortestGCRootPath((gcRootPath) =&gt; {
+            if (gcRootPath.length) {
+                gcRootPath = gcRootPath.slice().reverse();
+                let windowIndex = gcRootPath.findIndex((x) =&gt; {
+                    return x instanceof WebInspector.HeapSnapshotNodeProxy &amp;&amp; x.className === &quot;Window&quot;;
+                });
</ins><span class="cx"> 
</span><del>-            let heapSnapshotRootPath = WebInspector.HeapSnapshotRootPath.emptyPath();
-            for (let i = windowIndex === -1 ? 0 : windowIndex; i &lt; gcRootPath.length; ++i) {
-                let component = gcRootPath[i];
-                if (component instanceof WebInspector.HeapSnapshotNode) {
-                    if (component.className === &quot;Window&quot;)
-                        heapSnapshotRootPath = heapSnapshotRootPath.appendGlobalScopeName(component, &quot;window&quot;);
-                } else if (component instanceof WebInspector.HeapSnapshotEdge)
-                    heapSnapshotRootPath = heapSnapshotRootPath.appendEdge(component);
-            }
</del><ins>+                let heapSnapshotRootPath = WebInspector.HeapSnapshotRootPath.emptyPath();
+                for (let i = windowIndex === -1 ? 0 : windowIndex; i &lt; gcRootPath.length; ++i) {
+                    let component = gcRootPath[i];
+                    if (component instanceof WebInspector.HeapSnapshotNodeProxy) {
+                        if (component.className === &quot;Window&quot;)
+                            heapSnapshotRootPath = heapSnapshotRootPath.appendGlobalScopeName(component, &quot;window&quot;);
+                    } else if (component instanceof WebInspector.HeapSnapshotEdgeProxy)
+                        heapSnapshotRootPath = heapSnapshotRootPath.appendEdge(component);
+                }
</ins><span class="cx"> 
</span><del>-            if (!heapSnapshotRootPath.isFullPathImpossible()) {
-                synthetic = false;
-                text = heapSnapshotRootPath.fullPath;
</del><ins>+                if (!heapSnapshotRootPath.isFullPathImpossible()) {
+                    synthetic = false;
+                    text = heapSnapshotRootPath.fullPath;
+                }
</ins><span class="cx">             }
</span><del>-        }
</del><span class="cx"> 
</span><del>-        if (node.className === &quot;string&quot;) {
-            HeapAgent.getPreview(heapObjectIdentifier, function(error, string, functionDetails, objectPreviewPayload) {
-                let remoteObject = error ? WebInspector.RemoteObject.fromPrimitiveValue(undefined) : WebInspector.RemoteObject.fromPrimitiveValue(string);
-                WebInspector.consoleLogViewController.appendImmediateExecutionWithResult(text, remoteObject, synthetic);                
-            });
-        } else {
-            HeapAgent.getRemoteObject(heapObjectIdentifier, WebInspector.RuntimeManager.ConsoleObjectGroup, function(error, remoteObjectPayload) {
-                let remoteObject = error ? WebInspector.RemoteObject.fromPrimitiveValue(undefined) : WebInspector.RemoteObject.fromPayload(remoteObjectPayload);
-                WebInspector.consoleLogViewController.appendImmediateExecutionWithResult(text, remoteObject, synthetic);
-            });
-        }
</del><ins>+            if (node.className === &quot;string&quot;) {
+                HeapAgent.getPreview(heapObjectIdentifier, function(error, string, functionDetails, objectPreviewPayload) {
+                    let remoteObject = error ? WebInspector.RemoteObject.fromPrimitiveValue(undefined) : WebInspector.RemoteObject.fromPrimitiveValue(string);
+                    WebInspector.consoleLogViewController.appendImmediateExecutionWithResult(text, remoteObject, synthetic);                
+                });
+            } else {
+                HeapAgent.getRemoteObject(heapObjectIdentifier, WebInspector.RuntimeManager.ConsoleObjectGroup, function(error, remoteObjectPayload) {
+                    let remoteObject = error ? WebInspector.RemoteObject.fromPrimitiveValue(undefined) : WebInspector.RemoteObject.fromPayload(remoteObjectPayload);
+                    WebInspector.consoleLogViewController.appendImmediateExecutionWithResult(text, remoteObject, synthetic);
+                });
+            }
+        });
</ins><span class="cx">     }
</span><span class="cx"> 
</span><span class="cx">     // Protected
</span><span class="lines">@@ -211,13 +211,13 @@
</span><span class="cx"> 
</span><span class="cx">             path = path.slice().reverse();
</span><span class="cx">             let windowIndex = path.findIndex((x) =&gt; {
</span><del>-                return x instanceof WebInspector.HeapSnapshotNode &amp;&amp; x.className === &quot;Window&quot;;
</del><ins>+                return x instanceof WebInspector.HeapSnapshotNodeProxy &amp;&amp; x.className === &quot;Window&quot;;
</ins><span class="cx">             });
</span><span class="cx"> 
</span><span class="cx">             let edge = null;
</span><span class="cx">             for (let i = windowIndex === -1 ? 0 : windowIndex; i &lt; path.length; ++i) {
</span><span class="cx">                 let component = path[i];
</span><del>-                if (component instanceof WebInspector.HeapSnapshotEdge) {
</del><ins>+                if (component instanceof WebInspector.HeapSnapshotEdgeProxy) {
</ins><span class="cx">                     edge = component;
</span><span class="cx">                     continue;
</span><span class="cx">                 }
</span><span class="lines">@@ -271,32 +271,31 @@
</span><span class="cx"> 
</span><span class="cx">         function stringifyEdge(edge) {
</span><span class="cx">             switch(edge.type) {
</span><del>-            case WebInspector.HeapSnapshotEdge.EdgeType.Property:
-            case WebInspector.HeapSnapshotEdge.EdgeType.Variable:
</del><ins>+            case WebInspector.HeapSnapshotEdgeProxy.EdgeType.Property:
+            case WebInspector.HeapSnapshotEdgeProxy.EdgeType.Variable:
</ins><span class="cx">                 if (/^(?![0-9])\w+$/.test(edge.data))
</span><span class="cx">                     return edge.data;
</span><span class="cx">                 return &quot;[&quot; + doubleQuotedString(edge.data) + &quot;]&quot;;
</span><del>-            case WebInspector.HeapSnapshotEdge.EdgeType.Index:
</del><ins>+            case WebInspector.HeapSnapshotEdgeProxy.EdgeType.Index:
</ins><span class="cx">                 return &quot;[&quot; + edge.data + &quot;]&quot;;
</span><del>-            case WebInspector.HeapSnapshotEdge.EdgeType.Internal:
</del><ins>+            case WebInspector.HeapSnapshotEdgeProxy.EdgeType.Internal:
</ins><span class="cx">             default:
</span><span class="cx">                 return null;
</span><span class="cx">             }
</span><span class="cx">         }
</span><span class="cx"> 
</span><del>-        if (this._node.gcRoot) {
-            let textElement = popoverContentElement.appendChild(document.createElement(&quot;div&quot;));
-            textElement.textContent = WebInspector.UIString(&quot;This object is a root&quot;);
-        } else {
-            let path = this._node.shortestGCRootPath;
-            if (path)
</del><ins>+        this._node.shortestGCRootPath((path) =&gt; {
+            if (path.length)
</ins><span class="cx">                 appendPath(path);
</span><del>-            else {
</del><ins>+            else if (this._node.gcRoot) {
+                let textElement = popoverContentElement.appendChild(document.createElement(&quot;div&quot;));
+                textElement.textContent = WebInspector.UIString(&quot;This object is a root&quot;);
+            } else {
</ins><span class="cx">                 let emptyElement = popoverContentElement.appendChild(document.createElement(&quot;div&quot;));
</span><span class="cx">                 emptyElement.textContent = WebInspector.UIString(&quot;This object is referenced by internal objects&quot;);
</span><span class="cx">             }
</span><del>-        }
-
-        this._tree.popover.presentNewContentWithFrame(popoverContentElement, targetFrame.pad(2), [WebInspector.RectEdge.MAX_Y, WebInspector.RectEdge.MIN_Y, WebInspector.RectEdge.MAX_X]);
</del><ins>+            
+            this._tree.popover.presentNewContentWithFrame(popoverContentElement, targetFrame.pad(2), [WebInspector.RectEdge.MAX_Y, WebInspector.RectEdge.MIN_Y, WebInspector.RectEdge.MAX_X]);
+        });
</ins><span class="cx">     }
</span><span class="cx"> };
</span></span></pre></div>
<a id="trunkSourceWebInspectorUIUserInterfaceViewsHeapSnapshotInstancesContentViewjs"></a>
<div class="modfile"><h4>Modified: trunk/Source/WebInspectorUI/UserInterface/Views/HeapSnapshotInstancesContentView.js (198352 => 198353)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/WebInspectorUI/UserInterface/Views/HeapSnapshotInstancesContentView.js        2016-03-17 21:01:21 UTC (rev 198352)
+++ trunk/Source/WebInspectorUI/UserInterface/Views/HeapSnapshotInstancesContentView.js        2016-03-17 21:02:07 UTC (rev 198353)
</span><span class="lines">@@ -27,7 +27,7 @@
</span><span class="cx"> {
</span><span class="cx">     constructor(representedObject, extraArguments)
</span><span class="cx">     {
</span><del>-        console.assert(representedObject instanceof WebInspector.HeapSnapshot);
</del><ins>+        console.assert(representedObject instanceof WebInspector.HeapSnapshotProxy || representedObject instanceof WebInspector.HeapSnapshotDiffProxy);
</ins><span class="cx"> 
</span><span class="cx">         super(representedObject);
</span><span class="cx"> 
</span></span></pre></div>
<a id="trunkSourceWebInspectorUIUserInterfaceViewsHeapSnapshotInstancesDataGridTreejs"></a>
<div class="modfile"><h4>Modified: trunk/Source/WebInspectorUI/UserInterface/Views/HeapSnapshotInstancesDataGridTree.js (198352 => 198353)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/WebInspectorUI/UserInterface/Views/HeapSnapshotInstancesDataGridTree.js        2016-03-17 21:01:21 UTC (rev 198352)
+++ trunk/Source/WebInspectorUI/UserInterface/Views/HeapSnapshotInstancesDataGridTree.js        2016-03-17 21:02:07 UTC (rev 198353)
</span><span class="lines">@@ -29,7 +29,7 @@
</span><span class="cx">     {
</span><span class="cx">         super();
</span><span class="cx"> 
</span><del>-        console.assert(heapSnapshot instanceof WebInspector.HeapSnapshot);
</del><ins>+        console.assert(heapSnapshot instanceof WebInspector.HeapSnapshotProxy || heapSnapshot instanceof WebInspector.HeapSnapshotDiffProxy);
</ins><span class="cx"> 
</span><span class="cx">         this._heapSnapshot = heapSnapshot;
</span><span class="cx"> 
</span></span></pre></div>
<a id="trunkSourceWebInspectorUIUserInterfaceViewsHeapSnapshotSummaryContentViewjs"></a>
<div class="modfile"><h4>Modified: trunk/Source/WebInspectorUI/UserInterface/Views/HeapSnapshotSummaryContentView.js (198352 => 198353)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/WebInspectorUI/UserInterface/Views/HeapSnapshotSummaryContentView.js        2016-03-17 21:01:21 UTC (rev 198352)
+++ trunk/Source/WebInspectorUI/UserInterface/Views/HeapSnapshotSummaryContentView.js        2016-03-17 21:02:07 UTC (rev 198353)
</span><span class="lines">@@ -27,7 +27,7 @@
</span><span class="cx"> {
</span><span class="cx">     constructor(heapSnapshot, extraArguments)
</span><span class="cx">     {
</span><del>-        console.assert(heapSnapshot instanceof WebInspector.HeapSnapshot);
</del><ins>+        console.assert(heapSnapshot instanceof WebInspector.HeapSnapshotProxy || heapSnapshot instanceof WebInspector.HeapSnapshotDiffProxy);
</ins><span class="cx"> 
</span><span class="cx">         super(heapSnapshot);
</span><span class="cx"> 
</span><span class="lines">@@ -187,42 +187,30 @@
</span><span class="cx">             appendEmptyMessage.call(this, this._classCountBreakdownLegendElement, WebInspector.UIString(&quot;No objects&quot;));
</span><span class="cx"> 
</span><span class="cx">         // Allocation size groups.
</span><del>-        let small = 0;
-        let medium = 0;
-        let large = 0;
-        let veryLarge = 0;
-
</del><span class="cx">         const smallAllocationSize = 48;
</span><span class="cx">         const mediumAllocationSize = 128;
</span><span class="cx">         const largeAllocationSize = 512;
</span><span class="cx"> 
</span><del>-        this._heapSnapshot.instances.forEach(({size}) =&gt; {
-            if (size &lt; smallAllocationSize)
-                small++;
-            else if (size &lt; mediumAllocationSize)
-                medium++;
-            else if (size &lt; largeAllocationSize)
-                large++;
-            else
-                veryLarge++;
-        });
</del><ins>+        this._heapSnapshot.allocationBucketCounts([smallAllocationSize, mediumAllocationSize, largeAllocationSize], (results) =&gt; {
+            let [small, medium, large, veryLarge] = results;
</ins><span class="cx"> 
</span><del>-        if (small + medium + large + veryLarge) {
-            appendLegendRow.call(this, this._allocationSizeBreakdownLegendElement, &quot;small&quot;, WebInspector.UIString(&quot;Small %s&quot;).format(Number.bytesToString(smallAllocationSize)), small);
-            appendLegendRow.call(this, this._allocationSizeBreakdownLegendElement, &quot;medium&quot;, WebInspector.UIString(&quot;Medium %s&quot;).format(Number.bytesToString(mediumAllocationSize)), medium);
-            appendLegendRow.call(this, this._allocationSizeBreakdownLegendElement, &quot;large&quot;, WebInspector.UIString(&quot;Large %s&quot;).format(Number.bytesToString(largeAllocationSize)), large);
-            appendLegendRow.call(this, this._allocationSizeBreakdownLegendElement, &quot;very-large&quot;, WebInspector.UIString(&quot;Very Large&quot;), veryLarge);
</del><ins>+            if (small + medium + large + veryLarge) {
+                appendLegendRow.call(this, this._allocationSizeBreakdownLegendElement, &quot;small&quot;, WebInspector.UIString(&quot;Small %s&quot;).format(Number.bytesToString(smallAllocationSize)), small);
+                appendLegendRow.call(this, this._allocationSizeBreakdownLegendElement, &quot;medium&quot;, WebInspector.UIString(&quot;Medium %s&quot;).format(Number.bytesToString(mediumAllocationSize)), medium);
+                appendLegendRow.call(this, this._allocationSizeBreakdownLegendElement, &quot;large&quot;, WebInspector.UIString(&quot;Large %s&quot;).format(Number.bytesToString(largeAllocationSize)), large);
+                appendLegendRow.call(this, this._allocationSizeBreakdownLegendElement, &quot;very-large&quot;, WebInspector.UIString(&quot;Very Large&quot;), veryLarge);
</ins><span class="cx"> 
</span><del>-            this._allocationSizeBreakdownCircleChart.segments = [&quot;small&quot;, &quot;medium&quot;, &quot;large&quot;, &quot;very-large&quot;];
-            this._allocationSizeBreakdownCircleChart.values = [small, medium, large, veryLarge];
-            this._allocationSizeBreakdownCircleChart.updateLayout();
</del><ins>+                this._allocationSizeBreakdownCircleChart.segments = [&quot;small&quot;, &quot;medium&quot;, &quot;large&quot;, &quot;very-large&quot;];
+                this._allocationSizeBreakdownCircleChart.values = [small, medium, large, veryLarge];
+                this._allocationSizeBreakdownCircleChart.updateLayout();
</ins><span class="cx"> 
</span><del>-            let averageAllocationSizeElement = this._allocationSizeBreakdownCircleChart.centerElement.appendChild(document.createElement(&quot;div&quot;));
-            averageAllocationSizeElement.classList.add(&quot;average-allocation-size&quot;);
-            averageAllocationSizeElement.textContent = Number.bytesToString(this._heapSnapshot.totalSize / this._heapSnapshot.totalObjectCount);
-            averageAllocationSizeElement.title = WebInspector.UIString(&quot;Average allocation size&quot;);
-        } else
-            appendEmptyMessage.call(this, this._allocationSizeBreakdownLegendElement, WebInspector.UIString(&quot;No objects&quot;));
</del><ins>+                let averageAllocationSizeElement = this._allocationSizeBreakdownCircleChart.centerElement.appendChild(document.createElement(&quot;div&quot;));
+                averageAllocationSizeElement.classList.add(&quot;average-allocation-size&quot;);
+                averageAllocationSizeElement.textContent = Number.bytesToString(this._heapSnapshot.totalSize / this._heapSnapshot.totalObjectCount);
+                averageAllocationSizeElement.title = WebInspector.UIString(&quot;Average allocation size&quot;);
+            } else
+                appendEmptyMessage.call(this, this._allocationSizeBreakdownLegendElement, WebInspector.UIString(&quot;No objects&quot;));
+        });
</ins><span class="cx">     }
</span><span class="cx"> 
</span><span class="cx">     // Protected
</span></span></pre></div>
<a id="trunkSourceWebInspectorUIUserInterfaceWorkersHeapSnapshotHeapSnapshotjs"></a>
<div class="addfile"><h4>Added: trunk/Source/WebInspectorUI/UserInterface/Workers/HeapSnapshot/HeapSnapshot.js (0 => 198353)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/WebInspectorUI/UserInterface/Workers/HeapSnapshot/HeapSnapshot.js                                (rev 0)
+++ trunk/Source/WebInspectorUI/UserInterface/Workers/HeapSnapshot/HeapSnapshot.js        2016-03-17 21:02:07 UTC (rev 198353)
</span><span class="lines">@@ -0,0 +1,708 @@
</span><ins>+/*
+ * Copyright (C) 2011 Google Inc. All rights reserved.
+ * 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:
+ *
+ *     * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *     * 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.
+ *     * Neither the name of Google Inc. nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * &quot;AS IS&quot; 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 THE COPYRIGHT
+ * OWNER OR 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.
+ */
+
+// nodes
+// [&lt;0:id&gt;, &lt;1:size&gt;, &lt;2:classNameTableIndex&gt;, &lt;3:internal&gt;];
+const nodeFieldCount = 4;
+const nodeIdOffset = 0;
+const nodeSizeOffset = 1;
+const nodeClassNameOffset = 2;
+const nodeInternalOffset = 3;
+
+// edges
+// [&lt;0:fromId&gt;, &lt;1:toId&gt;, &lt;2:typeTableIndex&gt;, &lt;3:edgeDataIndexOrEdgeNameIndex&gt;]
+const edgeFieldCount = 4;
+const edgeFromIdOffset = 0;
+const edgeToIdOffset = 1;
+const edgeTypeOffset = 2;
+const edgeDataOffset = 3;
+
+// Other constants.
+const rootNodeIndex = 0;
+const rootNodeOrdinal = 0;
+const rootNodeIdentifier = 0;
+
+// Terminology:
+//   - `nodeIndex` is an index into the `nodes` list.
+//   - `nodeOrdinal` is the order of the node in the `nodes` list. (nodeIndex / nodeFieldCount)
+//   - `nodeIdentifier` is the node's id value. (nodes[nodeIndex + nodeIdOffset]).
+//   - `edgeIndex` is an index into the `edges` list.
+//
+// Lists:
+//   - _nodeOrdinalToFirstOutgoingEdge - `nodeOrdinal` to `edgeIndex` in `edges`.
+//     Iterate edges by walking `edges` (edgeFieldCount) and checking if fromIdentifier is current.
+//   - _nodeOrdinalToFirstIncomingEdge - `nodeOrdinal` to `incomingEdgeIndex` in `incomingEdges`.
+//     Iterate edges by walking `incomingEdges` until `nodeOrdinal+1`'s first incoming edge index.
+//   - _nodeOrdinalToDominatorNodeOrdinal - `nodeOrdinal` to `nodeOrdinal` of dominator
+//   - _nodeOrdinalToRetainedSizes - `nodeOrdinal` to retain size value
+//
+// Temporary Lists:
+//   - nodeOrdinalToPostOrderIndex - `nodeOrdinal` to a `postOrderIndex`.
+//   - postOrderIndexToNodeOrdinal - `postOrderIndex` to a `nodeOrdinal`.
+
+let nextSnapshotIdentifier = 1;
+
+HeapSnapshot = class HeapSnapshot
+{
+    constructor(objectId, snapshotDataString)
+    {
+        this._identifier = nextSnapshotIdentifier++;
+        this._objectId = objectId;
+
+        let json = JSON.parse(snapshotDataString);
+        snapshotDataString = null;
+
+        let {version, nodes, nodeClassNames, edges, edgeTypes, edgeNames} = json;
+        console.assert(version === 1, &quot;Expect JavaScriptCore Heap Snapshot version 1&quot;);
+
+        this._nodes = nodes;
+        this._nodeCount = nodes.length / nodeFieldCount;
+
+        this._edges = edges;
+        this._edgeCount = edges.length / edgeFieldCount;
+
+        this._edgeTypesTable = edgeTypes;
+        this._edgeNamesTable = edgeNames;
+        this._nodeClassNamesTable = nodeClassNames;
+
+        this._totalSize = 0;
+        this._nodeIdentifierToOrdinal = new Map; // &lt;node identifier&gt; =&gt; nodeOrdinal
+        for (let nodeIndex = 0; nodeIndex &lt; nodes.length; nodeIndex += nodeFieldCount) {
+            let nodeOrdinal = nodeIndex / nodeFieldCount;
+            this._nodeIdentifierToOrdinal.set(nodes[nodeIndex + nodeIdOffset], nodeOrdinal);
+            this._totalSize += nodes[nodeIndex + nodeSizeOffset];
+        }
+
+        // FIXME: Replace toIdentifier and fromIdentifier in edges with nodeIndex to reduce hash lookups?
+
+        this._nodeOrdinalToFirstOutgoingEdge = new Uint32Array(this._nodeCount); // nodeOrdinal =&gt; edgeIndex
+        this._buildOutgoingEdges();
+
+        this._nodeOrdinalToFirstIncomingEdge = new Uint32Array(this._nodeCount + 1); // nodeOrdinal =&gt; incomingNodes/incomingEdges index
+        this._incomingNodes = new Uint32Array(this._edgeCount); // from nodeOrdinals.
+        this._incomingEdges = new Uint32Array(this._edgeCount); // edgeIndex.
+        this._buildIncomingEdges();
+
+        let {nodeOrdinalToPostOrderIndex, postOrderIndexToNodeOrdinal} = this._buildPostOrderIndexes();
+
+        this._nodeOrdinalToDominatorNodeOrdinal = new Uint32Array(this._nodeCount);
+        this._nodeOrdinalIsGCRoot = new Uint8Array(this._nodeCount);
+        this._buildDominatorIndexes(nodeOrdinalToPostOrderIndex, postOrderIndexToNodeOrdinal);
+
+        nodeOrdinalToPostOrderIndex = null;
+
+        this._nodeOrdinalToRetainedSizes = new Uint32Array(this._nodeCount);
+        this._buildRetainedSizes(postOrderIndexToNodeOrdinal);
+
+        postOrderIndexToNodeOrdinal = null;
+
+        this._categories = HeapSnapshot.buildCategories(this);
+    }
+
+    // Static
+
+    static buildCategories(snapshot, allowNodeIdentifierCallback)
+    {
+        let categories = {};
+
+        let nodes = snapshot._nodes;
+        let nodeClassNamesTable = snapshot._nodeClassNamesTable;
+        let nodeOrdinalToRetainedSizes = snapshot._nodeOrdinalToRetainedSizes;
+
+        // Skip the &lt;root&gt; node.
+        let firstNodeIndex = nodeFieldCount;
+        let firstNodeOrdinal = 1;
+        for (let nodeIndex = firstNodeIndex, nodeOrdinal = firstNodeOrdinal; nodeIndex &lt; nodes.length; nodeIndex += nodeFieldCount, nodeOrdinal++) {
+            if (allowNodeIdentifierCallback &amp;&amp; !allowNodeIdentifierCallback(nodes[nodeIndex + nodeIdOffset]))
+                continue;
+
+            let classNameTableIndex = nodes[nodeIndex + nodeClassNameOffset];
+            let className = nodeClassNamesTable[classNameTableIndex];
+            let size = nodes[nodeIndex + nodeSizeOffset];
+            let retainedSize = nodeOrdinalToRetainedSizes[nodeOrdinal];
+            let internal = nodes[nodeIndex + nodeInternalOffset] ? true : false;
+
+            let category = categories[className];
+            if (!category)
+                category = categories[className] = {className, size: 0, retainedSize: 0, count: 0, internalCount: 0};
+
+            category.size += size;
+            category.retainedSize += retainedSize;
+            category.count += 1;
+            if (internal)
+                category.internalCount += 1;
+        }
+
+        return categories;
+    }
+
+    static allocationBucketCounts(snapshot, bucketSizes, allowNodeIdentifierCallback)
+    {
+        let counts = new Array(bucketSizes.length + 1);
+        let remainderBucket = counts.length - 1;
+        counts.fill(0);
+
+        let nodes = snapshot._nodes;
+
+        // Skip the &lt;root&gt; node.
+        let firstNodeIndex = nodeFieldCount;
+
+    outer:
+        for (let nodeIndex = firstNodeIndex; nodeIndex &lt; nodes.length; nodeIndex += nodeFieldCount) {
+            if (allowNodeIdentifierCallback &amp;&amp; !allowNodeIdentifierCallback(nodes[nodeIndex + nodeIdOffset]))
+                continue;
+
+            let size = nodes[nodeIndex + nodeSizeOffset];
+            for (let i = 0; i &lt; bucketSizes.length; ++i) {
+                if (size &lt; bucketSizes[i]) {
+                    counts[i]++;
+                    continue outer;
+                }
+            }
+            counts[remainderBucket]++;
+        }
+
+        return counts;
+    }
+
+    static instancesWithClassName(snapshot, className, allowNodeIdentifierCallback)
+    {
+        let instances = [];
+
+        let nodes = snapshot._nodes;
+        let nodeClassNamesTable = snapshot._nodeClassNamesTable;
+
+        // Skip the &lt;root&gt; node.
+        let firstNodeIndex = nodeFieldCount;
+        let firstNodeOrdinal = 1;
+        for (let nodeIndex = firstNodeIndex, nodeOrdinal = firstNodeOrdinal; nodeIndex &lt; nodes.length; nodeIndex += nodeFieldCount, nodeOrdinal++) {
+            if (allowNodeIdentifierCallback &amp;&amp; !allowNodeIdentifierCallback(nodes[nodeIndex + nodeIdOffset]))
+                continue;
+
+            let classNameTableIndex = nodes[nodeIndex + nodeClassNameOffset];
+            if (nodeClassNamesTable[classNameTableIndex] === className)
+                instances.push(nodeIndex);
+        }
+
+        return instances.map(snapshot.serializeNode.bind(snapshot));
+    }
+
+    // Worker Methods
+
+    allocationBucketCounts(bucketSizes)
+    {
+        return HeapSnapshot.allocationBucketCounts(this, bucketSizes);
+    }
+
+    instancesWithClassName(className)
+    {
+        return HeapSnapshot.instancesWithClassName(this, className);
+    }
+
+    nodeWithIdentifier(nodeIdentifier)
+    {
+        let nodeOrdinal = this._nodeIdentifierToOrdinal.get(nodeIdentifier);
+        let nodeIndex = nodeOrdinal * nodeFieldCount;
+        return this.serializeNode(nodeIndex);
+    }
+
+    shortestGCRootPath(nodeIdentifier)
+    {
+        // Returns an array from this node to a gcRoot node.
+        // E.g. [Node, Edge, Node, Edge, Node].
+        // Internal nodes are avoided, so if the path is empty this
+        // node is either a gcRoot or only reachable via Internal nodes.
+
+        let paths = this._gcRootPathes(nodeIdentifier);
+        if (!paths.length)
+            return [];
+
+        paths.sort((a, b) =&gt; a.length - b.length);
+        let shortestPath = paths[0];
+
+        console.assert(&quot;node&quot; in shortestPath[0], &quot;Path should start with a node&quot;);
+        console.assert(&quot;node&quot; in shortestPath[shortestPath.length - 1], &quot;Path should end with a node&quot;);
+
+        return shortestPath.map((component) =&gt; {
+            if (component.node)
+                return this.serializeNode(component.node);
+            return this.serializeEdge(component.edge);
+        });
+    }
+
+    dominatedNodes(nodeIdentifier)
+    {
+        let dominatedNodes = [];
+
+        let targetNodeOrdinal = this._nodeIdentifierToOrdinal.get(nodeIdentifier);
+        for (let nodeOrdinal = 0; nodeOrdinal &lt; this._nodeCount; ++nodeOrdinal) {
+            if (this._nodeOrdinalToDominatorNodeOrdinal[nodeOrdinal] === targetNodeOrdinal)
+                dominatedNodes.push(nodeOrdinal * nodeFieldCount);
+        }
+
+        return dominatedNodes.map(this.serializeNode.bind(this));
+    }
+
+    retainedNodes(nodeIdentifier)
+    {
+        let retainedNodes = [];
+
+        let nodeOrdinal = this._nodeIdentifierToOrdinal.get(nodeIdentifier);
+        let edgeIndex = this._nodeOrdinalToFirstOutgoingEdge[nodeOrdinal];
+        for (; this._edges[edgeIndex + edgeFromIdOffset] === nodeIdentifier; edgeIndex += edgeFieldCount) {
+            let toNodeIdentifier = this._edges[edgeIndex + edgeToIdOffset];
+            let toNodeOrdinal = this._nodeIdentifierToOrdinal.get(toNodeIdentifier);
+            let toNodeIndex = toNodeOrdinal * nodeFieldCount;
+            retainedNodes.push(toNodeIndex);
+        }
+
+        return retainedNodes.map(this.serializeNode.bind(this));
+    }
+
+    retainers(nodeIdentifier)
+    {
+        let retainers = [];
+
+        let nodeOrdinal = this._nodeIdentifierToOrdinal.get(nodeIdentifier);
+        let incomingEdgeIndex = this._nodeOrdinalToFirstIncomingEdge[nodeOrdinal];
+        let incomingEdgeIndexEnd = this._nodeOrdinalToFirstIncomingEdge[nodeOrdinal + 1];
+        for (let edgeIndex = incomingEdgeIndex; edgeIndex &lt; incomingEdgeIndexEnd; ++edgeIndex) {
+            let fromNodeOrdinal = this._incomingNodes[edgeIndex];
+            let fromNodeIndex = fromNodeOrdinal * nodeFieldCount;
+            retainers.push(fromNodeIndex);
+        }
+
+        return retainers.map(this.serializeNode.bind(this));
+    }
+
+    // Public
+
+    serialize()
+    {
+        return {
+            identifier: this._identifier,
+            totalSize: this._totalSize,
+            totalObjectCount: this._nodeCount - 1, // &lt;root&gt;.
+            categories: this._categories,
+        };
+    }
+
+    serializeNode(nodeIndex)
+    {
+        console.assert((nodeIndex % nodeFieldCount) === 0, &quot;Invalid nodeIndex to serialize&quot;);
+
+        let nodeOrdinal = nodeIndex / nodeFieldCount;
+
+        return {
+            id: this._nodes[nodeIndex + nodeIdOffset],
+            className: this._nodeClassNamesTable[this._nodes[nodeIndex + nodeClassNameOffset]],
+            size: this._nodes[nodeIndex + nodeSizeOffset],
+            retainedSize: this._nodeOrdinalToRetainedSizes[nodeOrdinal],
+            internal: this._nodes[nodeIndex + nodeInternalOffset] ? true : false,
+            gcRoot: this._nodeOrdinalIsGCRoot[nodeOrdinal] ? true : false,
+        };
+    }
+
+    serializeEdge(edgeIndex)
+    {
+        console.assert((edgeIndex % edgeFieldCount) === 0, &quot;Invalid edgeIndex to serialize&quot;);
+
+        let edgeType = this._edgeTypesTable[this._edges[edgeIndex + edgeTypeOffset]];
+        let edgeData = this._edges[edgeIndex + edgeDataOffset];
+        switch (edgeType) {
+        case &quot;Internal&quot;:
+            // edgeData can be ignored.
+            break;
+        case &quot;Property&quot;:
+        case &quot;Variable&quot;:
+            // edgeData is a table index.
+            edgeData = this._edgeNamesTable[edgeData];
+            break;
+        case &quot;Index&quot;:
+            // edgeData is the index.
+            break;
+        default:
+            console.error(&quot;Unexpected edge type: &quot; + edgeType);
+            break;
+        }
+
+        return {
+            from: this._edges[edgeIndex + edgeFromIdOffset],
+            to: this._edges[edgeIndex + edgeToIdOffset],
+            type: edgeType,
+            data: edgeData,
+        };
+    }
+
+    // Private
+
+    _buildOutgoingEdges()
+    {
+        let lastFromIdentifier = -1;
+        for (let edgeIndex = 0; edgeIndex &lt; this._edges.length; edgeIndex += edgeFieldCount) {
+            let fromIdentifier = this._edges[edgeIndex + edgeFromIdOffset];
+            console.assert(lastFromIdentifier &lt;= fromIdentifier, &quot;Edge list should be ordered by from node identifier&quot;);
+            if (fromIdentifier !== lastFromIdentifier) {
+                let nodeOrdinal = this._nodeIdentifierToOrdinal.get(fromIdentifier);
+                this._nodeOrdinalToFirstOutgoingEdge[nodeOrdinal] = edgeIndex;
+                lastFromIdentifier = fromIdentifier;
+            }
+        }
+    }
+
+    _buildIncomingEdges()
+    {
+        // First calculate the count of incoming edges for each node.
+        for (let edgeIndex = 0; edgeIndex &lt; this._edges.length; edgeIndex += edgeFieldCount) {
+            let toIdentifier = this._edges[edgeIndex + edgeToIdOffset];
+            let toNodeOrdinal = this._nodeIdentifierToOrdinal.get(toIdentifier);
+            this._nodeOrdinalToFirstIncomingEdge[toNodeOrdinal]++;
+        }
+
+        // Replace the counts with what will be the resulting index by running up the counts.
+        // Store the counts in what will be the edges list to use when placing edges in the list.
+        let runningFirstIndex = 0;
+        for (let nodeOrdinal = 0; nodeOrdinal &lt; this._nodeCount; ++nodeOrdinal) {
+            let count = this._nodeOrdinalToFirstIncomingEdge[nodeOrdinal];
+            this._nodeOrdinalToFirstIncomingEdge[nodeOrdinal] = runningFirstIndex;
+            this._incomingNodes[runningFirstIndex] = count;
+            runningFirstIndex += count;            
+        }
+
+        // Fill in the incoming edges list. Use the count as an offset when placing edges in the list.
+        for (let edgeIndex = 0; edgeIndex &lt; this._edges.length; edgeIndex += edgeFieldCount) {
+            let fromIdentifier = this._edges[edgeIndex + edgeFromIdOffset];
+            let fromNodeOrdinal = this._nodeIdentifierToOrdinal.get(fromIdentifier);
+            let toIdentifier = this._edges[edgeIndex + edgeToIdOffset];
+            let toNodeOrdinal = this._nodeIdentifierToOrdinal.get(toIdentifier);
+
+            let firstIncomingEdgeIndex = this._nodeOrdinalToFirstIncomingEdge[toNodeOrdinal];
+            console.assert(this._incomingNodes[firstIncomingEdgeIndex] &gt; 0, &quot;Should be expecting edges for this node&quot;);
+            let countAsOffset = this._incomingNodes[firstIncomingEdgeIndex]--;
+            let index = firstIncomingEdgeIndex + countAsOffset - 1;
+            this._incomingNodes[index] = fromNodeOrdinal;
+            this._incomingEdges[index] = edgeIndex;
+        }
+
+        // Duplicate value on the end. Incoming edge iteration walks firstIncomingEdge(ordinal) to firstIncomingEdge(ordinal+1).
+        this._nodeOrdinalToFirstIncomingEdge[this._nodeCount] = this._nodeOrdinalToFirstIncomingEdge[this._nodeCount - 1];
+    }
+
+    _buildPostOrderIndexes()
+    {
+        let postOrderIndex = 0;
+        let nodeOrdinalToPostOrderIndex = new Uint32Array(this._nodeCount);
+        let postOrderIndexToNodeOrdinal = new Uint32Array(this._nodeCount);
+
+        let stackNodes = new Uint32Array(this._nodeCount); // nodeOrdinal.
+        let stackEdges = new Uint32Array(this._nodeCount); // edgeIndex.
+        let visited = new Uint8Array(this._nodeCount);
+
+        let stackTop = 0;
+        stackNodes[stackTop] = rootNodeOrdinal;
+        stackEdges[stackTop] = this._nodeOrdinalToFirstOutgoingEdge[rootNodeOrdinal];
+
+        while (stackTop &gt;= 0) {
+            let nodeOrdinal = stackNodes[stackTop];
+            let nodeIdentifier = this._nodes[(nodeOrdinal * nodeFieldCount) + nodeIdOffset];
+            let edgeIndex = stackEdges[stackTop];
+
+            if (this._edges[edgeIndex + edgeFromIdOffset] === nodeIdentifier) {
+                // Prepare the next child for the current node.
+                stackEdges[stackTop] += edgeFieldCount;
+
+                let toIdentifier = this._edges[edgeIndex + edgeToIdOffset];
+                let toNodeOrdinal = this._nodeIdentifierToOrdinal.get(toIdentifier);
+                if (visited[toNodeOrdinal])
+                    continue;
+
+                // Child.
+                stackTop++;
+                stackNodes[stackTop] = toNodeOrdinal;
+                stackEdges[stackTop] = this._nodeOrdinalToFirstOutgoingEdge[toNodeOrdinal];
+                visited[toNodeOrdinal] = 1;
+            } else {
+                // Self.
+                nodeOrdinalToPostOrderIndex[nodeOrdinal] = postOrderIndex;
+                postOrderIndexToNodeOrdinal[postOrderIndex] = nodeOrdinal;
+                postOrderIndex++;
+                stackTop--;
+            }
+        }
+
+        // Unvisited nodes.
+        // This can happen if the parent node was disallowed on the backend, but other nodes
+        // that were only referenced from that disallowed node were eventually allowed because
+        // they may be generic system objects. Give these nodes a postOrderIndex anyways.
+        if (postOrderIndex !== this._nodeCount) {
+            // Root was the last node visited. Revert assigning it an index, add it back at the end.
+            postOrderIndex--;
+
+            // Visit unvisited nodes.
+            for (let nodeOrdinal = 1; nodeOrdinal &lt; this._nodeCount; ++nodeOrdinal) {
+                if (visited[nodeOrdinal])
+                    continue;
+                nodeOrdinalToPostOrderIndex[nodeOrdinal] = postOrderIndex;
+                postOrderIndexToNodeOrdinal[postOrderIndex] = nodeOrdinal;
+                postOrderIndex++;
+            }
+
+            // Visit root again.
+            nodeOrdinalToPostOrderIndex[rootNodeOrdinal] = postOrderIndex;
+            postOrderIndexToNodeOrdinal[postOrderIndex] = rootNodeOrdinal;
+            postOrderIndex++;
+        }
+
+        console.assert(postOrderIndex === this._nodeCount, &quot;All nodes were visited&quot;);
+        console.assert(nodeOrdinalToPostOrderIndex[rootNodeOrdinal] === this._nodeCount - 1, &quot;Root node should have the last possible postOrderIndex&quot;);
+
+        return {nodeOrdinalToPostOrderIndex, postOrderIndexToNodeOrdinal};
+    }
+
+    _buildDominatorIndexes(nodeOrdinalToPostOrderIndex, postOrderIndexToNodeOrdinal)
+    {
+        // The algorithm is based on the article:
+        // K. Cooper, T. Harvey and K. Kennedy &quot;A Simple, Fast Dominance Algorithm&quot;
+
+        let rootPostOrderIndex = this._nodeCount - 1;
+        let noEntry = this._nodeCount;
+
+        let affected = new Uint8Array(this._nodeCount);
+        let dominators = new Uint32Array(this._nodeCount);
+
+        // Initialize with unset value.
+        dominators.fill(noEntry);
+
+        // Mark the root's dominator value.
+        dominators[rootPostOrderIndex] = rootPostOrderIndex;        
+
+        // Affect the root's children. Also use this opportunity to mark them as GC roots.
+        let rootEdgeIndex = this._nodeOrdinalToFirstOutgoingEdge[rootNodeOrdinal];
+        for (let edgeIndex = rootEdgeIndex; this._edges[edgeIndex + edgeFromIdOffset] === rootNodeIdentifier; edgeIndex += edgeFieldCount) {
+            let toIdentifier = this._edges[edgeIndex + edgeToIdOffset];
+            let toNodeOrdinal = this._nodeIdentifierToOrdinal.get(toIdentifier);
+            let toPostOrderIndex = nodeOrdinalToPostOrderIndex[toNodeOrdinal];
+            affected[toPostOrderIndex] = 1;
+            this._nodeOrdinalIsGCRoot[toNodeOrdinal] = 1;
+        }
+
+        let changed = true;
+        while (changed) {
+            changed = false;
+
+            for (let postOrderIndex = rootPostOrderIndex - 1; postOrderIndex &gt;= 0; --postOrderIndex) {
+                if (!affected[postOrderIndex])
+                    continue;
+                affected[postOrderIndex] = 0;
+
+                // The dominator is already the root, nothing to do.
+                if (dominators[postOrderIndex] === rootPostOrderIndex)
+                    continue;
+
+                let newDominatorIndex = noEntry;
+                let nodeOrdinal = postOrderIndexToNodeOrdinal[postOrderIndex];
+                let incomingEdgeIndex = this._nodeOrdinalToFirstIncomingEdge[nodeOrdinal];
+                let incomingEdgeIndexEnd = this._nodeOrdinalToFirstIncomingEdge[nodeOrdinal + 1];
+                for (let edgeIndex = incomingEdgeIndex; edgeIndex &lt; incomingEdgeIndexEnd; ++edgeIndex) {
+                    let fromNodeOrdinal = this._incomingNodes[edgeIndex];
+                    let fromPostOrderIndex = nodeOrdinalToPostOrderIndex[fromNodeOrdinal];
+                    if (dominators[fromPostOrderIndex] !== noEntry) {
+                        if (newDominatorIndex === noEntry)
+                            newDominatorIndex = fromPostOrderIndex;
+                        else {
+                            while (fromPostOrderIndex !== newDominatorIndex) {
+                                while (fromPostOrderIndex &lt; newDominatorIndex)
+                                    fromPostOrderIndex = dominators[fromPostOrderIndex];
+                                while (newDominatorIndex &lt; fromPostOrderIndex)
+                                    newDominatorIndex = dominators[newDominatorIndex];
+                            }
+                        }
+                    }
+                    if (newDominatorIndex === rootPostOrderIndex)
+                        break;
+                }
+
+                // Changed. Affect children.
+                if (newDominatorIndex !== noEntry &amp;&amp; dominators[postOrderIndex] !== newDominatorIndex) {
+                    dominators[postOrderIndex] = newDominatorIndex;
+                    changed = true;
+
+                    let outgoingEdgeIndex = this._nodeOrdinalToFirstOutgoingEdge[nodeOrdinal];
+                    let nodeIdentifier = this._nodes[(nodeOrdinal * nodeFieldCount) + nodeIdOffset];
+                    for (let edgeIndex = outgoingEdgeIndex; this._edges[edgeIndex + edgeFromIdOffset] === nodeIdentifier; edgeIndex += edgeFieldCount) {
+                        let toNodeIdentifier = this._edges[edgeIndex + edgeToIdOffset];
+                        let toNodeOrdinal = this._nodeIdentifierToOrdinal.get(toNodeIdentifier);
+                        let toNodePostOrder = nodeOrdinalToPostOrderIndex[toNodeOrdinal];
+                        affected[toNodePostOrder] = 1;
+                    }
+                }
+            }
+        }
+
+        for (let postOrderIndex = 0; postOrderIndex &lt; this._nodeCount; ++postOrderIndex) {
+            let nodeOrdinal = postOrderIndexToNodeOrdinal[postOrderIndex];
+            let dominatorNodeOrdinal = postOrderIndexToNodeOrdinal[dominators[postOrderIndex]];
+            this._nodeOrdinalToDominatorNodeOrdinal[nodeOrdinal] = dominatorNodeOrdinal;
+        }
+    }
+
+    _buildRetainedSizes(postOrderIndexToNodeOrdinal)
+    {
+        // Self size.
+        for (let nodeIndex = 0, nodeOrdinal = 0; nodeOrdinal &lt; this._nodeCount; nodeIndex += nodeFieldCount, nodeOrdinal++)
+            this._nodeOrdinalToRetainedSizes[nodeOrdinal] = this._nodes[nodeIndex + nodeSizeOffset];
+
+        // Attribute size to dominator.
+        for (let postOrderIndex = 0; postOrderIndex &lt; this._nodeCount - 1; ++postOrderIndex) {
+            let nodeOrdinal = postOrderIndexToNodeOrdinal[postOrderIndex];
+            let nodeRetainedSize = this._nodeOrdinalToRetainedSizes[nodeOrdinal];
+            let dominatorNodeOrdinal = this._nodeOrdinalToDominatorNodeOrdinal[nodeOrdinal];
+            this._nodeOrdinalToRetainedSizes[dominatorNodeOrdinal] += nodeRetainedSize;
+        }
+    }
+
+    _gcRootPathes(nodeIdentifier)
+    {
+        let targetNodeOrdinal = this._nodeIdentifierToOrdinal.get(nodeIdentifier);
+
+        if (this._nodeOrdinalIsGCRoot[targetNodeOrdinal])
+            return [];
+
+        // FIXME: Array push/pop can affect performance here, but in practice it hasn't been an issue.
+
+        let paths = [];
+        let currentPath = [];
+        let visited = new Uint8Array(this._nodeCount);
+
+        function visitNode(nodeOrdinal)
+        {
+            if (this._nodeOrdinalIsGCRoot[nodeOrdinal]) {
+                let fullPath = currentPath.slice();
+                let nodeIndex = nodeOrdinal * nodeFieldCount;
+                fullPath.push({node: nodeIndex});
+                paths.push(fullPath);
+                return;
+            }
+
+            if (visited[nodeOrdinal])
+                return;
+            visited[nodeOrdinal] = 1;
+
+            let nodeIndex = nodeOrdinal * nodeFieldCount;
+            currentPath.push({node: nodeIndex});
+
+            // Loop in reverse order because edges were added in reverse order.
+            // It doesn't particularly matter other then consistency with previous code.
+            let incomingEdgeIndexStart = this._nodeOrdinalToFirstIncomingEdge[nodeOrdinal];
+            let incomingEdgeIndexEnd = this._nodeOrdinalToFirstIncomingEdge[nodeOrdinal + 1];
+            for (let incomingEdgeIndex = incomingEdgeIndexEnd - 1; incomingEdgeIndex &gt;= incomingEdgeIndexStart; --incomingEdgeIndex) {
+                let fromNodeOrdinal = this._incomingNodes[incomingEdgeIndex];
+                let fromNodeIndex = fromNodeOrdinal * nodeFieldCount;
+                let fromNodeIsInternal = this._nodes[fromNodeIndex + nodeInternalOffset];
+                if (fromNodeIsInternal)
+                    continue;
+
+                let edgeIndex = this._incomingEdges[incomingEdgeIndex];
+                currentPath.push({edge: edgeIndex});
+                visitNode.call(this, fromNodeOrdinal);
+                currentPath.pop();
+            }
+
+            currentPath.pop();
+        }
+
+        visitNode.call(this, targetNodeOrdinal);
+
+        return paths;
+    }
+};
+
+HeapSnapshotDiff = class HeapSnapshotDiff
+{
+    constructor(objectId, snapshot1, snapshot2)
+    {
+        this._objectId = objectId;
+
+        this._snapshot1 = snapshot1;
+        this._snapshot2 = snapshot2;
+
+        this._totalSize = 0;
+        this._addedNodeIdentifiers = new Set;
+
+        let known = new Map;
+        for (let nodeIndex = 0; nodeIndex &lt; this._snapshot1._nodes.length; nodeIndex += nodeFieldCount) {
+            let nodeIdentifier = this._snapshot1._nodes[nodeIndex + nodeIdOffset];
+            known.set(nodeIdentifier, nodeIndex);
+        }
+
+        for (let nodeIndex = 0; nodeIndex &lt; this._snapshot2._nodes.length; nodeIndex += nodeFieldCount) {
+            let nodeIdentifier = this._snapshot2._nodes[nodeIndex + nodeIdOffset];
+            let existed = known.delete(nodeIdentifier);
+            if (!existed) {
+                this._addedNodeIdentifiers.add(nodeIdentifier);
+                this._totalSize += this._snapshot2._nodes[nodeIndex + nodeSizeOffset];
+            }
+        }
+
+        this._categories = HeapSnapshot.buildCategories(this._snapshot2, (nodeIdentifier) =&gt; this._addedNodeIdentifiers.has(nodeIdentifier));
+    }
+
+    // Worker Methods
+
+    allocationBucketCounts(bucketSizes)
+    {
+        return HeapSnapshot.allocationBucketCounts(this._snapshot2, bucketSizes, (nodeIdentifier) =&gt; this._addedNodeIdentifiers.has(nodeIdentifier));
+    }
+
+    instancesWithClassName(className)
+    {
+        return HeapSnapshot.instancesWithClassName(this._snapshot2, className, (nodeIdentifier) =&gt; this._addedNodeIdentifiers.has(nodeIdentifier));
+    }
+
+    nodeWithIdentifier(nodeIdentifier) { return this._snapshot2.nodeWithIdentifier(nodeIdentifier); }
+    shortestGCRootPath(nodeIdentifier) { return this._snapshot2.shortestGCRootPath(nodeIdentifier); }
+    dominatedNodes(nodeIdentifier) { return this._snapshot2.dominatedNodes(nodeIdentifier); }
+    retainedNodes(nodeIdentifier) { return this._snapshot2.retainedNodes(nodeIdentifier); }
+    retainers(nodeIdentifier) { return this._snapshot2.retainers(nodeIdentifier); }
+
+    // Public
+
+    serialize()
+    {
+        return {
+            snapshot1: this._snapshot1.serialize(),
+            snapshot2: this._snapshot2.serialize(),
+            totalSize: this._totalSize,
+            totalObjectCount: this._addedNodeIdentifiers.size,
+            categories: this._categories,
+        };
+    }
+};
</ins></span></pre></div>
<a id="trunkSourceWebInspectorUIUserInterfaceWorkersHeapSnapshotHeapSnapshotWorkerjs"></a>
<div class="addfile"><h4>Added: trunk/Source/WebInspectorUI/UserInterface/Workers/HeapSnapshot/HeapSnapshotWorker.js (0 => 198353)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/WebInspectorUI/UserInterface/Workers/HeapSnapshot/HeapSnapshotWorker.js                                (rev 0)
+++ trunk/Source/WebInspectorUI/UserInterface/Workers/HeapSnapshot/HeapSnapshotWorker.js        2016-03-17 21:02:07 UTC (rev 198353)
</span><span class="lines">@@ -0,0 +1,97 @@
</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.
+ */
+
+importScripts(...[
+    &quot;HeapSnapshot.js&quot;
+]);
+
+HeapSnapshotWorker = class HeapSnapshotWorker
+{
+    constructor()
+    {
+        this._nextObjectId = 1;
+        this._objects = new Map;
+
+        self.addEventListener(&quot;message&quot;, this._handleMessage.bind(this));
+    }
+
+    // Actions
+
+    createSnapshot(snapshotString)
+    {
+        let objectId = this._nextObjectId++;
+        let snapshot = new HeapSnapshot(objectId, snapshotString);
+        this._objects.set(objectId, snapshot);
+        return {objectId, snapshot: snapshot.serialize()};
+    }
+
+    createSnapshotDiff(objectId1, objectId2)
+    {
+        let snapshot1 = this._objects.get(objectId1);
+        let snapshot2 = this._objects.get(objectId2);
+
+        console.assert(snapshot1 instanceof HeapSnapshot);
+        console.assert(snapshot2 instanceof HeapSnapshot);
+
+        let objectId = this._nextObjectId++;
+        let snapshotDiff = new HeapSnapshotDiff(objectId, snapshot1, snapshot2);
+        this._objects.set(objectId, snapshotDiff);
+        return {objectId, snapshotDiff: snapshotDiff.serialize()};
+    }
+
+    // Public
+
+    sendEvent(eventName, eventData)
+    {
+        self.postMessage({eventName, eventData});
+    }
+
+    // Private
+    
+    _handleMessage(event)
+    {
+        let data = event.data;
+
+        // Action.
+        if (data.actionName) {
+            let result = this[data.actionName](...data.actionArguments);
+            self.postMessage({callId: data.callId, result});
+            return;
+        }
+
+        // Method.
+        if (data.methodName) {
+            console.assert(data.objectId, &quot;Must have an objectId to call the method on&quot;);
+            let object = this._objects.get(data.objectId);
+            let result = object[data.methodName](...data.methodArguments);
+            self.postMessage({callId: data.callId, result});
+            return;
+        }
+
+        console.error(&quot;Unexpected HeapSnapshotWorker message&quot;, data);
+    }
+};
+
+self.heapSnapshotWorker = new HeapSnapshotWorker;
</ins></span></pre>
</div>
</div>

</body>
</html>