Timeline: quit using Progress, introduce TimelineLifecycleDelegate

- let TimelineLoader operate on TracingModel, not TimelineModel;
- introduce TimelineLifecycleDelegate, implemented by TimelinePanel;
- do not use Progress/ProgressIndicator in Timeline, use StatusPane instead
    for consistency with recording and fetching;
- use callback, not event in TimelineStatusPane to indicate stop is requested;
- rename "Finish" to "Stop" in the status pane.

BUG=

Review URL: https://codereview.chromium.org/1755123003

Cr-Commit-Position: refs/heads/master@{#378898}
diff --git a/third_party/WebKit/LayoutTests/http/tests/inspector/inspector-test.js b/third_party/WebKit/LayoutTests/http/tests/inspector/inspector-test.js
index 6ba202c..fe55ddb 100644
--- a/third_party/WebKit/LayoutTests/http/tests/inspector/inspector-test.js
+++ b/third_party/WebKit/LayoutTests/http/tests/inspector/inspector-test.js
@@ -796,7 +796,6 @@
 {
     this._chunks = [];
     this._name = name;
-    this._size = 0;
 }
 
 InspectorTest.TempFileMock.prototype = {
@@ -806,10 +805,11 @@
      */
     write: function(chunks, callback)
     {
+        var size = 0;
         for (var i = 0; i < chunks.length; ++i)
-            this._size += chunks[i].length;
+            size += chunks[i].length;
         this._chunks.push.apply(this._chunks, chunks);
-        setTimeout(callback.bind(this, this._size), 1);
+        setTimeout(callback.bind(this, size), 1);
     },
 
     finishWriting: function() { },
diff --git a/third_party/WebKit/LayoutTests/http/tests/inspector/timeline-test.js b/third_party/WebKit/LayoutTests/http/tests/inspector/timeline-test.js
index 2d9b050..e5e8d95 100644
--- a/third_party/WebKit/LayoutTests/http/tests/inspector/timeline-test.js
+++ b/third_party/WebKit/LayoutTests/http/tests/inspector/timeline-test.js
@@ -397,7 +397,7 @@
     }
 
     InspectorTest.override(WebInspector.TimelineLoader, "_createFileReader", createFileReader);
-    WebInspector.TimelineLoader.loadFromFile(InspectorTest.timelineModel(), {}, new WebInspector.Progress());
+    timeline._loadFromFile({});
 }
 
 };
diff --git a/third_party/WebKit/LayoutTests/inspector/tracing/tracing-timeline-load-expected.txt b/third_party/WebKit/LayoutTests/inspector/tracing/tracing-timeline-load-expected.txt
index 9d01dc2..cf66b45 100644
--- a/third_party/WebKit/LayoutTests/inspector/tracing/tracing-timeline-load-expected.txt
+++ b/third_party/WebKit/LayoutTests/inspector/tracing/tracing-timeline-load-expected.txt
@@ -2,18 +2,23 @@
 
 
 Running: testNormal
+TimelineLifecycleDelegate.loadingStarted()
+TimelineLifecycleDelegate.loadingProgress()
+TimelineLifecycleDelegate.loadingComplete(true)
 Saved data is equal to restored data: true
 
 Running: testJSONObjectFormat
+TimelineLifecycleDelegate.loadingStarted()
+TimelineLifecycleDelegate.loadingProgress()
+TimelineLifecycleDelegate.loadingComplete(true)
 Saved data is equal to restored data: true
 
 Running: testBroken
-error: TracingStartedInPage event not found.
+TimelineLifecycleDelegate.loadingStarted()
+TimelineLifecycleDelegate.loadingComplete(true)
 Model is empty: true
 
 Running: testMalformedJSON
-error: TracingStartedInPage event not found.
-error: TracingStartedInPage event not found.
-error: TracingStartedInPage event not found.
+TimelineLifecycleDelegate.loadingComplete(false)
 Model is empty: true
 
