Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(1301)

Unified Diff: Source/devtools/front_end/TimelineModel.js

Issue 183893010: DevTools: extract TimelineModel.Record from TimelinePresentationModel.Record. (Closed) Base URL: svn://svn.chromium.org/blink/trunk
Patch Set: For landing Created 6 years, 10 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
Index: Source/devtools/front_end/TimelineModel.js
diff --git a/Source/devtools/front_end/TimelineModel.js b/Source/devtools/front_end/TimelineModel.js
index 3718e517d359a0beec4074af9fb3ce6eb8de05f8..bc6a5208039725c81e716e587903aa79a04165fb 100644
--- a/Source/devtools/front_end/TimelineModel.js
+++ b/Source/devtools/front_end/TimelineModel.js
@@ -34,10 +34,12 @@
*/
WebInspector.TimelineModel = function()
{
+ this._payloads = [];
this._records = [];
this._minimumRecordTime = -1;
this._maximumRecordTime = -1;
this._stringPool = {};
+ this._bindings = new WebInspector.TimelineModel.InterRecordBindings();
WebInspector.timelineManager.addEventListener(WebInspector.TimelineManager.EventTypes.TimelineEventRecorded, this._onRecordAdded, this);
WebInspector.timelineManager.addEventListener(WebInspector.TimelineManager.EventTypes.TimelineStarted, this._onStarted, this);
@@ -115,8 +117,47 @@ WebInspector.TimelineModel.Events = {
RecordingStopped: "RecordingStopped"
}
+/**
+ * @param {!Array.<!WebInspector.TimelineModel.Record>} recordsArray
+ * @param {?function(!WebInspector.TimelineModel.Record)|?function(!WebInspector.TimelineModel.Record,number)} preOrderCallback
+ * @param {function(!WebInspector.TimelineModel.Record)|function(!WebInspector.TimelineModel.Record,number)=} postOrderCallback
+ */
+WebInspector.TimelineModel.forAllRecords = function(recordsArray, preOrderCallback, postOrderCallback)
+{
+ if (!recordsArray)
+ return;
+ var stack = [{array: recordsArray, index: 0}];
+ while (stack.length) {
+ var entry = stack[stack.length - 1];
+ var records = entry.array;
+ if (entry.index < records.length) {
+ var record = records[entry.index];
+ if (preOrderCallback && preOrderCallback(record, stack.length))
+ return;
+ if (record.children)
+ stack.push({array: record.children, index: 0, record: record});
+ else if (postOrderCallback && postOrderCallback(record, stack.length))
+ return;
+ ++entry.index;
+ } else {
+ if (entry.record && postOrderCallback && postOrderCallback(entry.record, stack.length))
+ return;
+ stack.pop();
+ }
+ }
+}
+
WebInspector.TimelineModel.prototype = {
/**
+ * @param {?function(!WebInspector.TimelineModel.Record)|?function(!WebInspector.TimelineModel.Record,number)} preOrderCallback
+ * @param {function(!WebInspector.TimelineModel.Record)|function(!WebInspector.TimelineModel.Record,number)=} postOrderCallback
+ */
+ forAllRecords: function(preOrderCallback, postOrderCallback)
+ {
+ WebInspector.TimelineModel.forAllRecords(this._records, preOrderCallback, postOrderCallback);
+ },
+
+ /**
* @param {boolean=} includeCounters
*/
startRecording: function(includeCounters)
@@ -149,7 +190,10 @@ WebInspector.TimelineModel.prototype = {
WebInspector.timelineManager.stop(this._fireRecordingStopped.bind(this));
},
- get records()
+ /**
+ * @return {!Array.<!WebInspector.TimelineModel.Record>}
+ */
+ records: function()
{
return this._records;
},
@@ -198,17 +242,39 @@ WebInspector.TimelineModel.prototype = {
},
/**
- * @param {!TimelineAgent.TimelineEvent} record
+ * @param {!TimelineAgent.TimelineEvent} payload
*/
- _addRecord: function(record)
+ _addRecord: function(payload)
{
- this._internStringsAndAssignEndTime(record);
+ this._internStrings(payload);
+ this._payloads.push(payload);
+ this._updateBoundaries(payload);
+
+ var record = this._innerAddRecord(payload, null);
this._records.push(record);
- this._updateBoundaries(record);
+
this.dispatchEventToListeners(WebInspector.TimelineModel.Events.RecordAdded, record);
},
/**
+ * @param {!TimelineAgent.TimelineEvent} payload
+ * @param {?WebInspector.TimelineModel.Record} parentRecord
+ * @return {!WebInspector.TimelineModel.Record}
+ * @this {!WebInspector.TimelineModel}
+ */
+ _innerAddRecord: function(payload, parentRecord)
+ {
+ var record = new WebInspector.TimelineModel.Record(this, payload, parentRecord);
+ for (var i = 0; payload.children && i < payload.children.length; ++i)
+ this._innerAddRecord.call(this, payload.children[i], record);
+
+ record.calculateAggregatedStats();
+ if (parentRecord)
+ parentRecord._selfTime -= record.endTime - record.startTime;
+ return record;
+ },
+
+ /**
* @param {!Blob} file
* @param {!WebInspector.Progress} progress
*/
@@ -256,7 +322,7 @@ WebInspector.TimelineModel.prototype = {
if (!accepted)
return;
var saver = new WebInspector.TimelineSaver(stream);
- saver.save(this._records, window.navigator.appVersion);
+ saver.save(this._payloads, window.navigator.appVersion);
}
stream.open(fileName, callback.bind(this));
},
@@ -264,9 +330,11 @@ WebInspector.TimelineModel.prototype = {
reset: function()
{
this._records = [];
+ this._payloads = [];
this._stringPool = {};
this._minimumRecordTime = -1;
this._maximumRecordTime = -1;
+ this._bindings._reset();
this.dispatchEventToListeners(WebInspector.TimelineModel.Events.RecordsCleared);
},
@@ -312,14 +380,8 @@ WebInspector.TimelineModel.prototype = {
/**
* @param {!TimelineAgent.TimelineEvent} record
*/
- _internStringsAndAssignEndTime: function(record)
+ _internStrings: function(record)
{
- // We'd like to dump raw protocol in tests, so add an option to not assign implicit end time.
- if (!WebInspector.TimelineModel["_doNotAssignEndTime"]) {
- if (typeof record.startTime === "number" && typeof record.endTime !== "number")
- record.endTime = record.startTime;
- }
-
for (var name in record) {
var value = record[name];
if (typeof value !== "string")
@@ -334,12 +396,421 @@ WebInspector.TimelineModel.prototype = {
var children = record.children;
for (var i = 0; children && i < children.length; ++i)
- this._internStringsAndAssignEndTime(children[i]);
+ this._internStrings(children[i]);
},
__proto__: WebInspector.Object.prototype
}
+
+/**
+ * @constructor
+ */
+WebInspector.TimelineModel.InterRecordBindings = function() {
+ this._reset();
+}
+
+WebInspector.TimelineModel.InterRecordBindings.prototype = {
+ _reset: function()
+ {
+ this._sendRequestRecords = {};
+ this._timerRecords = {};
+ this._requestAnimationFrameRecords = {};
+ this._layoutInvalidateStack = {};
+ this._lastScheduleStyleRecalculation = {};
+ this._webSocketCreateRecords = {};
+ }
+}
+
+/**
+ * @constructor
+ * @param {!WebInspector.TimelineModel} model
+ * @param {!TimelineAgent.TimelineEvent} record
+ * @param {?WebInspector.TimelineModel.Record} parentRecord
+ */
+WebInspector.TimelineModel.Record = function(model, record, parentRecord)
+{
+ this._model = model;
+ var bindings = this._model._bindings;
+ this._aggregatedStats = {};
+ this._record = record;
+ this._children = [];
+ if (parentRecord) {
+ this.parent = parentRecord;
+ parentRecord.children.push(this);
+ }
+
+ this._selfTime = this.endTime - this.startTime;
+ this._lastChildEndTime = this.endTime;
+ this._startTimeOffset = this.startTime - model.minimumRecordTime();
+
+ if (record.data) {
+ if (record.data["url"])
+ this.url = record.data["url"];
+ if (record.data["rootNode"])
+ this._relatedBackendNodeId = record.data["rootNode"];
+ else if (record.data["elementId"])
+ this._relatedBackendNodeId = record.data["elementId"];
+ if (record.data["scriptName"]) {
+ this.scriptName = record.data["scriptName"];
+ this.scriptLine = record.data["scriptLine"];
+ }
+ }
+
+ if (parentRecord && parentRecord.callSiteStackTrace)
+ this.callSiteStackTrace = parentRecord.callSiteStackTrace;
+
+ var recordTypes = WebInspector.TimelineModel.RecordType;
+ switch (record.type) {
+ case recordTypes.ResourceSendRequest:
+ // Make resource receive record last since request was sent; make finish record last since response received.
+ bindings._sendRequestRecords[record.data["requestId"]] = this;
+ break;
+
+ case recordTypes.ResourceReceiveResponse:
+ var sendRequestRecord = bindings._sendRequestRecords[record.data["requestId"]];
+ if (sendRequestRecord) // False if we started instrumentation in the middle of request.
+ this.url = sendRequestRecord.url;
+ break;
+
+ case recordTypes.ResourceReceivedData:
+ case recordTypes.ResourceFinish:
+ var sendRequestRecord = bindings._sendRequestRecords[record.data["requestId"]];
+ if (sendRequestRecord) // False for main resource.
+ this.url = sendRequestRecord.url;
+ break;
+
+ case recordTypes.TimerInstall:
+ this.timeout = record.data["timeout"];
+ this.singleShot = record.data["singleShot"];
+ bindings._timerRecords[record.data["timerId"]] = this;
+ break;
+
+ case recordTypes.TimerFire:
+ var timerInstalledRecord = bindings._timerRecords[record.data["timerId"]];
+ if (timerInstalledRecord) {
+ this.callSiteStackTrace = timerInstalledRecord.stackTrace;
+ this.timeout = timerInstalledRecord.timeout;
+ this.singleShot = timerInstalledRecord.singleShot;
+ }
+ break;
+
+ case recordTypes.RequestAnimationFrame:
+ bindings._requestAnimationFrameRecords[record.data["id"]] = this;
+ break;
+
+ case recordTypes.FireAnimationFrame:
+ var requestAnimationRecord = bindings._requestAnimationFrameRecords[record.data["id"]];
+ if (requestAnimationRecord)
+ this.callSiteStackTrace = requestAnimationRecord.stackTrace;
+ break;
+
+ case recordTypes.ConsoleTime:
+ var message = record.data["message"];
+ break;
+
+ case recordTypes.ScheduleStyleRecalculation:
+ bindings._lastScheduleStyleRecalculation[this.frameId] = this;
+ break;
+
+ case recordTypes.RecalculateStyles:
+ var scheduleStyleRecalculationRecord = bindings._lastScheduleStyleRecalculation[this.frameId];
+ if (!scheduleStyleRecalculationRecord)
+ break;
+ this.callSiteStackTrace = scheduleStyleRecalculationRecord.stackTrace;
+ break;
+
+ case recordTypes.InvalidateLayout:
+ // Consider style recalculation as a reason for layout invalidation,
+ // but only if we had no earlier layout invalidation records.
+ var styleRecalcStack;
+ if (!bindings._layoutInvalidateStack[this.frameId]) {
+ if (parentRecord.type === recordTypes.RecalculateStyles)
+ styleRecalcStack = parentRecord.callSiteStackTrace;
+ }
+ bindings._layoutInvalidateStack[this.frameId] = styleRecalcStack || this.stackTrace;
+ break;
+
+ case recordTypes.Layout:
+ var layoutInvalidateStack = bindings._layoutInvalidateStack[this.frameId];
+ if (layoutInvalidateStack)
+ this.callSiteStackTrace = layoutInvalidateStack;
+ if (this.stackTrace)
+ this.addWarning(WebInspector.UIString("Forced synchronous layout is a possible performance bottleneck."));
+
+ bindings._layoutInvalidateStack[this.frameId] = null;
+ this.highlightQuad = record.data.root || WebInspector.TimelineModel._quadFromRectData(record.data);
+ this._relatedBackendNodeId = record.data["rootNode"];
+ break;
+
+ case recordTypes.AutosizeText:
+ if (record.data.needsRelayout && parentRecord.type === recordTypes.Layout)
+ parentRecord.addWarning(WebInspector.UIString("Layout required two passes due to text autosizing, consider setting viewport."));
+ break;
+
+ case recordTypes.Paint:
+ this.highlightQuad = record.data.clip || WebInspector.TimelineModel._quadFromRectData(record.data);
+ break;
+
+ case recordTypes.WebSocketCreate:
+ this.webSocketURL = record.data["url"];
+ if (typeof record.data["webSocketProtocol"] !== "undefined")
+ this.webSocketProtocol = record.data["webSocketProtocol"];
+ bindings._webSocketCreateRecords[record.data["identifier"]] = this;
+ break;
+
+ case recordTypes.WebSocketSendHandshakeRequest:
+ case recordTypes.WebSocketReceiveHandshakeResponse:
+ case recordTypes.WebSocketDestroy:
+ var webSocketCreateRecord = bindings._webSocketCreateRecords[record.data["identifier"]];
+ if (webSocketCreateRecord) { // False if we started instrumentation in the middle of request.
+ this.webSocketURL = webSocketCreateRecord.webSocketURL;
+ if (typeof webSocketCreateRecord.webSocketProtocol !== "undefined")
+ this.webSocketProtocol = webSocketCreateRecord.webSocketProtocol;
+ }
+ break;
+
+ case recordTypes.EmbedderCallback:
+ this.embedderCallbackName = record.data["callbackName"];
+ break;
+ }
+}
+
+WebInspector.TimelineModel.Record.prototype = {
+ get lastChildEndTime()
+ {
+ return this._lastChildEndTime;
+ },
+
+ set lastChildEndTime(time)
+ {
+ this._lastChildEndTime = time;
+ },
+
+ get selfTime()
+ {
+ return this._selfTime;
+ },
+
+ get cpuTime()
+ {
+ return this._cpuTime;
+ },
+
+ /**
+ * @return {boolean}
+ */
+ isRoot: function()
+ {
+ return this.type === WebInspector.TimelineModel.RecordType.Root;
+ },
+
+ /**
+ * @return {!Array.<!WebInspector.TimelineModel.Record>}
+ */
+ get children()
+ {
+ return this._children;
+ },
+
+ /**
+ * @return {!WebInspector.TimelineCategory}
+ */
+ get category()
+ {
+ return WebInspector.TimelineUIUtils.categoryForRecord(this);
+ },
+
+ /**
+ * @return {string}
+ */
+ title: function()
+ {
+ return WebInspector.TimelineUIUtils.recordTitle(this);
+ },
+
+ /**
+ * @return {number}
+ */
+ get startTime()
+ {
+ return this._startTime || this._record.startTime;
+ },
+
+ set startTime(startTime)
+ {
+ this._startTime = startTime;
+ },
+
+ /**
+ * @return {string|undefined}
+ */
+ get thread()
+ {
+ return this._record.thread;
+ },
+
+ /**
+ * @return {number}
+ */
+ get startTimeOffset()
+ {
+ return this._startTimeOffset;
+ },
+
+ /**
+ * @return {number}
+ */
+ get endTime()
+ {
+ return this._endTime || this._record.endTime || this._record.startTime;
+ },
+
+ set endTime(endTime)
+ {
+ this._endTime = endTime;
+ },
+
+ /**
+ * @return {!Object}
+ */
+ get data()
+ {
+ return this._record.data;
+ },
+
+ /**
+ * @return {string}
+ */
+ get type()
+ {
+ return this._record.type;
+ },
+
+ /**
+ * @return {string}
+ */
+ get frameId()
+ {
+ return this._record.frameId || "";
+ },
+
+ /**
+ * @return {number}
+ */
+ get usedHeapSizeDelta()
+ {
+ return this._record.usedHeapSizeDelta || 0;
+ },
+
+ /**
+ * @return {number}
+ */
+ get jsHeapSizeUsed()
+ {
+ return this._record.counters ? this._record.counters.jsHeapSizeUsed || 0 : 0;
+ },
+
+ /**
+ * @return {!Object|undefined}
+ */
+ get counters()
+ {
+ return this._record.counters;
+ },
+
+ /**
+ * @return {?Array.<!ConsoleAgent.CallFrame>}
+ */
+ get stackTrace()
+ {
+ if (this._record.stackTrace && this._record.stackTrace.length)
+ return this._record.stackTrace;
+ return null;
+ },
+
+ /**
+ * @param {string} key
+ * @return {?Object}
+ */
+ getUserObject: function(key)
+ {
+ if (!this._userObjects)
+ return null;
+ return this._userObjects.get(key);
+ },
+
+ /**
+ * @param {string} key
+ * @param {?Object|undefined} value
+ */
+ setUserObject: function(key, value)
+ {
+ if (!this._userObjects)
+ this._userObjects = new StringMap();
+ this._userObjects.put(key, value);
+ },
+
+ /**
+ * @return {number} nodeId
+ */
+ relatedBackendNodeId: function()
+ {
+ return this._relatedBackendNodeId;
+ },
+
+ calculateAggregatedStats: function()
+ {
+ this._aggregatedStats = {};
+ this._cpuTime = this._selfTime;
+
+ for (var index = this._children.length; index; --index) {
+ var child = this._children[index - 1];
+ for (var category in child._aggregatedStats)
+ this._aggregatedStats[category] = (this._aggregatedStats[category] || 0) + child._aggregatedStats[category];
+ }
+ for (var category in this._aggregatedStats)
+ this._cpuTime += this._aggregatedStats[category];
+ this._aggregatedStats[this.category.name] = (this._aggregatedStats[this.category.name] || 0) + this._selfTime;
+ },
+
+ get aggregatedStats()
+ {
+ return this._aggregatedStats;
+ },
+
+ /**
+ * @param {string} message
+ */
+ addWarning: function(message)
+ {
+ if (this._warnings)
+ this._warnings.push(message);
+ else
+ this._warnings = [message];
+ },
+
+ /**
+ * @return {!Object}
+ */
+ warnings: function()
+ {
+ return this._warnings;
+ },
+
+ /**
+ * @param {!RegExp} regExp
+ * @return {boolean}
+ */
+ testContentMatching: function(regExp)
+ {
+ var tokens = [this.title()];
+ for (var key in this._record.data)
+ tokens.push(this._record.data[key])
+ return regExp.test(tokens.join("|"));
+ }
+}
+
/**
* @constructor
* @implements {WebInspector.OutputStream}
@@ -479,12 +950,12 @@ WebInspector.TimelineSaver = function(stream)
WebInspector.TimelineSaver.prototype = {
/**
- * @param {!Array.<*>} records
+ * @param {!Array.<*>} payloads
* @param {string} version
*/
- save: function(records, version)
+ save: function(payloads, version)
{
- this._records = records;
+ this._payloads = payloads;
this._recordIndex = 0;
this._prologue = "[" + JSON.stringify(version);
@@ -502,14 +973,14 @@ WebInspector.TimelineSaver.prototype = {
length += this._prologue.length;
delete this._prologue;
} else {
- if (this._recordIndex === this._records.length) {
+ if (this._recordIndex === this._payloads.length) {
stream.close();
return;
}
data.push("");
}
- while (this._recordIndex < this._records.length) {
- var item = JSON.stringify(this._records[this._recordIndex]);
+ while (this._recordIndex < this._payloads.length) {
+ var item = JSON.stringify(this._payloads[this._recordIndex]);
var itemLength = item.length + separator.length;
if (length + itemLength > WebInspector.TimelineModel.TransferChunkLengthBytes)
break;
@@ -517,7 +988,7 @@ WebInspector.TimelineSaver.prototype = {
data.push(item);
++this._recordIndex;
}
- if (this._recordIndex === this._records.length)
+ if (this._recordIndex === this._payloads.length)
data.push(data.pop() + "]");
stream.write(data.join(separator), this._writeNextChunk.bind(this));
}
@@ -559,4 +1030,19 @@ WebInspector.TimelineMergingRecordBuffer.prototype = {
this._backgroundRecordsBuffer = [];
return result;
}
-};
+}
+
+/**
+ * @param {!Object} data
+ * @return {?Array.<number>}
+ */
+WebInspector.TimelineModel._quadFromRectData = function(data)
+{
+ if (typeof data["x"] === "undefined" || typeof data["y"] === "undefined")
+ return null;
+ var x0 = data["x"];
+ var x1 = data["x"] + data["width"];
+ var y0 = data["y"];
+ var y1 = data["y"] + data["height"];
+ return [x0, y0, x1, y0, x1, y1, x0, y1];
+}
« no previous file with comments | « Source/devtools/front_end/TimelineMemoryOverview.js ('k') | Source/devtools/front_end/TimelineOverviewPane.js » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698