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()
38 this._bindings = new WebInspector.TimelineModel.InterRecordBindings();
42 WebInspector.timelineManager.addEventListener(WebInspector.TimelineManager.EventTypes.TimelineEventRecorded, this._onRecordAdded, this);
43 WebInspector.timelineManager.addEventListener(WebInspector.TimelineManager.EventTypes.TimelineStarted, this._onStarted, this);
44 WebInspector.timelineManager.addEventListener(WebInspector.TimelineManager.EventTypes.TimelineStopped, this._onStopped, this);
45 WebInspector.timelineManager.addEventListener(WebInspector.TimelineManager.EventTypes.TimelineProgress, this._onProgress, this);
48 WebInspector.TimelineModel.TransferChunkLengthBytes = 5000000;
50 WebInspector.TimelineModel.RecordType = {
53 EventDispatch: "EventDispatch",
57 RequestMainThreadFrame: "RequestMainThreadFrame",
58 BeginFrame: "BeginFrame",
59 ActivateLayerTree: "ActivateLayerTree",
60 DrawFrame: "DrawFrame",
61 ScheduleStyleRecalculation: "ScheduleStyleRecalculation",
62 RecalculateStyles: "RecalculateStyles",
63 InvalidateLayout: "InvalidateLayout",
65 UpdateLayerTree: "UpdateLayerTree",
66 AutosizeText: "AutosizeText",
67 PaintSetup: "PaintSetup",
69 Rasterize: "Rasterize",
70 ScrollLayer: "ScrollLayer",
71 DecodeImage: "DecodeImage",
72 ResizeImage: "ResizeImage",
73 CompositeLayers: "CompositeLayers",
75 ParseHTML: "ParseHTML",
77 TimerInstall: "TimerInstall",
78 TimerRemove: "TimerRemove",
79 TimerFire: "TimerFire",
81 XHRReadyStateChange: "XHRReadyStateChange",
83 EvaluateScript: "EvaluateScript",
86 MarkDOMContent: "MarkDOMContent",
87 MarkFirstPaint: "MarkFirstPaint",
89 TimeStamp: "TimeStamp",
90 ConsoleTime: "ConsoleTime",
92 ScheduleResourceRequest: "ScheduleResourceRequest",
93 ResourceSendRequest: "ResourceSendRequest",
94 ResourceReceiveResponse: "ResourceReceiveResponse",
95 ResourceReceivedData: "ResourceReceivedData",
96 ResourceFinish: "ResourceFinish",
98 FunctionCall: "FunctionCall",
101 RequestAnimationFrame: "RequestAnimationFrame",
102 CancelAnimationFrame: "CancelAnimationFrame",
103 FireAnimationFrame: "FireAnimationFrame",
105 WebSocketCreate : "WebSocketCreate",
106 WebSocketSendHandshakeRequest : "WebSocketSendHandshakeRequest",
107 WebSocketReceiveHandshakeResponse : "WebSocketReceiveHandshakeResponse",
108 WebSocketDestroy : "WebSocketDestroy",
110 EmbedderCallback : "EmbedderCallback",
113 WebInspector.TimelineModel.Events = {
114 RecordAdded: "RecordAdded",
115 RecordsCleared: "RecordsCleared",
116 RecordingStarted: "RecordingStarted",
117 RecordingStopped: "RecordingStopped",
118 RecordingProgress: "RecordingProgress",
119 RecordFilterChanged: "RecordFilterChanged"
123 * @param {!Array.<!WebInspector.TimelineModel.Record>} recordsArray
124 * @param {?function(!WebInspector.TimelineModel.Record)|?function(!WebInspector.TimelineModel.Record,number)} preOrderCallback
125 * @param {function(!WebInspector.TimelineModel.Record)|function(!WebInspector.TimelineModel.Record,number)=} postOrderCallback
128 WebInspector.TimelineModel.forAllRecords = function(recordsArray, preOrderCallback, postOrderCallback)
131 * @param {!Array.<!WebInspector.TimelineModel.Record>} records
132 * @param {number} depth
135 function processRecords(records, depth)
137 for (var i = 0; i < records.length; ++i) {
138 var record = records[i];
139 if (preOrderCallback && preOrderCallback(record, depth))
141 if (processRecords(record.children, depth + 1))
143 if (postOrderCallback && postOrderCallback(record, depth))
148 return processRecords(recordsArray, 0);
151 WebInspector.TimelineModel.prototype = {
153 * @param {?function(!WebInspector.TimelineModel.Record)|?function(!WebInspector.TimelineModel.Record,number)} preOrderCallback
154 * @param {function(!WebInspector.TimelineModel.Record)|function(!WebInspector.TimelineModel.Record,number)=} postOrderCallback
156 forAllRecords: function(preOrderCallback, postOrderCallback)
158 WebInspector.TimelineModel.forAllRecords(this._records, preOrderCallback, postOrderCallback);
162 * @param {!WebInspector.TimelineModel.Filter} filter
164 addFilter: function(filter)
166 this._filters.push(filter);
167 filter._model = this;
171 * @param {function(!WebInspector.TimelineModel.Record)|function(!WebInspector.TimelineModel.Record,number)} callback
173 forAllFilteredRecords: function(callback)
176 * @param {!WebInspector.TimelineModel.Record} record
177 * @param {number} depth
178 * @this {WebInspector.TimelineModel}
181 function processRecord(record, depth)
183 var visible = this.isVisible(record);
185 if (callback(record, depth))
189 for (var i = 0; i < record.children.length; ++i) {
190 if (processRecord.call(this, record.children[i], visible ? depth + 1 : depth))
196 for (var i = 0; i < this._records.length; ++i)
197 processRecord.call(this, this._records[i], 0);
201 * @param {!WebInspector.TimelineModel.Record} record
204 isVisible: function(record)
206 for (var i = 0; i < this._filters.length; ++i) {
207 if (!this._filters[i].accept(record))
213 _filterChanged: function()
215 this.dispatchEventToListeners(WebInspector.TimelineModel.Events.RecordFilterChanged);
218 startRecording: function()
220 this._clientInitiatedRecording = true;
222 var maxStackFrames = WebInspector.settings.timelineCaptureStacks.get() ? 30 : 0;
223 this._bufferEvents = WebInspector.experimentsSettings.timelineNoLiveUpdate.isEnabled();
224 var includeGPUEvents = WebInspector.experimentsSettings.gpuTimeline.isEnabled();
225 var liveEvents = [ WebInspector.TimelineModel.RecordType.BeginFrame,
226 WebInspector.TimelineModel.RecordType.DrawFrame,
227 WebInspector.TimelineModel.RecordType.RequestMainThreadFrame,
228 WebInspector.TimelineModel.RecordType.ActivateLayerTree ];
229 var includeCounters = true;
230 WebInspector.timelineManager.start(maxStackFrames, this._bufferEvents, liveEvents.join(","), includeCounters, includeGPUEvents, this._fireRecordingStarted.bind(this));
233 stopRecording: function()
235 if (!this._clientInitiatedRecording) {
236 WebInspector.timelineManager.start(undefined, undefined, undefined, undefined, undefined, stopTimeline.bind(this));
241 * Console started this one and we are just sniffing it. Initiate recording so that we
243 * @this {WebInspector.TimelineModel}
245 function stopTimeline()
247 WebInspector.timelineManager.stop(this._fireRecordingStopped.bind(this));
250 this._clientInitiatedRecording = false;
251 WebInspector.timelineManager.stop(this._fireRecordingStopped.bind(this));
255 * @return {!Array.<!WebInspector.TimelineModel.Record>}
259 return this._records;
263 * @param {!WebInspector.Event} event
265 _onRecordAdded: function(event)
267 if (this._collectionEnabled)
268 this._addRecord(/** @type {!TimelineAgent.TimelineEvent} */(event.data));
272 * @param {!WebInspector.Event} event
274 _onStarted: function(event)
277 // Started from console.
278 this._fireRecordingStarted();
283 * @param {!WebInspector.Event} event
285 _onStopped: function(event)
288 // Stopped from console.
289 this._fireRecordingStopped(null);
294 * @param {!WebInspector.Event} event
296 _onProgress: function(event)
298 this.dispatchEventToListeners(WebInspector.TimelineModel.Events.RecordingProgress, event.data);
301 _fireRecordingStarted: function()
303 this._collectionEnabled = true;
304 this.dispatchEventToListeners(WebInspector.TimelineModel.Events.RecordingStarted);
308 * @param {?Protocol.Error} error
309 * @param {!Array.<!TimelineAgent.TimelineEvent>=} events
311 _fireRecordingStopped: function(error, events)
313 this._bufferEvents = false;
314 this._collectionEnabled = false;
315 if (events && events.length) {
317 for (var i = 0; i < events.length; ++i)
318 this._addRecord(events[i]);
320 this.dispatchEventToListeners(WebInspector.TimelineModel.Events.RecordingStopped);
326 bufferEvents: function()
328 return this._bufferEvents;
332 * @param {!TimelineAgent.TimelineEvent} payload
334 _addRecord: function(payload)
336 this._internStrings(payload);
337 this._payloads.push(payload);
338 this._updateBoundaries(payload);
340 var record = this._innerAddRecord(payload, null);
341 this._records.push(record);
342 if (record.type === WebInspector.TimelineModel.RecordType.Program)
343 this._mainThreadTasks.push(record);
344 if (record.type === WebInspector.TimelineModel.RecordType.GPUTask)
345 this._gpuThreadTasks.push(record);
347 this.dispatchEventToListeners(WebInspector.TimelineModel.Events.RecordAdded, record);
351 * @param {!TimelineAgent.TimelineEvent} payload
352 * @param {?WebInspector.TimelineModel.Record} parentRecord
353 * @return {!WebInspector.TimelineModel.Record}
354 * @this {!WebInspector.TimelineModel}
356 _innerAddRecord: function(payload, parentRecord)
358 var record = new WebInspector.TimelineModel.Record(this, payload, parentRecord);
359 if (WebInspector.TimelineUIUtils.isEventDivider(record))
360 this._eventDividerRecords.push(record);
362 for (var i = 0; payload.children && i < payload.children.length; ++i)
363 this._innerAddRecord.call(this, payload.children[i], record);
365 record.calculateAggregatedStats();
367 parentRecord._selfTime -= record.endTime - record.startTime;
372 * @param {!Blob} file
373 * @param {!WebInspector.Progress} progress
375 loadFromFile: function(file, progress)
377 var delegate = new WebInspector.TimelineModelLoadFromFileDelegate(this, progress);
378 var fileReader = this._createFileReader(file, delegate);
379 var loader = new WebInspector.TimelineModelLoader(this, fileReader, progress);
380 fileReader.start(loader);
384 * @param {string} url
386 loadFromURL: function(url, progress)
388 var delegate = new WebInspector.TimelineModelLoadFromFileDelegate(this, progress);
389 var urlReader = new WebInspector.ChunkedXHRReader(url, delegate);
390 var loader = new WebInspector.TimelineModelLoader(this, urlReader, progress);
391 urlReader.start(loader);
394 _createFileReader: function(file, delegate)
396 return new WebInspector.ChunkedFileReader(file, WebInspector.TimelineModel.TransferChunkLengthBytes, delegate);
399 _createFileWriter: function()
401 return new WebInspector.FileOutputStream();
404 saveToFile: function()
406 var now = new Date();
407 var fileName = "TimelineRawData-" + now.toISO8601Compact() + ".json";
408 var stream = this._createFileWriter();
411 * @param {boolean} accepted
412 * @this {WebInspector.TimelineModel}
414 function callback(accepted)
418 var saver = new WebInspector.TimelineSaver(stream);
419 saver.save(this._payloads, window.navigator.appVersion);
421 stream.open(fileName, callback.bind(this));
428 this._stringPool = {};
429 this._minimumRecordTime = -1;
430 this._maximumRecordTime = -1;
431 this._bindings._reset();
432 /** @type {!Array.<!WebInspector.TimelineModel.Record>} */
433 this._mainThreadTasks = [];
434 /** @type {!Array.<!WebInspector.TimelineModel.Record>} */
435 this._gpuThreadTasks = [];
436 /** @type {!Array.<!WebInspector.TimelineModel.Record>} */
437 this._eventDividerRecords = [];
438 this.dispatchEventToListeners(WebInspector.TimelineModel.Events.RecordsCleared);
444 minimumRecordTime: function()
446 return this._minimumRecordTime;
452 maximumRecordTime: function()
454 return this._maximumRecordTime;
458 * @param {!TimelineAgent.TimelineEvent} record
460 _updateBoundaries: function(record)
462 var startTime = record.startTime;
463 var endTime = record.endTime;
465 if (this._minimumRecordTime === -1 || startTime < this._minimumRecordTime)
466 this._minimumRecordTime = startTime;
467 if ((this._maximumRecordTime === -1 && endTime) || endTime > this._maximumRecordTime)
468 this._maximumRecordTime = endTime;
472 * @return {!Array.<!WebInspector.TimelineModel.Record>}
474 mainThreadTasks: function()
476 return this._mainThreadTasks;
480 * @return {!Array.<!WebInspector.TimelineModel.Record>}
482 gpuThreadTasks: function()
484 return this._gpuThreadTasks;
488 * @return {!Array.<!WebInspector.TimelineModel.Record>}
490 eventDividerRecords: function()
492 return this._eventDividerRecords;
496 * @param {!TimelineAgent.TimelineEvent} record
498 _internStrings: function(record)
500 for (var name in record) {
501 var value = record[name];
502 if (typeof value !== "string")
505 var interned = this._stringPool[value];
506 if (typeof interned === "string")
507 record[name] = interned;
509 this._stringPool[value] = value;
512 var children = record.children;
513 for (var i = 0; children && i < children.length; ++i)
514 this._internStrings(children[i]);
517 __proto__: WebInspector.Object.prototype
524 WebInspector.TimelineModel.InterRecordBindings = function() {
528 WebInspector.TimelineModel.InterRecordBindings.prototype = {
531 this._sendRequestRecords = {};
532 this._timerRecords = {};
533 this._requestAnimationFrameRecords = {};
534 this._layoutInvalidateStack = {};
535 this._lastScheduleStyleRecalculation = {};
536 this._webSocketCreateRecords = {};
542 * @param {!WebInspector.TimelineModel} model
543 * @param {!TimelineAgent.TimelineEvent} record
544 * @param {?WebInspector.TimelineModel.Record} parentRecord
546 WebInspector.TimelineModel.Record = function(model, record, parentRecord)
549 var bindings = this._model._bindings;
550 this._aggregatedStats = {};
551 this._record = record;
554 this.parent = parentRecord;
555 parentRecord.children.push(this);
558 this._selfTime = this.endTime - this.startTime;
559 this._lastChildEndTime = this.endTime;
560 this._startTimeOffset = this.startTime - model.minimumRecordTime();
563 if (record.data["url"])
564 this.url = record.data["url"];
565 if (record.data["rootNode"])
566 this._relatedBackendNodeId = record.data["rootNode"];
567 else if (record.data["elementId"])
568 this._relatedBackendNodeId = record.data["elementId"];
569 if (record.data["scriptName"]) {
570 this.scriptName = record.data["scriptName"];
571 this.scriptLine = record.data["scriptLine"];
575 if (parentRecord && parentRecord.callSiteStackTrace)
576 this.callSiteStackTrace = parentRecord.callSiteStackTrace;
578 var recordTypes = WebInspector.TimelineModel.RecordType;
579 switch (record.type) {
580 case recordTypes.ResourceSendRequest:
581 // Make resource receive record last since request was sent; make finish record last since response received.
582 bindings._sendRequestRecords[record.data["requestId"]] = this;
585 case recordTypes.ResourceReceiveResponse:
586 var sendRequestRecord = bindings._sendRequestRecords[record.data["requestId"]];
587 if (sendRequestRecord) // False if we started instrumentation in the middle of request.
588 this.url = sendRequestRecord.url;
591 case recordTypes.ResourceReceivedData:
592 case recordTypes.ResourceFinish:
593 var sendRequestRecord = bindings._sendRequestRecords[record.data["requestId"]];
594 if (sendRequestRecord) // False for main resource.
595 this.url = sendRequestRecord.url;
598 case recordTypes.TimerInstall:
599 this.timeout = record.data["timeout"];
600 this.singleShot = record.data["singleShot"];
601 bindings._timerRecords[record.data["timerId"]] = this;
604 case recordTypes.TimerFire:
605 var timerInstalledRecord = bindings._timerRecords[record.data["timerId"]];
606 if (timerInstalledRecord) {
607 this.callSiteStackTrace = timerInstalledRecord.stackTrace;
608 this.timeout = timerInstalledRecord.timeout;
609 this.singleShot = timerInstalledRecord.singleShot;
613 case recordTypes.RequestAnimationFrame:
614 bindings._requestAnimationFrameRecords[record.data["id"]] = this;
617 case recordTypes.FireAnimationFrame:
618 var requestAnimationRecord = bindings._requestAnimationFrameRecords[record.data["id"]];
619 if (requestAnimationRecord)
620 this.callSiteStackTrace = requestAnimationRecord.stackTrace;
623 case recordTypes.ConsoleTime:
624 var message = record.data["message"];
627 case recordTypes.ScheduleStyleRecalculation:
628 bindings._lastScheduleStyleRecalculation[this.frameId] = this;
631 case recordTypes.RecalculateStyles:
632 var scheduleStyleRecalculationRecord = bindings._lastScheduleStyleRecalculation[this.frameId];
633 if (!scheduleStyleRecalculationRecord)
635 this.callSiteStackTrace = scheduleStyleRecalculationRecord.stackTrace;
638 case recordTypes.InvalidateLayout:
639 // Consider style recalculation as a reason for layout invalidation,
640 // but only if we had no earlier layout invalidation records.
641 var styleRecalcStack;
642 if (!bindings._layoutInvalidateStack[this.frameId]) {
643 if (parentRecord.type === recordTypes.RecalculateStyles)
644 styleRecalcStack = parentRecord.callSiteStackTrace;
646 bindings._layoutInvalidateStack[this.frameId] = styleRecalcStack || this.stackTrace;
649 case recordTypes.Layout:
650 var layoutInvalidateStack = bindings._layoutInvalidateStack[this.frameId];
651 if (layoutInvalidateStack)
652 this.callSiteStackTrace = layoutInvalidateStack;
654 this.addWarning(WebInspector.UIString("Forced synchronous layout is a possible performance bottleneck."));
656 bindings._layoutInvalidateStack[this.frameId] = null;
657 this.highlightQuad = record.data.root || WebInspector.TimelineModel._quadFromRectData(record.data);
658 this._relatedBackendNodeId = record.data["rootNode"];
661 case recordTypes.AutosizeText:
662 if (record.data.needsRelayout && parentRecord.type === recordTypes.Layout)
663 parentRecord.addWarning(WebInspector.UIString("Layout required two passes due to text autosizing, consider setting viewport."));
666 case recordTypes.Paint:
667 this.highlightQuad = record.data.clip || WebInspector.TimelineModel._quadFromRectData(record.data);
670 case recordTypes.WebSocketCreate:
671 this.webSocketURL = record.data["url"];
672 if (typeof record.data["webSocketProtocol"] !== "undefined")
673 this.webSocketProtocol = record.data["webSocketProtocol"];
674 bindings._webSocketCreateRecords[record.data["identifier"]] = this;
677 case recordTypes.WebSocketSendHandshakeRequest:
678 case recordTypes.WebSocketReceiveHandshakeResponse:
679 case recordTypes.WebSocketDestroy:
680 var webSocketCreateRecord = bindings._webSocketCreateRecords[record.data["identifier"]];
681 if (webSocketCreateRecord) { // False if we started instrumentation in the middle of request.
682 this.webSocketURL = webSocketCreateRecord.webSocketURL;
683 if (typeof webSocketCreateRecord.webSocketProtocol !== "undefined")
684 this.webSocketProtocol = webSocketCreateRecord.webSocketProtocol;
688 case recordTypes.EmbedderCallback:
689 this.embedderCallbackName = record.data["callbackName"];
694 WebInspector.TimelineModel.Record.prototype = {
695 get lastChildEndTime()
697 return this._lastChildEndTime;
700 set lastChildEndTime(time)
702 this._lastChildEndTime = time;
707 return this._selfTime;
712 return this._cpuTime;
720 return this.type === WebInspector.TimelineModel.RecordType.Root;
724 * @return {!Array.<!WebInspector.TimelineModel.Record>}
728 return this._children;
732 * @return {!WebInspector.TimelineCategory}
736 return WebInspector.TimelineUIUtils.categoryForRecord(this);
744 return WebInspector.TimelineUIUtils.recordTitle(this);
752 return this._startTime || this._record.startTime;
755 set startTime(startTime)
757 this._startTime = startTime;
761 * @return {string|undefined}
765 return this._record.thread;
771 get startTimeOffset()
773 return this._startTimeOffset;
781 return this._endTime || this._record.endTime || this._record.startTime;
786 this._endTime = endTime;
794 return this._record.data;
802 return this._record.type;
810 return this._record.frameId || "";
816 get usedHeapSizeDelta()
818 return this._record.usedHeapSizeDelta || 0;
826 return this._record.counters ? this._record.counters.jsHeapSizeUsed || 0 : 0;
830 * @return {!Object|undefined}
834 return this._record.counters;
838 * @return {?Array.<!ConsoleAgent.CallFrame>}
842 if (this._record.stackTrace && this._record.stackTrace.length)
843 return this._record.stackTrace;
848 * @param {string} key
851 getUserObject: function(key)
853 if (!this._userObjects)
855 return this._userObjects.get(key);
859 * @param {string} key
860 * @param {?Object|undefined} value
862 setUserObject: function(key, value)
864 if (!this._userObjects)
865 this._userObjects = new StringMap();
866 this._userObjects.put(key, value);
870 * @return {number} nodeId
872 relatedBackendNodeId: function()
874 return this._relatedBackendNodeId;
877 calculateAggregatedStats: function()
879 this._aggregatedStats = {};
880 this._cpuTime = this._selfTime;
882 for (var index = this._children.length; index; --index) {
883 var child = this._children[index - 1];
884 for (var category in child._aggregatedStats)
885 this._aggregatedStats[category] = (this._aggregatedStats[category] || 0) + child._aggregatedStats[category];
887 for (var category in this._aggregatedStats)
888 this._cpuTime += this._aggregatedStats[category];
889 this._aggregatedStats[this.category.name] = (this._aggregatedStats[this.category.name] || 0) + this._selfTime;
892 get aggregatedStats()
894 return this._aggregatedStats;
898 * @param {string} message
900 addWarning: function(message)
903 this._warnings.push(message);
905 this._warnings = [message];
906 for (var parent = this.parent; parent && !parent._childHasWarnings; parent = parent.parent)
907 parent._childHasWarnings = true;
912 * @return {?Array.<string>}
916 return this._warnings;
922 childHasWarnings: function()
924 return !!this._childHasWarnings;
928 * @param {!RegExp} regExp
931 testContentMatching: function(regExp)
933 var tokens = [this.title()];
934 for (var key in this._record.data)
935 tokens.push(this._record.data[key])
936 return regExp.test(tokens.join("|"));
944 WebInspector.TimelineModel.Filter = function()
946 /** @type {!WebInspector.TimelineModel} */
950 WebInspector.TimelineModel.Filter.prototype = {
952 * @param {!WebInspector.TimelineModel.Record} record
955 accept: function(record)
960 notifyFilterChanged: function()
962 this._model._filterChanged();
968 * @implements {WebInspector.OutputStream}
969 * @param {!WebInspector.TimelineModel} model
970 * @param {!{cancel: function()}} reader
971 * @param {!WebInspector.Progress} progress
973 WebInspector.TimelineModelLoader = function(model, reader, progress)
976 this._reader = reader;
977 this._progress = progress;
979 this._firstChunk = true;
982 WebInspector.TimelineModelLoader.prototype = {
984 * @param {string} chunk
986 write: function(chunk)
988 var data = this._buffer + chunk;
993 lastIndex = WebInspector.TextUtils.findBalancedCurlyBrackets(data, index);
994 } while (lastIndex !== -1)
996 var json = data.slice(0, index) + "]";
997 this._buffer = data.slice(index);
1002 // Prepending "0" to turn string into valid JSON.
1003 if (!this._firstChunk)
1008 items = /** @type {!Array.<!TimelineAgent.TimelineEvent>} */ (JSON.parse(json));
1010 WebInspector.console.showErrorMessage("Malformed timeline data.");
1011 this._model.reset();
1012 this._reader.cancel();
1013 this._progress.done();
1017 if (this._firstChunk) {
1018 this._version = items[0];
1019 this._firstChunk = false;
1020 this._model.reset();
1023 // Skip 0-th element - it is either version or 0.
1024 for (var i = 1, size = items.length; i < size; ++i)
1025 this._model._addRecord(items[i]);
1028 close: function() { }
1033 * @implements {WebInspector.OutputStreamDelegate}
1034 * @param {!WebInspector.TimelineModel} model
1035 * @param {!WebInspector.Progress} progress
1037 WebInspector.TimelineModelLoadFromFileDelegate = function(model, progress)
1039 this._model = model;
1040 this._progress = progress;
1043 WebInspector.TimelineModelLoadFromFileDelegate.prototype = {
1044 onTransferStarted: function()
1046 this._progress.setTitle(WebInspector.UIString("Loading\u2026"));
1050 * @param {!WebInspector.ChunkedReader} reader
1052 onChunkTransferred: function(reader)
1054 if (this._progress.isCanceled()) {
1056 this._progress.done();
1057 this._model.reset();
1061 var totalSize = reader.fileSize();
1063 this._progress.setTotalWork(totalSize);
1064 this._progress.setWorked(reader.loadedSize());
1068 onTransferFinished: function()
1070 this._progress.done();
1074 * @param {!WebInspector.ChunkedReader} reader
1076 onError: function(reader, event)
1078 this._progress.done();
1079 this._model.reset();
1080 switch (event.target.error.code) {
1081 case FileError.NOT_FOUND_ERR:
1082 WebInspector.console.showErrorMessage(WebInspector.UIString("File \"%s\" not found.", reader.fileName()));
1084 case FileError.NOT_READABLE_ERR:
1085 WebInspector.console.showErrorMessage(WebInspector.UIString("File \"%s\" is not readable", reader.fileName()));
1087 case FileError.ABORT_ERR:
1090 WebInspector.console.showErrorMessage(WebInspector.UIString("An error occurred while reading the file \"%s\"", reader.fileName()));
1098 WebInspector.TimelineSaver = function(stream)
1100 this._stream = stream;
1103 WebInspector.TimelineSaver.prototype = {
1105 * @param {!Array.<*>} payloads
1106 * @param {string} version
1108 save: function(payloads, version)
1110 this._payloads = payloads;
1111 this._recordIndex = 0;
1112 this._prologue = "[" + JSON.stringify(version);
1114 this._writeNextChunk(this._stream);
1117 _writeNextChunk: function(stream)
1119 const separator = ",\n";
1123 if (this._prologue) {
1124 data.push(this._prologue);
1125 length += this._prologue.length;
1126 delete this._prologue;
1128 if (this._recordIndex === this._payloads.length) {
1134 while (this._recordIndex < this._payloads.length) {
1135 var item = JSON.stringify(this._payloads[this._recordIndex]);
1136 var itemLength = item.length + separator.length;
1137 if (length + itemLength > WebInspector.TimelineModel.TransferChunkLengthBytes)
1139 length += itemLength;
1141 ++this._recordIndex;
1143 if (this._recordIndex === this._payloads.length)
1144 data.push(data.pop() + "]");
1145 stream.write(data.join(separator), this._writeNextChunk.bind(this));
1152 WebInspector.TimelineMergingRecordBuffer = function()
1154 this._backgroundRecordsBuffer = [];
1160 WebInspector.TimelineMergingRecordBuffer.prototype = {
1162 * @param {string} thread
1163 * @param {!Array.<!TimelineAgent.TimelineEvent>} records
1164 * @return {!Array.<!TimelineAgent.TimelineEvent>}
1166 process: function(thread, records)
1169 this._backgroundRecordsBuffer = this._backgroundRecordsBuffer.concat(records);
1173 * @param {!TimelineAgent.TimelineEvent} a
1174 * @param {!TimelineAgent.TimelineEvent} b
1176 function recordTimestampComparator(a, b)
1178 // Never return 0, as the merge function will squash identical entries.
1179 return a.startTime < b.startTime ? -1 : 1;
1181 var result = this._backgroundRecordsBuffer.mergeOrdered(records, recordTimestampComparator);
1182 this._backgroundRecordsBuffer = [];
1188 * @param {!Object} data
1189 * @return {?Array.<number>}
1191 WebInspector.TimelineModel._quadFromRectData = function(data)
1193 if (typeof data["x"] === "undefined" || typeof data["y"] === "undefined")
1196 var x1 = data["x"] + data["width"];
1198 var y1 = data["y"] + data["height"];
1199 return [x0, y0, x1, y0, x1, y1, x0, y1];