| OLD | NEW |
| (Empty) |
| 1 /* | |
| 2 * Copyright (C) 2013 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 are | |
| 6 * met: | |
| 7 * | |
| 8 * * Redistributions of source code must retain the above copyright | |
| 9 * notice, this list of conditions and the following disclaimer. | |
| 10 * * Redistributions in binary form must reproduce the above | |
| 11 * copyright notice, this list of conditions and the following disclaimer | |
| 12 * in the documentation and/or other materials provided with the | |
| 13 * distribution. | |
| 14 * * Neither the name of Google Inc. nor the names of its | |
| 15 * contributors may be used to endorse or promote products derived from | |
| 16 * this software without specific prior written permission. | |
| 17 * | |
| 18 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS | |
| 19 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT | |
| 20 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR | |
| 21 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT | |
| 22 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, | |
| 23 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT | |
| 24 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, | |
| 25 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY | |
| 26 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT | |
| 27 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE | |
| 28 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | |
| 29 */ | |
| 30 | |
| 31 /** | |
| 32 * @constructor | |
| 33 * @extends {WebInspector.VBox} | |
| 34 * @param {!WebInspector.CanvasProfileHeader} profile | |
| 35 */ | |
| 36 WebInspector.CanvasProfileView = function(profile) | |
| 37 { | |
| 38 WebInspector.VBox.call(this); | |
| 39 this.registerRequiredCSS("profiler/canvasProfiler.css"); | |
| 40 this.element.classList.add("canvas-profile-view"); | |
| 41 | |
| 42 this._profile = profile; | |
| 43 this._traceLogId = profile.traceLogId(); | |
| 44 this._traceLogPlayer = /** @type {!WebInspector.CanvasTraceLogPlayerProxy} *
/ (profile.traceLogPlayer()); | |
| 45 this._linkifier = new WebInspector.Linkifier(); | |
| 46 | |
| 47 this._replayInfoSplitView = new WebInspector.SplitView(true, true, "canvasPr
ofileViewReplaySplitViewState", 0.34); | |
| 48 this._replayInfoSplitView.show(this.element); | |
| 49 | |
| 50 this._imageSplitView = new WebInspector.SplitView(false, true, "canvasProfil
eViewSplitViewState", 300); | |
| 51 this._replayInfoSplitView.setMainView(this._imageSplitView); | |
| 52 | |
| 53 var replayImageContainerView = new WebInspector.VBoxWithResizeCallback(this.
_onReplayImageResize.bind(this)); | |
| 54 replayImageContainerView.setMinimumSize(50, 28); | |
| 55 this._imageSplitView.setMainView(replayImageContainerView); | |
| 56 | |
| 57 var replayImageContainer = replayImageContainerView.element; | |
| 58 replayImageContainer.id = "canvas-replay-image-container"; | |
| 59 var replayImageParent = replayImageContainer.createChild("div", "canvas-repl
ay-image-parent"); | |
| 60 replayImageParent.createChild("span"); // Helps to align the image verticall
y. | |
| 61 this._replayImageElement = replayImageParent.createChild("img"); | |
| 62 this._debugInfoElement = replayImageContainer.createChild("div", "canvas-deb
ug-info hidden"); | |
| 63 this._spinnerIcon = replayImageContainer.createChild("div", "spinner-icon sm
all hidden"); | |
| 64 | |
| 65 var replayLogContainerView = new WebInspector.VBox(); | |
| 66 replayLogContainerView.setMinimumSize(22, 22); | |
| 67 this._imageSplitView.setSidebarView(replayLogContainerView); | |
| 68 | |
| 69 var replayLogContainer = replayLogContainerView.element; | |
| 70 var controlsToolbar = new WebInspector.StatusBar(replayLogContainer); | |
| 71 var logGridContainer = replayLogContainer.createChild("div", "canvas-replay-
log"); | |
| 72 | |
| 73 this._createControlButton(controlsToolbar, "first-step-status-bar-item", Web
Inspector.UIString("First call."), this._onReplayFirstStepClick.bind(this)); | |
| 74 this._createControlButton(controlsToolbar, "step-out-status-bar-item", WebIn
spector.UIString("Previous call."), this._onReplayStepClick.bind(this, false)); | |
| 75 this._createControlButton(controlsToolbar, "step-in-status-bar-item", WebIns
pector.UIString("Next call."), this._onReplayStepClick.bind(this, true)); | |
| 76 this._createControlButton(controlsToolbar, "step-backwards-status-bar-item",
WebInspector.UIString("Previous drawing call."), this._onReplayDrawingCallClick
.bind(this, false)); | |
| 77 this._createControlButton(controlsToolbar, "step-over-status-bar-item", WebI
nspector.UIString("Next drawing call."), this._onReplayDrawingCallClick.bind(thi
s, true)); | |
| 78 this._createControlButton(controlsToolbar, "last-step-status-bar-item", WebI
nspector.UIString("Last call."), this._onReplayLastStepClick.bind(this)); | |
| 79 | |
| 80 this._replayContextSelector = new WebInspector.StatusBarComboBox(this._onRep
layContextChanged.bind(this)); | |
| 81 this._replayContextSelector.createOption(WebInspector.UIString("<screenshot
auto>"), WebInspector.UIString("Show screenshot of the last replayed resource.")
, ""); | |
| 82 controlsToolbar.appendStatusBarItem(this._replayContextSelector); | |
| 83 | |
| 84 this._installReplayInfoSidebarWidgets(replayLogContainer); | |
| 85 | |
| 86 this._replayStateView = new WebInspector.CanvasReplayStateView(this._traceLo
gPlayer); | |
| 87 this._replayInfoSplitView.setSidebarView(this._replayStateView); | |
| 88 | |
| 89 /** @type {!Object.<string, boolean>} */ | |
| 90 this._replayContexts = {}; | |
| 91 | |
| 92 var columns = [ | |
| 93 {title: "#", sortable: false, width: "5%"}, | |
| 94 {title: WebInspector.UIString("Call"), sortable: false, width: "75%", di
sclosure: true}, | |
| 95 {title: WebInspector.UIString("Location"), sortable: false, width: "20%"
} | |
| 96 ]; | |
| 97 | |
| 98 this._logGrid = new WebInspector.DataGrid(columns); | |
| 99 this._logGrid.element.classList.add("fill"); | |
| 100 this._logGrid.show(logGridContainer); | |
| 101 this._logGrid.addEventListener(WebInspector.DataGrid.Events.SelectedNode, th
is._replayTraceLog, this); | |
| 102 | |
| 103 this.element.addEventListener("mousedown", this._onMouseClick.bind(this), tr
ue); | |
| 104 | |
| 105 this._popoverHelper = new WebInspector.ObjectPopoverHelper(this.element, thi
s._popoverAnchor.bind(this), this._resolveObjectForPopover.bind(this), this._onH
idePopover.bind(this), true); | |
| 106 this._popoverHelper.setRemoteObjectFormatter(this._hexNumbersFormatter.bind(
this)); | |
| 107 | |
| 108 this._requestTraceLog(0); | |
| 109 } | |
| 110 | |
| 111 /** | |
| 112 * @const | |
| 113 * @type {number} | |
| 114 */ | |
| 115 WebInspector.CanvasProfileView.TraceLogPollingInterval = 500; | |
| 116 | |
| 117 WebInspector.CanvasProfileView.prototype = { | |
| 118 dispose: function() | |
| 119 { | |
| 120 this._linkifier.reset(); | |
| 121 }, | |
| 122 | |
| 123 /** | |
| 124 * @return {!Array.<!WebInspector.StatusBarItem>} | |
| 125 */ | |
| 126 statusBarItems: function() | |
| 127 { | |
| 128 return []; | |
| 129 }, | |
| 130 | |
| 131 get profile() | |
| 132 { | |
| 133 return this._profile; | |
| 134 }, | |
| 135 | |
| 136 _onReplayImageResize: function() | |
| 137 { | |
| 138 var parent = this._replayImageElement.parentElement; | |
| 139 this._replayImageElement.style.maxWidth = (parent.clientWidth - 1) + "px
"; | |
| 140 this._replayImageElement.style.maxHeight = (parent.clientHeight - 1) + "
px"; | |
| 141 }, | |
| 142 | |
| 143 /** | |
| 144 * @override | |
| 145 * @return {!Array.<!Element>} | |
| 146 */ | |
| 147 elementsToRestoreScrollPositionsFor: function() | |
| 148 { | |
| 149 return [this._logGrid.scrollContainer]; | |
| 150 }, | |
| 151 | |
| 152 /** | |
| 153 * @param {!Element} replayLogElement | |
| 154 */ | |
| 155 _installReplayInfoSidebarWidgets: function(replayLogElement) | |
| 156 { | |
| 157 this._replayInfoResizeWidgetElement = replayLogElement.createChild("div"
, "resizer-widget"); | |
| 158 this._replayInfoSplitView.addEventListener(WebInspector.SplitView.Events
.ShowModeChanged, this._updateReplayInfoResizeWidget, this); | |
| 159 this._updateReplayInfoResizeWidget(); | |
| 160 this._replayInfoSplitView.installResizer(this._replayInfoResizeWidgetEle
ment); | |
| 161 }, | |
| 162 | |
| 163 _updateReplayInfoResizeWidget: function() | |
| 164 { | |
| 165 this._replayInfoResizeWidgetElement.classList.toggle("hidden", this._rep
layInfoSplitView.showMode() !== WebInspector.SplitView.ShowMode.Both); | |
| 166 }, | |
| 167 | |
| 168 /** | |
| 169 * @param {!Event} event | |
| 170 */ | |
| 171 _onMouseClick: function(event) | |
| 172 { | |
| 173 var resourceLinkElement = event.target.enclosingNodeOrSelfWithClass("can
vas-formatted-resource"); | |
| 174 if (resourceLinkElement) { | |
| 175 this._replayInfoSplitView.showBoth(); | |
| 176 this._replayStateView.selectResource(resourceLinkElement.__resourceI
d); | |
| 177 event.consume(true); | |
| 178 return; | |
| 179 } | |
| 180 if (event.target.enclosingNodeOrSelfWithClass("webkit-html-resource-link
")) | |
| 181 event.consume(false); | |
| 182 }, | |
| 183 | |
| 184 /** | |
| 185 * @param {!WebInspector.StatusBar} toolbar | |
| 186 * @param {string} className | |
| 187 * @param {string} title | |
| 188 * @param {function(this:WebInspector.CanvasProfileView)} clickCallback | |
| 189 */ | |
| 190 _createControlButton: function(toolbar, className, title, clickCallback) | |
| 191 { | |
| 192 var button = new WebInspector.StatusBarButton(title, className + " canva
s-replay-button"); | |
| 193 toolbar.appendStatusBarItem(button); | |
| 194 | |
| 195 button.makeLongClickEnabled(); | |
| 196 button.addEventListener("click", clickCallback, this); | |
| 197 button.addEventListener("longClickDown", clickCallback, this); | |
| 198 button.addEventListener("longClickPress", clickCallback, this); | |
| 199 }, | |
| 200 | |
| 201 _onReplayContextChanged: function() | |
| 202 { | |
| 203 var selectedContextId = this._replayContextSelector.selectedOption().val
ue; | |
| 204 | |
| 205 /** | |
| 206 * @param {?CanvasAgent.ResourceState} resourceState | |
| 207 * @this {WebInspector.CanvasProfileView} | |
| 208 */ | |
| 209 function didReceiveResourceState(resourceState) | |
| 210 { | |
| 211 this._enableWaitIcon(false); | |
| 212 if (selectedContextId !== this._replayContextSelector.selectedOption
().value) | |
| 213 return; | |
| 214 var imageURL = (resourceState && resourceState.imageURL) || ""; | |
| 215 this._replayImageElement.src = imageURL; | |
| 216 this._replayImageElement.style.visibility = imageURL ? "" : "hidden"
; | |
| 217 } | |
| 218 | |
| 219 this._enableWaitIcon(true); | |
| 220 this._traceLogPlayer.getResourceState(selectedContextId, didReceiveResou
rceState.bind(this)); | |
| 221 }, | |
| 222 | |
| 223 /** | |
| 224 * @param {boolean} forward | |
| 225 */ | |
| 226 _onReplayStepClick: function(forward) | |
| 227 { | |
| 228 var selectedNode = this._logGrid.selectedNode; | |
| 229 if (!selectedNode) | |
| 230 return; | |
| 231 var nextNode = selectedNode; | |
| 232 do { | |
| 233 nextNode = forward ? nextNode.traverseNextNode(false) : nextNode.tra
versePreviousNode(false); | |
| 234 } while (nextNode && typeof nextNode.index !== "number"); | |
| 235 (nextNode || selectedNode).revealAndSelect(); | |
| 236 }, | |
| 237 | |
| 238 /** | |
| 239 * @param {boolean} forward | |
| 240 */ | |
| 241 _onReplayDrawingCallClick: function(forward) | |
| 242 { | |
| 243 var selectedNode = this._logGrid.selectedNode; | |
| 244 if (!selectedNode) | |
| 245 return; | |
| 246 var nextNode = selectedNode; | |
| 247 while (nextNode) { | |
| 248 var sibling = forward ? nextNode.nextSibling : nextNode.previousSibl
ing; | |
| 249 if (sibling) { | |
| 250 nextNode = sibling; | |
| 251 if (nextNode.hasChildren || nextNode.call.isDrawingCall) | |
| 252 break; | |
| 253 } else { | |
| 254 nextNode = nextNode.parent; | |
| 255 if (!forward) | |
| 256 break; | |
| 257 } | |
| 258 } | |
| 259 if (!nextNode && forward) | |
| 260 this._onReplayLastStepClick(); | |
| 261 else | |
| 262 (nextNode || selectedNode).revealAndSelect(); | |
| 263 }, | |
| 264 | |
| 265 _onReplayFirstStepClick: function() | |
| 266 { | |
| 267 var firstNode = this._logGrid.rootNode().children[0]; | |
| 268 if (firstNode) | |
| 269 firstNode.revealAndSelect(); | |
| 270 }, | |
| 271 | |
| 272 _onReplayLastStepClick: function() | |
| 273 { | |
| 274 var lastNode = this._logGrid.rootNode().children.peekLast(); | |
| 275 if (!lastNode) | |
| 276 return; | |
| 277 while (lastNode.expanded) { | |
| 278 var lastChild = lastNode.children.peekLast(); | |
| 279 if (!lastChild) | |
| 280 break; | |
| 281 lastNode = lastChild; | |
| 282 } | |
| 283 lastNode.revealAndSelect(); | |
| 284 }, | |
| 285 | |
| 286 /** | |
| 287 * @param {boolean} enable | |
| 288 */ | |
| 289 _enableWaitIcon: function(enable) | |
| 290 { | |
| 291 this._spinnerIcon.classList.toggle("hidden", !enable); | |
| 292 this._debugInfoElement.classList.toggle("hidden", enable); | |
| 293 }, | |
| 294 | |
| 295 _replayTraceLog: function() | |
| 296 { | |
| 297 if (this._pendingReplayTraceLogEvent) | |
| 298 return; | |
| 299 var index = this._selectedCallIndex(); | |
| 300 if (index === -1 || index === this._lastReplayCallIndex) | |
| 301 return; | |
| 302 this._lastReplayCallIndex = index; | |
| 303 this._pendingReplayTraceLogEvent = true; | |
| 304 | |
| 305 /** | |
| 306 * @param {?CanvasAgent.ResourceState} resourceState | |
| 307 * @param {number} replayTime | |
| 308 * @this {WebInspector.CanvasProfileView} | |
| 309 */ | |
| 310 function didReplayTraceLog(resourceState, replayTime) | |
| 311 { | |
| 312 delete this._pendingReplayTraceLogEvent; | |
| 313 this._enableWaitIcon(false); | |
| 314 | |
| 315 this._debugInfoElement.textContent = WebInspector.UIString("Replay t
ime: %s", Number.secondsToString(replayTime / 1000, true)); | |
| 316 this._onReplayContextChanged(); | |
| 317 | |
| 318 if (index !== this._selectedCallIndex()) | |
| 319 this._replayTraceLog(); | |
| 320 } | |
| 321 this._enableWaitIcon(true); | |
| 322 this._traceLogPlayer.replayTraceLog(index, didReplayTraceLog.bind(this))
; | |
| 323 }, | |
| 324 | |
| 325 /** | |
| 326 * @param {number} offset | |
| 327 */ | |
| 328 _requestTraceLog: function(offset) | |
| 329 { | |
| 330 /** | |
| 331 * @param {?CanvasAgent.TraceLog} traceLog | |
| 332 * @this {WebInspector.CanvasProfileView} | |
| 333 */ | |
| 334 function didReceiveTraceLog(traceLog) | |
| 335 { | |
| 336 this._enableWaitIcon(false); | |
| 337 if (!traceLog) | |
| 338 return; | |
| 339 var callNodes = []; | |
| 340 var calls = traceLog.calls; | |
| 341 var index = traceLog.startOffset; | |
| 342 for (var i = 0, n = calls.length; i < n; ++i) | |
| 343 callNodes.push(this._createCallNode(index++, calls[i])); | |
| 344 var contexts = traceLog.contexts; | |
| 345 for (var i = 0, n = contexts.length; i < n; ++i) { | |
| 346 var contextId = contexts[i].resourceId || ""; | |
| 347 var description = contexts[i].description || ""; | |
| 348 if (this._replayContexts[contextId]) | |
| 349 continue; | |
| 350 this._replayContexts[contextId] = true; | |
| 351 this._replayContextSelector.createOption(description, WebInspect
or.UIString("Show screenshot of this context's canvas."), contextId); | |
| 352 } | |
| 353 this._appendCallNodes(callNodes); | |
| 354 if (traceLog.alive) | |
| 355 setTimeout(this._requestTraceLog.bind(this, index), WebInspector
.CanvasProfileView.TraceLogPollingInterval); | |
| 356 else | |
| 357 this._flattenSingleFrameNode(); | |
| 358 this._profile._updateCapturingStatus(traceLog); | |
| 359 this._onReplayLastStepClick(); // Automatically replay the last step
. | |
| 360 } | |
| 361 this._enableWaitIcon(true); | |
| 362 this._traceLogPlayer.getTraceLog(offset, undefined, didReceiveTraceLog.b
ind(this)); | |
| 363 }, | |
| 364 | |
| 365 /** | |
| 366 * @return {number} | |
| 367 */ | |
| 368 _selectedCallIndex: function() | |
| 369 { | |
| 370 var node = this._logGrid.selectedNode; | |
| 371 return node ? this._peekLastRecursively(node).index : -1; | |
| 372 }, | |
| 373 | |
| 374 /** | |
| 375 * @param {!WebInspector.DataGridNode} node | |
| 376 * @return {!WebInspector.DataGridNode} | |
| 377 */ | |
| 378 _peekLastRecursively: function(node) | |
| 379 { | |
| 380 var lastChild; | |
| 381 while ((lastChild = node.children.peekLast())) | |
| 382 node = lastChild; | |
| 383 return node; | |
| 384 }, | |
| 385 | |
| 386 /** | |
| 387 * @param {!Array.<!WebInspector.DataGridNode>} callNodes | |
| 388 */ | |
| 389 _appendCallNodes: function(callNodes) | |
| 390 { | |
| 391 var rootNode = this._logGrid.rootNode(); | |
| 392 var frameNode = rootNode.children.peekLast(); | |
| 393 if (frameNode && this._peekLastRecursively(frameNode).call.isFrameEndCal
l) | |
| 394 frameNode = null; | |
| 395 for (var i = 0, n = callNodes.length; i < n; ++i) { | |
| 396 if (!frameNode) { | |
| 397 var index = rootNode.children.length; | |
| 398 var data = {}; | |
| 399 data[0] = ""; | |
| 400 data[1] = WebInspector.UIString("Frame #%d", index + 1); | |
| 401 data[2] = ""; | |
| 402 frameNode = new WebInspector.DataGridNode(data); | |
| 403 frameNode.selectable = true; | |
| 404 rootNode.appendChild(frameNode); | |
| 405 } | |
| 406 var nextFrameCallIndex = i + 1; | |
| 407 while (nextFrameCallIndex < n && !callNodes[nextFrameCallIndex - 1].
call.isFrameEndCall) | |
| 408 ++nextFrameCallIndex; | |
| 409 this._appendCallNodesToFrameNode(frameNode, callNodes, i, nextFrameC
allIndex); | |
| 410 i = nextFrameCallIndex - 1; | |
| 411 frameNode = null; | |
| 412 } | |
| 413 }, | |
| 414 | |
| 415 /** | |
| 416 * @param {!WebInspector.DataGridNode} frameNode | |
| 417 * @param {!Array.<!WebInspector.DataGridNode>} callNodes | |
| 418 * @param {number} fromIndex | |
| 419 * @param {number} toIndex not inclusive | |
| 420 */ | |
| 421 _appendCallNodesToFrameNode: function(frameNode, callNodes, fromIndex, toInd
ex) | |
| 422 { | |
| 423 var self = this; | |
| 424 function appendDrawCallGroup() | |
| 425 { | |
| 426 var index = self._drawCallGroupsCount || 0; | |
| 427 var data = {}; | |
| 428 data[0] = ""; | |
| 429 data[1] = WebInspector.UIString("Draw call group #%d", index + 1); | |
| 430 data[2] = ""; | |
| 431 var node = new WebInspector.DataGridNode(data); | |
| 432 node.selectable = true; | |
| 433 self._drawCallGroupsCount = index + 1; | |
| 434 frameNode.appendChild(node); | |
| 435 return node; | |
| 436 } | |
| 437 | |
| 438 function splitDrawCallGroup(drawCallGroup) | |
| 439 { | |
| 440 var splitIndex = 0; | |
| 441 var splitNode; | |
| 442 while ((splitNode = drawCallGroup.children[splitIndex])) { | |
| 443 if (splitNode.call.isDrawingCall) | |
| 444 break; | |
| 445 ++splitIndex; | |
| 446 } | |
| 447 var newDrawCallGroup = appendDrawCallGroup(); | |
| 448 var lastNode; | |
| 449 while ((lastNode = drawCallGroup.children[splitIndex + 1])) | |
| 450 newDrawCallGroup.appendChild(lastNode); | |
| 451 return newDrawCallGroup; | |
| 452 } | |
| 453 | |
| 454 var drawCallGroup = frameNode.children.peekLast(); | |
| 455 var groupHasDrawCall = false; | |
| 456 if (drawCallGroup) { | |
| 457 for (var i = 0, n = drawCallGroup.children.length; i < n; ++i) { | |
| 458 if (drawCallGroup.children[i].call.isDrawingCall) { | |
| 459 groupHasDrawCall = true; | |
| 460 break; | |
| 461 } | |
| 462 } | |
| 463 } else | |
| 464 drawCallGroup = appendDrawCallGroup(); | |
| 465 | |
| 466 for (var i = fromIndex; i < toIndex; ++i) { | |
| 467 var node = callNodes[i]; | |
| 468 drawCallGroup.appendChild(node); | |
| 469 if (node.call.isDrawingCall) { | |
| 470 if (groupHasDrawCall) | |
| 471 drawCallGroup = splitDrawCallGroup(drawCallGroup); | |
| 472 else | |
| 473 groupHasDrawCall = true; | |
| 474 } | |
| 475 } | |
| 476 }, | |
| 477 | |
| 478 /** | |
| 479 * @param {number} index | |
| 480 * @param {!CanvasAgent.Call} call | |
| 481 * @return {!WebInspector.DataGridNode} | |
| 482 */ | |
| 483 _createCallNode: function(index, call) | |
| 484 { | |
| 485 var callViewElement = createElement("div"); | |
| 486 | |
| 487 var data = {}; | |
| 488 data[0] = index + 1; | |
| 489 data[1] = callViewElement; | |
| 490 data[2] = ""; | |
| 491 if (call.sourceURL) { | |
| 492 // FIXME(62725): stack trace line/column numbers are one-based. | |
| 493 var lineNumber = Math.max(0, call.lineNumber - 1) || 0; | |
| 494 var columnNumber = Math.max(0, call.columnNumber - 1) || 0; | |
| 495 data[2] = this._linkifier.linkifyScriptLocation(this._profile.target
(), null, call.sourceURL, lineNumber, columnNumber); | |
| 496 } | |
| 497 | |
| 498 var node = new WebInspector.DataGridNode(data); | |
| 499 node.index = index; | |
| 500 node.selectable = true; | |
| 501 node.call = call; | |
| 502 | |
| 503 callViewElement.createChild("span", "canvas-function-name").textContent
= call.functionName || "context." + call.property; | |
| 504 var target = this._profile.target(); | |
| 505 if (!target) | |
| 506 return node; | |
| 507 | |
| 508 if (call.arguments) { | |
| 509 callViewElement.createTextChild("("); | |
| 510 for (var i = 0, n = call.arguments.length; i < n; ++i) { | |
| 511 var argument = /** @type {!CanvasAgent.CallArgument} */ (call.ar
guments[i]); | |
| 512 if (i) | |
| 513 callViewElement.createTextChild(", "); | |
| 514 var element = WebInspector.CanvasProfileDataGridHelper.createCal
lArgumentElement(target, argument); | |
| 515 element.__argumentIndex = i; | |
| 516 callViewElement.appendChild(element); | |
| 517 } | |
| 518 callViewElement.createTextChild(")"); | |
| 519 } else if (call.value) { | |
| 520 callViewElement.createTextChild(" = "); | |
| 521 callViewElement.appendChild(WebInspector.CanvasProfileDataGridHelper
.createCallArgumentElement(target, call.value)); | |
| 522 } | |
| 523 | |
| 524 if (call.result) { | |
| 525 callViewElement.createTextChild(" => "); | |
| 526 callViewElement.appendChild(WebInspector.CanvasProfileDataGridHelper
.createCallArgumentElement(target, call.result)); | |
| 527 } | |
| 528 | |
| 529 return node; | |
| 530 }, | |
| 531 | |
| 532 /** | |
| 533 * @param {!Element} element | |
| 534 * @param {!Event} event | |
| 535 * @return {!Element|!AnchorBox|undefined} | |
| 536 */ | |
| 537 _popoverAnchor: function(element, event) | |
| 538 { | |
| 539 var argumentElement = element.enclosingNodeOrSelfWithClass("canvas-call-
argument"); | |
| 540 if (!argumentElement || argumentElement.__suppressPopover) | |
| 541 return; | |
| 542 return argumentElement; | |
| 543 }, | |
| 544 | |
| 545 _resolveObjectForPopover: function(argumentElement, showCallback, objectGrou
pName) | |
| 546 { | |
| 547 /** | |
| 548 * @param {?Protocol.Error} error | |
| 549 * @param {!RuntimeAgent.RemoteObject=} result | |
| 550 * @param {!CanvasAgent.ResourceState=} resourceState | |
| 551 * @this {WebInspector.CanvasProfileView} | |
| 552 */ | |
| 553 function showObjectPopover(error, result, resourceState) | |
| 554 { | |
| 555 if (error) | |
| 556 return; | |
| 557 | |
| 558 // FIXME: handle resourceState also | |
| 559 if (!result) | |
| 560 return; | |
| 561 | |
| 562 this._popoverAnchorElement = argumentElement.cloneNode(true); | |
| 563 this._popoverAnchorElement.classList.add("canvas-popover-anchor"); | |
| 564 this._popoverAnchorElement.classList.add("source-frame-eval-expressi
on"); | |
| 565 argumentElement.parentElement.appendChild(this._popoverAnchorElement
); | |
| 566 | |
| 567 var diffLeft = this._popoverAnchorElement.boxInWindow().x - argument
Element.boxInWindow().x; | |
| 568 this._popoverAnchorElement.style.left = this._popoverAnchorElement.o
ffsetLeft - diffLeft + "px"; | |
| 569 | |
| 570 showCallback(this._profile.target().runtimeModel.createRemoteObject(
result), false, this._popoverAnchorElement); | |
| 571 } | |
| 572 | |
| 573 var evalResult = argumentElement.__evalResult; | |
| 574 if (evalResult) | |
| 575 showObjectPopover.call(this, null, evalResult); | |
| 576 else { | |
| 577 var dataGridNode = this._logGrid.dataGridNodeFromNode(argumentElemen
t); | |
| 578 if (!dataGridNode || typeof dataGridNode.index !== "number") { | |
| 579 this._popoverHelper.hidePopover(); | |
| 580 return; | |
| 581 } | |
| 582 var callIndex = dataGridNode.index; | |
| 583 var argumentIndex = argumentElement.__argumentIndex; | |
| 584 if (typeof argumentIndex !== "number") | |
| 585 argumentIndex = -1; | |
| 586 this._profile.target().canvasAgent().evaluateTraceLogCallArgument(th
is._traceLogId, callIndex, argumentIndex, objectGroupName, showObjectPopover.bin
d(this)); | |
| 587 } | |
| 588 }, | |
| 589 | |
| 590 /** | |
| 591 * @param {!WebInspector.RemoteObject} object | |
| 592 * @return {string} | |
| 593 */ | |
| 594 _hexNumbersFormatter: function(object) | |
| 595 { | |
| 596 if (object.type === "number") { | |
| 597 // Show enum values in hex with min length of 4 (e.g. 0x0012). | |
| 598 var str = "0000" + Number(object.description).toString(16).toUpperCa
se(); | |
| 599 str = str.replace(/^0+(.{4,})$/, "$1"); | |
| 600 return "0x" + str; | |
| 601 } | |
| 602 return object.description || ""; | |
| 603 }, | |
| 604 | |
| 605 _onHidePopover: function() | |
| 606 { | |
| 607 if (this._popoverAnchorElement) { | |
| 608 this._popoverAnchorElement.remove(); | |
| 609 delete this._popoverAnchorElement; | |
| 610 } | |
| 611 }, | |
| 612 | |
| 613 _flattenSingleFrameNode: function() | |
| 614 { | |
| 615 var rootNode = this._logGrid.rootNode(); | |
| 616 if (rootNode.children.length !== 1) | |
| 617 return; | |
| 618 var frameNode = rootNode.children[0]; | |
| 619 while (frameNode.children[0]) | |
| 620 rootNode.appendChild(frameNode.children[0]); | |
| 621 rootNode.removeChild(frameNode); | |
| 622 }, | |
| 623 | |
| 624 __proto__: WebInspector.VBox.prototype | |
| 625 } | |
| 626 | |
| 627 /** | |
| 628 * @constructor | |
| 629 * @implements {WebInspector.TargetManager.Observer} | |
| 630 * @extends {WebInspector.ProfileType} | |
| 631 */ | |
| 632 WebInspector.CanvasProfileType = function() | |
| 633 { | |
| 634 WebInspector.ProfileType.call(this, WebInspector.CanvasProfileType.TypeId, W
ebInspector.UIString("Capture Canvas Frame")); | |
| 635 this._recording = false; | |
| 636 this._lastProfileHeader = null; | |
| 637 | |
| 638 this._capturingModeSelector = new WebInspector.StatusBarComboBox(this._dispa
tchViewUpdatedEvent.bind(this)); | |
| 639 this._capturingModeSelector.element.title = WebInspector.UIString("Canvas ca
pture mode."); | |
| 640 this._capturingModeSelector.createOption(WebInspector.UIString("Single Frame
"), WebInspector.UIString("Capture a single canvas frame."), ""); | |
| 641 this._capturingModeSelector.createOption(WebInspector.UIString("Consecutive
Frames"), WebInspector.UIString("Capture consecutive canvas frames."), "1"); | |
| 642 | |
| 643 /** @type {!Object.<string, !Element>} */ | |
| 644 this._frameOptions = {}; | |
| 645 | |
| 646 /** @type {!Object.<string, boolean>} */ | |
| 647 this._framesWithCanvases = {}; | |
| 648 | |
| 649 this._frameSelector = new WebInspector.StatusBarComboBox(this._dispatchViewU
pdatedEvent.bind(this)); | |
| 650 this._frameSelector.element.title = WebInspector.UIString("Frame containing
the canvases to capture."); | |
| 651 this._frameSelector.element.classList.add("hidden"); | |
| 652 | |
| 653 this._target = null; | |
| 654 WebInspector.targetManager.observeTargets(this); | |
| 655 | |
| 656 this._canvasAgentEnabled = false; | |
| 657 | |
| 658 this._decorationElement = createElement("div"); | |
| 659 this._decorationElement.className = "profile-canvas-decoration"; | |
| 660 this._updateDecorationElement(); | |
| 661 } | |
| 662 | |
| 663 WebInspector.CanvasProfileType.TypeId = "CANVAS_PROFILE"; | |
| 664 | |
| 665 WebInspector.CanvasProfileType.prototype = { | |
| 666 /** | |
| 667 * @override | |
| 668 * @param {!WebInspector.Target} target | |
| 669 */ | |
| 670 targetAdded: function(target) | |
| 671 { | |
| 672 if (this._target || target !== WebInspector.targetManager.mainTarget()) | |
| 673 return; | |
| 674 this._target = target; | |
| 675 this._target.resourceTreeModel.frames().forEach(this._addFrame, this); | |
| 676 this._target.resourceTreeModel.addEventListener(WebInspector.ResourceTre
eModel.EventTypes.FrameAdded, this._frameAdded, this); | |
| 677 this._target.resourceTreeModel.addEventListener(WebInspector.ResourceTre
eModel.EventTypes.FrameDetached, this._frameRemoved, this); | |
| 678 new WebInspector.CanvasDispatcher(this._target, this); | |
| 679 }, | |
| 680 | |
| 681 /** | |
| 682 * @override | |
| 683 * @param {!WebInspector.Target} target | |
| 684 */ | |
| 685 targetRemoved: function(target) | |
| 686 { | |
| 687 if (this._target !== target) | |
| 688 return; | |
| 689 this._target.resourceTreeModel.removeEventListener(WebInspector.Resource
TreeModel.EventTypes.FrameAdded, this._frameAdded, this); | |
| 690 this._target.resourceTreeModel.removeEventListener(WebInspector.Resource
TreeModel.EventTypes.FrameDetached, this._frameRemoved, this); | |
| 691 this._target = null; | |
| 692 }, | |
| 693 | |
| 694 /** | |
| 695 * @override | |
| 696 * @return {!Array.<!WebInspector.StatusBarItem>} | |
| 697 */ | |
| 698 statusBarItems: function() | |
| 699 { | |
| 700 return [this._capturingModeSelector, this._frameSelector]; | |
| 701 }, | |
| 702 | |
| 703 get buttonTooltip() | |
| 704 { | |
| 705 if (this._isSingleFrameMode()) | |
| 706 return WebInspector.UIString("Capture next canvas frame."); | |
| 707 else | |
| 708 return this._recording ? WebInspector.UIString("Stop capturing canva
s frames.") : WebInspector.UIString("Start capturing canvas frames."); | |
| 709 }, | |
| 710 | |
| 711 /** | |
| 712 * @override | |
| 713 * @return {boolean} | |
| 714 */ | |
| 715 buttonClicked: function() | |
| 716 { | |
| 717 if (!this._canvasAgentEnabled) | |
| 718 return false; | |
| 719 if (this._recording) { | |
| 720 this._recording = false; | |
| 721 this._stopFrameCapturing(); | |
| 722 } else if (this._isSingleFrameMode()) { | |
| 723 this._recording = false; | |
| 724 this._runSingleFrameCapturing(); | |
| 725 } else { | |
| 726 this._recording = true; | |
| 727 this._startFrameCapturing(); | |
| 728 } | |
| 729 return this._recording; | |
| 730 }, | |
| 731 | |
| 732 _runSingleFrameCapturing: function() | |
| 733 { | |
| 734 var frameId = this._selectedFrameId(); | |
| 735 WebInspector.targetManager.suspendAllTargets(); | |
| 736 if (this._target) | |
| 737 this._target.canvasAgent().captureFrame(frameId, this._didStartCaptu
ringFrame.bind(this, frameId)); | |
| 738 WebInspector.targetManager.resumeAllTargets(); | |
| 739 }, | |
| 740 | |
| 741 _startFrameCapturing: function() | |
| 742 { | |
| 743 var frameId = this._selectedFrameId(); | |
| 744 WebInspector.targetManager.suspendAllTargets(); | |
| 745 this._target.canvasAgent().startCapturing(frameId, this._didStartCapturi
ngFrame.bind(this, frameId)); | |
| 746 }, | |
| 747 | |
| 748 _stopFrameCapturing: function() | |
| 749 { | |
| 750 if (!this._lastProfileHeader) { | |
| 751 WebInspector.targetManager.resumeAllTargets(); | |
| 752 return; | |
| 753 } | |
| 754 var profileHeader = this._lastProfileHeader; | |
| 755 var traceLogId = profileHeader.traceLogId(); | |
| 756 this._lastProfileHeader = null; | |
| 757 function didStopCapturing() | |
| 758 { | |
| 759 profileHeader._updateCapturingStatus(); | |
| 760 } | |
| 761 if (this._target) | |
| 762 this._target.canvasAgent().stopCapturing(traceLogId, didStopCapturin
g); | |
| 763 WebInspector.targetManager.resumeAllTargets(); | |
| 764 }, | |
| 765 | |
| 766 /** | |
| 767 * @param {string|undefined} frameId | |
| 768 * @param {?Protocol.Error} error | |
| 769 * @param {!CanvasAgent.TraceLogId} traceLogId | |
| 770 */ | |
| 771 _didStartCapturingFrame: function(frameId, error, traceLogId) | |
| 772 { | |
| 773 if (error || this._lastProfileHeader && this._lastProfileHeader.traceLog
Id() === traceLogId || !this._target) | |
| 774 return; | |
| 775 var profileHeader = new WebInspector.CanvasProfileHeader(this._target, t
his, traceLogId, frameId); | |
| 776 this._lastProfileHeader = profileHeader; | |
| 777 this.addProfile(profileHeader); | |
| 778 profileHeader._updateCapturingStatus(); | |
| 779 }, | |
| 780 | |
| 781 get treeItemTitle() | |
| 782 { | |
| 783 return WebInspector.UIString("CANVAS PROFILE"); | |
| 784 }, | |
| 785 | |
| 786 get description() | |
| 787 { | |
| 788 return WebInspector.UIString("Canvas calls instrumentation"); | |
| 789 }, | |
| 790 | |
| 791 /** | |
| 792 * @override | |
| 793 * @return {!Element} | |
| 794 */ | |
| 795 decorationElement: function() | |
| 796 { | |
| 797 return this._decorationElement; | |
| 798 }, | |
| 799 | |
| 800 /** | |
| 801 * @override | |
| 802 * @param {!WebInspector.ProfileHeader} profile | |
| 803 */ | |
| 804 removeProfile: function(profile) | |
| 805 { | |
| 806 WebInspector.ProfileType.prototype.removeProfile.call(this, profile); | |
| 807 if (this._recording && profile === this._lastProfileHeader) | |
| 808 this._recording = false; | |
| 809 }, | |
| 810 | |
| 811 /** | |
| 812 * @param {boolean=} forcePageReload | |
| 813 */ | |
| 814 _updateDecorationElement: function(forcePageReload) | |
| 815 { | |
| 816 this._decorationElement.removeChildren(); | |
| 817 this._decorationElement.createChild("label", "", "dt-icon-label").type =
"warning-icon"; | |
| 818 this._decorationElement.createTextChild(this._canvasAgentEnabled ? WebIn
spector.UIString("Canvas Profiler is enabled.") : WebInspector.UIString("Canvas
Profiler is disabled.")); | |
| 819 var button = createTextButton(this._canvasAgentEnabled ? WebInspector.UI
String("Disable") : WebInspector.UIString("Enable"), this._onProfilerEnableButto
nClick.bind(this, !this._canvasAgentEnabled)); | |
| 820 this._decorationElement.appendChild(button); | |
| 821 | |
| 822 var target = this._target; | |
| 823 if (!target) | |
| 824 return; | |
| 825 | |
| 826 /** | |
| 827 * @param {?Protocol.Error} error | |
| 828 * @param {boolean} result | |
| 829 */ | |
| 830 function hasUninstrumentedCanvasesCallback(error, result) | |
| 831 { | |
| 832 if (error || result) | |
| 833 target.resourceTreeModel.reloadPage(); | |
| 834 } | |
| 835 | |
| 836 if (forcePageReload) { | |
| 837 if (this._canvasAgentEnabled) { | |
| 838 target.canvasAgent().hasUninstrumentedCanvases(hasUninstrumented
CanvasesCallback); | |
| 839 } else { | |
| 840 for (var frameId in this._framesWithCanvases) { | |
| 841 if (this._framesWithCanvases.hasOwnProperty(frameId)) { | |
| 842 target.resourceTreeModel.reloadPage(); | |
| 843 break; | |
| 844 } | |
| 845 } | |
| 846 } | |
| 847 } | |
| 848 }, | |
| 849 | |
| 850 /** | |
| 851 * @param {boolean} enable | |
| 852 */ | |
| 853 _onProfilerEnableButtonClick: function(enable) | |
| 854 { | |
| 855 if (this._canvasAgentEnabled === enable || !this._target) | |
| 856 return; | |
| 857 | |
| 858 /** | |
| 859 * @param {?Protocol.Error} error | |
| 860 * @this {WebInspector.CanvasProfileType} | |
| 861 */ | |
| 862 function callback(error) | |
| 863 { | |
| 864 if (error) | |
| 865 return; | |
| 866 this._canvasAgentEnabled = enable; | |
| 867 this._updateDecorationElement(true); | |
| 868 this._dispatchViewUpdatedEvent(); | |
| 869 } | |
| 870 if (enable) | |
| 871 this._target.canvasAgent().enable(callback.bind(this)); | |
| 872 else | |
| 873 this._target.canvasAgent().disable(callback.bind(this)); | |
| 874 }, | |
| 875 | |
| 876 /** | |
| 877 * @return {boolean} | |
| 878 */ | |
| 879 _isSingleFrameMode: function() | |
| 880 { | |
| 881 return !this._capturingModeSelector.selectedOption().value; | |
| 882 }, | |
| 883 | |
| 884 /** | |
| 885 * @param {!WebInspector.Event} event | |
| 886 */ | |
| 887 _frameAdded: function(event) | |
| 888 { | |
| 889 var frame = /** @type {!WebInspector.ResourceTreeFrame} */ (event.data); | |
| 890 this._addFrame(frame); | |
| 891 }, | |
| 892 | |
| 893 /** | |
| 894 * @param {!WebInspector.ResourceTreeFrame} frame | |
| 895 */ | |
| 896 _addFrame: function(frame) | |
| 897 { | |
| 898 var frameId = frame.id; | |
| 899 var option = createElement("option"); | |
| 900 option.text = frame.displayName(); | |
| 901 option.title = frame.url; | |
| 902 option.value = frameId; | |
| 903 | |
| 904 this._frameOptions[frameId] = option; | |
| 905 | |
| 906 if (this._framesWithCanvases[frameId]) { | |
| 907 this._frameSelector.addOption(option); | |
| 908 this._dispatchViewUpdatedEvent(); | |
| 909 } | |
| 910 }, | |
| 911 | |
| 912 /** | |
| 913 * @param {!WebInspector.Event} event | |
| 914 */ | |
| 915 _frameRemoved: function(event) | |
| 916 { | |
| 917 var frame = /** @type {!WebInspector.ResourceTreeFrame} */ (event.data); | |
| 918 var frameId = frame.id; | |
| 919 var option = this._frameOptions[frameId]; | |
| 920 if (option && this._framesWithCanvases[frameId]) { | |
| 921 this._frameSelector.removeOption(option); | |
| 922 this._dispatchViewUpdatedEvent(); | |
| 923 } | |
| 924 delete this._frameOptions[frameId]; | |
| 925 delete this._framesWithCanvases[frameId]; | |
| 926 }, | |
| 927 | |
| 928 /** | |
| 929 * @param {string} frameId | |
| 930 */ | |
| 931 _contextCreated: function(frameId) | |
| 932 { | |
| 933 if (this._framesWithCanvases[frameId]) | |
| 934 return; | |
| 935 this._framesWithCanvases[frameId] = true; | |
| 936 var option = this._frameOptions[frameId]; | |
| 937 if (option) { | |
| 938 this._frameSelector.addOption(option); | |
| 939 this._dispatchViewUpdatedEvent(); | |
| 940 } | |
| 941 }, | |
| 942 | |
| 943 /** | |
| 944 * @param {!PageAgent.FrameId=} frameId | |
| 945 * @param {!CanvasAgent.TraceLogId=} traceLogId | |
| 946 */ | |
| 947 _traceLogsRemoved: function(frameId, traceLogId) | |
| 948 { | |
| 949 var sidebarElementsToDelete = []; | |
| 950 var sidebarElements = /** @type {!Array.<!WebInspector.ProfileSidebarTre
eElement>} */ (this.treeElement ? this.treeElement.children() : []); | |
| 951 for (var i = 0, n = sidebarElements.length; i < n; ++i) { | |
| 952 var header = /** @type {!WebInspector.CanvasProfileHeader} */ (sideb
arElements[i].profile); | |
| 953 if (!header) | |
| 954 continue; | |
| 955 if (frameId && frameId !== header.frameId()) | |
| 956 continue; | |
| 957 if (traceLogId && traceLogId !== header.traceLogId()) | |
| 958 continue; | |
| 959 sidebarElementsToDelete.push(sidebarElements[i]); | |
| 960 } | |
| 961 for (var i = 0, n = sidebarElementsToDelete.length; i < n; ++i) | |
| 962 sidebarElementsToDelete[i].ondelete(); | |
| 963 }, | |
| 964 | |
| 965 /** | |
| 966 * @return {string|undefined} | |
| 967 */ | |
| 968 _selectedFrameId: function() | |
| 969 { | |
| 970 var option = this._frameSelector.selectedOption(); | |
| 971 return option ? option.value : undefined; | |
| 972 }, | |
| 973 | |
| 974 _dispatchViewUpdatedEvent: function() | |
| 975 { | |
| 976 this._frameSelector.element.classList.toggle("hidden", this._frameSelect
or.size() <= 1); | |
| 977 this.dispatchEventToListeners(WebInspector.ProfileType.Events.ViewUpdate
d); | |
| 978 }, | |
| 979 | |
| 980 /** | |
| 981 * @override | |
| 982 * @return {boolean} | |
| 983 */ | |
| 984 isInstantProfile: function() | |
| 985 { | |
| 986 return this._isSingleFrameMode(); | |
| 987 }, | |
| 988 | |
| 989 /** | |
| 990 * @override | |
| 991 * @return {boolean} | |
| 992 */ | |
| 993 isEnabled: function() | |
| 994 { | |
| 995 return this._canvasAgentEnabled; | |
| 996 }, | |
| 997 | |
| 998 __proto__: WebInspector.ProfileType.prototype | |
| 999 } | |
| 1000 | |
| 1001 /** | |
| 1002 * @constructor | |
| 1003 * @implements {CanvasAgent.Dispatcher} | |
| 1004 * @param {!WebInspector.Target} target | |
| 1005 * @param {!WebInspector.CanvasProfileType} profileType | |
| 1006 */ | |
| 1007 WebInspector.CanvasDispatcher = function(target, profileType) | |
| 1008 { | |
| 1009 this._profileType = profileType; | |
| 1010 target.registerCanvasDispatcher(this); | |
| 1011 } | |
| 1012 | |
| 1013 WebInspector.CanvasDispatcher.prototype = { | |
| 1014 /** | |
| 1015 * @override | |
| 1016 * @param {string} frameId | |
| 1017 */ | |
| 1018 contextCreated: function(frameId) | |
| 1019 { | |
| 1020 this._profileType._contextCreated(frameId); | |
| 1021 }, | |
| 1022 | |
| 1023 /** | |
| 1024 * @override | |
| 1025 * @param {!PageAgent.FrameId=} frameId | |
| 1026 * @param {!CanvasAgent.TraceLogId=} traceLogId | |
| 1027 */ | |
| 1028 traceLogsRemoved: function(frameId, traceLogId) | |
| 1029 { | |
| 1030 this._profileType._traceLogsRemoved(frameId, traceLogId); | |
| 1031 } | |
| 1032 } | |
| 1033 | |
| 1034 /** | |
| 1035 * @constructor | |
| 1036 * @extends {WebInspector.ProfileHeader} | |
| 1037 * @param {!WebInspector.Target} target | |
| 1038 * @param {!WebInspector.CanvasProfileType} type | |
| 1039 * @param {!CanvasAgent.TraceLogId=} traceLogId | |
| 1040 * @param {!PageAgent.FrameId=} frameId | |
| 1041 */ | |
| 1042 WebInspector.CanvasProfileHeader = function(target, type, traceLogId, frameId) | |
| 1043 { | |
| 1044 WebInspector.ProfileHeader.call(this, target, type, WebInspector.UIString("T
race Log %d", type.nextProfileUid())); | |
| 1045 /** @type {!CanvasAgent.TraceLogId} */ | |
| 1046 this._traceLogId = traceLogId || ""; | |
| 1047 this._frameId = frameId; | |
| 1048 this._alive = true; | |
| 1049 this._traceLogSize = 0; | |
| 1050 this._traceLogPlayer = traceLogId ? new WebInspector.CanvasTraceLogPlayerPro
xy(target, traceLogId) : null; | |
| 1051 } | |
| 1052 | |
| 1053 WebInspector.CanvasProfileHeader.prototype = { | |
| 1054 /** | |
| 1055 * @return {!CanvasAgent.TraceLogId} | |
| 1056 */ | |
| 1057 traceLogId: function() | |
| 1058 { | |
| 1059 return this._traceLogId; | |
| 1060 }, | |
| 1061 | |
| 1062 /** | |
| 1063 * @return {?WebInspector.CanvasTraceLogPlayerProxy} | |
| 1064 */ | |
| 1065 traceLogPlayer: function() | |
| 1066 { | |
| 1067 return this._traceLogPlayer; | |
| 1068 }, | |
| 1069 | |
| 1070 /** | |
| 1071 * @return {!PageAgent.FrameId|undefined} | |
| 1072 */ | |
| 1073 frameId: function() | |
| 1074 { | |
| 1075 return this._frameId; | |
| 1076 }, | |
| 1077 | |
| 1078 /** | |
| 1079 * @override | |
| 1080 * @param {!WebInspector.ProfileType.DataDisplayDelegate} panel | |
| 1081 * @return {!WebInspector.ProfileSidebarTreeElement} | |
| 1082 */ | |
| 1083 createSidebarTreeElement: function(panel) | |
| 1084 { | |
| 1085 return new WebInspector.ProfileSidebarTreeElement(panel, this, "profile-
sidebar-tree-item"); | |
| 1086 }, | |
| 1087 | |
| 1088 /** | |
| 1089 * @override | |
| 1090 * @return {!WebInspector.CanvasProfileView} | |
| 1091 */ | |
| 1092 createView: function() | |
| 1093 { | |
| 1094 return new WebInspector.CanvasProfileView(this); | |
| 1095 }, | |
| 1096 | |
| 1097 /** | |
| 1098 * @override | |
| 1099 */ | |
| 1100 dispose: function() | |
| 1101 { | |
| 1102 if (this._traceLogPlayer) | |
| 1103 this._traceLogPlayer.dispose(); | |
| 1104 clearTimeout(this._requestStatusTimer); | |
| 1105 this._alive = false; | |
| 1106 }, | |
| 1107 | |
| 1108 /** | |
| 1109 * @param {!CanvasAgent.TraceLog=} traceLog | |
| 1110 */ | |
| 1111 _updateCapturingStatus: function(traceLog) | |
| 1112 { | |
| 1113 if (!this._traceLogId) | |
| 1114 return; | |
| 1115 | |
| 1116 if (traceLog) { | |
| 1117 this._alive = traceLog.alive; | |
| 1118 this._traceLogSize = traceLog.totalAvailableCalls; | |
| 1119 } | |
| 1120 | |
| 1121 var subtitle = this._alive ? WebInspector.UIString("Capturing\u2026 %d c
alls", this._traceLogSize) : WebInspector.UIString("Captured %d calls", this._tr
aceLogSize); | |
| 1122 this.updateStatus(subtitle, this._alive); | |
| 1123 | |
| 1124 if (this._alive) { | |
| 1125 clearTimeout(this._requestStatusTimer); | |
| 1126 this._requestStatusTimer = setTimeout(this._requestCapturingStatus.b
ind(this), WebInspector.CanvasProfileView.TraceLogPollingInterval); | |
| 1127 } | |
| 1128 }, | |
| 1129 | |
| 1130 _requestCapturingStatus: function() | |
| 1131 { | |
| 1132 /** | |
| 1133 * @param {?CanvasAgent.TraceLog} traceLog | |
| 1134 * @this {WebInspector.CanvasProfileHeader} | |
| 1135 */ | |
| 1136 function didReceiveTraceLog(traceLog) | |
| 1137 { | |
| 1138 if (!traceLog) | |
| 1139 return; | |
| 1140 this._alive = traceLog.alive; | |
| 1141 this._traceLogSize = traceLog.totalAvailableCalls; | |
| 1142 this._updateCapturingStatus(); | |
| 1143 } | |
| 1144 this._traceLogPlayer.getTraceLog(0, 0, didReceiveTraceLog.bind(this)); | |
| 1145 }, | |
| 1146 | |
| 1147 __proto__: WebInspector.ProfileHeader.prototype | |
| 1148 } | |
| 1149 | |
| 1150 WebInspector.CanvasProfileDataGridHelper = { | |
| 1151 /** | |
| 1152 * @param {!WebInspector.Target} target | |
| 1153 * @param {!CanvasAgent.CallArgument} callArgument | |
| 1154 * @return {!Element} | |
| 1155 */ | |
| 1156 createCallArgumentElement: function(target, callArgument) | |
| 1157 { | |
| 1158 if (callArgument.enumName) | |
| 1159 return WebInspector.CanvasProfileDataGridHelper.createEnumValueEleme
nt(target, callArgument.enumName, +callArgument.description); | |
| 1160 var element = createElement("span"); | |
| 1161 element.className = "canvas-call-argument"; | |
| 1162 var description = callArgument.description; | |
| 1163 if (callArgument.type === "string") { | |
| 1164 const maxStringLength = 150; | |
| 1165 element.createTextChild("\""); | |
| 1166 element.createChild("span", "canvas-formatted-string").textContent =
description.trimMiddle(maxStringLength); | |
| 1167 element.createTextChild("\""); | |
| 1168 element.__suppressPopover = (description.length <= maxStringLength &
& !/[\r\n]/.test(description)); | |
| 1169 if (!element.__suppressPopover) | |
| 1170 element.__evalResult = target.runtimeModel.createRemoteObjectFro
mPrimitiveValue(description); | |
| 1171 } else { | |
| 1172 var type = callArgument.subtype || callArgument.type; | |
| 1173 if (type) { | |
| 1174 element.classList.add("canvas-formatted-" + type); | |
| 1175 if (["null", "undefined", "boolean", "number"].indexOf(type) >=
0) | |
| 1176 element.__suppressPopover = true; | |
| 1177 } | |
| 1178 element.textContent = description; | |
| 1179 if (callArgument.remoteObject) | |
| 1180 element.__evalResult = target.runtimeModel.createRemoteObject(ca
llArgument.remoteObject); | |
| 1181 } | |
| 1182 if (callArgument.resourceId) { | |
| 1183 element.classList.add("canvas-formatted-resource"); | |
| 1184 element.__resourceId = callArgument.resourceId; | |
| 1185 } | |
| 1186 return element; | |
| 1187 }, | |
| 1188 | |
| 1189 /** | |
| 1190 * @param {!WebInspector.Target} target | |
| 1191 * @param {string} enumName | |
| 1192 * @param {number} enumValue | |
| 1193 * @return {!Element} | |
| 1194 */ | |
| 1195 createEnumValueElement: function(target, enumName, enumValue) | |
| 1196 { | |
| 1197 var element = createElement("span"); | |
| 1198 element.className = "canvas-call-argument canvas-formatted-number"; | |
| 1199 element.textContent = enumName; | |
| 1200 element.__evalResult = target.runtimeModel.createRemoteObjectFromPrimiti
veValue(enumValue); | |
| 1201 return element; | |
| 1202 } | |
| 1203 } | |
| 1204 | |
| 1205 /** | |
| 1206 * @extends {WebInspector.SDKObject} | |
| 1207 * @constructor | |
| 1208 * @param {!WebInspector.Target} target | |
| 1209 * @param {!CanvasAgent.TraceLogId} traceLogId | |
| 1210 */ | |
| 1211 WebInspector.CanvasTraceLogPlayerProxy = function(target, traceLogId) | |
| 1212 { | |
| 1213 WebInspector.SDKObject.call(this, target); | |
| 1214 this._traceLogId = traceLogId; | |
| 1215 /** @type {!Object.<string, !CanvasAgent.ResourceState>} */ | |
| 1216 this._currentResourceStates = {}; | |
| 1217 /** @type {?CanvasAgent.ResourceId} */ | |
| 1218 this._defaultResourceId = null; | |
| 1219 } | |
| 1220 | |
| 1221 /** @enum {string} */ | |
| 1222 WebInspector.CanvasTraceLogPlayerProxy.Events = { | |
| 1223 CanvasTraceLogReceived: "CanvasTraceLogReceived", | |
| 1224 CanvasReplayStateChanged: "CanvasReplayStateChanged", | |
| 1225 CanvasResourceStateReceived: "CanvasResourceStateReceived", | |
| 1226 } | |
| 1227 | |
| 1228 WebInspector.CanvasTraceLogPlayerProxy.prototype = { | |
| 1229 /** | |
| 1230 * @param {number|undefined} startOffset | |
| 1231 * @param {number|undefined} maxLength | |
| 1232 * @param {function(?CanvasAgent.TraceLog):void} userCallback | |
| 1233 */ | |
| 1234 getTraceLog: function(startOffset, maxLength, userCallback) | |
| 1235 { | |
| 1236 /** | |
| 1237 * @param {?Protocol.Error} error | |
| 1238 * @param {!CanvasAgent.TraceLog} traceLog | |
| 1239 * @this {WebInspector.CanvasTraceLogPlayerProxy} | |
| 1240 */ | |
| 1241 function callback(error, traceLog) | |
| 1242 { | |
| 1243 if (error || !traceLog) { | |
| 1244 userCallback(null); | |
| 1245 return; | |
| 1246 } | |
| 1247 userCallback(traceLog); | |
| 1248 this.dispatchEventToListeners(WebInspector.CanvasTraceLogPlayerProxy
.Events.CanvasTraceLogReceived, traceLog); | |
| 1249 } | |
| 1250 this.target().canvasAgent().getTraceLog(this._traceLogId, startOffset, m
axLength, callback.bind(this)); | |
| 1251 }, | |
| 1252 | |
| 1253 dispose: function() | |
| 1254 { | |
| 1255 this._currentResourceStates = {}; | |
| 1256 CanvasAgent.dropTraceLog(this._traceLogId); | |
| 1257 this.dispatchEventToListeners(WebInspector.CanvasTraceLogPlayerProxy.Eve
nts.CanvasReplayStateChanged); | |
| 1258 }, | |
| 1259 | |
| 1260 /** | |
| 1261 * @param {?CanvasAgent.ResourceId} resourceId | |
| 1262 * @param {function(?CanvasAgent.ResourceState):void} userCallback | |
| 1263 */ | |
| 1264 getResourceState: function(resourceId, userCallback) | |
| 1265 { | |
| 1266 resourceId = resourceId || this._defaultResourceId; | |
| 1267 if (!resourceId) { | |
| 1268 userCallback(null); // Has not been replayed yet. | |
| 1269 return; | |
| 1270 } | |
| 1271 var effectiveResourceId = /** @type {!CanvasAgent.ResourceId} */ (resour
ceId); | |
| 1272 if (this._currentResourceStates[effectiveResourceId]) { | |
| 1273 userCallback(this._currentResourceStates[effectiveResourceId]); | |
| 1274 return; | |
| 1275 } | |
| 1276 | |
| 1277 /** | |
| 1278 * @param {?Protocol.Error} error | |
| 1279 * @param {!CanvasAgent.ResourceState} resourceState | |
| 1280 * @this {WebInspector.CanvasTraceLogPlayerProxy} | |
| 1281 */ | |
| 1282 function callback(error, resourceState) | |
| 1283 { | |
| 1284 if (error || !resourceState) { | |
| 1285 userCallback(null); | |
| 1286 return; | |
| 1287 } | |
| 1288 this._currentResourceStates[effectiveResourceId] = resourceState; | |
| 1289 userCallback(resourceState); | |
| 1290 this.dispatchEventToListeners(WebInspector.CanvasTraceLogPlayerProxy
.Events.CanvasResourceStateReceived, resourceState); | |
| 1291 } | |
| 1292 this.target().canvasAgent().getResourceState(this._traceLogId, effective
ResourceId, callback.bind(this)); | |
| 1293 }, | |
| 1294 | |
| 1295 /** | |
| 1296 * @param {number} index | |
| 1297 * @param {function(?CanvasAgent.ResourceState, number):void} userCallback | |
| 1298 */ | |
| 1299 replayTraceLog: function(index, userCallback) | |
| 1300 { | |
| 1301 /** | |
| 1302 * @param {?Protocol.Error} error | |
| 1303 * @param {!CanvasAgent.ResourceState} resourceState | |
| 1304 * @param {number} replayTime | |
| 1305 * @this {WebInspector.CanvasTraceLogPlayerProxy} | |
| 1306 */ | |
| 1307 function callback(error, resourceState, replayTime) | |
| 1308 { | |
| 1309 this._currentResourceStates = {}; | |
| 1310 if (error) { | |
| 1311 userCallback(null, replayTime); | |
| 1312 } else { | |
| 1313 this._defaultResourceId = resourceState.id; | |
| 1314 this._currentResourceStates[resourceState.id] = resourceState; | |
| 1315 userCallback(resourceState, replayTime); | |
| 1316 } | |
| 1317 this.dispatchEventToListeners(WebInspector.CanvasTraceLogPlayerProxy
.Events.CanvasReplayStateChanged); | |
| 1318 if (!error) | |
| 1319 this.dispatchEventToListeners(WebInspector.CanvasTraceLogPlayerP
roxy.Events.CanvasResourceStateReceived, resourceState); | |
| 1320 } | |
| 1321 this.target().canvasAgent().replayTraceLog(this._traceLogId, index, call
back.bind(this)); | |
| 1322 }, | |
| 1323 | |
| 1324 clearResourceStates: function() | |
| 1325 { | |
| 1326 this._currentResourceStates = {}; | |
| 1327 this.dispatchEventToListeners(WebInspector.CanvasTraceLogPlayerProxy.Eve
nts.CanvasReplayStateChanged); | |
| 1328 }, | |
| 1329 | |
| 1330 __proto__: WebInspector.SDKObject.prototype | |
| 1331 } | |
| OLD | NEW |