Web Inspector: Timelines UI redesign: use DataGridNode for TimelineView selection...
[WebKit-https.git] / Source / WebInspectorUI / UserInterface / Views / ScriptTimelineDataGridNode.js
1 /*
2  * Copyright (C) 2013, 2015 Apple 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
6  * are met:
7  * 1. Redistributions of source code must retain the above copyright
8  *    notice, this list of conditions and the following disclaimer.
9  * 2. Redistributions in binary form must reproduce the above copyright
10  *    notice, this list of conditions and the following disclaimer in the
11  *    documentation and/or other materials provided with the distribution.
12  *
13  * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
14  * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
15  * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
16  * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
17  * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
18  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
19  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
20  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
21  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
22  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
23  * THE POSSIBILITY OF SUCH DAMAGE.
24  */
25
26 WebInspector.ScriptTimelineDataGridNode = class ScriptTimelineDataGridNode extends WebInspector.TimelineDataGridNode
27 {
28     constructor(scriptTimelineRecord, baseStartTime, rangeStartTime, rangeEndTime)
29     {
30         super(false, null);
31
32         this._record = scriptTimelineRecord;
33         this._baseStartTime = baseStartTime || 0;
34         this._rangeStartTime = rangeStartTime || 0;
35         this._rangeEndTime = typeof rangeEndTime === "number" ? rangeEndTime : Infinity;
36     }
37
38     // Public
39
40     get records()
41     {
42         return [this._record];
43     }
44
45     get baseStartTime()
46     {
47         return this._baseStartTime;
48     }
49
50     get rangeStartTime()
51     {
52         return this._rangeStartTime;
53     }
54
55     get rangeEndTime()
56     {
57         return this._rangeEndTime;
58     }
59
60     get data()
61     {
62         if (!this._cachedData) {
63             var startTime = this._record.startTime;
64             var duration = this._record.startTime + this._record.duration - startTime;
65             var callFrameOrSourceCodeLocation = this._record.initiatorCallFrame || this._record.sourceCodeLocation;
66
67             // COMPATIBILITY (iOS 8): Profiles included per-call information and can be finely partitioned.
68             if (this._record.profile) {
69                 var oneRootNode = this._record.profile.topDownRootNodes[0];
70                 if (oneRootNode && oneRootNode.calls) {
71                     startTime = Math.max(this._rangeStartTime, this._record.startTime);
72                     duration = Math.min(this._record.startTime + this._record.duration, this._rangeEndTime) - startTime;
73                 }
74             }
75
76             this._cachedData = {
77                 eventType: this._record.eventType,
78                 startTime,
79                 selfTime: duration,
80                 totalTime: duration,
81                 averageTime: duration,
82                 callCount: this._record.callCountOrSamples,
83                 location: callFrameOrSourceCodeLocation,
84             };
85         }
86
87         return this._cachedData;
88     }
89
90     updateRangeTimes(startTime, endTime)
91     {
92         var oldRangeStartTime = this._rangeStartTime;
93         var oldRangeEndTime = this._rangeEndTime;
94
95         if (oldRangeStartTime === startTime && oldRangeEndTime === endTime)
96             return;
97
98         this._rangeStartTime = startTime;
99         this._rangeEndTime = endTime;
100
101         // If we have no duration the range does not matter.
102         if (!this._record.duration)
103             return;
104
105         // We only need a refresh if the new range time changes the visible portion of this record.
106         var recordStart = this._record.startTime;
107         var recordEnd = this._record.startTime + this._record.duration;
108         var oldStartBoundary = Number.constrain(oldRangeStartTime, recordStart, recordEnd);
109         var oldEndBoundary = Number.constrain(oldRangeEndTime, recordStart, recordEnd);
110         var newStartBoundary = Number.constrain(startTime, recordStart, recordEnd);
111         var newEndBoundary = Number.constrain(endTime, recordStart, recordEnd);
112
113         if (oldStartBoundary !== newStartBoundary || oldEndBoundary !== newEndBoundary)
114             this.needsRefresh();
115     }
116
117     createCellContent(columnIdentifier, cell)
118     {
119         var value = this.data[columnIdentifier];
120
121         switch (columnIdentifier) {
122         case "name":
123             cell.classList.add(...this.iconClassNames());
124             return this._createNameCellDocumentFragment();
125
126         case "startTime":
127             return isNaN(value) ? emDash : Number.secondsToString(value - this._baseStartTime, true);
128
129         case "selfTime":
130         case "totalTime":
131         case "averageTime":
132             return isNaN(value) ? emDash : Number.secondsToString(value, true);
133
134         case "callCount":
135             return isNaN(value) ? emDash : value;
136         }
137
138         return super.createCellContent(columnIdentifier, cell);
139     }
140
141     // Private
142
143     _createNameCellDocumentFragment(cellElement)
144     {
145         let fragment = document.createDocumentFragment();
146         fragment.append(this.displayName());
147
148         if (this._record.eventType === WebInspector.ScriptTimelineRecord.EventType.TimerInstalled) {
149             let subtitleElement = document.createElement("span");
150             subtitleElement.classList.add("subtitle");
151             fragment.append(subtitleElement);
152
153             let timeoutString = Number.secondsToString(this._record.details.timeout / 1000);
154             if (this._record.details.repeating)
155                 subtitleElement.textContent = WebInspector.UIString("%s interval").format(timeoutString);
156             else
157                 subtitleElement.textContent = WebInspector.UIString("%s delay").format(timeoutString);
158         }
159
160         return fragment;
161     }
162 };