| 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 |