Web Inspector: Timelines UI redesign: use DataGridNode for TimelineView selection...
[WebKit-https.git] / Source / WebInspectorUI / UserInterface / Views / ProfileNodeDataGridNode.js
1 /*
2  * Copyright (C) 2014, 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.ProfileNodeDataGridNode = class ProfileNodeDataGridNode extends WebInspector.TimelineDataGridNode
27 {
28     constructor(profileNode, baseStartTime, rangeStartTime, rangeEndTime)
29     {
30         var hasChildren = !!profileNode.childNodes.length;
31
32         super(false, null, hasChildren);
33
34         this._profileNode = profileNode;
35         this._baseStartTime = baseStartTime || 0;
36         this._rangeStartTime = rangeStartTime || 0;
37         this._rangeEndTime = typeof rangeEndTime === "number" ? rangeEndTime : Infinity;
38
39         this._cachedData = null;
40     }
41
42     // Public
43
44     get profileNode()
45     {
46         return this._profileNode;
47     }
48
49     get records()
50     {
51         return null;
52     }
53
54     get baseStartTime()
55     {
56         return this._baseStartTime;
57     }
58
59     get rangeStartTime()
60     {
61         return this._rangeStartTime;
62     }
63
64     get rangeEndTime()
65     {
66         return this._rangeEndTime;
67     }
68
69     get data()
70     {
71         if (!this._cachedData) {
72             this._cachedData = this._profileNode.computeCallInfoForTimeRange(this._rangeStartTime, this._rangeEndTime);
73             this._cachedData.name = this.displayName();
74             this._cachedData.location = this._profileNode.sourceCodeLocation;
75         }
76
77         return this._cachedData;
78     }
79
80     updateRangeTimes(startTime, endTime)
81     {
82         var oldRangeStartTime = this._rangeStartTime;
83         var oldRangeEndTime = this._rangeEndTime;
84
85         if (oldRangeStartTime === startTime && oldRangeEndTime === endTime)
86             return;
87
88         this._rangeStartTime = startTime;
89         this._rangeEndTime = endTime;
90
91         // We only need a refresh if the new range time changes the visible portion of this record.
92         var profileStart = this._profileNode.startTime;
93         var profileEnd = this._profileNode.endTime;
94         var oldStartBoundary = Number.constrain(oldRangeStartTime, profileStart, profileEnd);
95         var oldEndBoundary = Number.constrain(oldRangeEndTime, profileStart, profileEnd);
96         var newStartBoundary = Number.constrain(startTime, profileStart, profileEnd);
97         var newEndBoundary = Number.constrain(endTime, profileStart, profileEnd);
98
99         if (oldStartBoundary !== newStartBoundary || oldEndBoundary !== newEndBoundary)
100             this.needsRefresh();
101     }
102
103     refresh()
104     {
105         this._data = this._profileNode.computeCallInfoForTimeRange(this._rangeStartTime, this._rangeEndTime);
106         this._data.location = this._profileNode.sourceCodeLocation;
107
108         super.refresh();
109     }
110
111     createCellContent(columnIdentifier, cell)
112     {
113         var value = this.data[columnIdentifier];
114
115         switch (columnIdentifier) {
116         case "name":
117             cell.classList.add(...this.iconClassNames());
118             return value;
119
120         case "startTime":
121             return isNaN(value) ? emDash : Number.secondsToString(value - this._baseStartTime, true);
122
123         case "selfTime":
124         case "totalTime":
125         case "averageTime":
126             return isNaN(value) ? emDash : Number.secondsToString(value, true);
127         }
128
129         return super.createCellContent(columnIdentifier, cell);
130     }
131
132     displayName()
133     {
134         let title = this._profileNode.functionName;
135         if (!title) {
136             switch (this._profileNode.type) {
137             case WebInspector.ProfileNode.Type.Function:
138                 title = WebInspector.UIString("(anonymous function)");
139                 break;
140             case WebInspector.ProfileNode.Type.Program:
141                 title = WebInspector.UIString("(program)");
142                 break;
143             default:
144                 title = WebInspector.UIString("(anonymous function)");
145                 console.error("Unknown ProfileNode type: " + this._profileNode.type);
146             }
147         }
148
149         return title;
150     }
151
152     iconClassNames()
153     {
154         let className;
155         switch (this._profileNode.type) {
156         case WebInspector.ProfileNode.Type.Function:
157             className = WebInspector.CallFrameView.FunctionIconStyleClassName;
158             if (!this._profileNode.sourceCodeLocation)
159                 className = WebInspector.CallFrameView.NativeIconStyleClassName;
160             break;
161         case WebInspector.ProfileNode.Type.Program:
162             className = WebInspector.TimelineRecordTreeElement.EvaluatedRecordIconStyleClass;
163             break;
164         }
165
166         console.assert(className);
167
168         // This is more than likely an event listener function with an "on" prefix and it is
169         // as long or longer than the shortest event listener name -- "oncut".
170         if (this._profileNode.functionName && this._profileNode.functionName.startsWith("on") && this._profileNode.functionName.length >= 5)
171             className = WebInspector.CallFrameView.EventListenerIconStyleClassName;
172
173         return [className];
174     }
175 };