e6f499fca8c9efe34b9359ccb3b7aa150f3512ca
[platform/framework/web/crosswalk.git] / src / third_party / WebKit / Source / devtools / front_end / timeline / TimelineModel.js
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.Object}
34  */
35 WebInspector.TimelineModel = function()
36 {
37     WebInspector.Object.call(this);
38     this._filters = [];
39 }
40
41 WebInspector.TimelineModel.RecordType = {
42     Root: "Root",
43     Program: "Program",
44     EventDispatch: "EventDispatch",
45
46     GPUTask: "GPUTask",
47
48     RequestMainThreadFrame: "RequestMainThreadFrame",
49     BeginFrame: "BeginFrame",
50     ActivateLayerTree: "ActivateLayerTree",
51     DrawFrame: "DrawFrame",
52     ScheduleStyleRecalculation: "ScheduleStyleRecalculation",
53     RecalculateStyles: "RecalculateStyles",
54     InvalidateLayout: "InvalidateLayout",
55     Layout: "Layout",
56     UpdateLayerTree: "UpdateLayerTree",
57     PaintSetup: "PaintSetup",
58     Paint: "Paint",
59     Rasterize: "Rasterize",
60     ScrollLayer: "ScrollLayer",
61     DecodeImage: "DecodeImage",
62     ResizeImage: "ResizeImage",
63     CompositeLayers: "CompositeLayers",
64
65     ParseHTML: "ParseHTML",
66
67     TimerInstall: "TimerInstall",
68     TimerRemove: "TimerRemove",
69     TimerFire: "TimerFire",
70
71     XHRReadyStateChange: "XHRReadyStateChange",
72     XHRLoad: "XHRLoad",
73     EvaluateScript: "EvaluateScript",
74
75     MarkLoad: "MarkLoad",
76     MarkDOMContent: "MarkDOMContent",
77     MarkFirstPaint: "MarkFirstPaint",
78
79     TimeStamp: "TimeStamp",
80     ConsoleTime: "ConsoleTime",
81
82     ResourceSendRequest: "ResourceSendRequest",
83     ResourceReceiveResponse: "ResourceReceiveResponse",
84     ResourceReceivedData: "ResourceReceivedData",
85     ResourceFinish: "ResourceFinish",
86
87     FunctionCall: "FunctionCall",
88     GCEvent: "GCEvent",
89
90     UpdateCounters: "UpdateCounters",
91
92     RequestAnimationFrame: "RequestAnimationFrame",
93     CancelAnimationFrame: "CancelAnimationFrame",
94     FireAnimationFrame: "FireAnimationFrame",
95
96     WebSocketCreate : "WebSocketCreate",
97     WebSocketSendHandshakeRequest : "WebSocketSendHandshakeRequest",
98     WebSocketReceiveHandshakeResponse : "WebSocketReceiveHandshakeResponse",
99     WebSocketDestroy : "WebSocketDestroy",
100
101     EmbedderCallback : "EmbedderCallback",
102 }
103
104 WebInspector.TimelineModel.Events = {
105     RecordAdded: "RecordAdded",
106     RecordsCleared: "RecordsCleared",
107     RecordingStarted: "RecordingStarted",
108     RecordingStopped: "RecordingStopped",
109     RecordingProgress: "RecordingProgress",
110     RecordFilterChanged: "RecordFilterChanged"
111 }
112
113 WebInspector.TimelineModel.MainThreadName = "main";
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  */
121 WebInspector.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
144 WebInspector.TimelineModel.TransferChunkLengthBytes = 5000000;
145
146 WebInspector.TimelineModel.prototype = {
147     /**
148      * @param {boolean} captureCauses
149      * @param {boolean} captureMemory
150      * @param {boolean} capturePictures
151      */
152     startRecording: function(captureCauses, captureMemory, capturePictures)
153     {
154     },
155
156     stopRecording: function()
157     {
158     },
159
160     /**
161      * @param {?function(!WebInspector.TimelineModel.Record)|?function(!WebInspector.TimelineModel.Record,number)} preOrderCallback
162      * @param {function(!WebInspector.TimelineModel.Record)|function(!WebInspector.TimelineModel.Record,number)=} postOrderCallback
163      */
164     forAllRecords: function(preOrderCallback, postOrderCallback)
165     {
166         WebInspector.TimelineModel.forAllRecords(this._records, preOrderCallback, postOrderCallback);
167     },
168
169     /**
170      * @param {!WebInspector.TimelineModel.Filter} filter
171      */
172     addFilter: function(filter)
173     {
174         this._filters.push(filter);
175         filter._model = this;
176     },
177
178     /**
179      * @param {function(!WebInspector.TimelineModel.Record)|function(!WebInspector.TimelineModel.Record,number)} callback
180      */
181     forAllFilteredRecords: function(callback)
182     {
183         /**
184          * @param {!WebInspector.TimelineModel.Record} record
185          * @param {number} depth
186          * @this {WebInspector.TimelineModel}
187          * @return {boolean}
188          */
189         function processRecord(record, depth)
190         {
191             var visible = this.isVisible(record);
192             if (visible) {
193                 if (callback(record, depth))
194                     return true;
195             }
196
197             for (var i = 0; i < record.children().length; ++i) {
198                 if (processRecord.call(this, record.children()[i], visible ? depth + 1 : depth))
199                     return true;
200             }
201             return false;
202         }
203
204         for (var i = 0; i < this._records.length; ++i)
205             processRecord.call(this, this._records[i], 0);
206     },
207
208     /**
209      * @param {!WebInspector.TimelineModel.Record} record
210      * @return {boolean}
211      */
212     isVisible: function(record)
213     {
214         for (var i = 0; i < this._filters.length; ++i) {
215             if (!this._filters[i].accept(record))
216                 return false;
217         }
218         return true;
219     },
220
221     _filterChanged: function()
222     {
223         this.dispatchEventToListeners(WebInspector.TimelineModel.Events.RecordFilterChanged);
224     },
225
226     /**
227      * @return {!Array.<!WebInspector.TimelineModel.Record>}
228      */
229     records: function()
230     {
231         return this._records;
232     },
233
234     /**
235      * @param {!Blob} file
236      * @param {!WebInspector.Progress} progress
237      */
238     loadFromFile: function(file, progress)
239     {
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);
244     },
245
246     /**
247      * @param {!WebInspector.ChunkedFileReader} fileReader
248      * @param {!WebInspector.Progress} progress
249      * @return {!WebInspector.OutputStream}
250      */
251     createLoader: function(fileReader, progress)
252     {
253         throw new Error("Not implemented.");
254     },
255
256     _createFileReader: function(file, delegate)
257     {
258         return new WebInspector.ChunkedFileReader(file, WebInspector.TimelineModel.TransferChunkLengthBytes, delegate);
259     },
260
261     _createFileWriter: function()
262     {
263         return new WebInspector.FileOutputStream();
264     },
265
266     saveToFile: function()
267     {
268         var now = new Date();
269         var fileName = "TimelineRawData-" + now.toISO8601Compact() + ".json";
270         var stream = this._createFileWriter();
271
272         /**
273          * @param {boolean} accepted
274          * @this {WebInspector.TimelineModel}
275          */
276         function callback(accepted)
277         {
278             if (!accepted)
279                 return;
280             this.writeToStream(stream);
281         }
282         stream.open(fileName, callback.bind(this));
283     },
284
285     /**
286      * @param {!WebInspector.OutputStream} stream
287      */
288     writeToStream: function(stream)
289     {
290         throw new Error("Not implemented.");
291     },
292
293     reset: function()
294     {
295         this._records = [];
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);
305     },
306
307     /**
308      * @return {number}
309      */
310     minimumRecordTime: function()
311     {
312         throw new Error("Not implemented.");
313     },
314
315     /**
316      * @return {number}
317      */
318     maximumRecordTime: function()
319     {
320         throw new Error("Not implemented.");
321     },
322
323     /**
324      * @return {boolean}
325      */
326     isEmpty: function()
327     {
328         return this.minimumRecordTime() === 0 && this.maximumRecordTime() === 0;
329     },
330
331     /**
332      * @return {!Array.<!WebInspector.TimelineModel.Record>}
333      */
334     mainThreadTasks: function()
335     {
336         return this._mainThreadTasks;
337     },
338
339     /**
340      * @return {!Array.<!WebInspector.TimelineModel.Record>}
341      */
342     gpuThreadTasks: function()
343     {
344         return this._gpuThreadTasks;
345     },
346
347     /**
348      * @return {!Array.<!WebInspector.TimelineModel.Record>}
349      */
350     eventDividerRecords: function()
351     {
352         return this._eventDividerRecords;
353     },
354
355     __proto__: WebInspector.Object.prototype
356 }
357
358 /**
359  * @interface
360  */
361 WebInspector.TimelineModel.Record = function()
362 {
363 }
364
365 WebInspector.TimelineModel.Record.prototype = {
366     /**
367      * @return {?Array.<!ConsoleAgent.CallFrame>}
368      */
369     callSiteStackTrace: function() { },
370
371     /**
372      * @return {?WebInspector.TimelineModel.Record}
373      */
374     initiator: function() { },
375
376     /**
377      * @return {?WebInspector.Target}
378      */
379     target: function() { },
380
381     /**
382      * @return {number}
383      */
384     selfTime: function() { },
385
386     /**
387      * @return {!Array.<!WebInspector.TimelineModel.Record>}
388      */
389     children: function() { },
390
391     /**
392      * @return {number}
393      */
394     startTime: function() { },
395
396     /**
397      * @return {string}
398      */
399     thread: function() { },
400
401     /**
402      * @return {number}
403      */
404     endTime: function() { },
405
406     /**
407      * @param {number} endTime
408      */
409     setEndTime: function(endTime) { },
410
411     /**
412      * @return {!Object}
413      */
414     data: function() { },
415
416     /**
417      * @return {string}
418      */
419     type: function() { },
420
421     /**
422      * @return {string}
423      */
424     frameId: function() { },
425
426     /**
427      * @return {?Array.<!ConsoleAgent.CallFrame>}
428      */
429     stackTrace: function() { },
430
431     /**
432      * @param {string} key
433      * @return {?Object}
434      */
435     getUserObject: function(key) { },
436
437     /**
438      * @param {string} key
439      * @param {?Object|undefined} value
440      */
441     setUserObject: function(key, value) { },
442
443     /**
444      * @return {?Array.<string>}
445      */
446     warnings: function() { }
447 }
448
449 /**
450  * @constructor
451  */
452 WebInspector.TimelineModel.Filter = function()
453 {
454     /** @type {!WebInspector.TimelineModel} */
455     this._model;
456 }
457
458 WebInspector.TimelineModel.Filter.prototype = {
459     /**
460      * @param {!WebInspector.TimelineModel.Record} record
461      * @return {boolean}
462      */
463     accept: function(record)
464     {
465         return true;
466     },
467
468     notifyFilterChanged: function()
469     {
470         this._model._filterChanged();
471     }
472 }
473
474 /**
475  * @constructor
476  * @extends {WebInspector.TimelineModel.Filter}
477  * @param {!Array.<string>} recordTypes
478  */
479 WebInspector.TimelineRecordTypeFilter = function(recordTypes)
480 {
481     WebInspector.TimelineModel.Filter.call(this);
482     this._recordTypes = recordTypes.keySet();
483 }
484
485 WebInspector.TimelineRecordTypeFilter.prototype = {
486     __proto__: WebInspector.TimelineModel.Filter.prototype
487 }
488
489 /**
490  * @constructor
491  * @extends {WebInspector.TimelineRecordTypeFilter}
492  * @param {!Array.<string>} recordTypes
493  */
494 WebInspector.TimelineRecordHiddenEmptyTypeFilter = function(recordTypes)
495 {
496     WebInspector.TimelineRecordTypeFilter.call(this, recordTypes);
497 }
498
499 WebInspector.TimelineRecordHiddenEmptyTypeFilter.prototype = {
500     /**
501      * @param {!WebInspector.TimelineModel.Record} record
502      * @return {boolean}
503      */
504     accept: function(record)
505     {
506         return record.children().length !== 0 || !this._recordTypes[record.type()];
507     },
508
509     __proto__: WebInspector.TimelineRecordTypeFilter.prototype
510 }
511
512 /**
513  * @constructor
514  * @extends {WebInspector.TimelineRecordTypeFilter}
515  * @param {!Array.<string>} recordTypes
516  */
517 WebInspector.TimelineRecordHiddenTypeFilter = function(recordTypes)
518 {
519     WebInspector.TimelineRecordTypeFilter.call(this, recordTypes);
520 }
521
522 WebInspector.TimelineRecordHiddenTypeFilter.prototype = {
523     /**
524      * @param {!WebInspector.TimelineModel.Record} record
525      * @return {boolean}
526      */
527     accept: function(record)
528     {
529         return !this._recordTypes[record.type()];
530     },
531
532     __proto__: WebInspector.TimelineRecordTypeFilter.prototype
533 }
534
535 /**
536  * @constructor
537  * @extends {WebInspector.TimelineRecordTypeFilter}
538  * @param {!Array.<string>} recordTypes
539  */
540 WebInspector.TimelineRecordVisibleTypeFilter = function(recordTypes)
541 {
542     WebInspector.TimelineRecordTypeFilter.call(this, recordTypes);
543 }
544
545 WebInspector.TimelineRecordVisibleTypeFilter.prototype = {
546     /**
547      * @param {!WebInspector.TimelineModel.Record} record
548      * @return {boolean}
549      */
550     accept: function(record)
551     {
552         return !!this._recordTypes[record.type()];
553     },
554
555     __proto__: WebInspector.TimelineRecordTypeFilter.prototype
556 }
557
558 /**
559  * @constructor
560  */
561 WebInspector.TimelineMergingRecordBuffer = function()
562 {
563     this._backgroundRecordsBuffer = [];
564 }
565
566 /**
567  * @constructor
568  */
569 WebInspector.TimelineMergingRecordBuffer.prototype = {
570     /**
571      * @param {string} thread
572      * @param {!Array.<!WebInspector.TimelineModel.Record>} records
573      * @return {!Array.<!WebInspector.TimelineModel.Record>}
574      */
575     process: function(thread, records)
576     {
577         if (thread !== WebInspector.TimelineModel.MainThreadName) {
578             this._backgroundRecordsBuffer = this._backgroundRecordsBuffer.concat(records);
579             return [];
580         }
581         /**
582          * @param {!WebInspector.TimelineModel.Record} a
583          * @param {!WebInspector.TimelineModel.Record} b
584          */
585         function recordTimestampComparator(a, b)
586         {
587             // Never return 0, as the merge function will squash identical entries.
588             return a.startTime() < b.startTime() ? -1 : 1;
589         }
590         var result = this._backgroundRecordsBuffer.mergeOrdered(records, recordTimestampComparator);
591         this._backgroundRecordsBuffer = [];
592         return result;
593     }
594 }
595
596 /**
597  * @constructor
598  * @implements {WebInspector.OutputStreamDelegate}
599  * @param {!WebInspector.TimelineModel} model
600  * @param {!WebInspector.Progress} progress
601  */
602 WebInspector.TimelineModelLoadFromFileDelegate = function(model, progress)
603 {
604     this._model = model;
605     this._progress = progress;
606 }
607
608 WebInspector.TimelineModelLoadFromFileDelegate.prototype = {
609     onTransferStarted: function()
610     {
611         this._progress.setTitle(WebInspector.UIString("Loading\u2026"));
612     },
613
614     /**
615      * @param {!WebInspector.ChunkedReader} reader
616      */
617     onChunkTransferred: function(reader)
618     {
619         if (this._progress.isCanceled()) {
620             reader.cancel();
621             this._progress.done();
622             this._model.reset();
623             return;
624         }
625
626         var totalSize = reader.fileSize();
627         if (totalSize) {
628             this._progress.setTotalWork(totalSize);
629             this._progress.setWorked(reader.loadedSize());
630         }
631     },
632
633     onTransferFinished: function()
634     {
635         this._progress.done();
636     },
637
638     /**
639      * @param {!WebInspector.ChunkedReader} reader
640      * @param {!Event} event
641      */
642     onError: function(reader, event)
643     {
644         this._progress.done();
645         this._model.reset();
646         switch (event.target.error.code) {
647         case FileError.NOT_FOUND_ERR:
648             WebInspector.console.error(WebInspector.UIString("File \"%s\" not found.", reader.fileName()));
649             break;
650         case FileError.NOT_READABLE_ERR:
651             WebInspector.console.error(WebInspector.UIString("File \"%s\" is not readable", reader.fileName()));
652             break;
653         case FileError.ABORT_ERR:
654             break;
655         default:
656             WebInspector.console.error(WebInspector.UIString("An error occurred while reading the file \"%s\"", reader.fileName()));
657         }
658     }
659 }