diff --git a/third_party/WebKit/LayoutTests/inspector/tracing/tracing-timeline-load.html b/third_party/WebKit/LayoutTests/inspector/tracing/tracing-timeline-load.html
index 573909450..559c36e 100644
--- a/third_party/WebKit/LayoutTests/inspector/tracing/tracing-timeline-load.html
+++ b/third_party/WebKit/LayoutTests/inspector/tracing/tracing-timeline-load.html
@@ -7,10 +7,28 @@
 
 function test()
 {
+    InspectorTest.TestTimelineLifecycleDelegate = function() {}
+
+    InspectorTest.TestTimelineLifecycleDelegate.prototype = {
+        loadingStarted: function()
+        {
+            InspectorTest.addResult("TimelineLifecycleDelegate.loadingStarted()");
+        },
+
+        loadingProgress: function()
+        {
+            InspectorTest.addResult("TimelineLifecycleDelegate.loadingProgress()");
+        },
+
+        loadingComplete: function(success)
+        {
+            InspectorTest.addResult(`TimelineLifecycleDelegate.loadingComplete(${success})`);
+        },
+    }
+
     function runTestWithDataAndCheck(input, expectedOutput, callback)
     {
-        InspectorTest.tracingModel().reset();
-        var model = InspectorTest.tracingTimelineModel();
+        var model = InspectorTest.tracingModel();
         model.reset();
         var timeline = WebInspector.panels.timeline;
 
@@ -27,18 +45,20 @@
         }
 
         InspectorTest.override(WebInspector.TimelineLoader, "_createFileReader", createFileReader);
-        WebInspector.TimelineLoader.loadFromFile(model, {}, new WebInspector.Progress());
+        WebInspector.TimelineLoader.loadFromFile(model, {}, new InspectorTest.TestTimelineLifecycleDelegate());
+        model.tracingComplete();
 
+        var saver = new WebInspector.TracingTimelineSaver();
         var stream = new InspectorTest.StringOutputStream(InspectorTest.safeWrap(checkSaveData));
-        var saver = new WebInspector.TracingTimelineSaver(stream);
         var storage = timeline._tracingModelBackingStorage;
         stream.open("", storage.writeToStream.bind(storage, stream, saver));
     }
 
     function runTestOnMalformedInput(input, callback)
     {
-        InspectorTest.tracingModel().reset();
-        var model = InspectorTest.tracingTimelineModel();
+        var model = InspectorTest.tracingModel();
+        var timeline = WebInspector.panels.timeline;
+
         model.reset();
 
         function createFileReader(file, delegate)
@@ -48,12 +68,12 @@
 
         function checkLoadedData(data)
         {
-            InspectorTest.addResult("Model is empty: " + model.isEmpty());
+            InspectorTest.addResult("Model is empty: " + (!model.minimumRecordTime() && !model.maximumRecordTime()));
             callback();
         }
 
         InspectorTest.override(WebInspector.TimelineLoader, "_createFileReader", createFileReader);
-        WebInspector.TimelineLoader.loadFromFile(model, {}, new WebInspector.Progress());
+        WebInspector.TimelineLoader.loadFromFile(model, {}, new InspectorTest.TestTimelineLifecycleDelegate());
     }
 
     var data = [{"args":{"number":32},"cat":"__metadata","name":"num_cpus","ph":"M","pid":32127,"tid":0,"ts":0},
diff --git a/third_party/WebKit/Source/devtools/front_end/timeline/TimelineLoader.js b/third_party/WebKit/Source/devtools/front_end/timeline/TimelineLoader.js
index 8b786f8e..051bd61 100644
--- a/third_party/WebKit/Source/devtools/front_end/timeline/TimelineLoader.js
+++ b/third_party/WebKit/Source/devtools/front_end/timeline/TimelineLoader.js
@@ -6,23 +6,20 @@
  * @constructor
  * @implements {WebInspector.OutputStream}
  * @implements {WebInspector.OutputStreamDelegate}
- * @param {!WebInspector.TimelineModel} model
- * @param {!WebInspector.Progress} progress
+ * @param {!WebInspector.TracingModel} model
+ * @param {!WebInspector.TimelineLifecycleDelegate} delegate
  */
-WebInspector.TimelineLoader = function(model, progress)
+WebInspector.TimelineLoader = function(model, delegate)
 {
     this._model = model;
+    this._delegate = delegate;
 
     /** @type {?function()} */
     this._canceledCallback = null;
-    this._progress = progress;
-    this._progress.setTitle(WebInspector.UIString("Loading"));
-    this._progress.setTotalWork(WebInspector.TimelineLoader._totalProgress);  // Unknown, will loop the values.
 
     this._state = WebInspector.TimelineLoader.State.Initial;
     this._buffer = "";
     this._firstChunk = true;
-    this._wasCanceledOnce = false;
 
     this._loadedBytes = 0;
     /** @type {number} */
@@ -31,31 +28,36 @@
 }
 
 /**
- * @param {!WebInspector.TimelineModel} model
+ * @param {!WebInspector.TracingModel} model
  * @param {!File} file
- * @param {!WebInspector.Progress} progress
+ * @param {!WebInspector.TimelineLifecycleDelegate} delegate
+ * @return {!WebInspector.TimelineLoader}
  */
-WebInspector.TimelineLoader.loadFromFile = function(model, file, progress)
+WebInspector.TimelineLoader.loadFromFile = function(model, file, delegate)
 {
-    var loader = new WebInspector.TimelineLoader(model, progress);
+    var loader = new WebInspector.TimelineLoader(model, delegate);
     var fileReader = WebInspector.TimelineLoader._createFileReader(file, loader);
     loader._canceledCallback = fileReader.cancel.bind(fileReader);
     loader._totalSize = file.size;
-    progress.setTotalWork(loader._totalSize);
     fileReader.start(loader);
+    return loader;
 }
 
 /**
- * @param {!WebInspector.TimelineModel} model
+ * @param {!WebInspector.TracingModel} model
  * @param {string} url
- * @param {!WebInspector.Progress} progress
+ * @param {!WebInspector.TimelineLifecycleDelegate} delegate
+ * @return {!WebInspector.TimelineLoader}
  */
-WebInspector.TimelineLoader.loadFromURL = function(model, url, progress)
+WebInspector.TimelineLoader.loadFromURL = function(model, url, delegate)
 {
-    var stream = new WebInspector.TimelineLoader(model, progress);
+    var stream = new WebInspector.TimelineLoader(model, delegate);
     WebInspector.ResourceLoader.loadAsStream(url, null, stream);
+    return stream;
 }
 
+WebInspector.TimelineLoader.TransferChunkLengthBytes = 5000000;
+
 /**
  * @param {!File} file
  * @param {!WebInspector.OutputStreamDelegate} delegate
@@ -63,12 +65,9 @@
  */
 WebInspector.TimelineLoader._createFileReader = function(file, delegate)
 {
-    return new WebInspector.ChunkedFileReader(file, WebInspector.TimelineModel.TransferChunkLengthBytes, delegate);
+    return new WebInspector.ChunkedFileReader(file, WebInspector.TimelineLoader.TransferChunkLengthBytes, delegate);
 }
 
-
-WebInspector.TimelineLoader._totalProgress = 100000;
-
 WebInspector.TimelineLoader.State = {
     Initial: "Initial",
     LookingForEvents: "LookingForEvents",
@@ -76,26 +75,27 @@
 }
 
 WebInspector.TimelineLoader.prototype = {
+    cancel: function()
+    {
+        this._model.reset();
+        this._delegate.loadingComplete(false);
+        this._delegate = null;
+        if (this._canceledCallback)
+            this._canceledCallback();
+    },
+
     /**
      * @override
      * @param {string} chunk
      */
     write: function(chunk)
     {
-        this._loadedBytes += chunk.length;
-        if (this._progress.isCanceled() && !this._wasCanceledOnce) {
-            this._wasCanceled = true;
-            this._reportErrorAndCancelLoading();
+        if (!this._delegate)
             return;
-        }
-        if (this._firstChunk)
-            this._progress.setTitle(WebInspector.UIString("Loading\u2026"));
-        if (this._totalSize) {
-            this._progress.setWorked(this._loadedBytes);
-        } else {
-            this._progress.setWorked(this._loadedBytes % WebInspector.TimelineLoader._totalProgress,
-                                     WebInspector.UIString("Loaded %s", Number.bytesToString(this._loadedBytes)));
-        }
+        this._loadedBytes += chunk.length;
+        if (!this._firstChunk)
+            this._delegate.loadingProgress(this._totalSize ? this._loadedBytes / this._totalSize : undefined);
+
         if (this._state === WebInspector.TimelineLoader.State.Initial) {
             if (chunk[0] === "{")
                 this._state = WebInspector.TimelineLoader.State.LookingForEvents;
@@ -129,7 +129,7 @@
         var json = data + "]";
 
         if (this._firstChunk) {
-            this._model.startCollectingTraceEvents(true);
+            this._delegate.loadingStarted();
         } else {
             var commaIndex = json.indexOf(",");
             if (commaIndex !== -1)
@@ -154,7 +154,7 @@
         }
 
         try {
-            this._model.traceEventsCollected(items);
+            this._model.addEvents(items);
         } catch(e) {
             this._reportErrorAndCancelLoading(WebInspector.UIString("Malformed timeline data: %s", e.toString()));
             return;
@@ -168,11 +168,7 @@
     {
         if (message)
             WebInspector.console.error(message);
-        this._model.tracingComplete();
-        this._model.reset();
-        if (this._canceledCallback)
-            this._canceledCallback();
-        this._progress.done();
+        this.cancel();
     },
 
     /**
@@ -190,8 +186,8 @@
     close: function()
     {
         this._model._loadedFromFile = true;
-        this._model.tracingComplete();
-        this._progress.done();
+        if (this._delegate)
+            this._delegate.loadingComplete(true);
     },
 
     /**
diff --git a/third_party/WebKit/Source/devtools/front_end/timeline/TimelineModel.js b/third_party/WebKit/Source/devtools/front_end/timeline/TimelineModel.js
index 21905ea0..3f27d0c 100644
--- a/third_party/WebKit/Source/devtools/front_end/timeline/TimelineModel.js
+++ b/third_party/WebKit/Source/devtools/front_end/timeline/TimelineModel.js
@@ -261,8 +261,6 @@
     return processRecords(recordsArray, 0);
 }
 
-WebInspector.TimelineModel.TransferChunkLengthBytes = 5000000;
-
 WebInspector.TimelineModel.DevToolsMetadataEvent = {
     TracingStartedInBrowser: "TracingStartedInBrowser",
     TracingStartedInPage: "TracingStartedInPage",
@@ -660,6 +658,7 @@
      */
     startCollectingTraceEvents: function(fromFile)
     {
+        this._loadedFromFile = fromFile;
         this._tracingModel.reset();
         this.reset();
         this.dispatchEventToListeners(WebInspector.TimelineModel.Events.RecordingStarted, { fromFile: fromFile });
diff --git a/third_party/WebKit/Source/devtools/front_end/timeline/TimelinePanel.js b/third_party/WebKit/Source/devtools/front_end/timeline/TimelinePanel.js
index cb89855..e527444 100644
--- a/third_party/WebKit/Source/devtools/front_end/timeline/TimelinePanel.js
+++ b/third_party/WebKit/Source/devtools/front_end/timeline/TimelinePanel.js
@@ -32,6 +32,7 @@
 /**
  * @constructor
  * @extends {WebInspector.Panel}
+ * @implements {WebInspector.TimelineLifecycleDelegate}
  * @implements {WebInspector.TimelineModeViewDelegate}
  * @implements {WebInspector.Searchable}
  */
@@ -388,7 +389,7 @@
 
         this._updateTimelineControls();
         var clearButton = new WebInspector.ToolbarButton(WebInspector.UIString("Clear recording"), "clear-toolbar-item");
-        clearButton.addEventListener("click", this._onClearButtonClick, this);
+        clearButton.addEventListener("click", this._clear, this);
         this._panelToolbar.appendToolbarItem(clearButton);
 
         this._panelToolbar.appendSeparator();
@@ -444,33 +445,12 @@
                 addGroupingOption.call(this, WebInspector.UIString("%fx slowdown", rate), rate);
             this._panelToolbar.appendToolbarItem(this._cpuThrottlingCombobox);
         }
-
-        this._progressToolbarItem = new WebInspector.ToolbarItem(createElement("div"));
-        this._progressToolbarItem.setVisible(false);
-        this._panelToolbar.appendToolbarItem(this._progressToolbarItem);
     },
 
-    /**
-     * @return {!WebInspector.Progress}
-     */
     _prepareToLoadTimeline: function()
     {
-        /**
-         * @this {!WebInspector.TimelinePanel}
-         */
-        function finishLoading()
-        {
-            this._setState(WebInspector.TimelinePanel.State.Idle);
-            this._progressToolbarItem.setVisible(false);
-            this._progressToolbarItem.element.removeChildren();
-            this._hideRecordingHelpMessage();
-        }
-        console.assert(this._state === WebInspector.TimelinePanel.State.Idle);
-        this._setState(WebInspector.TimelinePanel.State.Loading);
-        var progressIndicator = new WebInspector.ProgressIndicator();
-        this._progressToolbarItem.setVisible(true);
-        this._progressToolbarItem.element.appendChild(progressIndicator.element);
-        return new WebInspector.ProgressProxy(progressIndicator, finishLoading.bind(this));
+         console.assert(this._state === WebInspector.TimelinePanel.State.Idle);
+         this._setState(WebInspector.TimelinePanel.State.Loading);
     },
 
     _createFileSelector: function()
@@ -536,7 +516,8 @@
     {
         if (this._state !== WebInspector.TimelinePanel.State.Idle)
             return;
-        WebInspector.TimelineLoader.loadFromFile(this._model, file, this._prepareToLoadTimeline());
+        this._prepareToLoadTimeline();
+        this._loader = WebInspector.TimelineLoader.loadFromFile(this._tracingModel, file, this);
         this._createFileSelector();
     },
 
@@ -547,7 +528,8 @@
     {
         if (this._state !== WebInspector.TimelinePanel.State.Idle)
             return;
-        WebInspector.TimelineLoader.loadFromURL(this._model, url, this._prepareToLoadTimeline());
+        this._prepareToLoadTimeline();
+        this._loader = WebInspector.TimelineLoader.loadFromURL(this._tracingModel, url, this);
     },
 
     _refreshViews: function()
@@ -637,8 +619,7 @@
     {
         console.assert(!this._statusPane, "Status pane is already opened.");
         this._setState(WebInspector.TimelinePanel.State.StartPending);
-        this._statusPane = new WebInspector.TimelinePanel.StatusPane();
-        this._statusPane.addEventListener(WebInspector.TimelinePanel.StatusPane.Events.Finish, this._stopRecording, this);
+        this._statusPane = new WebInspector.TimelinePanel.StatusPane(true, this._stopRecording.bind(this));
         this._statusPane.showPane(this._statusPaneContainer);
         this._statusPane.updateStatus(WebInspector.UIString("Initializing recording\u2026"));
 
@@ -698,7 +679,7 @@
             targets[i].heapProfilerAgent().collectGarbage();
     },
 
-    _onClearButtonClick: function()
+    _clear: function()
     {
         this._tracingModel.reset();
         this._model.reset();
@@ -816,6 +797,53 @@
         this._detailsSplitWidget.showBoth();
     },
 
+    /**
+     * @override
+     */
+    loadingStarted: function()
+    {
+        this._hideRecordingHelpMessage();
+        this._model.startCollectingTraceEvents(true);
+
+        if (this._statusPane)
+            this._statusPane.hide();
+        this._statusPane = new WebInspector.TimelinePanel.StatusPane(false, this._cancelLoading.bind(this));
+        this._statusPane.showPane(this._statusPaneContainer);
+        this._statusPane.updateStatus(WebInspector.UIString("Loading timeline\u2026"));
+        this.loadingProgress(0);
+    },
+
+    /**
+     * @override
+     * @param {number=} progress
+     */
+    loadingProgress: function(progress)
+    {
+        if (typeof progress === "number")
+            this._statusPane.updateProgressBar(WebInspector.UIString("Received"), progress * 100);
+    },
+
+    /**
+     * @override
+     * @param {boolean} success
+     */
+    loadingComplete: function(success)
+    {
+        if (!success) {
+            this._onRecordingStopped();
+            this._clear();
+        } else {
+            this._model.tracingComplete();
+        }
+        delete this._loader;
+    },
+
+    _cancelLoading: function()
+    {
+        if (this._loader)
+            this._loader.cancel();
+    },
+
     _setMarkers: function()
     {
         var markers = new Map();
@@ -1245,6 +1273,28 @@
 }
 
 /**
+ * @interface
+ */
+WebInspector.TimelineLifecycleDelegate = function()
+{
+}
+
+WebInspector.TimelineLifecycleDelegate.prototype = {
+    loadingStarted: function() {},
+
+    /**
+     * @param {number=} progress
+     */
+    loadingProgress: function(progress) {},
+
+    /**
+     * @param {boolean} success
+     */
+    loadingComplete: function(success) {},
+};
+
+
+/**
  * @constructor
  * @extends {WebInspector.VBox}
  * @implements {WebInspector.TimelineModeView}
@@ -1740,8 +1790,10 @@
 /**
  * @constructor
  * @extends {WebInspector.VBox}
+ * @param {boolean} showTimer
+ * @param {function()} stopCallback
  */
-WebInspector.TimelinePanel.StatusPane = function()
+WebInspector.TimelinePanel.StatusPane = function(showTimer, stopCallback)
 {
     WebInspector.VBox.call(this, true);
     this.registerRequiredCSS("timeline/timelineStatusDialog.css");
@@ -1751,22 +1803,19 @@
     statusLine.createChild("div", "label").textContent = WebInspector.UIString("Status");
     this._status = statusLine.createChild("div", "content");
 
-    var timeLine = this.contentElement.createChild("div", "status-dialog-line time");
-    timeLine.createChild("div", "label").textContent = WebInspector.UIString("Time");
-    this._time = timeLine.createChild("div", "content");
-
+    if (showTimer) {
+        var timeLine = this.contentElement.createChild("div", "status-dialog-line time");
+        timeLine.createChild("div", "label").textContent = WebInspector.UIString("Time");
+        this._time = timeLine.createChild("div", "content");
+    }
     var progressLine = this.contentElement.createChild("div", "status-dialog-line progress");
     this._progressLabel = progressLine.createChild("div", "label");
     this._progressBar = progressLine.createChild("div", "indicator-container").createChild("div", "indicator");
 
-    this._stopButton = createTextButton(WebInspector.UIString("Finish"), this._onFinish.bind(this));
+    this._stopButton = createTextButton(WebInspector.UIString("Stop"), stopCallback);
     this.contentElement.createChild("div", "stop-button").appendChild(this._stopButton);
 }
 
-WebInspector.TimelinePanel.StatusPane.Events = {
-    Finish: "Finish"
-}
-
 WebInspector.TimelinePanel.StatusPane.prototype = {
     finish: function()
     {
@@ -1808,11 +1857,6 @@
         this._updateTimer();
     },
 
-    _onFinish: function()
-    {
-        this.dispatchEventToListeners(WebInspector.TimelinePanel.StatusPane.Events.Finish);
-    },
-
     startTimer: function()
     {
         this._startTime = Date.now();