Upstream version 10.39.225.0
[platform/framework/web/crosswalk.git] / src / third_party / WebKit / Source / devtools / front_end / timeline / TimelinePresentationModel.js
1 /*
2  * Copyright (C) 2013 Google Inc. All rights reserved.
3  * Copyright (C) 2012 Intel Inc. All rights reserved.
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions are
7  * met:
8  *
9  *     * Redistributions of source code must retain the above copyright
10  * notice, this list of conditions and the following disclaimer.
11  *     * Redistributions in binary form must reproduce the above
12  * copyright notice, this list of conditions and the following disclaimer
13  * in the documentation and/or other materials provided with the
14  * distribution.
15  *     * Neither the name of Google Inc. nor the names of its
16  * contributors may be used to endorse or promote products derived from
17  * this software without specific prior written permission.
18  *
19  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
20  * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
21  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
22  * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
23  * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
24  * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
25  * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
26  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
27  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
28  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
29  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
30  */
31
32 /**
33  * @constructor
34  * @extends {WebInspector.Object}
35  * @param {!WebInspector.TimelineModel} model
36  * @param {!WebInspector.TimelineUIUtils} uiUtils
37  */
38 WebInspector.TimelinePresentationModel = function(model, uiUtils)
39 {
40     this._model = model;
41     this._uiUtils = uiUtils;
42     this._filters = [];
43     /**
44      * @type {!Map.<!WebInspector.TimelineModel.Record, !WebInspector.TimelinePresentationModel.Record>}
45      */
46     this._recordToPresentationRecord = new Map();
47     this.reset();
48 }
49
50 WebInspector.TimelinePresentationModel.prototype = {
51     /**
52      * @param {number} startTime
53      * @param {number} endTime
54      */
55     setWindowTimes: function(startTime, endTime)
56     {
57         this._windowStartTime = startTime;
58         this._windowEndTime = endTime;
59     },
60
61     /**
62      * @param {?WebInspector.TimelineModel.Record} record
63      * @return {?WebInspector.TimelinePresentationModel.Record}
64      */
65     toPresentationRecord: function(record)
66     {
67         return record ? this._recordToPresentationRecord.get(record) || null : null;
68     },
69
70     /**
71      * @return {!WebInspector.TimelinePresentationModel.Record}
72      */
73     rootRecord: function()
74     {
75         return this._rootRecord;
76     },
77
78     reset: function()
79     {
80         this._recordToPresentationRecord.clear();
81         this._rootRecord = new WebInspector.TimelinePresentationModel.RootRecord();
82         /** @type {!Object.<string, !WebInspector.TimelinePresentationModel.Record>} */
83         this._coalescingBuckets = {};
84     },
85
86     /**
87      * @param {!WebInspector.TimelineModel.Record} record
88      */
89     addRecord: function(record)
90     {
91         if (this._uiUtils.isProgram(record)) {
92             var records = record.children();
93             for (var i = 0; i < records.length; ++i)
94                 this._innerAddRecord(this._rootRecord, records[i]);
95         } else {
96             this._innerAddRecord(this._rootRecord, record);
97         }
98     },
99
100     /**
101      * @param {!WebInspector.TimelinePresentationModel.Record} parentRecord
102      * @param {!WebInspector.TimelineModel.Record} record
103      */
104     _innerAddRecord: function(parentRecord, record)
105     {
106         var coalescingBucket;
107
108         // On main thread, only coalesce if the last event is of same type.
109         if (parentRecord === this._rootRecord)
110             coalescingBucket = record.thread() ? record.type() : "mainThread";
111         var coalescedRecord = this._findCoalescedParent(record, parentRecord, coalescingBucket);
112         if (coalescedRecord)
113             parentRecord = coalescedRecord;
114
115         var formattedRecord = new WebInspector.TimelinePresentationModel.ActualRecord(record, parentRecord);
116         this._recordToPresentationRecord.set(record, formattedRecord);
117
118         formattedRecord._collapsed = parentRecord === this._rootRecord;
119         if (coalescingBucket)
120             this._coalescingBuckets[coalescingBucket] = formattedRecord;
121
122         for (var i = 0; record.children() && i < record.children().length; ++i)
123             this._innerAddRecord(formattedRecord, record.children()[i]);
124
125         if (parentRecord.coalesced())
126             this._updateCoalescingParent(formattedRecord);
127     },
128
129     /**
130      * @param {!WebInspector.TimelineModel.Record} record
131      * @param {!WebInspector.TimelinePresentationModel.Record} newParent
132      * @param {string=} bucket
133      * @return {?WebInspector.TimelinePresentationModel.Record}
134      */
135     _findCoalescedParent: function(record, newParent, bucket)
136     {
137         const coalescingThresholdMillis = 5;
138
139         var lastRecord = bucket ? this._coalescingBuckets[bucket] : newParent._presentationChildren.peekLast();
140         if (lastRecord && lastRecord.coalesced())
141             lastRecord = lastRecord._presentationChildren.peekLast();
142         var startTime = record.startTime();
143         var endTime = record.endTime();
144         if (!lastRecord)
145             return null;
146         if (lastRecord.record().type() !== record.type())
147             return null;
148         if (!this._uiUtils.isCoalescable(record.type()))
149             return null;
150         if (lastRecord.record().endTime() + coalescingThresholdMillis < startTime)
151             return null;
152         if (endTime + coalescingThresholdMillis < lastRecord.record().startTime())
153             return null;
154         if (lastRecord.presentationParent().coalesced())
155             return lastRecord.presentationParent();
156         return this._replaceWithCoalescedRecord(lastRecord);
157     },
158
159     /**
160      * @param {!WebInspector.TimelinePresentationModel.Record} presentationRecord
161      * @return {!WebInspector.TimelinePresentationModel.Record}
162      */
163     _replaceWithCoalescedRecord: function(presentationRecord)
164     {
165         var record = presentationRecord.record();
166         var parent = presentationRecord._presentationParent;
167         var coalescedRecord = new WebInspector.TimelinePresentationModel.CoalescedRecord(record);
168
169         coalescedRecord._collapsed = true;
170         coalescedRecord._presentationChildren.push(presentationRecord);
171         presentationRecord._presentationParent = coalescedRecord;
172         if (presentationRecord.hasWarnings() || presentationRecord.childHasWarnings())
173             coalescedRecord._childHasWarnings = true;
174
175         coalescedRecord._presentationParent = parent;
176         parent._presentationChildren[parent._presentationChildren.indexOf(presentationRecord)] = coalescedRecord;
177
178         return coalescedRecord;
179     },
180
181     /**
182      * @param {!WebInspector.TimelinePresentationModel.Record} presentationRecord
183      */
184     _updateCoalescingParent: function(presentationRecord)
185     {
186         var parentRecord = presentationRecord._presentationParent;
187         if (parentRecord.endTime() < presentationRecord.endTime())
188             parentRecord._endTime = presentationRecord.endTime();
189     },
190
191     /**
192      * @param {?RegExp} textFilter
193      */
194     setTextFilter: function(textFilter)
195     {
196         var records = this._recordToPresentationRecord.values();
197         for (var i = 0; i < records.length; ++i)
198             records[i]._expandedOrCollapsedWhileFiltered = false;
199         this._textFilter = textFilter;
200     },
201
202     refreshRecords: function()
203     {
204         this.reset();
205         var modelRecords = this._model.records();
206         for (var i = 0; i < modelRecords.length; ++i)
207             this.addRecord(modelRecords[i]);
208     },
209
210     invalidateFilteredRecords: function()
211     {
212         delete this._filteredRecords;
213     },
214
215     /**
216      * @return {!Array.<!WebInspector.TimelinePresentationModel.Record>}
217      */
218     filteredRecords: function()
219     {
220         if (this._filteredRecords)
221             return this._filteredRecords;
222
223         var recordsInWindow = [];
224
225         var stack = [{children: this._rootRecord._presentationChildren, index: 0, parentIsCollapsed: false, parentRecord: {}}];
226         var revealedDepth = 0;
227
228         function revealRecordsInStack() {
229             for (var depth = revealedDepth + 1; depth < stack.length; ++depth) {
230                 if (stack[depth - 1].parentIsCollapsed) {
231                     stack[depth].parentRecord._presentationParent._expandable = true;
232                     return;
233                 }
234                 stack[depth - 1].parentRecord._collapsed = false;
235                 recordsInWindow.push(stack[depth].parentRecord);
236                 stack[depth].windowLengthBeforeChildrenTraversal = recordsInWindow.length;
237                 stack[depth].parentIsRevealed = true;
238                 revealedDepth = depth;
239             }
240         }
241
242         while (stack.length) {
243             var entry = stack[stack.length - 1];
244             var records = entry.children;
245             if (records && entry.index < records.length) {
246                 var record = records[entry.index];
247                 ++entry.index;
248                 if (record.startTime() < this._windowEndTime && record.endTime() > this._windowStartTime) {
249                     if (this._model.isVisible(record.record())) {
250                         record._presentationParent._expandable = true;
251                         if (this._textFilter)
252                             revealRecordsInStack();
253                         if (!entry.parentIsCollapsed) {
254                             recordsInWindow.push(record);
255                             revealedDepth = stack.length;
256                             entry.parentRecord._collapsed = false;
257                         }
258                     }
259                 }
260
261                 record._expandable = false;
262
263                 stack.push({children: record._presentationChildren,
264                             index: 0,
265                             parentIsCollapsed: entry.parentIsCollapsed || (record._collapsed && (!this._textFilter || record._expandedOrCollapsedWhileFiltered)),
266                             parentRecord: record,
267                             windowLengthBeforeChildrenTraversal: recordsInWindow.length});
268             } else {
269                 stack.pop();
270                 revealedDepth = Math.min(revealedDepth, stack.length - 1);
271                 entry.parentRecord._visibleChildrenCount = recordsInWindow.length - entry.windowLengthBeforeChildrenTraversal;
272             }
273         }
274
275         this._filteredRecords = recordsInWindow;
276         return recordsInWindow;
277     },
278
279     __proto__: WebInspector.Object.prototype
280 }
281
282 /**
283  * @constructor
284  * @param {?WebInspector.TimelinePresentationModel.Record} parentRecord
285  */
286 WebInspector.TimelinePresentationModel.Record = function(parentRecord)
287 {
288     /**
289      * @type {!Array.<!WebInspector.TimelinePresentationModel.Record>}
290      */
291     this._presentationChildren = [];
292
293     if (parentRecord) {
294         this._presentationParent = parentRecord;
295         parentRecord._presentationChildren.push(this);
296     }
297 }
298
299 WebInspector.TimelinePresentationModel.Record.prototype = {
300     /**
301      * @return {number}
302      */
303     startTime: function()
304     {
305         throw new Error("Not implemented.");
306     },
307
308     /**
309      * @return {number}
310      */
311     endTime: function()
312     {
313         throw new Error("Not implemented.");
314     },
315
316     /**
317      * @return {number}
318      */
319     selfTime: function()
320     {
321         throw new Error("Not implemented.");
322     },
323
324     /**
325      * @return {!WebInspector.TimelineModel.Record}
326      */
327     record: function()
328     {
329         throw new Error("Not implemented.");
330     },
331
332     /**
333      * @return {!Array.<!WebInspector.TimelinePresentationModel.Record>}
334      */
335     presentationChildren: function()
336     {
337         return this._presentationChildren;
338     },
339
340     /**
341      * @return {boolean}
342      */
343     coalesced: function()
344     {
345         return false;
346     },
347
348     /**
349      * @return {boolean}
350      */
351     collapsed: function()
352     {
353         return this._collapsed;
354     },
355
356     /**
357      * @param {boolean} collapsed
358      */
359     setCollapsed: function(collapsed)
360     {
361         this._collapsed = collapsed;
362         this._expandedOrCollapsedWhileFiltered = true;
363     },
364
365     /**
366      * @return {?WebInspector.TimelinePresentationModel.Record}
367      */
368     presentationParent: function()
369     {
370         return this._presentationParent || null;
371     },
372
373     /**
374      * @return {number}
375      */
376     visibleChildrenCount: function()
377     {
378         return this._visibleChildrenCount || 0;
379     },
380
381     /**
382      * @return {boolean}
383      */
384     expandable: function()
385     {
386         return !!this._expandable;
387     },
388
389     /**
390      * @return {boolean}
391      */
392     hasWarnings: function()
393     {
394         return false;
395     },
396
397     /**
398      * @return {boolean}
399      */
400     childHasWarnings: function()
401     {
402         return this._childHasWarnings;
403     },
404
405     /**
406      * @return {?WebInspector.TimelineRecordListRow}
407      */
408     listRow: function()
409     {
410         return this._listRow;
411     },
412
413     /**
414      * @param {!WebInspector.TimelineRecordListRow} listRow
415      */
416     setListRow: function(listRow)
417     {
418         this._listRow = listRow;
419     },
420
421     /**
422      * @return {?WebInspector.TimelineRecordGraphRow}
423      */
424     graphRow: function()
425     {
426         return this._graphRow;
427     },
428
429     /**
430      * @param {!WebInspector.TimelineRecordGraphRow} graphRow
431      */
432     setGraphRow: function(graphRow)
433     {
434         this._graphRow = graphRow;
435     }
436 }
437
438 /**
439  * @constructor
440  * @extends {WebInspector.TimelinePresentationModel.Record}
441  * @param {!WebInspector.TimelineModel.Record} record
442  * @param {?WebInspector.TimelinePresentationModel.Record} parentRecord
443  */
444 WebInspector.TimelinePresentationModel.ActualRecord = function(record, parentRecord)
445 {
446     WebInspector.TimelinePresentationModel.Record.call(this, parentRecord);
447     this._record = record;
448
449     if (this.hasWarnings()) {
450         for (var parent = this._presentationParent; parent && !parent._childHasWarnings; parent = parent._presentationParent)
451             parent._childHasWarnings = true;
452     }
453 }
454
455 WebInspector.TimelinePresentationModel.ActualRecord.prototype = {
456     /**
457      * @return {number}
458      */
459     startTime: function()
460     {
461         return this._record.startTime();
462     },
463
464     /**
465      * @return {number}
466      */
467     endTime: function()
468     {
469         return this._record.endTime();
470     },
471
472     /**
473      * @return {number}
474      */
475     selfTime: function()
476     {
477         return this._record.selfTime();
478     },
479
480     /**
481      * @return {!WebInspector.TimelineModel.Record}
482      */
483     record: function()
484     {
485         return this._record;
486     },
487
488     /**
489      * @return {boolean}
490      */
491     hasWarnings: function()
492     {
493         return !!this._record.warnings();
494     },
495
496     __proto__: WebInspector.TimelinePresentationModel.Record.prototype
497 }
498
499 /**
500  * @constructor
501  * @extends {WebInspector.TimelinePresentationModel.Record}
502  * @param {!WebInspector.TimelineModel.Record} record
503  */
504 WebInspector.TimelinePresentationModel.CoalescedRecord = function(record)
505 {
506     WebInspector.TimelinePresentationModel.Record.call(this, null);
507     this._startTime = record.startTime();
508     this._endTime = record.endTime();
509 }
510
511 WebInspector.TimelinePresentationModel.CoalescedRecord.prototype = {
512     /**
513      * @return {number}
514      */
515     startTime: function()
516     {
517         return this._startTime;
518     },
519
520     /**
521      * @return {number}
522      */
523     endTime: function()
524     {
525         return this._endTime;
526     },
527
528     /**
529      * @return {number}
530      */
531     selfTime: function()
532     {
533         return 0;
534     },
535
536     /**
537      * @return {!WebInspector.TimelineModel.Record}
538      */
539     record: function()
540     {
541         return this._presentationChildren[0].record();
542     },
543
544     /**
545      * @return {boolean}
546      */
547     coalesced: function()
548     {
549         return true;
550     },
551
552     /**
553      * @return {boolean}
554      */
555     hasWarnings: function()
556     {
557         return false;
558     },
559
560     __proto__: WebInspector.TimelinePresentationModel.Record.prototype
561 }
562
563 /**
564  * @constructor
565  * @extends {WebInspector.TimelinePresentationModel.Record}
566  */
567 WebInspector.TimelinePresentationModel.RootRecord = function()
568 {
569     WebInspector.TimelinePresentationModel.Record.call(this, null);
570 }
571
572 WebInspector.TimelinePresentationModel.RootRecord.prototype = {
573     /**
574      * @return {boolean}
575      */
576     hasWarnings: function()
577     {
578         return false;
579     },
580
581     __proto__: WebInspector.TimelinePresentationModel.Record.prototype
582 }