2 * Copyright (C) 2012 Google Inc. All rights reserved.
4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions are
8 * * Redistributions of source code must retain the above copyright
9 * notice, this list of conditions and the following disclaimer.
10 * * Redistributions in binary form must reproduce the above
11 * copyright notice, this list of conditions and the following disclaimer
12 * in the documentation and/or other materials provided with the
14 * * Neither the name of Google Inc. nor the names of its
15 * contributors may be used to endorse or promote products derived from
16 * this software without specific prior written permission.
18 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
19 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
20 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
21 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
22 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
23 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
24 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
25 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
26 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
27 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
28 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
33 * @extends {WebInspector.Object}
35 WebInspector.TimelineModel = function()
37 WebInspector.Object.call(this);
41 WebInspector.TimelineModel.RecordType = {
44 EventDispatch: "EventDispatch",
48 RequestMainThreadFrame: "RequestMainThreadFrame",
49 BeginFrame: "BeginFrame",
50 ActivateLayerTree: "ActivateLayerTree",
51 DrawFrame: "DrawFrame",
52 ScheduleStyleRecalculation: "ScheduleStyleRecalculation",
53 RecalculateStyles: "RecalculateStyles",
54 InvalidateLayout: "InvalidateLayout",
56 UpdateLayerTree: "UpdateLayerTree",
57 PaintSetup: "PaintSetup",
59 Rasterize: "Rasterize",
60 ScrollLayer: "ScrollLayer",
61 DecodeImage: "DecodeImage",
62 ResizeImage: "ResizeImage",
63 CompositeLayers: "CompositeLayers",
65 ParseHTML: "ParseHTML",
67 TimerInstall: "TimerInstall",
68 TimerRemove: "TimerRemove",
69 TimerFire: "TimerFire",
71 XHRReadyStateChange: "XHRReadyStateChange",
73 EvaluateScript: "EvaluateScript",
76 MarkDOMContent: "MarkDOMContent",
77 MarkFirstPaint: "MarkFirstPaint",
79 TimeStamp: "TimeStamp",
80 ConsoleTime: "ConsoleTime",
82 ResourceSendRequest: "ResourceSendRequest",
83 ResourceReceiveResponse: "ResourceReceiveResponse",
84 ResourceReceivedData: "ResourceReceivedData",
85 ResourceFinish: "ResourceFinish",
87 FunctionCall: "FunctionCall",
90 UpdateCounters: "UpdateCounters",
92 RequestAnimationFrame: "RequestAnimationFrame",
93 CancelAnimationFrame: "CancelAnimationFrame",
94 FireAnimationFrame: "FireAnimationFrame",
96 WebSocketCreate : "WebSocketCreate",
97 WebSocketSendHandshakeRequest : "WebSocketSendHandshakeRequest",
98 WebSocketReceiveHandshakeResponse : "WebSocketReceiveHandshakeResponse",
99 WebSocketDestroy : "WebSocketDestroy",
101 EmbedderCallback : "EmbedderCallback",
104 WebInspector.TimelineModel.Events = {
105 RecordAdded: "RecordAdded",
106 RecordsCleared: "RecordsCleared",
107 RecordingStarted: "RecordingStarted",
108 RecordingStopped: "RecordingStopped",
109 RecordingProgress: "RecordingProgress",
110 RecordFilterChanged: "RecordFilterChanged"
113 WebInspector.TimelineModel.MainThreadName = "main";
116 * @param {!Array.<!WebInspector.TimelineModel.Record>} recordsArray
117 * @param {?function(!WebInspector.TimelineModel.Record)|?function(!WebInspector.TimelineModel.Record,number)} preOrderCallback
118 * @param {function(!WebInspector.TimelineModel.Record)|function(!WebInspector.TimelineModel.Record,number)=} postOrderCallback
121 WebInspector.TimelineModel.forAllRecords = function(recordsArray, preOrderCallback, postOrderCallback)
124 * @param {!Array.<!WebInspector.TimelineModel.Record>} records
125 * @param {number} depth
128 function processRecords(records, depth)
130 for (var i = 0; i < records.length; ++i) {
131 var record = records[i];
132 if (preOrderCallback && preOrderCallback(record, depth))
134 if (processRecords(record.children(), depth + 1))
136 if (postOrderCallback && postOrderCallback(record, depth))
141 return processRecords(recordsArray, 0);
144 WebInspector.TimelineModel.TransferChunkLengthBytes = 5000000;
146 WebInspector.TimelineModel.prototype = {
148 * @param {boolean} captureCauses
149 * @param {boolean} captureMemory
150 * @param {boolean} capturePictures
152 startRecording: function(captureCauses, captureMemory, capturePictures)
156 stopRecording: function()
161 * @param {?function(!WebInspector.TimelineModel.Record)|?function(!WebInspector.TimelineModel.Record,number)} preOrderCallback
162 * @param {function(!WebInspector.TimelineModel.Record)|function(!WebInspector.TimelineModel.Record,number)=} postOrderCallback
164 forAllRecords: function(preOrderCallback, postOrderCallback)
166 WebInspector.TimelineModel.forAllRecords(this._records, preOrderCallback, postOrderCallback);
170 * @param {!WebInspector.TimelineModel.Filter} filter
172 addFilter: function(filter)
174 this._filters.push(filter);
175 filter._model = this;
179 * @param {function(!WebInspector.TimelineModel.Record)|function(!WebInspector.TimelineModel.Record,number)} callback
181 forAllFilteredRecords: function(callback)
184 * @param {!WebInspector.TimelineModel.Record} record
185 * @param {number} depth
186 * @this {WebInspector.TimelineModel}
189 function processRecord(record, depth)
191 var visible = this.isVisible(record);
193 if (callback(record, depth))
197 for (var i = 0; i < record.children().length; ++i) {
198 if (processRecord.call(this, record.children()[i], visible ? depth + 1 : depth))
204 for (var i = 0; i < this._records.length; ++i)
205 processRecord.call(this, this._records[i], 0);
209 * @param {!WebInspector.TimelineModel.Record} record
212 isVisible: function(record)
214 for (var i = 0; i < this._filters.length; ++i) {
215 if (!this._filters[i].accept(record))
221 _filterChanged: function()
223 this.dispatchEventToListeners(WebInspector.TimelineModel.Events.RecordFilterChanged);
227 * @return {!Array.<!WebInspector.TimelineModel.Record>}
231 return this._records;
235 * @param {!Blob} file
236 * @param {!WebInspector.Progress} progress
238 loadFromFile: function(file, progress)
240 var delegate = new WebInspector.TimelineModelLoadFromFileDelegate(this, progress);
241 var fileReader = this._createFileReader(file, delegate);
242 var loader = this.createLoader(fileReader, progress);
243 fileReader.start(loader);
247 * @param {!WebInspector.ChunkedFileReader} fileReader
248 * @param {!WebInspector.Progress} progress
249 * @return {!WebInspector.OutputStream}
251 createLoader: function(fileReader, progress)
253 throw new Error("Not implemented.");
256 _createFileReader: function(file, delegate)
258 return new WebInspector.ChunkedFileReader(file, WebInspector.TimelineModel.TransferChunkLengthBytes, delegate);
261 _createFileWriter: function()
263 return new WebInspector.FileOutputStream();
266 saveToFile: function()
268 var now = new Date();
269 var fileName = "TimelineRawData-" + now.toISO8601Compact() + ".json";
270 var stream = this._createFileWriter();
273 * @param {boolean} accepted
274 * @this {WebInspector.TimelineModel}
276 function callback(accepted)
280 this.writeToStream(stream);
282 stream.open(fileName, callback.bind(this));
286 * @param {!WebInspector.OutputStream} stream
288 writeToStream: function(stream)
290 throw new Error("Not implemented.");
296 this._minimumRecordTime = 0;
297 this._maximumRecordTime = 0;
298 /** @type {!Array.<!WebInspector.TimelineModel.Record>} */
299 this._mainThreadTasks = [];
300 /** @type {!Array.<!WebInspector.TimelineModel.Record>} */
301 this._gpuThreadTasks = [];
302 /** @type {!Array.<!WebInspector.TimelineModel.Record>} */
303 this._eventDividerRecords = [];
304 this.dispatchEventToListeners(WebInspector.TimelineModel.Events.RecordsCleared);
310 minimumRecordTime: function()
312 throw new Error("Not implemented.");
318 maximumRecordTime: function()
320 throw new Error("Not implemented.");
328 return this.minimumRecordTime() === 0 && this.maximumRecordTime() === 0;
332 * @return {!Array.<!WebInspector.TimelineModel.Record>}
334 mainThreadTasks: function()
336 return this._mainThreadTasks;
340 * @return {!Array.<!WebInspector.TimelineModel.Record>}
342 gpuThreadTasks: function()
344 return this._gpuThreadTasks;
348 * @return {!Array.<!WebInspector.TimelineModel.Record>}
350 eventDividerRecords: function()
352 return this._eventDividerRecords;
355 __proto__: WebInspector.Object.prototype
361 WebInspector.TimelineModel.Record = function()
365 WebInspector.TimelineModel.Record.prototype = {
367 * @return {?Array.<!ConsoleAgent.CallFrame>}
369 callSiteStackTrace: function() { },
372 * @return {?WebInspector.TimelineModel.Record}
374 initiator: function() { },
377 * @return {?WebInspector.Target}
379 target: function() { },
384 selfTime: function() { },
387 * @return {!Array.<!WebInspector.TimelineModel.Record>}
389 children: function() { },
394 startTime: function() { },
399 thread: function() { },
404 endTime: function() { },
407 * @param {number} endTime
409 setEndTime: function(endTime) { },
414 data: function() { },
419 type: function() { },
424 frameId: function() { },
427 * @return {?Array.<!ConsoleAgent.CallFrame>}
429 stackTrace: function() { },
432 * @param {string} key
435 getUserObject: function(key) { },
438 * @param {string} key
439 * @param {?Object|undefined} value
441 setUserObject: function(key, value) { },
444 * @return {?Array.<string>}
446 warnings: function() { }
452 WebInspector.TimelineModel.Filter = function()
454 /** @type {!WebInspector.TimelineModel} */
458 WebInspector.TimelineModel.Filter.prototype = {
460 * @param {!WebInspector.TimelineModel.Record} record
463 accept: function(record)
468 notifyFilterChanged: function()
470 this._model._filterChanged();
476 * @extends {WebInspector.TimelineModel.Filter}
477 * @param {!Array.<string>} recordTypes
479 WebInspector.TimelineRecordTypeFilter = function(recordTypes)
481 WebInspector.TimelineModel.Filter.call(this);
482 this._recordTypes = recordTypes.keySet();
485 WebInspector.TimelineRecordTypeFilter.prototype = {
486 __proto__: WebInspector.TimelineModel.Filter.prototype
491 * @extends {WebInspector.TimelineRecordTypeFilter}
492 * @param {!Array.<string>} recordTypes
494 WebInspector.TimelineRecordHiddenEmptyTypeFilter = function(recordTypes)
496 WebInspector.TimelineRecordTypeFilter.call(this, recordTypes);
499 WebInspector.TimelineRecordHiddenEmptyTypeFilter.prototype = {
501 * @param {!WebInspector.TimelineModel.Record} record
504 accept: function(record)
506 return record.children().length !== 0 || !this._recordTypes[record.type()];
509 __proto__: WebInspector.TimelineRecordTypeFilter.prototype
514 * @extends {WebInspector.TimelineRecordTypeFilter}
515 * @param {!Array.<string>} recordTypes
517 WebInspector.TimelineRecordHiddenTypeFilter = function(recordTypes)
519 WebInspector.TimelineRecordTypeFilter.call(this, recordTypes);
522 WebInspector.TimelineRecordHiddenTypeFilter.prototype = {
524 * @param {!WebInspector.TimelineModel.Record} record
527 accept: function(record)
529 return !this._recordTypes[record.type()];
532 __proto__: WebInspector.TimelineRecordTypeFilter.prototype
537 * @extends {WebInspector.TimelineRecordTypeFilter}
538 * @param {!Array.<string>} recordTypes
540 WebInspector.TimelineRecordVisibleTypeFilter = function(recordTypes)
542 WebInspector.TimelineRecordTypeFilter.call(this, recordTypes);
545 WebInspector.TimelineRecordVisibleTypeFilter.prototype = {
547 * @param {!WebInspector.TimelineModel.Record} record
550 accept: function(record)
552 return !!this._recordTypes[record.type()];
555 __proto__: WebInspector.TimelineRecordTypeFilter.prototype
561 WebInspector.TimelineMergingRecordBuffer = function()
563 this._backgroundRecordsBuffer = [];
569 WebInspector.TimelineMergingRecordBuffer.prototype = {
571 * @param {string} thread
572 * @param {!Array.<!WebInspector.TimelineModel.Record>} records
573 * @return {!Array.<!WebInspector.TimelineModel.Record>}
575 process: function(thread, records)
577 if (thread !== WebInspector.TimelineModel.MainThreadName) {
578 this._backgroundRecordsBuffer = this._backgroundRecordsBuffer.concat(records);
582 * @param {!WebInspector.TimelineModel.Record} a
583 * @param {!WebInspector.TimelineModel.Record} b
585 function recordTimestampComparator(a, b)
587 // Never return 0, as the merge function will squash identical entries.
588 return a.startTime() < b.startTime() ? -1 : 1;
590 var result = this._backgroundRecordsBuffer.mergeOrdered(records, recordTimestampComparator);
591 this._backgroundRecordsBuffer = [];
598 * @implements {WebInspector.OutputStreamDelegate}
599 * @param {!WebInspector.TimelineModel} model
600 * @param {!WebInspector.Progress} progress
602 WebInspector.TimelineModelLoadFromFileDelegate = function(model, progress)
605 this._progress = progress;
608 WebInspector.TimelineModelLoadFromFileDelegate.prototype = {
609 onTransferStarted: function()
611 this._progress.setTitle(WebInspector.UIString("Loading\u2026"));
615 * @param {!WebInspector.ChunkedReader} reader
617 onChunkTransferred: function(reader)
619 if (this._progress.isCanceled()) {
621 this._progress.done();
626 var totalSize = reader.fileSize();
628 this._progress.setTotalWork(totalSize);
629 this._progress.setWorked(reader.loadedSize());
633 onTransferFinished: function()
635 this._progress.done();
639 * @param {!WebInspector.ChunkedReader} reader
640 * @param {!Event} event
642 onError: function(reader, event)
644 this._progress.done();
646 switch (event.target.error.code) {
647 case FileError.NOT_FOUND_ERR:
648 WebInspector.console.error(WebInspector.UIString("File \"%s\" not found.", reader.fileName()));
650 case FileError.NOT_READABLE_ERR:
651 WebInspector.console.error(WebInspector.UIString("File \"%s\" is not readable", reader.fileName()));
653 case FileError.ABORT_ERR:
656 WebInspector.console.error(WebInspector.UIString("An error occurred while reading the file \"%s\"", reader.fileName()));