<!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>[166040] 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/166040">166040</a></dd>
<dt>Author</dt> <dd>bburg@apple.com</dd>
<dt>Date</dt> <dd>2014-03-20 21:45:14 -0700 (Thu, 20 Mar 2014)</dd>
</dl>

<h3>Log Message</h3>
<pre>Web Inspector: add frontend controller and models for replay sessions
https://bugs.webkit.org/show_bug.cgi?id=130145

Reviewed by Joseph Pecoraro.

Source/JavaScriptCore:

* inspector/scripts/CodeGeneratorInspector.py: Add the conditional Replay domain.

Source/WebInspectorUI:

Upstream the frontend models and controller for web replay. The replay manager
syncs with the backend controller's replay state and replay sessions by using
the same state machines and transitions.

Session and segment models update their data asynchronously using promises.

* UserInterface/Base/Main.js:
(WebInspector.loaded): Add the replay manager.
* UserInterface/Base/Test.js:
(WebInspector.loaded): Add the replay manager.
(InspectorTest.debugLog): Fix a bug in the unescape/escape trick.
(InspectorTest.addResult): Don't try to add results until the test page has loaded.
(InspectorTest.testPageDidLoad): Clear the isReloading flag.
(InspectorTest.reloadPage): Reimplement using promises. Return a promise.
* UserInterface/Base/Utilities.js: Implement Map.take in the obvious way.
* UserInterface/Controllers/ReplayManager.js: Added.
(WebInspector.ReplayManager):
(WebInspector):
(WebInspector.ReplayManager.prototype.get sessionState):
(WebInspector.ReplayManager.prototype.get segmentState):
(WebInspector.ReplayManager.prototype.get activeSessionIdentifier):
(WebInspector.ReplayManager.prototype.get activeSegmentIdentifier):
(WebInspector.ReplayManager.prototype.get playbackSpeed):
(WebInspector.ReplayManager.prototype.set playbackSpeed):
(WebInspector.ReplayManager.prototype.get currentPosition):
(WebInspector.ReplayManager.prototype.getSession.get var):
(WebInspector.ReplayManager.prototype.getSegment.get var):
(WebInspector.ReplayManager.prototype.captureStarted):
(WebInspector.ReplayManager.prototype.captureStopped):
(WebInspector.ReplayManager.prototype.playbackStarted):
(WebInspector.ReplayManager.prototype.playbackHitPosition):
(WebInspector.ReplayManager.prototype.playbackPaused):
(WebInspector.ReplayManager.prototype.playbackFinished):
(WebInspector.ReplayManager.prototype.sessionCreated.set catch):
(WebInspector.ReplayManager.prototype.sessionCreated.this):
(WebInspector.ReplayManager.prototype.sessionCreated):
(WebInspector.ReplayManager.prototype.sessionModified):
(WebInspector.ReplayManager.prototype.sessionRemoved.then):
(WebInspector.ReplayManager.prototype.sessionRemoved):
(WebInspector.ReplayManager.prototype.segmentCreated.set this):
(WebInspector.ReplayManager.prototype.segmentCompleted.set catch):
(WebInspector.ReplayManager.prototype.segmentCompleted):
(WebInspector.ReplayManager.prototype.segmentRemoved.then):
(WebInspector.ReplayManager.prototype.segmentRemoved):
(WebInspector.ReplayManager.prototype.segmentLoaded):
(WebInspector.ReplayManager.prototype.segmentUnloaded):
(WebInspector.ReplayManager.prototype.startCapturing):
(WebInspector.ReplayManager.prototype.stopCapturing):
(WebInspector.ReplayManager.prototype.replayToMarkIndex):
(WebInspector.ReplayManager.prototype.replayToCompletion):
(WebInspector.ReplayManager.prototype.pausePlayback):
(WebInspector.ReplayManager.prototype.stopPlayback):
(WebInspector.ReplayManager.prototype._changeSessionState):
(WebInspector.ReplayManager.prototype._changeSegmentState):
* UserInterface/Main.html:
* UserInterface/Models/ReplaySession.js: Added.
(WebInspector.ReplaySession):
(WebInspector.ReplaySession.fromPayload):
(WebInspector.ReplaySession.prototype.get segments):
(WebInspector.ReplaySession.prototype.segmentsChanged):
(WebInspector.ReplaySession.prototype._updateFromPayload):
* UserInterface/Models/ReplaySessionSegment.js: Added.
(WebInspector.IncompleteSessionSegment):
(WebInspector.IncompleteSessionSegment.prototype.get isComplete):
(WebInspector.ReplaySessionSegment):
(WebInspector.ReplaySessionSegment.prototype.get isComplete):
* UserInterface/Protocol/InspectorBackend.js:
(InspectorBackendClass.prototype.registerCommand):
(InspectorBackendClass.prototype._promise): Add a promise-returning method for
invoking backend commands that return a result asynchronously.
* UserInterface/Protocol/ReplayObserver.js: Added.
(WebInspector.ReplayPosition):
(WebInspector.ReplayPosition.fromProtocol):
(WebInspector.ReplayObserver):
(WebInspector.ReplayObserver.prototype.captureStarted):
(WebInspector.ReplayObserver.prototype.captureStopped):
(WebInspector.ReplayObserver.prototype.playbackStarted):
(WebInspector.ReplayObserver.prototype.playbackHitPosition):
(WebInspector.ReplayObserver.prototype.playbackPaused):
(WebInspector.ReplayObserver.prototype.playbackFinished):
(WebInspector.ReplayObserver.prototype.inputSuppressionChanged):
(WebInspector.ReplayObserver.prototype.sessionCreated):
(WebInspector.ReplayObserver.prototype.sessionModified):
(WebInspector.ReplayObserver.prototype.sessionRemoved):
(WebInspector.ReplayObserver.prototype.sessionLoaded):
(WebInspector.ReplayObserver.prototype.segmentCreated):
(WebInspector.ReplayObserver.prototype.segmentRemoved):
(WebInspector.ReplayObserver.prototype.segmentCompleted):
(WebInspector.ReplayObserver.prototype.segmentLoaded):
(WebInspector.ReplayObserver.prototype.segmentUnloaded):
* UserInterface/Test.html:

LayoutTests:

Add tests for existing nondeterministic inputs handled in JSC.
They are skipped for all platforms until WEB_REPLAY is enabled.

The new mechanism here is the single-segment replay reftest. It will
load the test page once to inject test code into the inspector. Then,
the reftest will reload the test page and start capturing. The test
page performs some nondeterministic computation before the load event.
Then, the inspector test dumps the computed nondeterministic state.
Capturing is stopped, and the session is replayed once. When the load
event fires on the replayed page execution, the nondeterministic states
from capturing and replaying are compared. They should be the same.

* inspector/replay/javascript-random-seed-expected.txt: Added.
* inspector/replay/javascript-random-seed.html: Added.
* inspector/replay/replay-test.js: Added. This contains the bulk of
the replay-specific testing logic for the added tests.

(InspectorTestProxy.registerInitializer.):
(InspectorTestProxy.registerInitializer):</pre>

<h3>Modified Paths</h3>
<ul>
<li><a href="#trunkLayoutTestsChangeLog">trunk/LayoutTests/ChangeLog</a></li>
<li><a href="#trunkLayoutTestsinspectorinspectortestjs">trunk/LayoutTests/inspector/inspector-test.js</a></li>
<li><a href="#trunkSourceJavaScriptCoreChangeLog">trunk/Source/JavaScriptCore/ChangeLog</a></li>
<li><a href="#trunkSourceJavaScriptCoreinspectorscriptsCodeGeneratorInspectorpy">trunk/Source/JavaScriptCore/inspector/scripts/CodeGeneratorInspector.py</a></li>
<li><a href="#trunkSourceWebInspectorUIChangeLog">trunk/Source/WebInspectorUI/ChangeLog</a></li>
<li><a href="#trunkSourceWebInspectorUIUserInterfaceBaseMainjs">trunk/Source/WebInspectorUI/UserInterface/Base/Main.js</a></li>
<li><a href="#trunkSourceWebInspectorUIUserInterfaceBaseTestjs">trunk/Source/WebInspectorUI/UserInterface/Base/Test.js</a></li>
<li><a href="#trunkSourceWebInspectorUIUserInterfaceBaseUtilitiesjs">trunk/Source/WebInspectorUI/UserInterface/Base/Utilities.js</a></li>
<li><a href="#trunkSourceWebInspectorUIUserInterfaceMainhtml">trunk/Source/WebInspectorUI/UserInterface/Main.html</a></li>
<li><a href="#trunkSourceWebInspectorUIUserInterfaceProtocolInspectorBackendjs">trunk/Source/WebInspectorUI/UserInterface/Protocol/InspectorBackend.js</a></li>
<li><a href="#trunkSourceWebInspectorUIUserInterfaceTesthtml">trunk/Source/WebInspectorUI/UserInterface/Test.html</a></li>
</ul>

