OLD | NEW |
| (Empty) |
1 /* | |
2 * Copyright (C) 2011 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 | |
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. ``AS IS'' AND ANY | |
14 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE | |
15 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR | |
16 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR | |
17 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, | |
18 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, | |
19 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR | |
20 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY | |
21 * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT | |
22 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE | |
23 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | |
24 */ | |
25 | |
26 /** | |
27 * @constructor | |
28 * @extends WebInspector.DataGridNode | |
29 * @param {WebInspector.CSSSelectorProfileView} profileView | |
30 * @param {CSSAgent.SelectorProfile} data | |
31 */ | |
32 WebInspector.CSSSelectorDataGridNode = function(profileView, data) | |
33 { | |
34 WebInspector.DataGridNode.call(this, data, false); | |
35 this._profileView = profileView; | |
36 } | |
37 | |
38 WebInspector.CSSSelectorDataGridNode.prototype = { | |
39 get data() | |
40 { | |
41 var data = {}; | |
42 data.selector = this._data.selector; | |
43 data.matches = this._data.matchCount; | |
44 | |
45 if (this._profileView.showTimeAsPercent.get()) | |
46 data.time = Number(this._data.timePercent).toFixed(1) + "%"; | |
47 else | |
48 data.time = Number.secondsToString(this._data.time / 1000, true); | |
49 | |
50 return data; | |
51 }, | |
52 | |
53 get rawData() | |
54 { | |
55 return this._data; | |
56 }, | |
57 | |
58 createCell: function(columnIdentifier) | |
59 { | |
60 var cell = WebInspector.DataGridNode.prototype.createCell.call(this, col
umnIdentifier); | |
61 | |
62 if (columnIdentifier !== "source") | |
63 return cell; | |
64 | |
65 cell.removeChildren(); | |
66 | |
67 if (this.rawData.url) { | |
68 var wrapperDiv = cell.createChild("div"); | |
69 wrapperDiv.appendChild(WebInspector.linkifyResourceAsNode(this.rawDa
ta.url, this.rawData.lineNumber)); | |
70 } | |
71 | |
72 return cell; | |
73 }, | |
74 | |
75 __proto__: WebInspector.DataGridNode.prototype | |
76 } | |
77 | |
78 /** | |
79 * @constructor | |
80 * @extends WebInspector.View | |
81 * @param {CSSAgent.SelectorProfile} profile | |
82 */ | |
83 WebInspector.CSSSelectorProfileView = function(profile) | |
84 { | |
85 WebInspector.View.call(this); | |
86 | |
87 this.element.addStyleClass("profile-view"); | |
88 | |
89 this.showTimeAsPercent = WebInspector.settings.createSetting("selectorProfil
erShowTimeAsPercent", true); | |
90 | |
91 var columns = [ | |
92 {id: "selector", title: WebInspector.UIString("Selector"), width: "550px
", sortable: true, longText: true}, | |
93 {id: "source", title: WebInspector.UIString("Source"), width: "100px", s
ortable: true}, | |
94 {id: "time", title: WebInspector.UIString("Total"), width: "72px", sort:
WebInspector.DataGrid.Order.Descending, sortable: true}, | |
95 {id: "matches", title: WebInspector.UIString("Matches"), width: "72px",
sortable: true} | |
96 ]; | |
97 | |
98 this.dataGrid = new WebInspector.DataGrid(columns); | |
99 this.dataGrid.element.addStyleClass("selector-profile-view"); | |
100 this.dataGrid.addEventListener(WebInspector.DataGrid.Events.SortingChanged,
this._sortProfile, this); | |
101 this.dataGrid.element.addEventListener("mousedown", this._mouseDownInDataGri
d.bind(this), true); | |
102 this.dataGrid.show(this.element); | |
103 | |
104 this.percentButton = new WebInspector.StatusBarButton("", "percent-time-stat
us-bar-item"); | |
105 this.percentButton.addEventListener("click", this._percentClicked, this); | |
106 | |
107 this.profile = profile; | |
108 | |
109 this._createProfileNodes(); | |
110 this._sortProfile(); | |
111 this._updatePercentButton(); | |
112 } | |
113 | |
114 WebInspector.CSSSelectorProfileView.prototype = { | |
115 get statusBarItems() | |
116 { | |
117 return [this.percentButton.element]; | |
118 }, | |
119 | |
120 get profile() | |
121 { | |
122 return this._profile; | |
123 }, | |
124 | |
125 set profile(profile) | |
126 { | |
127 this._profile = profile; | |
128 }, | |
129 | |
130 _createProfileNodes: function() | |
131 { | |
132 var data = this.profile.data; | |
133 if (!data) { | |
134 // The profiler may have been terminated with the "Clear all profile
s." button. | |
135 return; | |
136 } | |
137 | |
138 this.profile.children = []; | |
139 for (var i = 0; i < data.length; ++i) { | |
140 data[i].timePercent = data[i].time * 100 / this.profile.totalTime; | |
141 var node = new WebInspector.CSSSelectorDataGridNode(this, data[i]); | |
142 this.profile.children.push(node); | |
143 } | |
144 }, | |
145 | |
146 rebuildGridItems: function() | |
147 { | |
148 this.dataGrid.rootNode().removeChildren(); | |
149 | |
150 var children = this.profile.children; | |
151 var count = children.length; | |
152 | |
153 for (var index = 0; index < count; ++index) | |
154 this.dataGrid.rootNode().appendChild(children[index]); | |
155 }, | |
156 | |
157 refreshData: function() | |
158 { | |
159 var child = this.dataGrid.rootNode().children[0]; | |
160 while (child) { | |
161 child.refresh(); | |
162 child = child.traverseNextNode(false, null, true); | |
163 } | |
164 }, | |
165 | |
166 refreshShowAsPercents: function() | |
167 { | |
168 this._updatePercentButton(); | |
169 this.refreshData(); | |
170 }, | |
171 | |
172 _percentClicked: function(event) | |
173 { | |
174 this.showTimeAsPercent.set(!this.showTimeAsPercent.get()); | |
175 this.refreshShowAsPercents(); | |
176 }, | |
177 | |
178 _updatePercentButton: function() | |
179 { | |
180 if (this.showTimeAsPercent.get()) { | |
181 this.percentButton.title = WebInspector.UIString("Show absolute time
s."); | |
182 this.percentButton.toggled = true; | |
183 } else { | |
184 this.percentButton.title = WebInspector.UIString("Show times as perc
entages."); | |
185 this.percentButton.toggled = false; | |
186 } | |
187 }, | |
188 | |
189 _sortProfile: function() | |
190 { | |
191 var sortAscending = this.dataGrid.isSortOrderAscending(); | |
192 var sortColumnIdentifier = this.dataGrid.sortColumnIdentifier(); | |
193 | |
194 function selectorComparator(a, b) | |
195 { | |
196 var result = b.rawData.selector.compareTo(a.rawData.selector); | |
197 return sortAscending ? -result : result; | |
198 } | |
199 | |
200 function sourceComparator(a, b) | |
201 { | |
202 var aRawData = a.rawData; | |
203 var bRawData = b.rawData; | |
204 var result = bRawData.url.compareTo(aRawData.url); | |
205 if (!result) | |
206 result = bRawData.lineNumber - aRawData.lineNumber; | |
207 return sortAscending ? -result : result; | |
208 } | |
209 | |
210 function timeComparator(a, b) | |
211 { | |
212 const result = b.rawData.time - a.rawData.time; | |
213 return sortAscending ? -result : result; | |
214 } | |
215 | |
216 function matchesComparator(a, b) | |
217 { | |
218 const result = b.rawData.matchCount - a.rawData.matchCount; | |
219 return sortAscending ? -result : result; | |
220 } | |
221 | |
222 var comparator; | |
223 switch (sortColumnIdentifier) { | |
224 case "time": | |
225 comparator = timeComparator; | |
226 break; | |
227 case "matches": | |
228 comparator = matchesComparator; | |
229 break; | |
230 case "selector": | |
231 comparator = selectorComparator; | |
232 break; | |
233 case "source": | |
234 comparator = sourceComparator; | |
235 break; | |
236 } | |
237 | |
238 this.profile.children.sort(comparator); | |
239 | |
240 this.rebuildGridItems(); | |
241 }, | |
242 | |
243 _mouseDownInDataGrid: function(event) | |
244 { | |
245 if (event.detail < 2) | |
246 return; | |
247 | |
248 var cell = event.target.enclosingNodeOrSelfWithNodeName("td"); | |
249 if (!cell) | |
250 return; | |
251 | |
252 if (cell.hasStyleClass("time-column")) | |
253 this.showTimeAsPercent.set(!this.showTimeAsPercent.get()); | |
254 else | |
255 return; | |
256 | |
257 this.refreshShowAsPercents(); | |
258 | |
259 event.consume(true); | |
260 }, | |
261 | |
262 __proto__: WebInspector.View.prototype | |
263 } | |
264 | |
265 /** | |
266 * @constructor | |
267 * @extends {WebInspector.ProfileType} | |
268 */ | |
269 WebInspector.CSSSelectorProfileType = function() | |
270 { | |
271 WebInspector.ProfileType.call(this, WebInspector.CSSSelectorProfileType.Type
Id, WebInspector.UIString("Collect CSS Selector Profile")); | |
272 this._recording = false; | |
273 this._profileUid = 1; | |
274 WebInspector.CSSSelectorProfileType.instance = this; | |
275 } | |
276 | |
277 WebInspector.CSSSelectorProfileType.TypeId = "SELECTOR"; | |
278 | |
279 WebInspector.CSSSelectorProfileType.prototype = { | |
280 get buttonTooltip() | |
281 { | |
282 return this._recording ? WebInspector.UIString("Stop CSS selector profil
ing.") : WebInspector.UIString("Start CSS selector profiling."); | |
283 }, | |
284 | |
285 /** | |
286 * @override | |
287 * @return {boolean} | |
288 */ | |
289 buttonClicked: function() | |
290 { | |
291 if (this._recording) { | |
292 this._stopRecordingProfile(); | |
293 return false; | |
294 } else { | |
295 this._startRecordingProfile(); | |
296 return true; | |
297 } | |
298 }, | |
299 | |
300 get treeItemTitle() | |
301 { | |
302 return WebInspector.UIString("CSS SELECTOR PROFILES"); | |
303 }, | |
304 | |
305 get description() | |
306 { | |
307 return WebInspector.UIString("CSS selector profiles show how long the se
lector matching has taken in total and how many times a certain selector has mat
ched DOM elements. The results are approximate due to matching algorithm optimiz
ations."); | |
308 }, | |
309 | |
310 reset: function() | |
311 { | |
312 this._profileUid = 1; | |
313 }, | |
314 | |
315 setRecordingProfile: function(isProfiling) | |
316 { | |
317 this._recording = isProfiling; | |
318 }, | |
319 | |
320 _startRecordingProfile: function() | |
321 { | |
322 this._recording = true; | |
323 CSSAgent.startSelectorProfiler(); | |
324 }, | |
325 | |
326 _stopRecordingProfile: function() | |
327 { | |
328 /** | |
329 * @param {?Protocol.Error} error | |
330 * @param {CSSAgent.SelectorProfile} profile | |
331 */ | |
332 function callback(error, profile) | |
333 { | |
334 if (error) | |
335 return; | |
336 | |
337 var uid = this._profileUid++; | |
338 var title = WebInspector.UIString("Profile %d", uid) + String.sprint
f(" (%s)", Number.secondsToString(profile.totalTime / 1000)); | |
339 this.addProfile(new WebInspector.CSSProfileHeader(this, title, uid,
profile)); | |
340 } | |
341 | |
342 this._recording = false; | |
343 CSSAgent.stopSelectorProfiler(callback.bind(this)); | |
344 }, | |
345 | |
346 /** | |
347 * @override | |
348 * @param {string=} title | |
349 * @return {WebInspector.ProfileHeader} | |
350 */ | |
351 createTemporaryProfile: function(title) | |
352 { | |
353 title = title || WebInspector.UIString("Recording\u2026"); | |
354 return new WebInspector.CSSProfileHeader(this, title); | |
355 }, | |
356 | |
357 __proto__: WebInspector.ProfileType.prototype | |
358 } | |
359 | |
360 | |
361 /** | |
362 * @constructor | |
363 * @extends {WebInspector.ProfileHeader} | |
364 * @param {!WebInspector.CSSSelectorProfileType} type | |
365 * @param {string} title | |
366 * @param {number=} uid | |
367 * @param {CSSAgent.SelectorProfile=} protocolData | |
368 */ | |
369 WebInspector.CSSProfileHeader = function(type, title, uid, protocolData) | |
370 { | |
371 WebInspector.ProfileHeader.call(this, type, title, uid); | |
372 this._protocolData = protocolData; | |
373 } | |
374 | |
375 WebInspector.CSSProfileHeader.prototype = { | |
376 /** | |
377 * @override | |
378 */ | |
379 createSidebarTreeElement: function() | |
380 { | |
381 return new WebInspector.ProfileSidebarTreeElement(this, this.title, "pro
file-sidebar-tree-item"); | |
382 }, | |
383 | |
384 /** | |
385 * @override | |
386 * @param {WebInspector.ProfilesPanel} profilesPanel | |
387 */ | |
388 createView: function(profilesPanel) | |
389 { | |
390 var profile = /** @type {CSSAgent.SelectorProfile} */ (this._protocolDat
a); | |
391 return new WebInspector.CSSSelectorProfileView(profile); | |
392 }, | |
393 | |
394 __proto__: WebInspector.ProfileHeader.prototype | |
395 } | |
OLD | NEW |