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 |