<h3>Added Paths</h3>
<ul>
<li>trunk/LayoutTests/inspector/replay/</li>
<li><a href="#trunkLayoutTestsinspectorreplayjavascriptdatenowexpectedtxt">trunk/LayoutTests/inspector/replay/javascript-date-now-expected.txt</a></li>
<li><a href="#trunkLayoutTestsinspectorreplayjavascriptdatenowhtml">trunk/LayoutTests/inspector/replay/javascript-date-now.html</a></li>
<li><a href="#trunkLayoutTestsinspectorreplayjavascriptrandomseedexpectedtxt">trunk/LayoutTests/inspector/replay/javascript-random-seed-expected.txt</a></li>
<li><a href="#trunkLayoutTestsinspectorreplayjavascriptrandomseedhtml">trunk/LayoutTests/inspector/replay/javascript-random-seed.html</a></li>
<li><a href="#trunkLayoutTestsinspectorreplayreplaytestjs">trunk/LayoutTests/inspector/replay/replay-test.js</a></li>
<li><a href="#trunkSourceWebInspectorUIUserInterfaceControllersReplayManagerjs">trunk/Source/WebInspectorUI/UserInterface/Controllers/ReplayManager.js</a></li>
<li><a href="#trunkSourceWebInspectorUIUserInterfaceModelsReplaySessionjs">trunk/Source/WebInspectorUI/UserInterface/Models/ReplaySession.js</a></li>
<li><a href="#trunkSourceWebInspectorUIUserInterfaceModelsReplaySessionSegmentjs">trunk/Source/WebInspectorUI/UserInterface/Models/ReplaySessionSegment.js</a></li>
<li><a href="#trunkSourceWebInspectorUIUserInterfaceProtocolReplayObserverjs">trunk/Source/WebInspectorUI/UserInterface/Protocol/ReplayObserver.js</a></li>
</ul>

