• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1/*
2 * Copyright (C) 2012 Google Inc. All rights reserved.
3 *
4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions are
6 * met:
7 *
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
13 * distribution.
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.
17 *
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.
29 */
30
31/**
32 * @constructor
33 * @extends {WebInspector.TargetAwareObject}
34 * @param {!WebInspector.Target} target
35 */
36WebInspector.TimelineModel = function(target)
37{
38    WebInspector.TargetAwareObject.call(this, target);
39    this._filters = [];
40}
41
42WebInspector.TimelineModel.RecordType = {
43    Root: "Root",
44    Program: "Program",
45    EventDispatch: "EventDispatch",
46
47    GPUTask: "GPUTask",
48
49    RequestMainThreadFrame: "RequestMainThreadFrame",
50    BeginFrame: "BeginFrame",
51    ActivateLayerTree: "ActivateLayerTree",
52    DrawFrame: "DrawFrame",
53    ScheduleStyleRecalculation: "ScheduleStyleRecalculation",
54    RecalculateStyles: "RecalculateStyles",
55    InvalidateLayout: "InvalidateLayout",
56    Layout: "Layout",
57    UpdateLayerTree: "UpdateLayerTree",
58    PaintSetup: "PaintSetup",
59    Paint: "Paint",
60    Rasterize: "Rasterize",
61    ScrollLayer: "ScrollLayer",
62    DecodeImage: "DecodeImage",
63    ResizeImage: "ResizeImage",
64    CompositeLayers: "CompositeLayers",
65
66    ParseHTML: "ParseHTML",
67
68    TimerInstall: "TimerInstall",
69    TimerRemove: "TimerRemove",
70    TimerFire: "TimerFire",
71
72    XHRReadyStateChange: "XHRReadyStateChange",
73    XHRLoad: "XHRLoad",
74    EvaluateScript: "EvaluateScript",
75
76    MarkLoad: "MarkLoad",
77    MarkDOMContent: "MarkDOMContent",
78    MarkFirstPaint: "MarkFirstPaint",
79
80    TimeStamp: "TimeStamp",
81    ConsoleTime: "ConsoleTime",
82
83    ResourceSendRequest: "ResourceSendRequest",
84    ResourceReceiveResponse: "ResourceReceiveResponse",
85    ResourceReceivedData: "ResourceReceivedData",
86    ResourceFinish: "ResourceFinish",
87
88    FunctionCall: "FunctionCall",
89    GCEvent: "GCEvent",
90    JSFrame: "JSFrame",
91
92    UpdateCounters: "UpdateCounters",
93
94    RequestAnimationFrame: "RequestAnimationFrame",
95    CancelAnimationFrame: "CancelAnimationFrame",
96    FireAnimationFrame: "FireAnimationFrame",
97
98    WebSocketCreate : "WebSocketCreate",
99    WebSocketSendHandshakeRequest : "WebSocketSendHandshakeRequest",
100    WebSocketReceiveHandshakeResponse : "WebSocketReceiveHandshakeResponse",
101    WebSocketDestroy : "WebSocketDestroy",
102
103    EmbedderCallback : "EmbedderCallback",
104}
105
106WebInspector.TimelineModel.Events = {
107    RecordAdded: "RecordAdded",
108    RecordsCleared: "RecordsCleared",
109    RecordingStarted: "RecordingStarted",
110    RecordingStopped: "RecordingStopped",
111    RecordingProgress: "RecordingProgress",
112    RecordFilterChanged: "RecordFilterChanged"
113}
114
115/**
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
119 * @return {boolean}
120 */
121WebInspector.TimelineModel.forAllRecords = function(recordsArray, preOrderCallback, postOrderCallback)
122{
123    /**
124     * @param {!Array.<!WebInspector.TimelineModel.Record>} records
125     * @param {number} depth
126     * @return {boolean}
127     */
128    function processRecords(records, depth)
129    {
130        for (var i = 0; i < records.length; ++i) {
131            var record = records[i];
132            if (preOrderCallback && preOrderCallback(record, depth))
133                return true;
134            if (processRecords(record.children(), depth + 1))
135                return true;
136            if (postOrderCallback && postOrderCallback(record, depth))
137                return true;
138        }
139        return false;
140    }
141    return processRecords(recordsArray, 0);
142}
143
144WebInspector.TimelineModel.prototype = {
145    /**
146     * @param {boolean} captureStacks
147     * @param {boolean} captureMemory
148     * @param {boolean} capturePictures
149     */
150    startRecording: function(captureStacks, captureMemory, capturePictures)
151    {
152    },
153
154    stopRecording: function()
155    {
156    },
157
158    /**
159     * @return {boolean}
160     */
161    loadedFromFile: function()
162    {
163        return false;
164    },
165
166    /**
167     * @param {?function(!WebInspector.TimelineModel.Record)|?function(!WebInspector.TimelineModel.Record,number)} preOrderCallback
168     * @param {function(!WebInspector.TimelineModel.Record)|function(!WebInspector.TimelineModel.Record,number)=} postOrderCallback
169     */
170    forAllRecords: function(preOrderCallback, postOrderCallback)
171    {
172        WebInspector.TimelineModel.forAllRecords(this._records, preOrderCallback, postOrderCallback);
173    },
174
175    /**
176     * @param {!WebInspector.TimelineModel.Filter} filter
177     */
178    addFilter: function(filter)
179    {
180        this._filters.push(filter);
181        filter._model = this;
182    },
183
184    /**
185     * @param {function(!WebInspector.TimelineModel.Record)|function(!WebInspector.TimelineModel.Record,number)} callback
186     */
187    forAllFilteredRecords: function(callback)
188    {
189        /**
190         * @param {!WebInspector.TimelineModel.Record} record
191         * @param {number} depth
192         * @this {WebInspector.TimelineModel}
193         * @return {boolean}
194         */
195        function processRecord(record, depth)
196        {
197            var visible = this.isVisible(record);
198            if (visible) {
199                if (callback(record, depth))
200                    return true;
201            }
202
203            for (var i = 0; i < record.children().length; ++i) {
204                if (processRecord.call(this, record.children()[i], visible ? depth + 1 : depth))
205                    return true;
206            }
207            return false;
208        }
209
210        for (var i = 0; i < this._records.length; ++i)
211            processRecord.call(this, this._records[i], 0);
212    },
213
214    /**
215     * @param {!WebInspector.TimelineModel.Record} record
216     * @return {boolean}
217     */
218    isVisible: function(record)
219    {
220        for (var i = 0; i < this._filters.length; ++i) {
221            if (!this._filters[i].accept(record))
222                return false;
223        }
224        return true;
225    },
226
227    _filterChanged: function()
228    {
229        this.dispatchEventToListeners(WebInspector.TimelineModel.Events.RecordFilterChanged);
230    },
231
232    /**
233     * @return {!Array.<!WebInspector.TimelineModel.Record>}
234     */
235    records: function()
236    {
237        return this._records;
238    },
239
240    /**
241     * @param {!Blob} file
242     * @param {!WebInspector.Progress} progress
243     */
244    loadFromFile: function(file, progress)
245    {
246        throw new Error("Not implemented");
247    },
248
249    /**
250     * @param {string} url
251     * @param {!WebInspector.Progress} progress
252     */
253    loadFromURL: function(url, progress)
254    {
255        throw new Error("Not implemented");
256    },
257
258    saveToFile: function()
259    {
260        throw new Error("Not implemented");
261    },
262
263    reset: function()
264    {
265        this._loadedFromFile = false;
266        this._records = [];
267        this._minimumRecordTime = 0;
268        this._maximumRecordTime = 0;
269        /** @type {!Array.<!WebInspector.TimelineModel.Record>} */
270        this._mainThreadTasks =  [];
271        /** @type {!Array.<!WebInspector.TimelineModel.Record>} */
272        this._gpuThreadTasks = [];
273        /** @type {!Array.<!WebInspector.TimelineModel.Record>} */
274        this._eventDividerRecords = [];
275        this.dispatchEventToListeners(WebInspector.TimelineModel.Events.RecordsCleared);
276    },
277
278    /**
279     * @return {number}
280     */
281    minimumRecordTime: function()
282    {
283        return this._minimumRecordTime;
284    },
285
286    /**
287     * @return {number}
288     */
289    maximumRecordTime: function()
290    {
291        return this._maximumRecordTime;
292    },
293
294    /**
295     * @param {!WebInspector.TimelineModel.Record} record
296     */
297    _updateBoundaries: function(record)
298    {
299        var startTime = record.startTime();
300        var endTime = record.endTime();
301
302        if (!this._minimumRecordTime || startTime < this._minimumRecordTime)
303            this._minimumRecordTime = startTime;
304        if (endTime > this._maximumRecordTime)
305            this._maximumRecordTime = endTime;
306    },
307
308    /**
309     * @return {!Array.<!WebInspector.TimelineModel.Record>}
310     */
311    mainThreadTasks: function()
312    {
313        return this._mainThreadTasks;
314    },
315
316    /**
317     * @return {!Array.<!WebInspector.TimelineModel.Record>}
318     */
319    gpuThreadTasks: function()
320    {
321        return this._gpuThreadTasks;
322    },
323
324    /**
325     * @return {!Array.<!WebInspector.TimelineModel.Record>}
326     */
327    eventDividerRecords: function()
328    {
329        return this._eventDividerRecords;
330    },
331
332    __proto__: WebInspector.TargetAwareObject.prototype
333}
334
335/**
336 * @interface
337 */
338WebInspector.TimelineModel.Record = function()
339{
340}
341
342WebInspector.TimelineModel.Record.prototype = {
343    /**
344     * @return {?Array.<!ConsoleAgent.CallFrame>}
345     */
346    callSiteStackTrace: function() { },
347
348    /**
349     * @return {?WebInspector.TimelineModel.Record}
350     */
351    initiator: function() { },
352
353    /**
354     * @return {!WebInspector.Target}
355     */
356    target: function() { },
357
358    /**
359     * @return {number}
360     */
361    selfTime: function() { },
362
363    /**
364     * @return {!Array.<!WebInspector.TimelineModel.Record>}
365     */
366    children: function() { },
367
368    /**
369     * @return {!WebInspector.TimelineCategory}
370     */
371    category: function() { },
372
373    /**
374     * @return {number}
375     */
376    startTime: function() { },
377
378    /**
379     * @return {string|undefined}
380     */
381    thread: function() { },
382
383    /**
384     * @return {number}
385     */
386    endTime: function() { },
387
388    /**
389     * @param {number} endTime
390     */
391    setEndTime: function(endTime) { },
392
393    /**
394     * @return {!Object}
395     */
396    data: function() { },
397
398    /**
399     * @return {string}
400     */
401    type: function() { },
402
403    /**
404     * @return {string}
405     */
406    frameId: function() { },
407
408    /**
409     * @return {?Array.<!ConsoleAgent.CallFrame>}
410     */
411    stackTrace: function() { },
412
413    /**
414     * @param {string} key
415     * @return {?Object}
416     */
417    getUserObject: function(key) { },
418
419    /**
420     * @param {string} key
421     * @param {?Object|undefined} value
422     */
423    setUserObject: function(key, value) { },
424
425    /**
426     * @return {!Object.<string, number>}
427     */
428    aggregatedStats: function() { },
429
430    /**
431     * @return {?Array.<string>}
432     */
433    warnings: function() { }
434}
435
436/**
437 * @constructor
438 */
439WebInspector.TimelineModel.Filter = function()
440{
441    /** @type {!WebInspector.TimelineModel} */
442    this._model;
443}
444
445WebInspector.TimelineModel.Filter.prototype = {
446    /**
447     * @param {!WebInspector.TimelineModel.Record} record
448     * @return {boolean}
449     */
450    accept: function(record)
451    {
452        return true;
453    },
454
455    notifyFilterChanged: function()
456    {
457        this._model._filterChanged();
458    }
459}
460
461/**
462 * @constructor
463 */
464WebInspector.TimelineMergingRecordBuffer = function()
465{
466    this._backgroundRecordsBuffer = [];
467}
468
469/**
470 * @constructor
471 */
472WebInspector.TimelineMergingRecordBuffer.prototype = {
473    /**
474     * @param {string} thread
475     * @param {!Array.<!WebInspector.TimelineModel.Record>} records
476     * @return {!Array.<!WebInspector.TimelineModel.Record>}
477     */
478    process: function(thread, records)
479    {
480        if (thread) {
481            this._backgroundRecordsBuffer = this._backgroundRecordsBuffer.concat(records);
482            return [];
483        }
484        /**
485         * @param {!WebInspector.TimelineModel.Record} a
486         * @param {!WebInspector.TimelineModel.Record} b
487         */
488        function recordTimestampComparator(a, b)
489        {
490            // Never return 0, as the merge function will squash identical entries.
491            return a.startTime() < b.startTime() ? -1 : 1;
492        }
493        var result = this._backgroundRecordsBuffer.mergeOrdered(records, recordTimestampComparator);
494        this._backgroundRecordsBuffer = [];
495        return result;
496    }
497}
498