From dcd80177552bc8728fadbeee17cdeffa24e3cf23 Mon Sep 17 00:00:00 2001 From: caseq Date: Tue, 1 Mar 2016 09:25:29 -0800 Subject: [PATCH] Reland of Timeline: extract and rename TracingModelLoader into a file of its own (patchset #1 id:1 of https://codereview.chromium.org/1752453002/ ) Reason for revert: Reverting of revert, it doesn't look to me this was the culprit in layout test failures. Original issue's description: > Revert of Timeline: extract and rename TracingModelLoader into a file of its own (patchset #4 id:60001 of https://codereview.chromium.org/1739193003/ ) > > Reason for revert: > Possible cause behind failure in webkit-tests on WebKit Mac10.11 and WebKit Mac10.11 (retina). > > Original issue's description: > > Timeline: extract and rename TracingModelLoader into a file of its own > > > > This just extracts TimelineLoader with the aim of later refactoring > > it to untangle timeline lifecycle events from TimelineModel. > > > > Committed: https://crrev.com/b994e8b0a8af48aca6aa2182b5e8157b4873c0e9 > > Cr-Commit-Position: refs/heads/master@{#378109} > > TBR=alph@chromium.org,caseq@chromium.org > # Not skipping CQ checks because original CL landed more than 1 days ago. > > Committed: https://crrev.com/c4ffa58b1a2c1eb0484dc21b68632e96c070652c > Cr-Commit-Position: refs/heads/master@{#378291} TBR=alph@chromium.org,nektar@chromium.org # Skipping CQ checks because original CL landed less than 1 days ago. NOPRESUBMIT=true NOTREECHECKS=true NOTRY=true Review URL: https://codereview.chromium.org/1754713002 Cr-Commit-Position: refs/heads/master@{#378464} --- devtools.gypi | 1 + front_end/timeline/TimelineLoader.js | 300 +++++++++++++++++++++++++++ front_end/timeline/TimelineModel.js | 291 +------------------------- front_end/timeline/TimelinePanel.js | 4 +- front_end/timeline/module.json | 1 + 5 files changed, 307 insertions(+), 290 deletions(-) create mode 100644 front_end/timeline/TimelineLoader.js diff --git a/devtools.gypi b/devtools.gypi index c950018bee..b91bfb8f46 100644 --- a/devtools.gypi +++ b/devtools.gypi @@ -683,6 +683,7 @@ 'front_end/timeline/TimelineIRModel.js', 'front_end/timeline/TimelineJSProfile.js', 'front_end/timeline/TimelineLayersView.js', + 'front_end/timeline/TimelineLoader.js', 'front_end/timeline/TimelineModel.js', 'front_end/timeline/TimelinePaintProfilerView.js', 'front_end/timeline/TimelinePanel.js', diff --git a/front_end/timeline/TimelineLoader.js b/front_end/timeline/TimelineLoader.js new file mode 100644 index 0000000000..e937c9a366 --- /dev/null +++ b/front_end/timeline/TimelineLoader.js @@ -0,0 +1,300 @@ +// Copyright 2016 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +/** + * @constructor + * @implements {WebInspector.OutputStream} + * @param {!WebInspector.TimelineModel} model + * @param {!WebInspector.Progress} progress + * @param {function()=} canceledCallback + */ +WebInspector.TimelineLoader = function(model, progress, canceledCallback) +{ + this._model = model; + + this._canceledCallback = canceledCallback; + 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; + this._jsonTokenizer = new WebInspector.TextUtils.BalancedJSONTokenizer(this._writeBalancedJSON.bind(this), true); +} + +/** + * @param {!WebInspector.TimelineModel} model + * @param {!File} file + * @param {!WebInspector.Progress} progress + */ +WebInspector.TimelineLoader.loadFromFile = function(model, file, progress) +{ + var delegate = new WebInspector.TimelineModelLoadFromFileDelegate(model, progress); + var fileReader = WebInspector.TimelineLoader._createFileReader(file, delegate); + var loader = new WebInspector.TimelineLoader(model, new WebInspector.ProgressProxy(null), fileReader.cancel.bind(fileReader)); + fileReader.start(loader); +} + +/** + * @param {!WebInspector.TimelineModel} model + * @param {string} url + * @param {!WebInspector.Progress} progress + */ +WebInspector.TimelineLoader.loadFromURL = function(model, url, progress) +{ + var stream = new WebInspector.TimelineLoader(model, progress); + WebInspector.ResourceLoader.loadAsStream(url, null, stream); +} + +/** + * @param {!File} file + * @param {!WebInspector.OutputStreamDelegate} delegate + * @return {!WebInspector.ChunkedReader} + */ +WebInspector.TimelineLoader._createFileReader = function(file, delegate) +{ + return new WebInspector.ChunkedFileReader(file, WebInspector.TimelineModel.TransferChunkLengthBytes, delegate); +} + + +WebInspector.TimelineLoader._totalProgress = 100000; + +WebInspector.TimelineLoader.State = { + Initial: "Initial", + LookingForEvents: "LookingForEvents", + ReadingEvents: "ReadingEvents" +} + +WebInspector.TimelineLoader.prototype = { + /** + * @override + * @param {string} chunk + */ + write: function(chunk) + { + this._loadedBytes += chunk.length; + if (this._progress.isCanceled() && !this._wasCanceledOnce) { + this._wasCanceled = true; + this._reportErrorAndCancelLoading(); + return; + } + this._progress.setWorked(this._loadedBytes % WebInspector.TimelineLoader._totalProgress, + WebInspector.UIString("Loaded %s", Number.bytesToString(this._loadedBytes))); + if (this._state === WebInspector.TimelineLoader.State.Initial) { + if (chunk[0] === "{") + this._state = WebInspector.TimelineLoader.State.LookingForEvents; + else if (chunk[0] === "[") + this._state = WebInspector.TimelineLoader.State.ReadingEvents; + else { + this._reportErrorAndCancelLoading(WebInspector.UIString("Malformed timeline data: Unknown JSON format")); + return; + } + } + + if (this._state === WebInspector.TimelineLoader.State.LookingForEvents) { + var objectName = "\"traceEvents\":"; + var startPos = this._buffer.length - objectName.length; + this._buffer += chunk; + var pos = this._buffer.indexOf(objectName, startPos); + if (pos === -1) + return; + chunk = this._buffer.slice(pos + objectName.length) + this._state = WebInspector.TimelineLoader.State.ReadingEvents; + } + + this._jsonTokenizer.write(chunk); + }, + + /** + * @param {string} data + */ + _writeBalancedJSON: function(data) + { + var json = data + "]"; + + if (this._firstChunk) { + this._model.startCollectingTraceEvents(true); + } else { + var commaIndex = json.indexOf(","); + if (commaIndex !== -1) + json = json.slice(commaIndex + 1); + json = "[" + json; + } + + var items; + try { + items = /** @type {!Array.} */ (JSON.parse(json)); + } catch (e) { + this._reportErrorAndCancelLoading(WebInspector.UIString("Malformed timeline data: %s", e.toString())); + return; + } + + if (this._firstChunk) { + this._firstChunk = false; + if (this._looksLikeAppVersion(items[0])) { + this._reportErrorAndCancelLoading(WebInspector.UIString("Legacy Timeline format is not supported.")); + return; + } + } + + try { + this._model.traceEventsCollected(items); + } catch(e) { + this._reportErrorAndCancelLoading(WebInspector.UIString("Malformed timeline data: %s", e.toString())); + return; + } + }, + + /** + * @param {string=} message + */ + _reportErrorAndCancelLoading: function(message) + { + if (message) + WebInspector.console.error(message); + this._model.tracingComplete(); + this._model.reset(); + if (this._canceledCallback) + this._canceledCallback(); + this._progress.done(); + }, + + /** + * @param {*} item + * @return {boolean} + */ + _looksLikeAppVersion: function(item) + { + return typeof item === "string" && item.indexOf("Chrome") !== -1; + }, + + /** + * @override + */ + close: function() + { + this._model._loadedFromFile = true; + this._model.tracingComplete(); + if (this._progress) + this._progress.done(); + } +} + +/** + * @constructor + * @implements {WebInspector.OutputStreamDelegate} + * @param {!WebInspector.TimelineModel} model + * @param {!WebInspector.Progress} progress + */ +WebInspector.TimelineModelLoadFromFileDelegate = function(model, progress) +{ + this._model = model; + this._progress = progress; +} + +WebInspector.TimelineModelLoadFromFileDelegate.prototype = { + /** + * @override + */ + onTransferStarted: function() + { + this._progress.setTitle(WebInspector.UIString("Loading\u2026")); + }, + + /** + * @override + * @param {!WebInspector.ChunkedReader} reader + */ + onChunkTransferred: function(reader) + { + if (this._progress.isCanceled()) { + reader.cancel(); + this._progress.done(); + this._model.reset(); + return; + } + + var totalSize = reader.fileSize(); + if (totalSize) { + this._progress.setTotalWork(totalSize); + this._progress.setWorked(reader.loadedSize()); + } + }, + + /** + * @override + */ + onTransferFinished: function() + { + this._progress.done(); + }, + + /** + * @override + * @param {!WebInspector.ChunkedReader} reader + * @param {!Event} event + */ + onError: function(reader, event) + { + this._progress.done(); + this._model.reset(); + switch (event.target.error.code) { + case FileError.NOT_FOUND_ERR: + WebInspector.console.error(WebInspector.UIString("File \"%s\" not found.", reader.fileName())); + break; + case FileError.NOT_READABLE_ERR: + WebInspector.console.error(WebInspector.UIString("File \"%s\" is not readable", reader.fileName())); + break; + case FileError.ABORT_ERR: + break; + default: + WebInspector.console.error(WebInspector.UIString("An error occurred while reading the file \"%s\"", reader.fileName())); + } + } +} + +/** + * @constructor + * @param {!WebInspector.OutputStream} stream + * @implements {WebInspector.OutputStreamDelegate} + */ +WebInspector.TracingTimelineSaver = function(stream) +{ + this._stream = stream; +} + +WebInspector.TracingTimelineSaver.prototype = { + /** + * @override + */ + onTransferStarted: function() + { + this._stream.write("["); + }, + + /** + * @override + */ + onTransferFinished: function() + { + this._stream.write("]"); + }, + + /** + * @override + * @param {!WebInspector.ChunkedReader} reader + */ + onChunkTransferred: function(reader) { }, + + /** + * @override + * @param {!WebInspector.ChunkedReader} reader + * @param {!Event} event + */ + onError: function(reader, event) { } +} diff --git a/front_end/timeline/TimelineModel.js b/front_end/timeline/TimelineModel.js index 8786a5129f..21905ea098 100644 --- a/front_end/timeline/TimelineModel.js +++ b/front_end/timeline/TimelineModel.js @@ -551,7 +551,7 @@ WebInspector.TimelineModel.prototype = { */ setEventsForTest: function(events) { - this._startCollectingTraceEvents(false); + this.startCollectingTraceEvents(false); this._tracingModel.addEvents(events); this.tracingComplete(); }, @@ -658,7 +658,7 @@ WebInspector.TimelineModel.prototype = { /** * @param {boolean} fromFile */ - _startCollectingTraceEvents: function(fromFile) + startCollectingTraceEvents: function(fromFile) { this._tracingModel.reset(); this.reset(); @@ -670,7 +670,7 @@ WebInspector.TimelineModel.prototype = { */ tracingStarted: function() { - this._startCollectingTraceEvents(false); + this.startCollectingTraceEvents(false); }, /** @@ -1348,33 +1348,6 @@ WebInspector.TimelineModel.prototype = { } }, - /** - * @param {!Blob} file - * @param {!WebInspector.Progress} progress - */ - loadFromFile: function(file, progress) - { - var delegate = new WebInspector.TimelineModelLoadFromFileDelegate(this, progress); - var fileReader = this._createFileReader(file, delegate); - var loader = new WebInspector.TracingModelLoader(this, new WebInspector.ProgressProxy(null), fileReader.cancel.bind(fileReader)); - fileReader.start(loader); - }, - - /** - * @param {string} url - * @param {!WebInspector.Progress} progress - */ - loadFromURL: function(url, progress) - { - var stream = new WebInspector.TracingModelLoader(this, progress); - WebInspector.ResourceLoader.loadAsStream(url, null, stream); - }, - - _createFileReader: function(file, delegate) - { - return new WebInspector.ChunkedFileReader(file, WebInspector.TimelineModel.TransferChunkLengthBytes, delegate); - }, - reset: function() { this._virtualThreads = []; @@ -1625,79 +1598,6 @@ WebInspector.TimelineVisibleEventsFilter.prototype = { __proto__: WebInspector.TimelineModel.Filter.prototype } -/** - * @constructor - * @implements {WebInspector.OutputStreamDelegate} - * @param {!WebInspector.TimelineModel} model - * @param {!WebInspector.Progress} progress - */ -WebInspector.TimelineModelLoadFromFileDelegate = function(model, progress) -{ - this._model = model; - this._progress = progress; -} - -WebInspector.TimelineModelLoadFromFileDelegate.prototype = { - /** - * @override - */ - onTransferStarted: function() - { - this._progress.setTitle(WebInspector.UIString("Loading\u2026")); - }, - - /** - * @override - * @param {!WebInspector.ChunkedReader} reader - */ - onChunkTransferred: function(reader) - { - if (this._progress.isCanceled()) { - reader.cancel(); - this._progress.done(); - this._model.reset(); - return; - } - - var totalSize = reader.fileSize(); - if (totalSize) { - this._progress.setTotalWork(totalSize); - this._progress.setWorked(reader.loadedSize()); - } - }, - - /** - * @override - */ - onTransferFinished: function() - { - this._progress.done(); - }, - - /** - * @override - * @param {!WebInspector.ChunkedReader} reader - * @param {!Event} event - */ - onError: function(reader, event) - { - this._progress.done(); - this._model.reset(); - switch (event.target.error.code) { - case FileError.NOT_FOUND_ERR: - WebInspector.console.error(WebInspector.UIString("File \"%s\" not found.", reader.fileName())); - break; - case FileError.NOT_READABLE_ERR: - WebInspector.console.error(WebInspector.UIString("File \"%s\" is not readable", reader.fileName())); - break; - case FileError.ABORT_ERR: - break; - default: - WebInspector.console.error(WebInspector.UIString("An error occurred while reading the file \"%s\"", reader.fileName())); - } - } -} - /** * @constructor * @extends {WebInspector.TimelineModel.Filter} @@ -1746,191 +1646,6 @@ WebInspector.ExcludeTopLevelFilter.prototype = { __proto__: WebInspector.TimelineModel.Filter.prototype } -/** - * @constructor - * @implements {WebInspector.OutputStream} - * @param {!WebInspector.TimelineModel} model - * @param {!WebInspector.Progress} progress - * @param {function()=} canceledCallback - */ -WebInspector.TracingModelLoader = function(model, progress, canceledCallback) -{ - this._model = model; - - this._canceledCallback = canceledCallback; - this._progress = progress; - this._progress.setTitle(WebInspector.UIString("Loading")); - this._progress.setTotalWork(WebInspector.TracingModelLoader._totalProgress); // Unknown, will loop the values. - - this._state = WebInspector.TracingModelLoader.State.Initial; - this._buffer = ""; - this._firstChunk = true; - this._wasCanceledOnce = false; - - this._loadedBytes = 0; - this._jsonTokenizer = new WebInspector.TextUtils.BalancedJSONTokenizer(this._writeBalancedJSON.bind(this), true); -} - -WebInspector.TracingModelLoader._totalProgress = 100000; - -WebInspector.TracingModelLoader.State = { - Initial: "Initial", - LookingForEvents: "LookingForEvents", - ReadingEvents: "ReadingEvents" -} - -WebInspector.TracingModelLoader.prototype = { - /** - * @override - * @param {string} chunk - */ - write: function(chunk) - { - this._loadedBytes += chunk.length; - if (this._progress.isCanceled() && !this._wasCanceledOnce) { - this._wasCanceled = true; - this._reportErrorAndCancelLoading(); - return; - } - this._progress.setWorked(this._loadedBytes % WebInspector.TracingModelLoader._totalProgress, - WebInspector.UIString("Loaded %s", Number.bytesToString(this._loadedBytes))); - if (this._state === WebInspector.TracingModelLoader.State.Initial) { - if (chunk[0] === "{") - this._state = WebInspector.TracingModelLoader.State.LookingForEvents; - else if (chunk[0] === "[") - this._state = WebInspector.TracingModelLoader.State.ReadingEvents; - else { - this._reportErrorAndCancelLoading(WebInspector.UIString("Malformed timeline data: Unknown JSON format")); - return; - } - } - - if (this._state === WebInspector.TracingModelLoader.State.LookingForEvents) { - var objectName = "\"traceEvents\":"; - var startPos = this._buffer.length - objectName.length; - this._buffer += chunk; - var pos = this._buffer.indexOf(objectName, startPos); - if (pos === -1) - return; - chunk = this._buffer.slice(pos + objectName.length) - this._state = WebInspector.TracingModelLoader.State.ReadingEvents; - } - - this._jsonTokenizer.write(chunk); - }, - - /** - * @param {string} data - */ - _writeBalancedJSON: function(data) - { - var json = data + "]"; - - if (this._firstChunk) { - this._model._startCollectingTraceEvents(true); - } else { - var commaIndex = json.indexOf(","); - if (commaIndex !== -1) - json = json.slice(commaIndex + 1); - json = "[" + json; - } - - var items; - try { - items = /** @type {!Array.} */ (JSON.parse(json)); - } catch (e) { - this._reportErrorAndCancelLoading(WebInspector.UIString("Malformed timeline data: %s", e.toString())); - return; - } - - if (this._firstChunk) { - this._firstChunk = false; - if (this._looksLikeAppVersion(items[0])) { - this._reportErrorAndCancelLoading(WebInspector.UIString("Legacy Timeline format is not supported.")); - return; - } - } - - try { - this._model._tracingModel.addEvents(items); - } catch(e) { - this._reportErrorAndCancelLoading(WebInspector.UIString("Malformed timeline data: %s", e.toString())); - return; - } - }, - - /** - * @param {string=} message - */ - _reportErrorAndCancelLoading: function(message) - { - if (message) - WebInspector.console.error(message); - this._model.tracingComplete(); - this._model.reset(); - if (this._canceledCallback) - this._canceledCallback(); - this._progress.done(); - }, - - _looksLikeAppVersion: function(item) - { - return typeof item === "string" && item.indexOf("Chrome") !== -1; - }, - - /** - * @override - */ - close: function() - { - this._model._loadedFromFile = true; - this._model.tracingComplete(); - if (this._progress) - this._progress.done(); - } -} - -/** - * @constructor - * @param {!WebInspector.OutputStream} stream - * @implements {WebInspector.OutputStreamDelegate} - */ -WebInspector.TracingTimelineSaver = function(stream) -{ - this._stream = stream; -} - -WebInspector.TracingTimelineSaver.prototype = { - /** - * @override - */ - onTransferStarted: function() - { - this._stream.write("["); - }, - - /** - * @override - */ - onTransferFinished: function() - { - this._stream.write("]"); - }, - - /** - * @override - * @param {!WebInspector.ChunkedReader} reader - */ - onChunkTransferred: function(reader) { }, - - /** - * @override - * @param {!WebInspector.ChunkedReader} reader - * @param {!Event} event - */ - onError: function(reader, event) { } -} - /** * @constructor * @param {!WebInspector.TracingModel.Event} event diff --git a/front_end/timeline/TimelinePanel.js b/front_end/timeline/TimelinePanel.js index 8a67949084..dab326240c 100644 --- a/front_end/timeline/TimelinePanel.js +++ b/front_end/timeline/TimelinePanel.js @@ -536,7 +536,7 @@ WebInspector.TimelinePanel.prototype = { { if (this._state !== WebInspector.TimelinePanel.State.Idle) return; - this._model.loadFromFile(file, this._prepareToLoadTimeline()); + WebInspector.TimelineLoader.loadFromFile(this._model, file, this._prepareToLoadTimeline()); this._createFileSelector(); }, @@ -547,7 +547,7 @@ WebInspector.TimelinePanel.prototype = { { if (this._state !== WebInspector.TimelinePanel.State.Idle) return; - this._model.loadFromURL(url, this._prepareToLoadTimeline()); + WebInspector.TimelineLoader.loadFromURL(this._model, url, this._prepareToLoadTimeline()); }, _refreshViews: function() diff --git a/front_end/timeline/module.json b/front_end/timeline/module.json index 16d2370ed6..6ffb537628 100644 --- a/front_end/timeline/module.json +++ b/front_end/timeline/module.json @@ -109,6 +109,7 @@ "TimelineModel.js", "TimelineIRModel.js", "TimelineJSProfile.js", + "TimelineLoader.js", "TimelineFrameModel.js", "TimelineEventOverview.js", "TimelineFlameChart.js",