</div>
<div id="patch">
<h3>Diff</h3>
<a id="trunkLayoutTestsChangeLog"></a>
<div class="modfile"><h4>Modified: trunk/LayoutTests/ChangeLog (166039 => 166040)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/LayoutTests/ChangeLog        2014-03-21 04:13:37 UTC (rev 166039)
+++ trunk/LayoutTests/ChangeLog        2014-03-21 04:45:14 UTC (rev 166040)
</span><span class="lines">@@ -1,3 +1,30 @@
</span><ins>+2014-03-20  Brian Burg  &lt;bburg@apple.com&gt;
+
+        Web Inspector: add frontend controller and models for replay sessions
+        https://bugs.webkit.org/show_bug.cgi?id=130145
+
+        Reviewed by Joseph Pecoraro.
+
+        Add tests for existing nondeterministic inputs handled in JSC.
+        They are skipped for all platforms until WEB_REPLAY is enabled.
+
+        The new mechanism here is the single-segment replay reftest. It will
+        load the test page once to inject test code into the inspector. Then,
+        the reftest will reload the test page and start capturing. The test
+        page performs some nondeterministic computation before the load event.
+        Then, the inspector test dumps the computed nondeterministic state.
+        Capturing is stopped, and the session is replayed once. When the load
+        event fires on the replayed page execution, the nondeterministic states
+        from capturing and replaying are compared. They should be the same.
+
+        * inspector/replay/javascript-random-seed-expected.txt: Added.
+        * inspector/replay/javascript-random-seed.html: Added.
+        * inspector/replay/replay-test.js: Added. This contains the bulk of
+        the replay-specific testing logic for the added tests.
+
+        (InspectorTestProxy.registerInitializer.):
+        (InspectorTestProxy.registerInitializer):
+
</ins><span class="cx"> 2014-03-20  Brent Fulgham  &lt;bfulgham@apple.com&gt;
</span><span class="cx"> 
</span><span class="cx">         Rename TextTrackRegion/TextTrackRegionList to VTTRegion/VTTRegionList
</span></span></pre></div>
<a id="trunkLayoutTestsinspectorinspectortestjs"></a>
<div class="modfile"><h4>Modified: trunk/LayoutTests/inspector/inspector-test.js (166039 => 166040)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/LayoutTests/inspector/inspector-test.js        2014-03-21 04:13:37 UTC (rev 166039)
+++ trunk/LayoutTests/inspector/inspector-test.js        2014-03-21 04:45:14 UTC (rev 166040)
</span><span class="lines">@@ -55,12 +55,12 @@
</span><span class="cx"> 
</span><span class="cx">     function runInitializationMethodsInFrontend(initializersArray)
</span><span class="cx">     {
</span><del>-        if (InspectorTest.didInjectTestCode) {
-            // If the test page reloaded but we started running the test in a previous
-            // navigation, then don't initialize the inspector frontend again.
-            InspectorTest.testPageDidLoad();
</del><ins>+        InspectorTest.testPageDidLoad();
+
+        // If the test page reloaded but we started running the test in a previous
+        // navigation, then don't initialize the inspector frontend again.
+        if (InspectorTest.didInjectTestCode)
</ins><span class="cx">             return;
</span><del>-        }
</del><span class="cx"> 
</span><span class="cx">         for (var initializer of initializersArray) {
</span><span class="cx">             try {
</span></span></pre></div>
<a id="trunkLayoutTestsinspectorreplayjavascriptdatenowexpectedtxt"></a>
<div class="addfile"><h4>Added: trunk/LayoutTests/inspector/replay/javascript-date-now-expected.txt (0 => 166040)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/LayoutTests/inspector/replay/javascript-date-now-expected.txt                                (rev 0)
+++ trunk/LayoutTests/inspector/replay/javascript-date-now-expected.txt        2014-03-21 04:45:14 UTC (rev 166040)
</span><span class="lines">@@ -0,0 +1,14 @@
</span><ins>+Tests that we can capture and replay nondeterminism in Date.now() and the Date constructor.
+
+Waiting for test page to finish its initial load...
+Test page initial load done.
+Waiting for capturing to start...
+Capturing has started.
+Waiting to capture initial navigation...
+Initial navigation captured. Dumping state....
+Capture stopped, now starting replay to completion...
+Playback has started.
+Waiting to replay initial navigation...
+Initial navigation replayed. Dumping state...
+PASS: Nondeterministic state should not differ during capture and replay.
+
</ins></span></pre></div>
<a id="trunkLayoutTestsinspectorreplayjavascriptdatenowhtml"></a>
<div class="addfile"><h4>Added: trunk/LayoutTests/inspector/replay/javascript-date-now.html (0 => 166040)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/LayoutTests/inspector/replay/javascript-date-now.html                                (rev 0)
+++ trunk/LayoutTests/inspector/replay/javascript-date-now.html        2014-03-21 04:45:14 UTC (rev 166040)
</span><span class="lines">@@ -0,0 +1,38 @@
</span><ins>+&lt;html&gt;
+&lt;head&gt;
+&lt;script type=&quot;text/javascript&quot; src=&quot;../inspector-test.js&quot;&gt;&lt;/script&gt;
+&lt;script type=&quot;text/javascript&quot; src=&quot;./replay-test.js&quot;&gt;&lt;/script&gt;
+&lt;script&gt;
+
+window._times = [Date.now(), new Date().toString()];
+
+function dumpNondeterministicState()
+{
+    return window._times;
+}
+
+function test()
+{
+    function statesAreEqual(a, b)
+    {
+        if (typeof a !== &quot;array&quot; || typeof b !== &quot;array&quot;)
+            return false;
+        if (a.length !== b.length)
+            return false;
+
+        for (var i = 0; i &lt; a.length; ++i)
+            if (a[i] !== b[i])
+                return false;
+
+        return true;
+    }
+
+    InspectorTest.Replay.runSingleSegmentRefTest(statesAreEqual);
+}
+
+&lt;/script&gt;
+&lt;/head&gt;
+&lt;body onload=&quot;runTest()&quot;&gt;
+&lt;p&gt;Tests that we can capture and replay nondeterminism in Date.now() and the Date constructor.&lt;/p&gt;
+&lt;/body&gt;
+&lt;/html&gt;
</ins></span></pre></div>
<a id="trunkLayoutTestsinspectorreplayjavascriptrandomseedexpectedtxt"></a>
<div class="addfile"><h4>Added: trunk/LayoutTests/inspector/replay/javascript-random-seed-expected.txt (0 => 166040)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/LayoutTests/inspector/replay/javascript-random-seed-expected.txt                                (rev 0)
+++ trunk/LayoutTests/inspector/replay/javascript-random-seed-expected.txt        2014-03-21 04:45:14 UTC (rev 166040)
</span><span class="lines">@@ -0,0 +1,14 @@
</span><ins>+Tests that we can capture and replay nondeterminism in Math.random().
+
+Waiting for test page to finish its initial load...
+Test page initial load done.
+Waiting for capturing to start...
+Capturing has started.
+Waiting to capture initial navigation...
+Initial navigation captured. Dumping state....
+Capture stopped, now starting replay to completion...
+Playback has started.
+Waiting to replay initial navigation...
+Initial navigation replayed. Dumping state...
+PASS: Nondeterministic state should not differ during capture and replay.
+
</ins></span></pre></div>
<a id="trunkLayoutTestsinspectorreplayjavascriptrandomseedhtml"></a>
<div class="addfile"><h4>Added: trunk/LayoutTests/inspector/replay/javascript-random-seed.html (0 => 166040)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/LayoutTests/inspector/replay/javascript-random-seed.html                                (rev 0)
+++ trunk/LayoutTests/inspector/replay/javascript-random-seed.html        2014-03-21 04:45:14 UTC (rev 166040)
</span><span class="lines">@@ -0,0 +1,29 @@
</span><ins>+&lt;html&gt;
+&lt;head&gt;
+&lt;script type=&quot;text/javascript&quot; src=&quot;../inspector-test.js&quot;&gt;&lt;/script&gt;
+&lt;script type=&quot;text/javascript&quot; src=&quot;./replay-test.js&quot;&gt;&lt;/script&gt;
+&lt;script&gt;
+
+window._randomNumber = Math.random();
+
+function dumpNondeterministicState()
+{
+    return window._randomNumber;
+}
+
+function test()
+{
+    function statesAreEqual(a, b)
+    {
+        return a === b;
+    }
+
+    InspectorTest.Replay.runSingleSegmentRefTest(statesAreEqual);
+}
+
+&lt;/script&gt;
+&lt;/head&gt;
+&lt;body onload=&quot;runTest()&quot;&gt;
+&lt;p&gt;Tests that we can capture and replay nondeterminism in Math.random().&lt;/p&gt;
+&lt;/body&gt;
+&lt;/html&gt;
</ins></span></pre></div>
<a id="trunkLayoutTestsinspectorreplayreplaytestjs"></a>
<div class="addfile"><h4>Added: trunk/LayoutTests/inspector/replay/replay-test.js (0 => 166040)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/LayoutTests/inspector/replay/replay-test.js                                (rev 0)
+++ trunk/LayoutTests/inspector/replay/replay-test.js        2014-03-21 04:45:14 UTC (rev 166040)
</span><span class="lines">@@ -0,0 +1,79 @@
</span><ins>+InspectorTestProxy.registerInitializer(function() {
+
+InspectorTest.Replay = {};
+
+InspectorTest.Replay.runSingleSegmentRefTest = function(stateComparator)
+{
+    var stateDuringCapturing = null;
+    var stateDuringReplaying = null;
+
+    var ignoreCacheOnReload = true;
+    InspectorTest.reloadPage(ignoreCacheOnReload)
+    .then(function() {
+        return new Promise(function waitForMainResourceBeforeStarting(resolve, reject) {
+            InspectorTest.eventDispatcher.addEventListener(InspectorTest.EventDispatcher.Event.TestPageDidLoad, resolve);
+            InspectorTest.log(&quot;Waiting for test page to finish its initial load...&quot;);
+        });
+    })
+    .then(function() {
+        InspectorTest.log(&quot;Test page initial load done.&quot;);
+        return new Promise(function startCapturing(resolve, reject) {
+            InspectorTest.log(&quot;Waiting for capturing to start...&quot;);
+            WebInspector.replayManager.startCapturing();
+            WebInspector.replayManager.addEventListener(WebInspector.ReplayManager.Event.CaptureStarted, resolve);
+        });
+    })
+    .then(function() {
+        InspectorTest.log(&quot;Capturing has started.&quot;);
+        return new Promise(function waitToCaptureInitialNavigation(resolve, reject) {
+            InspectorTest.log(&quot;Waiting to capture initial navigation...&quot;);
+            InspectorTest.eventDispatcher.addEventListener(InspectorTest.EventDispatcher.Event.TestPageDidLoad, resolve);
+        });
+    })
+    .then(function() {
+        InspectorTest.log(&quot;Initial navigation captured. Dumping state....&quot;);
+        return RuntimeAgent.evaluate.promise(&quot;dumpNondeterministicState()&quot;);
+    })
+    .then(function(payload) {
+        stateDuringCapturing = payload.value;
+        return new Promise(function stopCapturing(resolve, reject) {
+            WebInspector.replayManager.stopCapturing();
+            WebInspector.replayManager.addEventListener(WebInspector.ReplayManager.Event.CaptureStopped, resolve);
+        });
+    })
+    .then(function() {
+        InspectorTest.log(&quot;Capture stopped, now starting replay to completion...&quot;)
+        return new Promise(function startPlayback(resolve, reject) {
+            WebInspector.replayManager.replayToCompletion();
+            WebInspector.replayManager.addEventListener(WebInspector.ReplayManager.Event.PlaybackStarted, resolve);
+        });
+    })
+    .then(function() {
+        InspectorTest.log(&quot;Playback has started.&quot;);
+        return new Promise(function waitForMainResourceWhenReplaying(resolve, reject) {
+            InspectorTest.log(&quot;Waiting to replay initial navigation...&quot;);
+            InspectorTest.eventDispatcher.addEventListener(InspectorTest.EventDispatcher.Event.TestPageDidLoad, resolve);
+        });
+    })
+    .then(function() {
+        InspectorTest.log(&quot;Initial navigation replayed. Dumping state...&quot;);
+        return RuntimeAgent.evaluate.promise(&quot;dumpNondeterministicState()&quot;);
+    })
+    .then(function(payload) {
+        stateDuringReplaying = payload.value;
+        return new Promise(function waitForReplayingToFinish(resolve, reject) {
+            WebInspector.replayManager.addEventListener(WebInspector.ReplayManager.Event.PlaybackFinished, resolve);
+        });
+    })
+    .then(function() {
+        var statesEqual = stateDuringCapturing === stateDuringReplaying;
+        InspectorTest.expectThat(statesEqual, &quot;Nondeterministic state should not differ during capture and replay.&quot;);
+        if (!statesEqual) {
+            InspectorTest.log(&quot;State during capturing: &quot; + stateDuringCapturing);
+            InspectorTest.log(&quot;State during replaying: &quot; + stateDuringReplaying);
+        }
+        InspectorTest.completeTest();
+    });
+};
+
+});
</ins><span class="cx">\ No newline at end of file
</span></span></pre></div>
<a id="trunkSourceJavaScriptCoreChangeLog"></a>
<div class="modfile"><h4>Modified: trunk/Source/JavaScriptCore/ChangeLog (166039 => 166040)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/JavaScriptCore/ChangeLog        2014-03-21 04:13:37 UTC (rev 166039)
+++ trunk/Source/JavaScriptCore/ChangeLog        2014-03-21 04:45:14 UTC (rev 166040)
</span><span class="lines">@@ -1,3 +1,12 @@
</span><ins>+2014-03-20  Brian Burg  &lt;bburg@apple.com&gt;
+
+        Web Inspector: add frontend controller and models for replay sessions
+        https://bugs.webkit.org/show_bug.cgi?id=130145
+
+        Reviewed by Joseph Pecoraro.
+
+        * inspector/scripts/CodeGeneratorInspector.py: Add the conditional Replay domain.
+
</ins><span class="cx"> 2014-03-20  Filip Pizlo  &lt;fpizlo@apple.com&gt;
</span><span class="cx"> 
</span><span class="cx">         FTL ValueToInt32 mishandles the constant case, and by the way, there is a constant case that the FTL sees
</span></span></pre></div>
<a id="trunkSourceJavaScriptCoreinspectorscriptsCodeGeneratorInspectorpy"></a>
<div class="modfile"><h4>Modified: trunk/Source/JavaScriptCore/inspector/scripts/CodeGeneratorInspector.py (166039 => 166040)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/JavaScriptCore/inspector/scripts/CodeGeneratorInspector.py        2014-03-21 04:13:37 UTC (rev 166039)
+++ trunk/Source/JavaScriptCore/inspector/scripts/CodeGeneratorInspector.py        2014-03-21 04:45:14 UTC (rev 166040)
</span><span class="lines">@@ -45,6 +45,7 @@
</span><span class="cx"> DOMAIN_DEFINE_NAME_MAP = {
</span><span class="cx">     &quot;Database&quot;: &quot;SQL_DATABASE&quot;,
</span><span class="cx">     &quot;IndexedDB&quot;: &quot;INDEXED_DATABASE&quot;,
</span><ins>+    &quot;Replay&quot;: &quot;WEB_REPLAY&quot;,
</ins><span class="cx"> }
</span><span class="cx"> 
</span><span class="cx"> 
</span></span></pre></div>
<a id="trunkSourceWebInspectorUIChangeLog"></a>
<div class="modfile"><h4>Modified: trunk/Source/WebInspectorUI/ChangeLog (166039 => 166040)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/WebInspectorUI/ChangeLog        2014-03-21 04:13:37 UTC (rev 166039)
+++ trunk/Source/WebInspectorUI/ChangeLog        2014-03-21 04:45:14 UTC (rev 166040)
</span><span class="lines">@@ -1,3 +1,102 @@
</span><ins>+2014-03-20  Brian Burg  &lt;bburg@apple.com&gt;
+
+        Web Inspector: add frontend controller and models for replay sessions
+        https://bugs.webkit.org/show_bug.cgi?id=130145
+
+        Reviewed by Joseph Pecoraro.
+
+        Upstream the frontend models and controller for web replay. The replay manager
+        syncs with the backend controller's replay state and replay sessions by using
+        the same state machines and transitions.
+
+        Session and segment models update their data asynchronously using promises.
+
+        * UserInterface/Base/Main.js:
+        (WebInspector.loaded): Add the replay manager.
+        * UserInterface/Base/Test.js:
+        (WebInspector.loaded): Add the replay manager.
+        (InspectorTest.debugLog): Fix a bug in the unescape/escape trick.
+        (InspectorTest.addResult): Don't try to add results until the test page has loaded.
+        (InspectorTest.testPageDidLoad): Clear the isReloading flag.
+        (InspectorTest.reloadPage): Reimplement using promises. Return a promise.
+        * UserInterface/Base/Utilities.js: Implement Map.take in the obvious way.
+        * UserInterface/Controllers/ReplayManager.js: Added.
+        (WebInspector.ReplayManager):
+        (WebInspector):
+        (WebInspector.ReplayManager.prototype.get sessionState):
+        (WebInspector.ReplayManager.prototype.get segmentState):
+        (WebInspector.ReplayManager.prototype.get activeSessionIdentifier):
+        (WebInspector.ReplayManager.prototype.get activeSegmentIdentifier):
+        (WebInspector.ReplayManager.prototype.get playbackSpeed):
+        (WebInspector.ReplayManager.prototype.set playbackSpeed):
+        (WebInspector.ReplayManager.prototype.get currentPosition):
+        (WebInspector.ReplayManager.prototype.getSession.get var):
+        (WebInspector.ReplayManager.prototype.getSegment.get var):
+        (WebInspector.ReplayManager.prototype.captureStarted):
+        (WebInspector.ReplayManager.prototype.captureStopped):
+        (WebInspector.ReplayManager.prototype.playbackStarted):
+        (WebInspector.ReplayManager.prototype.playbackHitPosition):
+        (WebInspector.ReplayManager.prototype.playbackPaused):
+        (WebInspector.ReplayManager.prototype.playbackFinished):
+        (WebInspector.ReplayManager.prototype.sessionCreated.set catch):
+        (WebInspector.ReplayManager.prototype.sessionCreated.this):
+        (WebInspector.ReplayManager.prototype.sessionCreated):
+        (WebInspector.ReplayManager.prototype.sessionModified):
+        (WebInspector.ReplayManager.prototype.sessionRemoved.then):
+        (WebInspector.ReplayManager.prototype.sessionRemoved):
+        (WebInspector.ReplayManager.prototype.segmentCreated.set this):
+        (WebInspector.ReplayManager.prototype.segmentCompleted.set catch):
+        (WebInspector.ReplayManager.prototype.segmentCompleted):
+        (WebInspector.ReplayManager.prototype.segmentRemoved.then):
+        (WebInspector.ReplayManager.prototype.segmentRemoved):
+        (WebInspector.ReplayManager.prototype.segmentLoaded):
+        (WebInspector.ReplayManager.prototype.segmentUnloaded):
+        (WebInspector.ReplayManager.prototype.startCapturing):
+        (WebInspector.ReplayManager.prototype.stopCapturing):
+        (WebInspector.ReplayManager.prototype.replayToMarkIndex):
+        (WebInspector.ReplayManager.prototype.replayToCompletion):
+        (WebInspector.ReplayManager.prototype.pausePlayback):
+        (WebInspector.ReplayManager.prototype.stopPlayback):
+        (WebInspector.ReplayManager.prototype._changeSessionState):
+        (WebInspector.ReplayManager.prototype._changeSegmentState):
+        * UserInterface/Main.html:
+        * UserInterface/Models/ReplaySession.js: Added.
+        (WebInspector.ReplaySession):
+        (WebInspector.ReplaySession.fromPayload):
+        (WebInspector.ReplaySession.prototype.get segments):
+        (WebInspector.ReplaySession.prototype.segmentsChanged):
+        (WebInspector.ReplaySession.prototype._updateFromPayload):
+        * UserInterface/Models/ReplaySessionSegment.js: Added.
+        (WebInspector.IncompleteSessionSegment):
+        (WebInspector.IncompleteSessionSegment.prototype.get isComplete):
+        (WebInspector.ReplaySessionSegment):
+        (WebInspector.ReplaySessionSegment.prototype.get isComplete):
+        * UserInterface/Protocol/InspectorBackend.js:
+        (InspectorBackendClass.prototype.registerCommand):
+        (InspectorBackendClass.prototype._promise): Add a promise-returning method for
+        invoking backend commands that return a result asynchronously.
+        * UserInterface/Protocol/ReplayObserver.js: Added.
+        (WebInspector.ReplayPosition):
+        (WebInspector.ReplayPosition.fromProtocol):
+        (WebInspector.ReplayObserver):
+        (WebInspector.ReplayObserver.prototype.captureStarted):
+        (WebInspector.ReplayObserver.prototype.captureStopped):
+        (WebInspector.ReplayObserver.prototype.playbackStarted):
+        (WebInspector.ReplayObserver.prototype.playbackHitPosition):
+        (WebInspector.ReplayObserver.prototype.playbackPaused):
+        (WebInspector.ReplayObserver.prototype.playbackFinished):
+        (WebInspector.ReplayObserver.prototype.inputSuppressionChanged):
+        (WebInspector.ReplayObserver.prototype.sessionCreated):
+        (WebInspector.ReplayObserver.prototype.sessionModified):
+        (WebInspector.ReplayObserver.prototype.sessionRemoved):
+        (WebInspector.ReplayObserver.prototype.sessionLoaded):
+        (WebInspector.ReplayObserver.prototype.segmentCreated):
+        (WebInspector.ReplayObserver.prototype.segmentRemoved):
+        (WebInspector.ReplayObserver.prototype.segmentCompleted):
+        (WebInspector.ReplayObserver.prototype.segmentLoaded):
+        (WebInspector.ReplayObserver.prototype.segmentUnloaded):
+        * UserInterface/Test.html:
+
</ins><span class="cx"> 2014-03-20  Joseph Pecoraro  &lt;pecoraro@apple.com&gt;
</span><span class="cx"> 
</span><span class="cx">         Web Inspector: DebuggerDashboardView looks scrunched debugging JSContext
</span></span></pre></div>
<a id="trunkSourceWebInspectorUIUserInterfaceBaseMainjs"></a>
<div class="modfile"><h4>Modified: trunk/Source/WebInspectorUI/UserInterface/Base/Main.js (166039 => 166040)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/WebInspectorUI/UserInterface/Base/Main.js        2014-03-21 04:13:37 UTC (rev 166039)
+++ trunk/Source/WebInspectorUI/UserInterface/Base/Main.js        2014-03-21 04:45:14 UTC (rev 166040)
</span><span class="lines">@@ -84,6 +84,8 @@
</span><span class="cx">         InspectorBackend.registerLayerTreeDispatcher(new WebInspector.LayerTreeObserver);
</span><span class="cx">     if (InspectorBackend.registerRuntimeDispatcher)
</span><span class="cx">         InspectorBackend.registerRuntimeDispatcher(new WebInspector.RuntimeObserver);
</span><ins>+    if (InspectorBackend.registerReplayDispatcher)
+        InspectorBackend.registerReplayDispatcher(new WebInspector.ReplayObserver);
</ins><span class="cx"> 
</span><span class="cx">     // Enable agents.
</span><span class="cx">     if (window.InspectorAgent)
</span><span class="lines">@@ -115,6 +117,7 @@
</span><span class="cx">     this.layerTreeManager = new WebInspector.LayerTreeManager;
</span><span class="cx">     this.dashboardManager = new WebInspector.DashboardManager;
</span><span class="cx">     this.probeManager = new WebInspector.ProbeManager;
</span><ins>+    this.replayManager = new WebInspector.ReplayManager;
</ins><span class="cx"> 
</span><span class="cx">     // Enable the Console Agent after creating the singleton managers.
</span><span class="cx">     if (window.ConsoleAgent)
</span></span></pre></div>
<a id="trunkSourceWebInspectorUIUserInterfaceBaseTestjs"></a>
<div class="modfile"><h4>Modified: trunk/Source/WebInspectorUI/UserInterface/Base/Test.js (166039 => 166040)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/WebInspectorUI/UserInterface/Base/Test.js        2014-03-21 04:13:37 UTC (rev 166039)
+++ trunk/Source/WebInspectorUI/UserInterface/Base/Test.js        2014-03-21 04:45:14 UTC (rev 166040)
</span><span class="lines">@@ -35,6 +35,8 @@
</span><span class="cx">     InspectorBackend.registerTimelineDispatcher(new WebInspector.TimelineObserver);
</span><span class="cx">     InspectorBackend.registerCSSDispatcher(new WebInspector.CSSObserver);
</span><span class="cx">     InspectorBackend.registerRuntimeDispatcher(new WebInspector.RuntimeObserver);
</span><ins>+    if (InspectorBackend.registerReplayDispatcher)
+        InspectorBackend.registerReplayDispatcher(new WebInspector.ReplayObserver);
</ins><span class="cx"> 
</span><span class="cx">     // Instantiate controllers used by tests.
</span><span class="cx">     this.frameResourceManager = new WebInspector.FrameResourceManager;
</span><span class="lines">@@ -44,6 +46,7 @@
</span><span class="cx">     this.timelineManager = new WebInspector.TimelineManager;
</span><span class="cx">     this.debuggerManager = new WebInspector.DebuggerManager;
</span><span class="cx">     this.probeManager = new WebInspector.ProbeManager;
</span><ins>+    this.replayManager = new WebInspector.ReplayManager;
</ins><span class="cx"> 
</span><span class="cx">     document.addEventListener(&quot;DOMContentLoaded&quot;, this.contentLoaded.bind(this));
</span><span class="cx"> 
</span><span class="lines">@@ -69,6 +72,29 @@
</span><span class="cx"> // which are provided by `inspector-test.js`.
</span><span class="cx"> InspectorTest = {};
</span><span class="cx"> 
</span><ins>+// This is a workaround for the fact that it would be hard to set up a constructor,
+// prototype, and prototype chain for the singleton InspectorTest.
+InspectorTest.EventDispatcher = function()
+{
+    WebInspector.Object.call(this);
+};
+
+InspectorTest.EventDispatcher.Event = {
+    TestPageDidLoad: &quot;inspector-test-test-page-did-load&quot;
+};
+
+InspectorTest.EventDispatcher.prototype = {
+    __proto__: WebInspector.Object.prototype,
+    constructor: InspectorTest.EventDispatcher,
+
+    dispatchEvent: function(event)
+    {
+        this.dispatchEventToListeners(event);
+    }
+};
+
+InspectorTest.eventDispatcher = new InspectorTest.EventDispatcher;
+
</ins><span class="cx"> // Note: Additional InspectorTest methods are included on a per-test basis from
</span><span class="cx"> // files like `debugger-test.js`.
</span><span class="cx"> 
</span><span class="lines">@@ -100,7 +126,7 @@
</span><span class="cx"> // This function should only be used to debug tests and not to produce normal test output.
</span><span class="cx"> InspectorTest.debugLog = function(message)
</span><span class="cx"> {
</span><del>-    this.evaluateInPage(&quot;InspectorTestProxy.debugLog(unescape(&quot; + escape(JSON.stringify(message)) + &quot;))&quot;);
</del><ins>+    this.evaluateInPage(&quot;InspectorTestProxy.debugLog(unescape('&quot; + escape(JSON.stringify(message)) + &quot;'))&quot;);
</ins><span class="cx"> }
</span><span class="cx"> 
</span><span class="cx"> InspectorTest.completeTest = function()
</span><span class="lines">@@ -132,7 +158,9 @@
</span><span class="cx"> InspectorTest.addResult = function(text)
</span><span class="cx"> {
</span><span class="cx">     this._results.push(text);
</span><del>-    this.evaluateInPage(&quot;InspectorTestProxy.addResult(unescape('&quot; + escape(text) + &quot;'))&quot;);
</del><ins>+
+    if (!this._testPageIsReloading)
+        this.evaluateInPage(&quot;InspectorTestProxy.addResult(unescape('&quot; + escape(text) + &quot;'))&quot;);
</ins><span class="cx"> }
</span><span class="cx"> 
</span><span class="cx"> InspectorTest._resendResults = function(callback)
</span><span class="lines">@@ -154,13 +182,21 @@
</span><span class="cx"> 
</span><span class="cx"> InspectorTest.testPageDidLoad = function()
</span><span class="cx"> {
</span><del>-    this._shouldResendResults = true;
</del><ins>+    this._testPageIsReloading = false;
</ins><span class="cx">     this._resendResults();
</span><ins>+
+    this.eventDispatcher.dispatchEvent(InspectorTest.EventDispatcher.Event.TestPageDidLoad);
</ins><span class="cx"> }
</span><span class="cx"> 
</span><span class="cx"> InspectorTest.reloadPage = function(shouldIgnoreCache)
</span><span class="cx"> {
</span><del>-    PageAgent.reload.invoke({ignoreCache: !!shouldIgnoreCache});
</del><ins>+    return PageAgent.reload.promise(!!shouldIgnoreCache)
+        .then(function() {
+            this._shouldResendResults = true;
+            this._testPageIsReloading = true;
+
+            return Promise.resolve(null);
+        });
</ins><span class="cx"> }
</span><span class="cx"> 
</span><span class="cx"> InspectorTest.reportUncaughtException = function(message, url, lineNumber)
</span></span></pre></div>
<a id="trunkSourceWebInspectorUIUserInterfaceBaseUtilitiesjs"></a>
<div class="modfile"><h4>Modified: trunk/Source/WebInspectorUI/UserInterface/Base/Utilities.js (166039 => 166040)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/WebInspectorUI/UserInterface/Base/Utilities.js        2014-03-21 04:13:37 UTC (rev 166039)
+++ trunk/Source/WebInspectorUI/UserInterface/Base/Utilities.js        2014-03-21 04:45:14 UTC (rev 166040)
</span><span class="lines">@@ -90,6 +90,16 @@
</span><span class="cx">     }
</span><span class="cx"> });
</span><span class="cx"> 
</span><ins>+Object.defineProperty(Map.prototype, &quot;take&quot;,
+{
+    value: function(key)
+    {
+        var deletedValue = this.get(key);
+        this.delete(key);
+        return deletedValue;
+    }
+});
+
</ins><span class="cx"> Object.defineProperty(Node.prototype, &quot;enclosingNodeOrSelfWithClass&quot;,
</span><span class="cx"> {
</span><span class="cx">     value: function(className)
</span></span></pre></div>
<a id="trunkSourceWebInspectorUIUserInterfaceControllersReplayManagerjs"></a>
<div class="addfile"><h4>Added: trunk/Source/WebInspectorUI/UserInterface/Controllers/ReplayManager.js (0 => 166040)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/WebInspectorUI/UserInterface/Controllers/ReplayManager.js                                (rev 0)
+++ trunk/Source/WebInspectorUI/UserInterface/Controllers/ReplayManager.js        2014-03-21 04:45:14 UTC (rev 166040)
</span><span class="lines">@@ -0,0 +1,442 @@
</span><ins>+/*
+ * Copyright (C) 2013 University of Washington. All rights reserved.
+ * Copyright (C) 2014 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.ReplayManager = function()
+{
+    WebInspector.Object.call(this);
+
+    this._sessionState = WebInspector.ReplayManager.SessionState.Inactive;
+    this._segmentState = WebInspector.ReplayManager.SegmentState.Unloaded;
+
+    this._activeSessionIdentifier = null;
+    this._activeSegmentIdentifier = null;
+    this._currentPosition = new WebInspector.ReplayPosition(0, 0);
+
+    // These hold actual instances of sessions and segments.
+    this._sessions = new Map;
+    this._segments = new Map;
+    // These hold promises that resolve when the instance data is recieved.
+    this._sessionPromises = new Map;
+    this._segmentPromises = new Map;
+
+    // Playback speed is specified in replayToPosition commands, and persists
+    // for the duration of the playback command until another playback begins.
+    this._playbackSpeed = WebInspector.ReplayManager.PlaybackSpeed.RealTime;
+
+    if (!window.ReplayAgent)
+        return;
+
+    ReplayAgent.getAvailableSessions.promise()
+        .then(function(sessionIds) {
+            for (var sessionId of sessionIds)
+                WebInspector.replayManager.sessionCreated(sessionId);
+        });
+};
+
+WebInspector.ReplayManager.Event = {
+    CaptureStarted: &quot;replay-manager-capture-started&quot;,
+    CaptureStopped: &quot;replay-manager-capture-stopped&quot;,
+
+    PlaybackStarted: &quot;replay-manager-playback-started&quot;,
+    PlaybackPaused: &quot;replay-manager-playback-paused&quot;,
+    PlaybackFinished: &quot;replay-manager-playback-finished&quot;,
+    PlaybackPositionChanged: &quot;replay-manager-play-back-position-changed&quot;,
+
+    ActiveSessionChanged: &quot;replay-manager-active-session-changed&quot;,
+    ActiveSegmentChanged: &quot;replay-manager-active-segment-changed&quot;,
+
+    SessionSegmentAdded: &quot;replay-manager-session-segment-added&quot;,
+    SessionSegmentRemoved: &quot;replay-manager-session-segment-removed&quot;,
+
+    SessionAdded: &quot;replay-manager-session-added&quot;,
+    SessionRemoved: &quot;replay-manager-session-removed&quot;,
+};
+
+WebInspector.ReplayManager.SessionState = {
+    Capturing: &quot;replay-manager-session-state-capturing&quot;,
+    Inactive: &quot;replay-manager-session-state-inactive&quot;,
+    Replaying: &quot;replay-manager-session-state-replaying&quot;,
+};
+
+WebInspector.ReplayManager.SegmentState = {
+    Appending: &quot;replay-manager-segment-state-appending&quot;,
+    Unloaded: &quot;replay-manager-segment-state-unloaded&quot;,
+    Loaded: &quot;replay-manager-segment-state-loaded&quot;,
+    Dispatching: &quot;replay-manager-segment-state-dispatching&quot;,
+};
+
+WebInspector.ReplayManager.PlaybackSpeed = {
+    RealTime: &quot;replay-manager-playback-speed-real-time&quot;,
+    FastForward: &quot;replay-manager-playback-speed-fast-forward&quot;,
+};
+
+WebInspector.ReplayManager.prototype = {
+    constructor: WebInspector.ReplayManager,
+    __proto__: WebInspector.Object.prototype,
+
+    // Public
+
+    get sessionState()
+    {
+        return this._sessionState;
+    },
+
+    get segmentState()
+    {
+        return this._segmentState;
+    },
+
+    get activeSessionIdentifier()
+    {
+        return this._activeSessionIdentifier;
+    },
+
+    get activeSegmentIdentifier()
+    {
+        return this._activeSegmentIdentifier;
+    },
+
+    get playbackSpeed()
+    {
+        return this._playbackSpeed;
+    },
+
+    set playbackSpeed(value)
+    {
+        this._playbackSpeed = value;
+    },
+
+    get currentPosition()
+    {
+        return this._currentPosition;
+    },
+
+    // These return promises even if the relevant instance is already created.
+    getSession: function(sessionId)
+    {
+        if (this._sessionPromises.has(sessionId))
+            return this._sessionPromises.get(sessionId);
+
+        var newPromise = ReplayAgent.getSerializedSession.promise(sessionId)
+            .then(function(payload) {
+                return Promise.resolve(WebInspector.ReplaySession.fromPayload(sessionId, payload));
+            });
+
+        this._sessionPromises.set(sessionId, newPromise);
+        return newPromise;
+    },
+
+    getSegment: function(segmentId)
+    {
+        if (this._segmentPromises.has(segmentId))
+            return this._segmentPromises.get(segmentId);
+
+        var newPromise = ReplayAgent.getSerializedSegment.promise(segmentId)
+            .then(function(payload) {
+                return Promise.resolve(new WebInspector.ReplaySessionSegment(segmentId, payload));
+            });
+
+        this._segmentPromises.set(segmentId, newPromise);
+        return newPromise;
+    },
+
+    // Protected (called by ReplayObserver)
+
+    captureStarted: function()
+    {
+        this._changeSessionState(WebInspector.ReplayManager.SessionState.Capturing);
+
+        this.dispatchEventToListeners(WebInspector.ReplayManager.Event.CaptureStarted);
+    },
+
+    captureStopped: function()
+    {
+        this._changeSessionState(WebInspector.ReplayManager.SessionState.Inactive);
+        this._changeSegmentState(WebInspector.ReplayManager.SegmentState.Unloaded);
+
+        this.dispatchEventToListeners(WebInspector.ReplayManager.Event.CaptureStopped);
+    },
+
+    playbackStarted: function()
+    {
+        if (this.sessionState === WebInspector.ReplayManager.SessionState.Inactive)
+            this._changeSessionState(WebInspector.ReplayManager.SessionState.Replaying);
+
+        this._changeSegmentState(WebInspector.ReplayManager.SegmentState.Dispatching);
+
+        this.dispatchEventToListeners(WebInspector.ReplayManager.Event.PlaybackStarted);
+    },
+
+    playbackHitPosition: function(replayPosition, timestamp)
+    {
+        console.assert(this.sessionState === WebInspector.ReplayManager.SessionState.Replaying);
+        console.assert(this.segmentState === WebInspector.ReplayManager.SegmentState.Dispatching);
+        console.assert(replayPosition instanceof WebInspector.ReplayPosition);
+
+        this._currentPosition = replayPosition;
+        this.dispatchEventToListeners(WebInspector.ReplayManager.Event.PlaybackPositionChanged);
+    },
+
+    playbackPaused: function(mark)
+    {
+        console.assert(this.sessionState === WebInspector.ReplayManager.SessionState.Replaying);
+        this._changeSegmentState(WebInspector.ReplayManager.SegmentState.Loaded);
+
+        this.dispatchEventToListeners(WebInspector.ReplayManager.Event.PlaybackPaused);
+    },
+
+    playbackFinished: function()
+    {
+        this._changeSessionState(WebInspector.ReplayManager.SessionState.Inactive);
+        console.assert(this.segmentState === WebInspector.ReplayManager.SegmentState.Unloaded);
+
+        this.dispatchEventToListeners(WebInspector.ReplayManager.Event.PlaybackFinished);
+    },
+
+    sessionCreated: function(sessionId)
+    {
+        console.assert(!this._sessions.has(sessionId), &quot;Tried to add duplicate session identifier:&quot;, sessionId);
+        var sessionMap = this._sessions;
+        this.getSession(sessionId)
+            .then(function(session) {
+                sessionMap.set(sessionId, session);
+            })
+            .catch(function(error) {
+                console.error(&quot;Error obtaining session data: &quot;, error);
+            });
+
+        this.dispatchEventToListeners(WebInspector.ReplayManager.Event.SessionCreated, {sessionId: sessionId});
+    },
+
+    sessionModified: function(sessionId)
+    {
+        this.getSession(sessionId).then(function(session) {
+            session.segmentsChanged();
+        });
+    },
+
+    sessionRemoved: function(sessionId)
+    {
+        console.assert(this._sessions.has(sessionId), &quot;Unknown session identifier:&quot;, sessionId);
+
+        if (!this._sessionPromises.has(sessionId))
+            return;
+
+        var manager = this;
+
+        // Wait for any outstanding promise to settle so it doesn't get re-added.
+        this.getSession(sessionId)
+            .catch(function(error) {
+                return Promise.resolve();
+            })
+            .then(function() {
+                manager._sessionPromises.delete(sessionId);
+                var removedSession = manager._sessions.take(sessionId);
+                console.assert(removedSession);
+                manager.dispatchEventToListeners(WebInspector.ReplayManager.Event.SessionRemoved, {removedSession: removedSession});
+            });
+    },
+
+    segmentCreated: function(segmentId)
+    {
+        console.assert(!this._segments.has(segmentId), &quot;Tried to add duplicate segment identifier:&quot;, segmentId);
+
+        this._changeSegmentState(WebInspector.ReplayManager.SegmentState.Appending);
+
+        // Create a dummy segment, and don't try to load any data for it. It will
+        // be removed once the segment is complete, and then its data will be fetched.
+        var incompleteSegment = new WebInspector.IncompleteSessionSegment(segmentId);
+        this._segments.set(segmentId, incompleteSegment);
+        this._segmentPromises.set(segmentId, Promise.resolve(incompleteSegment));
+
+        this.dispatchEventToListeners(WebInspector.ReplayManager.Event.SegmentCreated, {segmentIdentifier: segmentId});
+    },
+
+    segmentCompleted: function(segmentId)
+    {
+        var placeholderSegment = this._segments.take(segmentId);
+        console.assert(placeholderSegment instanceof WebInspector.IncompleteSessionSegment);
+        this._segmentPromises.delete(segmentId);
+
+        var segmentMap = this._segments;
+        this.getSegment(segmentId)
+            .then(function(segment) {
+                segmentMap.set(segmentId, segment);
+            })
+            .catch(function(error) {
+                console.error(&quot;Error obtaining segment data: &quot;, error);
+            });
+    },
+
+    segmentRemoved: function(segmentId)
+    {
+        console.assert(this._segments.has(segmentId), &quot;Unknown segment identifier:&quot;, segmentId);
+
+        if (!this._segmentPromises.has(segmentId))
+            return;
+
+        var manager = this;
+
+        // Wait for any outstanding promise to settle so it doesn't get re-added.
+        this.getSegment(segmentId)
+            .catch(function(error) {
+                return Promise.resolve();
+            })
+            .then(function() {
+                manager._segmentPromises.delete(segmentId);
+                var removedSegment = manager._segments.take(segmentId);
+                console.assert(removedSegment);
+                manager.dispatchEventToListeners(WebInspector.ReplayManager.Event.SessionSegmentRemoved, {removedSegment: removedSegment});
+            });
+    },
+
+    segmentLoaded: function(segmentId)
+    {
+        console.assert(this._segments.has(segmentId), &quot;Unknown segment identifier:&quot;, segmentId);
+
+        console.assert(this.sessionState !== WebInspector.ReplayManager.SessionState.Capturing);
+        this._changeSegmentState(WebInspector.ReplayManager.SegmentState.Loaded);
+
+        this._activeSegmentIdentifier = segmentId;
+        this.dispatchEventToListeners(WebInspector.ReplayManager.Event.SegmentLoaded);
+   },
+
+    segmentUnloaded: function()
+    {
+        console.assert(this.sessionState === WebInspector.ReplayManager.SessionState.Replaying);
+        this._changeSegmentState(WebInspector.ReplayManager.SegmentState.Unloaded);
+
+        var unloadedSegmentIdentifier = this._activeSegmentIdentifier;
+        this._activeSegmentIdentifier = null;
+        this.dispatchEventToListeners(WebInspector.ReplayManager.Event.segmentUnloaded, {unloadedSegmentIdentifier: unloadedSegmentIdentifier});
+    },
+
+    // Private
+
+    // FIXME: these methods should be private and only accessed by implementations of the public API,
+    // which should properly chain async calls leading up to these low-level backend calls. Otherwise,
+    // their preconditions on session and segment state are too strong for scenarios triggered by the UI.
+
+    startCapturing: function()
+    {
+        console.assert(this.sessionState === WebInspector.ReplayManager.SessionState.Inactive);
+        console.assert(this.segmentState === WebInspector.ReplayManager.SegmentState.Unloaded);
+
+        ReplayAgent.startCapturing();
+    },
+
+    stopCapturing: function()
+    {
+        console.assert(this.sessionState === WebInspector.ReplayManager.SessionState.Capturing);
+        console.assert(this.segmentState === WebInspector.ReplayManager.SegmentState.Appending);
+
+        ReplayAgent.stopCapturing();
+    },
+
+    replayToMarkIndex: function(replayPosition)
+    {
+        console.assert(replayPosition instanceof WebInspector.ReplayPosition);
+
+        console.assert(this.sessionState !== WebInspector.ReplayManager.SessionState.Capturing);
+        console.assert(this.segmentState === WebInspector.ReplayManager.SegmentState.Loaded);
+
+        this.dispatchEventToListeners(WebInspector.ReplayManager.Event.PlaybackWillStart);
+        ReplayAgent.replayToPosition(replayPosition, this.playbackSpeed === WebInspector.ReplayManager.PlaybackSpeed.FastForward);
+    },
+
+    replayToCompletion: function()
+    {
+        console.assert(this.sessionState !== WebInspector.ReplayManager.SessionState.Capturing);
+        console.assert(this.segmentState === WebInspector.ReplayManager.SegmentState.Loaded);
+
+        this.dispatchEventToListeners(WebInspector.ReplayManager.Event.PlaybackWillStart);
+        ReplayAgent.replayToCompletion(this.playbackSpeed === WebInspector.ReplayManager.PlaybackSpeed.FastForward);
+    },
+
+    pausePlayback: function()
+    {
+        console.assert(this.sessionState === WebInspector.ReplayManager.SessionState.Replaying);
+        console.assert(this.segmentState === WebInspector.ReplayManager.SegmentState.Dispatching);
+
+        ReplayAgent.pausePlayback();
+    },
+
+    stopPlayback: function()
+    {
+        console.assert(this.sessionState === WebInspector.ReplayManager.SessionState.Replaying);
+        console.assert(this.segmentState === WebInspector.ReplayManager.SegmentState.Loaded);
+
+        ReplayAgent.stopPlayback();
+    },
+
+    // Private
+
+    _changeSessionState: function(newState)
+    {
+        // Warn about no-op state changes. We shouldn't be seeing them.
+        var isAllowed = this._sessionState !== newState;
+
+        switch (this._sessionState) {
+        case WebInspector.ReplayManager.SessionState.Capturing:
+            isAllowed &amp;= newState === WebInspector.ReplayManager.SessionState.Inactive;
+            break;
+
+        case WebInspector.ReplayManager.SessionState.Replaying:
+            isAllowed &amp;= newState === WebInspector.ReplayManager.SessionState.Inactive;
+            break;
+        }
+
+        console.assert(isAllowed, &quot;Invalid session state change: &quot;, this._sessionState, &quot; to &quot;, newState);
+        if (isAllowed)
+            this._sessionState = newState;
+    },
+
+    _changeSegmentState: function(newState)
+    {
+        // Warn about no-op state changes. We shouldn't be seeing them.
+        var isAllowed = this._segmentState !== newState;
+
+        switch (this._segmentState) {
+            case WebInspector.ReplayManager.SegmentState.Appending:
+                isAllowed &amp;= newState === WebInspector.ReplayManager.SegmentState.Unloaded;
+                break;
+            case WebInspector.ReplayManager.SegmentState.Unloaded:
+                isAllowed &amp;= newState === WebInspector.ReplayManager.SegmentState.Appending || newState === WebInspector.ReplayManager.SegmentState.Loaded;
+                break;
+            case WebInspector.ReplayManager.SegmentState.Loaded:
+                isAllowed &amp;= newState === WebInspector.ReplayManager.SegmentState.Unloaded || newState === WebInspector.ReplayManager.SegmentState.Dispatching;
+                break;
+            case WebInspector.ReplayManager.SegmentState.Dispatching:
+                isAllowed &amp;= newState === WebInspector.ReplayManager.SegmentState.Loaded;
+                break;
+        }
+
+        console.assert(isAllowed, &quot;Invalid segment state change: &quot;, this._segmentState, &quot; to &quot;, newState);
+        if (isAllowed)
+            this._segmentState = newState;
+    }
+};
+
</ins></span></pre></div>
<a id="trunkSourceWebInspectorUIUserInterfaceMainhtml"></a>
<div class="modfile"><h4>Modified: trunk/Source/WebInspectorUI/UserInterface/Main.html (166039 => 166040)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/WebInspectorUI/UserInterface/Main.html        2014-03-21 04:13:37 UTC (rev 166039)
+++ trunk/Source/WebInspectorUI/UserInterface/Main.html        2014-03-21 04:45:14 UTC (rev 166040)
</span><span class="lines">@@ -190,6 +190,7 @@
</span><span class="cx">     &lt;script src=&quot;Protocol/NetworkObserver.js&quot;&gt;&lt;/script&gt;
</span><span class="cx">     &lt;script src=&quot;Protocol/PageObserver.js&quot;&gt;&lt;/script&gt;
</span><span class="cx">     &lt;script src=&quot;Protocol/RemoteObject.js&quot;&gt;&lt;/script&gt;
</span><ins>+    &lt;script src=&quot;Protocol/ReplayObserver.js&quot;&gt;&lt;/script&gt;
</ins><span class="cx">     &lt;script src=&quot;Protocol/RuntimeObserver.js&quot;&gt;&lt;/script&gt;
</span><span class="cx">     &lt;script src=&quot;Protocol/TimelineObserver.js&quot;&gt;&lt;/script&gt;
</span><span class="cx"> 
</span><span class="lines">@@ -243,6 +244,8 @@
</span><span class="cx">     &lt;script src=&quot;Models/Profile.js&quot;&gt;&lt;/script&gt;
</span><span class="cx">     &lt;script src=&quot;Models/ProfileNode.js&quot;&gt;&lt;/script&gt;
</span><span class="cx">     &lt;script src=&quot;Models/ProfileNodeCall.js&quot;&gt;&lt;/script&gt;
</span><ins>+    &lt;script src=&quot;Models/ReplaySession.js&quot;&gt;&lt;/script&gt;
+    &lt;script src=&quot;Models/ReplaySessionSegment.js&quot;&gt;&lt;/script&gt;
</ins><span class="cx">     &lt;script src=&quot;Models/Resource.js&quot;&gt;&lt;/script&gt;
</span><span class="cx">     &lt;script src=&quot;Models/ResourceCollection.js&quot;&gt;&lt;/script&gt;
</span><span class="cx">     &lt;script src=&quot;Models/ResourceSearchMatchObject.js&quot;&gt;&lt;/script&gt;
</span><span class="lines">@@ -476,6 +479,7 @@
</span><span class="cx">     &lt;script src=&quot;Controllers/LegacyProfileManager.js&quot;&gt;&lt;/script&gt;
</span><span class="cx">     &lt;script src=&quot;Controllers/LogManager.js&quot;&gt;&lt;/script&gt;
</span><span class="cx">     &lt;script src=&quot;Controllers/ProbeManager.js&quot;&gt;&lt;/script&gt;
</span><ins>+    &lt;script src=&quot;Controllers/ReplayManager.js&quot;&gt;&lt;/script&gt;
</ins><span class="cx">     &lt;script src=&quot;Controllers/RuntimeManager.js&quot;&gt;&lt;/script&gt;
</span><span class="cx">     &lt;script src=&quot;Controllers/SourceMapManager.js&quot;&gt;&lt;/script&gt;
</span><span class="cx">     &lt;script src=&quot;Controllers/StorageManager.js&quot;&gt;&lt;/script&gt;
</span></span></pre></div>
<a id="trunkSourceWebInspectorUIUserInterfaceModelsReplaySessionjs"></a>
<div class="addfile"><h4>Added: trunk/Source/WebInspectorUI/UserInterface/Models/ReplaySession.js (0 => 166040)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/WebInspectorUI/UserInterface/Models/ReplaySession.js                                (rev 0)
+++ trunk/Source/WebInspectorUI/UserInterface/Models/ReplaySession.js        2014-03-21 04:45:14 UTC (rev 166040)
</span><span class="lines">@@ -0,0 +1,85 @@
</span><ins>+/*
+ * Copyright (C) 2013 University of Washington. All rights reserved.
+ * Copyright (C) 2014 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.ReplaySession = function(identifier)
+{
+    WebInspector.Object.call(this);
+
+    this.identifier = identifier;
+    this._segments = [];
+    this._timestamp = null;
+};
+
+WebInspector.ReplaySession.fromPayload = function(identifier, payload)
+{
+    var session = new WebInspector.ReplaySession(identifier);
+    session._updateFromPayload(payload);
+    return session;
+}
+
+WebInspector.ReplaySession.Event = {
+    SegmentsChanged: &quot;replay-session-segments-changed&quot;,
+};
+
+WebInspector.ReplaySession.prototype = {
+    constructor: WebInspector.ReplaySession,
+    __proto__: WebInspector.Object.prototype,
+
+    get segments()
+    {
+        return this._segments.slice();
+    },
+
+    segmentsChanged: function()
+    {
+        // The replay manager won't update the session's list of segments nor create a new session.
+        ReplayAgent.getSerializedSession.promise(this.identifier)
+            .then(this._updateFromPayload.bind(this));
+    },
+
+    _updateFromPayload: function(payload)
+    {
+        console.assert(payload.id === this.identifier);
+
+        var segmentIds = payload.segments;
+        var oldSegments = this._segments;
+        var pendingSegments = [];
+        for (var segmentId of segmentIds)
+            pendingSegments.push(WebInspector.replayManager.getSegment(segmentId));
+
+        var session = this;
+        Promise.all(pendingSegments).then(
+            function(segmentsArray) {
+                session._segments = segmentsArray;
+                session.dispatchEventToListeners(WebInspector.ReplaySession.Event.SegmentsChanged, {oldSegments: oldSegments});
+            },
+            function(error) {
+                console.error(&quot;Problem resolving segments: &quot;, error);
+            }
+        );
+    }
+};
</ins></span></pre></div>
<a id="trunkSourceWebInspectorUIUserInterfaceModelsReplaySessionSegmentjs"></a>
<div class="addfile"><h4>Added: trunk/Source/WebInspectorUI/UserInterface/Models/ReplaySessionSegment.js (0 => 166040)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/WebInspectorUI/UserInterface/Models/ReplaySessionSegment.js                                (rev 0)
+++ trunk/Source/WebInspectorUI/UserInterface/Models/ReplaySessionSegment.js        2014-03-21 04:45:14 UTC (rev 166040)
</span><span class="lines">@@ -0,0 +1,68 @@
</span><ins>+/*
+ * Copyright (C) 2013 University of Washington. All rights reserved.
+ * Copyright (C) 2014 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.IncompleteSessionSegment = function(identifier)
+{
+    WebInspector.Object.call(this);
+
+    this.identifier = identifier;
+    this._timestamp = Date.now();
+}
+
+WebInspector.IncompleteSessionSegment.prototype = {
+    constructor: WebInspector.IncompleteSessionSegment,
+    __proto__: WebInspector.Object.prototype,
+
+    get isComplete()
+    {
+        return false;
+    }
+};
+
+WebInspector.ReplaySessionSegment = function(identifier, payload)
+{
+    WebInspector.Object.call(this);
+
+    console.assert(identifier === payload.id);
+
+    this.identifier = identifier;
+    this._timestamp = payload.timestamp;
+
+    this._queues = payload.queues;
+
+    // XXX: make objects for the queues and inputs?
+};
+
+WebInspector.ReplaySessionSegment.prototype = {
+    constructor: WebInspector.ReplaySessionSegment,
+    __proto__: WebInspector.Object.prototype,
+
+    get isComplete()
+    {
+        return true;
+    }
+};
</ins></span></pre></div>
<a id="trunkSourceWebInspectorUIUserInterfaceProtocolInspectorBackendjs"></a>
<div class="modfile"><h4>Modified: trunk/Source/WebInspectorUI/UserInterface/Protocol/InspectorBackend.js (166039 => 166040)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/WebInspectorUI/UserInterface/Protocol/InspectorBackend.js        2014-03-21 04:13:37 UTC (rev 166039)
+++ trunk/Source/WebInspectorUI/UserInterface/Protocol/InspectorBackend.js        2014-03-21 04:45:14 UTC (rev 166040)
</span><span class="lines">@@ -76,6 +76,7 @@
</span><span class="cx"> 
</span><span class="cx">         agent[domainAndMethod[1]] = this._sendMessageToBackend.bind(this, method, signature);
</span><span class="cx">         agent[domainAndMethod[1]][&quot;invoke&quot;] = this._invoke.bind(this, method, signature);
</span><ins>+        agent[domainAndMethod[1]][&quot;promise&quot;] = this._promise.bind(this, method, signature);
</ins><span class="cx">         agent[domainAndMethod[1]][&quot;supports&quot;] = this._supports.bind(this, method, signature);
</span><span class="cx">         this._replyArgs[method] = replyArgs;
</span><span class="cx">     },
</span><span class="lines">@@ -98,6 +99,22 @@
</span><span class="cx">         this._wrapCallbackAndSendMessageObject(method, args, callback);
</span><span class="cx">     },
</span><span class="cx"> 
</span><ins>+    _promise: function(method, signature)
+    {
+        var backend = this;
+        var promiseArguments = Array.prototype.slice.call(arguments);
+        return new Promise(function(resolve, reject) {
+            function convertToPromiseCallback(error, payload) {
+                if (error)
+                    reject(error);
+                else
+                    resolve(payload);
+            }
+            promiseArguments.push(convertToPromiseCallback);
+            backend._sendMessageToBackend.apply(backend, promiseArguments);
+        });
+    },
+
</ins><span class="cx">     _supports: function(method, signature, paramName)
</span><span class="cx">     {
</span><span class="cx">         for (var i = 0; i &lt; signature.length; ++i) {
</span></span></pre></div>
<a id="trunkSourceWebInspectorUIUserInterfaceProtocolReplayObserverjs"></a>
<div class="addfile"><h4>Added: trunk/Source/WebInspectorUI/UserInterface/Protocol/ReplayObserver.js (0 => 166040)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/WebInspectorUI/UserInterface/Protocol/ReplayObserver.js                                (rev 0)
+++ trunk/Source/WebInspectorUI/UserInterface/Protocol/ReplayObserver.js        2014-03-21 04:45:14 UTC (rev 166040)
</span><span class="lines">@@ -0,0 +1,126 @@
</span><ins>+/*
+ * Copyright (C) 2013 University of Washington. All rights reserved.
+ * Copyright (C) 2014 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.ReplayPosition = function(segmentOffset, inputOffset)
+{
+    this.segmentOffset = segmentOffset;
+    this.inputOffset = inputOffset;
+};
+
+WebInspector.ReplayPosition.fromProtocol = function(payload)
+{
+    return new WebInspector.ReplayPosition(payload.segmentOffset, payload.inputOffset);
+}
+
+WebInspector.ReplayObserver = function()
+{
+    WebInspector.Object.call(this);
+};
+
+WebInspector.ReplayObserver.prototype = {
+    constructor: WebInspector.ReplayObserver,
+    __proto__: WebInspector.Object.prototype,
+
+    captureStarted: function()
+    {
+        WebInspector.replayManager.captureStarted();
+    },
+
+    captureStopped: function()
+    {
+        WebInspector.replayManager.captureStopped();
+    },
+
+    playbackStarted: function()
+    {
+        WebInspector.replayManager.playbackStarted();
+    },
+
+    playbackHitPosition: function(replayPosition, timestamp)
+    {
+        WebInspector.replayManager.playbackHitPosition(WebInspector.ReplayPosition.fromProtocol(replayPosition), timestamp);
+    },
+
+    playbackPaused: function(replayPosition)
+    {
+        WebInspector.replayManager.playbackPaused(WebInspector.ReplayPosition.fromProtocol(replayPosition));
+    },
+
+    playbackFinished: function()
+    {
+        WebInspector.replayManager.playbackFinished();
+    },
+
+    inputSuppressionChanged: function(willSuppress)
+    {
+        // Not handled yet.
+    },
+
+    sessionCreated: function(sessionId)
+    {
+        WebInspector.replayManager.sessionCreated(sessionId);
+    },
+
+    sessionModified: function(sessionId)
+    {
+        WebInspector.replayManager.sessionModified(sessionId);
+    },
+
+    sessionRemoved: function(sessionId)
+    {
+        WebInspector.replayManager.sessionRemoved(sessionId);
+    },
+
+    sessionLoaded: function(sessionId)
+    {
+        WebInspector.replayManager.sessionLoaded(sessionId);
+    },
+
+    segmentCreated: function(segmentId)
+    {
+        WebInspector.replayManager.segmentCreated(segmentId);
+    },
+
+    segmentRemoved: function(segmentId)
+    {
+        WebInspector.replayManager.segmentRemoved(segmentId);
+    },
+
+    segmentCompleted: function(segmentId)
+    {
+        WebInspector.replayManager.segmentCompleted(segmentId);
+    },
+
+    segmentLoaded: function(segmentId)
+    {
+        WebInspector.replayManager.segmentLoaded(segmentId);
+    },
+
+    segmentUnloaded: function()
+    {
+        WebInspector.replayManager.segmentUnloaded();
+    }
+};
</ins></span></pre></div>
<a id="trunkSourceWebInspectorUIUserInterfaceTesthtml"></a>
<div class="modfile"><h4>Modified: trunk/Source/WebInspectorUI/UserInterface/Test.html (166039 => 166040)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/WebInspectorUI/UserInterface/Test.html        2014-03-21 04:13:37 UTC (rev 166039)
+++ trunk/Source/WebInspectorUI/UserInterface/Test.html        2014-03-21 04:45:14 UTC (rev 166040)
</span><span class="lines">@@ -30,9 +30,10 @@
</span><span class="cx">     These resouces should match the order and groups used in Main.html.
</span><span class="cx">     --&gt;
</span><span class="cx">     &lt;script src=&quot;Base/WebInspector.js&quot;&gt;&lt;/script&gt;
</span><ins>+    &lt;script src=&quot;Base/Object.js&quot;&gt;&lt;/script&gt;
+
</ins><span class="cx">     &lt;script src=&quot;Base/Test.js&quot;&gt;&lt;/script&gt;
</span><span class="cx"> 
</span><del>-    &lt;script src=&quot;Base/Object.js&quot;&gt;&lt;/script&gt;
</del><span class="cx">     &lt;script src=&quot;Base/DOMUtilities.js&quot;&gt;&lt;/script&gt;
</span><span class="cx">     &lt;script src=&quot;Base/URLUtilities.js&quot;&gt;&lt;/script&gt;
</span><span class="cx">     &lt;script src=&quot;Base/Utilities.js&quot;&gt;&lt;/script&gt;
</span><span class="lines">@@ -51,6 +52,7 @@
</span><span class="cx">     &lt;script src=&quot;Protocol/NetworkObserver.js&quot;&gt;&lt;/script&gt;
</span><span class="cx">     &lt;script src=&quot;Protocol/PageObserver.js&quot;&gt;&lt;/script&gt;
</span><span class="cx">     &lt;script src=&quot;Protocol/RemoteObject.js&quot;&gt;&lt;/script&gt;
</span><ins>+    &lt;script src=&quot;Protocol/ReplayObserver.js&quot;&gt;&lt;/script&gt;
</ins><span class="cx">     &lt;script src=&quot;Protocol/RuntimeObserver.js&quot;&gt;&lt;/script&gt;
</span><span class="cx">     &lt;script src=&quot;Protocol/TimelineObserver.js&quot;&gt;&lt;/script&gt;
</span><span class="cx"> 
</span><span class="lines">@@ -82,12 +84,15 @@
</span><span class="cx">     &lt;script src=&quot;Models/Profile.js&quot;&gt;&lt;/script&gt;
</span><span class="cx">     &lt;script src=&quot;Models/ProfileNode.js&quot;&gt;&lt;/script&gt;
</span><span class="cx">     &lt;script src=&quot;Models/ProfileNodeCall.js&quot;&gt;&lt;/script&gt;
</span><ins>+    &lt;script src=&quot;Models/ReplaySession.js&quot;&gt;&lt;/script&gt;
+    &lt;script src=&quot;Models/ReplaySessionSegment.js&quot;&gt;&lt;/script&gt;
</ins><span class="cx">     &lt;script src=&quot;Models/Resource.js&quot;&gt;&lt;/script&gt;
</span><span class="cx">     &lt;script src=&quot;Models/ResourceCollection.js&quot;&gt;&lt;/script&gt;
</span><span class="cx">     &lt;script src=&quot;Models/ResourceTimelineRecord.js&quot;&gt;&lt;/script&gt;
</span><span class="cx">     &lt;script src=&quot;Models/Revision.js&quot;&gt;&lt;/script&gt;
</span><span class="cx">     &lt;script src=&quot;Models/Script.js&quot;&gt;&lt;/script&gt;
</span><span class="cx">     &lt;script src=&quot;Models/ScriptTimelineRecord.js&quot;&gt;&lt;/script&gt;
</span><ins>+    &lt;script src=&quot;Models/Setting.js&quot;&gt;&lt;/script&gt;
</ins><span class="cx">     &lt;script src=&quot;Models/SourceCodeLocation.js&quot;&gt;&lt;/script&gt;
</span><span class="cx">     &lt;script src=&quot;Models/SourceCodeRevision.js&quot;&gt;&lt;/script&gt;
</span><span class="cx">     &lt;script src=&quot;Models/SourceCodeTimeline.js&quot;&gt;&lt;/script&gt;
</span><span class="lines">@@ -101,6 +106,7 @@
</span><span class="cx">     &lt;script src=&quot;Controllers/DebuggerManager.js&quot;&gt;&lt;/script&gt;
</span><span class="cx">     &lt;script src=&quot;Controllers/FrameResourceManager.js&quot;&gt;&lt;/script&gt;
</span><span class="cx">     &lt;script src=&quot;Controllers/ProbeManager.js&quot;&gt;&lt;/script&gt;
</span><ins>+    &lt;script src=&quot;Controllers/ReplayManager.js&quot;&gt;&lt;/script&gt;
</ins><span class="cx">     &lt;script src=&quot;Controllers/RuntimeManager.js&quot;&gt;&lt;/script&gt;
</span><span class="cx">     &lt;script src=&quot;Controllers/StorageManager.js&quot;&gt;&lt;/script&gt;
</span><span class="cx">     &lt;script src=&quot;Controllers/TimelineManager.js&quot;&gt;&lt;/script&gt;
</span></span></pre>
</div>
</div>

</body>
</html>