CanvasProfileView.js 45 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298
  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. * @constructor
  32. * @extends {WebInspector.View}
  33. * @param {!WebInspector.CanvasProfileHeader} profile
  34. */
  35. WebInspector.CanvasProfileView = function(profile)
  36. {
  37. WebInspector.View.call(this);
  38. this.registerRequiredCSS("canvasProfiler.css");
  39. this.element.addStyleClass("canvas-profile-view");
  40. this._profile = profile;
  41. this._traceLogId = profile.traceLogId();
  42. this._traceLogPlayer = profile.traceLogPlayer();
  43. this._linkifier = new WebInspector.Linkifier();
  44. const defaultReplayLogWidthPercent = 0.34;
  45. this._replayInfoSplitView = new WebInspector.SplitView(true, "canvasProfileViewReplaySplitLocation", defaultReplayLogWidthPercent);
  46. this._replayInfoSplitView.setMainElementConstraints(defaultReplayLogWidthPercent, defaultReplayLogWidthPercent);
  47. this._replayInfoSplitView.show(this.element);
  48. this._imageSplitView = new WebInspector.SplitView(false, "canvasProfileViewSplitLocation", 300);
  49. this._imageSplitView.show(this._replayInfoSplitView.firstElement());
  50. var replayImageContainer = this._imageSplitView.firstElement();
  51. replayImageContainer.id = "canvas-replay-image-container";
  52. this._replayImageElement = replayImageContainer.createChild("image", "canvas-replay-image");
  53. this._debugInfoElement = replayImageContainer.createChild("div", "canvas-debug-info hidden");
  54. this._spinnerIcon = replayImageContainer.createChild("img", "canvas-spinner-icon hidden");
  55. var replayLogContainer = this._imageSplitView.secondElement();
  56. var controlsContainer = replayLogContainer.createChild("div", "status-bar");
  57. var logGridContainer = replayLogContainer.createChild("div", "canvas-replay-log");
  58. this._createControlButton(controlsContainer, "canvas-replay-first-step", WebInspector.UIString("First call."), this._onReplayFirstStepClick.bind(this));
  59. this._createControlButton(controlsContainer, "canvas-replay-prev-step", WebInspector.UIString("Previous call."), this._onReplayStepClick.bind(this, false));
  60. this._createControlButton(controlsContainer, "canvas-replay-next-step", WebInspector.UIString("Next call."), this._onReplayStepClick.bind(this, true));
  61. this._createControlButton(controlsContainer, "canvas-replay-prev-draw", WebInspector.UIString("Previous drawing call."), this._onReplayDrawingCallClick.bind(this, false));
  62. this._createControlButton(controlsContainer, "canvas-replay-next-draw", WebInspector.UIString("Next drawing call."), this._onReplayDrawingCallClick.bind(this, true));
  63. this._createControlButton(controlsContainer, "canvas-replay-last-step", WebInspector.UIString("Last call."), this._onReplayLastStepClick.bind(this));
  64. this._replayContextSelector = new WebInspector.StatusBarComboBox(this._onReplayContextChanged.bind(this));
  65. this._replayContextSelector.createOption(WebInspector.UIString("<screenshot auto>"), WebInspector.UIString("Show screenshot of the last replayed resource."), "");
  66. controlsContainer.appendChild(this._replayContextSelector.element);
  67. this._installReplayInfoSidebarWidgets(controlsContainer);
  68. this._replayStateView = new WebInspector.CanvasReplayStateView(this._traceLogPlayer);
  69. this._replayStateView.show(this._replayInfoSplitView.secondElement());
  70. /** @type {!Object.<string, boolean>} */
  71. this._replayContexts = {};
  72. var columns = [
  73. {title: "#", sortable: false, width: "5%"},
  74. {title: WebInspector.UIString("Call"), sortable: false, width: "75%", disclosure: true},
  75. {title: WebInspector.UIString("Location"), sortable: false, width: "20%"}
  76. ];
  77. this._logGrid = new WebInspector.DataGrid(columns);
  78. this._logGrid.element.addStyleClass("fill");
  79. this._logGrid.show(logGridContainer);
  80. this._logGrid.addEventListener(WebInspector.DataGrid.Events.SelectedNode, this._replayTraceLog, this);
  81. this.element.addEventListener("mousedown", this._onMouseClick.bind(this), true);
  82. this._popoverHelper = new WebInspector.ObjectPopoverHelper(this.element, this._popoverAnchor.bind(this), this._resolveObjectForPopover.bind(this), this._onHidePopover.bind(this), true);
  83. this._popoverHelper.setRemoteObjectFormatter(this._hexNumbersFormatter.bind(this));
  84. this._requestTraceLog(0);
  85. }
  86. /**
  87. * @const
  88. * @type {number}
  89. */
  90. WebInspector.CanvasProfileView.TraceLogPollingInterval = 500;
  91. WebInspector.CanvasProfileView.prototype = {
  92. dispose: function()
  93. {
  94. this._linkifier.reset();
  95. },
  96. get statusBarItems()
  97. {
  98. return [];
  99. },
  100. get profile()
  101. {
  102. return this._profile;
  103. },
  104. /**
  105. * @override
  106. * @return {Array.<Element>}
  107. */
  108. elementsToRestoreScrollPositionsFor: function()
  109. {
  110. return [this._logGrid.scrollContainer];
  111. },
  112. /**
  113. * @param {!Element} controlsContainer
  114. */
  115. _installReplayInfoSidebarWidgets: function(controlsContainer)
  116. {
  117. this._replayInfoResizeWidgetElement = controlsContainer.createChild("div", "resizer-widget");
  118. this._replayInfoSplitView.installResizer(this._replayInfoResizeWidgetElement);
  119. this._toggleReplayStateSidebarButton = new WebInspector.StatusBarButton("", "right-sidebar-show-hide-button canvas-sidebar-show-hide-button", 3);
  120. this._toggleReplayStateSidebarButton.addEventListener("click", clickHandler, this);
  121. controlsContainer.appendChild(this._toggleReplayStateSidebarButton.element);
  122. this._enableReplayInfoSidebar(false);
  123. function clickHandler()
  124. {
  125. this._enableReplayInfoSidebar(this._toggleReplayStateSidebarButton.state === "left");
  126. }
  127. },
  128. /**
  129. * @param {boolean} show
  130. */
  131. _enableReplayInfoSidebar: function(show)
  132. {
  133. if (show) {
  134. this._toggleReplayStateSidebarButton.state = "right";
  135. this._toggleReplayStateSidebarButton.title = WebInspector.UIString("Hide sidebar.");
  136. this._replayInfoSplitView.showBoth();
  137. } else {
  138. this._toggleReplayStateSidebarButton.state = "left";
  139. this._toggleReplayStateSidebarButton.title = WebInspector.UIString("Show sidebar.");
  140. this._replayInfoSplitView.showOnlyFirst();
  141. }
  142. this._replayInfoResizeWidgetElement.enableStyleClass("hidden", !show);
  143. },
  144. /**
  145. * @param {Event} event
  146. */
  147. _onMouseClick: function(event)
  148. {
  149. var resourceLinkElement = event.target.enclosingNodeOrSelfWithClass("canvas-formatted-resource");
  150. if (resourceLinkElement) {
  151. this._enableReplayInfoSidebar(true);
  152. this._replayStateView.selectResource(resourceLinkElement.__resourceId);
  153. event.consume(true);
  154. return;
  155. }
  156. if (event.target.enclosingNodeOrSelfWithClass("webkit-html-resource-link"))
  157. event.consume(false);
  158. },
  159. /**
  160. * @param {Element} parent
  161. * @param {string} className
  162. * @param {string} title
  163. * @param {function(this:WebInspector.CanvasProfileView)} clickCallback
  164. */
  165. _createControlButton: function(parent, className, title, clickCallback)
  166. {
  167. var button = new WebInspector.StatusBarButton(title, className + " canvas-replay-button");
  168. parent.appendChild(button.element);
  169. button.makeLongClickEnabled();
  170. button.addEventListener("click", clickCallback, this);
  171. button.addEventListener("longClickDown", clickCallback, this);
  172. button.addEventListener("longClickPress", clickCallback, this);
  173. },
  174. _onReplayContextChanged: function()
  175. {
  176. var selectedContextId = this._replayContextSelector.selectedOption().value;
  177. /**
  178. * @param {?CanvasAgent.ResourceState} resourceState
  179. */
  180. function didReceiveResourceState(resourceState)
  181. {
  182. this._enableWaitIcon(false);
  183. if (selectedContextId !== this._replayContextSelector.selectedOption().value)
  184. return;
  185. var imageURL = (resourceState && resourceState.imageURL) || "";
  186. this._replayImageElement.src = imageURL;
  187. this._replayImageElement.style.visibility = imageURL ? "" : "hidden";
  188. }
  189. this._enableWaitIcon(true);
  190. this._traceLogPlayer.getResourceState(selectedContextId, didReceiveResourceState.bind(this));
  191. },
  192. /**
  193. * @param {boolean} forward
  194. */
  195. _onReplayStepClick: function(forward)
  196. {
  197. var selectedNode = this._logGrid.selectedNode;
  198. if (!selectedNode)
  199. return;
  200. var nextNode = selectedNode;
  201. do {
  202. nextNode = forward ? nextNode.traverseNextNode(false) : nextNode.traversePreviousNode(false);
  203. } while (nextNode && typeof nextNode.index !== "number");
  204. (nextNode || selectedNode).revealAndSelect();
  205. },
  206. /**
  207. * @param {boolean} forward
  208. */
  209. _onReplayDrawingCallClick: function(forward)
  210. {
  211. var selectedNode = this._logGrid.selectedNode;
  212. if (!selectedNode)
  213. return;
  214. var nextNode = selectedNode;
  215. while (nextNode) {
  216. var sibling = forward ? nextNode.nextSibling : nextNode.previousSibling;
  217. if (sibling) {
  218. nextNode = sibling;
  219. if (nextNode.hasChildren || nextNode.call.isDrawingCall)
  220. break;
  221. } else {
  222. nextNode = nextNode.parent;
  223. if (!forward)
  224. break;
  225. }
  226. }
  227. if (!nextNode && forward)
  228. this._onReplayLastStepClick();
  229. else
  230. (nextNode || selectedNode).revealAndSelect();
  231. },
  232. _onReplayFirstStepClick: function()
  233. {
  234. var firstNode = this._logGrid.rootNode().children[0];
  235. if (firstNode)
  236. firstNode.revealAndSelect();
  237. },
  238. _onReplayLastStepClick: function()
  239. {
  240. var lastNode = this._logGrid.rootNode().children.peekLast();
  241. if (!lastNode)
  242. return;
  243. while (lastNode.expanded) {
  244. var lastChild = lastNode.children.peekLast();
  245. if (!lastChild)
  246. break;
  247. lastNode = lastChild;
  248. }
  249. lastNode.revealAndSelect();
  250. },
  251. /**
  252. * @param {boolean} enable
  253. */
  254. _enableWaitIcon: function(enable)
  255. {
  256. this._spinnerIcon.enableStyleClass("hidden", !enable);
  257. this._debugInfoElement.enableStyleClass("hidden", enable);
  258. },
  259. _replayTraceLog: function()
  260. {
  261. if (this._pendingReplayTraceLogEvent)
  262. return;
  263. var index = this._selectedCallIndex();
  264. if (index === -1 || index === this._lastReplayCallIndex)
  265. return;
  266. this._lastReplayCallIndex = index;
  267. this._pendingReplayTraceLogEvent = true;
  268. /**
  269. * @param {CanvasAgent.ResourceState} resourceState
  270. * @param {number} replayTime
  271. */
  272. function didReplayTraceLog(resourceState, replayTime)
  273. {
  274. delete this._pendingReplayTraceLogEvent;
  275. this._enableWaitIcon(false);
  276. this._debugInfoElement.textContent = "Replay time: " + Number.secondsToString(replayTime / 1000, true);
  277. this._onReplayContextChanged();
  278. if (index !== this._selectedCallIndex())
  279. this._replayTraceLog();
  280. }
  281. this._enableWaitIcon(true);
  282. this._traceLogPlayer.replayTraceLog(index, didReplayTraceLog.bind(this));
  283. },
  284. /**
  285. * @param {number} offset
  286. */
  287. _requestTraceLog: function(offset)
  288. {
  289. /**
  290. * @param {?CanvasAgent.TraceLog} traceLog
  291. */
  292. function didReceiveTraceLog(traceLog)
  293. {
  294. this._enableWaitIcon(false);
  295. if (!traceLog)
  296. return;
  297. var callNodes = [];
  298. var calls = traceLog.calls;
  299. var index = traceLog.startOffset;
  300. for (var i = 0, n = calls.length; i < n; ++i)
  301. callNodes.push(this._createCallNode(index++, calls[i]));
  302. var contexts = traceLog.contexts;
  303. for (var i = 0, n = contexts.length; i < n; ++i) {
  304. var contextId = contexts[i].resourceId || "";
  305. var description = contexts[i].description || "";
  306. if (this._replayContexts[contextId])
  307. continue;
  308. this._replayContexts[contextId] = true;
  309. this._replayContextSelector.createOption(description, WebInspector.UIString("Show screenshot of this context's canvas."), contextId);
  310. }
  311. this._appendCallNodes(callNodes);
  312. if (traceLog.alive)
  313. setTimeout(this._requestTraceLog.bind(this, index), WebInspector.CanvasProfileView.TraceLogPollingInterval);
  314. else
  315. this._flattenSingleFrameNode();
  316. this._profile._updateCapturingStatus(traceLog);
  317. this._onReplayLastStepClick(); // Automatically replay the last step.
  318. }
  319. this._enableWaitIcon(true);
  320. this._traceLogPlayer.getTraceLog(offset, undefined, didReceiveTraceLog.bind(this));
  321. },
  322. /**
  323. * @return {number}
  324. */
  325. _selectedCallIndex: function()
  326. {
  327. var node = this._logGrid.selectedNode;
  328. return node ? this._peekLastRecursively(node).index : -1;
  329. },
  330. /**
  331. * @param {!WebInspector.DataGridNode} node
  332. * @return {!WebInspector.DataGridNode}
  333. */
  334. _peekLastRecursively: function(node)
  335. {
  336. var lastChild;
  337. while ((lastChild = node.children.peekLast()))
  338. node = lastChild;
  339. return node;
  340. },
  341. /**
  342. * @param {!Array.<!WebInspector.DataGridNode>} callNodes
  343. */
  344. _appendCallNodes: function(callNodes)
  345. {
  346. var rootNode = this._logGrid.rootNode();
  347. var frameNode = rootNode.children.peekLast();
  348. if (frameNode && this._peekLastRecursively(frameNode).call.isFrameEndCall)
  349. frameNode = null;
  350. for (var i = 0, n = callNodes.length; i < n; ++i) {
  351. if (!frameNode) {
  352. var index = rootNode.children.length;
  353. var data = {};
  354. data[0] = "";
  355. data[1] = "Frame #" + (index + 1);
  356. data[2] = "";
  357. frameNode = new WebInspector.DataGridNode(data);
  358. frameNode.selectable = true;
  359. rootNode.appendChild(frameNode);
  360. }
  361. var nextFrameCallIndex = i + 1;
  362. while (nextFrameCallIndex < n && !callNodes[nextFrameCallIndex - 1].call.isFrameEndCall)
  363. ++nextFrameCallIndex;
  364. this._appendCallNodesToFrameNode(frameNode, callNodes, i, nextFrameCallIndex);
  365. i = nextFrameCallIndex - 1;
  366. frameNode = null;
  367. }
  368. },
  369. /**
  370. * @param {!WebInspector.DataGridNode} frameNode
  371. * @param {!Array.<!WebInspector.DataGridNode>} callNodes
  372. * @param {number} fromIndex
  373. * @param {number} toIndex not inclusive
  374. */
  375. _appendCallNodesToFrameNode: function(frameNode, callNodes, fromIndex, toIndex)
  376. {
  377. var self = this;
  378. function appendDrawCallGroup()
  379. {
  380. var index = self._drawCallGroupsCount || 0;
  381. var data = {};
  382. data[0] = "";
  383. data[1] = "Draw call group #" + (index + 1);
  384. data[2] = "";
  385. var node = new WebInspector.DataGridNode(data);
  386. node.selectable = true;
  387. self._drawCallGroupsCount = index + 1;
  388. frameNode.appendChild(node);
  389. return node;
  390. }
  391. function splitDrawCallGroup(drawCallGroup)
  392. {
  393. var splitIndex = 0;
  394. var splitNode;
  395. while ((splitNode = drawCallGroup.children[splitIndex])) {
  396. if (splitNode.call.isDrawingCall)
  397. break;
  398. ++splitIndex;
  399. }
  400. var newDrawCallGroup = appendDrawCallGroup();
  401. var lastNode;
  402. while ((lastNode = drawCallGroup.children[splitIndex + 1]))
  403. newDrawCallGroup.appendChild(lastNode);
  404. return newDrawCallGroup;
  405. }
  406. var drawCallGroup = frameNode.children.peekLast();
  407. var groupHasDrawCall = false;
  408. if (drawCallGroup) {
  409. for (var i = 0, n = drawCallGroup.children.length; i < n; ++i) {
  410. if (drawCallGroup.children[i].call.isDrawingCall) {
  411. groupHasDrawCall = true;
  412. break;
  413. }
  414. }
  415. } else
  416. drawCallGroup = appendDrawCallGroup();
  417. for (var i = fromIndex; i < toIndex; ++i) {
  418. var node = callNodes[i];
  419. drawCallGroup.appendChild(node);
  420. if (node.call.isDrawingCall) {
  421. if (groupHasDrawCall)
  422. drawCallGroup = splitDrawCallGroup(drawCallGroup);
  423. else
  424. groupHasDrawCall = true;
  425. }
  426. }
  427. },
  428. /**
  429. * @param {number} index
  430. * @param {CanvasAgent.Call} call
  431. * @return {!WebInspector.DataGridNode}
  432. */
  433. _createCallNode: function(index, call)
  434. {
  435. var callViewElement = document.createElement("div");
  436. var data = {};
  437. data[0] = index + 1;
  438. data[1] = callViewElement;
  439. data[2] = "";
  440. if (call.sourceURL) {
  441. // FIXME(62725): stack trace line/column numbers are one-based.
  442. var lineNumber = Math.max(0, call.lineNumber - 1) || 0;
  443. var columnNumber = Math.max(0, call.columnNumber - 1) || 0;
  444. data[2] = this._linkifier.linkifyLocation(call.sourceURL, lineNumber, columnNumber);
  445. }
  446. callViewElement.createChild("span", "canvas-function-name").textContent = call.functionName || "context." + call.property;
  447. if (call.arguments) {
  448. callViewElement.createTextChild("(");
  449. for (var i = 0, n = call.arguments.length; i < n; ++i) {
  450. var argument = /** @type {!CanvasAgent.CallArgument} */ (call.arguments[i]);
  451. if (i)
  452. callViewElement.createTextChild(", ");
  453. var element = WebInspector.CanvasProfileDataGridHelper.createCallArgumentElement(argument);
  454. element.__argumentIndex = i;
  455. callViewElement.appendChild(element);
  456. }
  457. callViewElement.createTextChild(")");
  458. } else if (call.value) {
  459. callViewElement.createTextChild(" = ");
  460. callViewElement.appendChild(WebInspector.CanvasProfileDataGridHelper.createCallArgumentElement(call.value));
  461. }
  462. if (call.result) {
  463. callViewElement.createTextChild(" => ");
  464. callViewElement.appendChild(WebInspector.CanvasProfileDataGridHelper.createCallArgumentElement(call.result));
  465. }
  466. var node = new WebInspector.DataGridNode(data);
  467. node.index = index;
  468. node.selectable = true;
  469. node.call = call;
  470. return node;
  471. },
  472. _popoverAnchor: function(element, event)
  473. {
  474. var argumentElement = element.enclosingNodeOrSelfWithClass("canvas-call-argument");
  475. if (!argumentElement || argumentElement.__suppressPopover)
  476. return null;
  477. return argumentElement;
  478. },
  479. _resolveObjectForPopover: function(argumentElement, showCallback, objectGroupName)
  480. {
  481. /**
  482. * @param {?Protocol.Error} error
  483. * @param {RuntimeAgent.RemoteObject=} result
  484. * @param {CanvasAgent.ResourceState=} resourceState
  485. */
  486. function showObjectPopover(error, result, resourceState)
  487. {
  488. if (error)
  489. return;
  490. // FIXME: handle resourceState also
  491. if (!result)
  492. return;
  493. this._popoverAnchorElement = argumentElement.cloneNode(true);
  494. this._popoverAnchorElement.addStyleClass("canvas-popover-anchor");
  495. this._popoverAnchorElement.addStyleClass("source-frame-eval-expression");
  496. argumentElement.parentElement.appendChild(this._popoverAnchorElement);
  497. var diffLeft = this._popoverAnchorElement.boxInWindow().x - argumentElement.boxInWindow().x;
  498. this._popoverAnchorElement.style.left = this._popoverAnchorElement.offsetLeft - diffLeft + "px";
  499. showCallback(WebInspector.RemoteObject.fromPayload(result), false, this._popoverAnchorElement);
  500. }
  501. var evalResult = argumentElement.__evalResult;
  502. if (evalResult)
  503. showObjectPopover.call(this, null, evalResult);
  504. else {
  505. var dataGridNode = this._logGrid.dataGridNodeFromNode(argumentElement);
  506. if (!dataGridNode || typeof dataGridNode.index !== "number") {
  507. this._popoverHelper.hidePopover();
  508. return;
  509. }
  510. var callIndex = dataGridNode.index;
  511. var argumentIndex = argumentElement.__argumentIndex;
  512. if (typeof argumentIndex !== "number")
  513. argumentIndex = -1;
  514. CanvasAgent.evaluateTraceLogCallArgument(this._traceLogId, callIndex, argumentIndex, objectGroupName, showObjectPopover.bind(this));
  515. }
  516. },
  517. /**
  518. * @param {WebInspector.RemoteObject} object
  519. * @return {string}
  520. */
  521. _hexNumbersFormatter: function(object)
  522. {
  523. if (object.type === "number") {
  524. // Show enum values in hex with min length of 4 (e.g. 0x0012).
  525. var str = "0000" + Number(object.description).toString(16).toUpperCase();
  526. str = str.replace(/^0+(.{4,})$/, "$1");
  527. return "0x" + str;
  528. }
  529. return object.description || "";
  530. },
  531. _onHidePopover: function()
  532. {
  533. if (this._popoverAnchorElement) {
  534. this._popoverAnchorElement.remove()
  535. delete this._popoverAnchorElement;
  536. }
  537. },
  538. _flattenSingleFrameNode: function()
  539. {
  540. var rootNode = this._logGrid.rootNode();
  541. if (rootNode.children.length !== 1)
  542. return;
  543. var frameNode = rootNode.children[0];
  544. while (frameNode.children[0])
  545. rootNode.appendChild(frameNode.children[0]);
  546. rootNode.removeChild(frameNode);
  547. },
  548. __proto__: WebInspector.View.prototype
  549. }
  550. /**
  551. * @constructor
  552. * @extends {WebInspector.ProfileType}
  553. */
  554. WebInspector.CanvasProfileType = function()
  555. {
  556. WebInspector.ProfileType.call(this, WebInspector.CanvasProfileType.TypeId, WebInspector.UIString("Capture Canvas Frame"));
  557. this._nextProfileUid = 1;
  558. this._recording = false;
  559. this._lastProfileHeader = null;
  560. this._capturingModeSelector = new WebInspector.StatusBarComboBox(this._dispatchViewUpdatedEvent.bind(this));
  561. this._capturingModeSelector.element.title = WebInspector.UIString("Canvas capture mode.");
  562. this._capturingModeSelector.createOption(WebInspector.UIString("Single Frame"), WebInspector.UIString("Capture a single canvas frame."), "");
  563. this._capturingModeSelector.createOption(WebInspector.UIString("Consecutive Frames"), WebInspector.UIString("Capture consecutive canvas frames."), "1");
  564. /** @type {!Object.<string, Element>} */
  565. this._frameOptions = {};
  566. /** @type {!Object.<string, boolean>} */
  567. this._framesWithCanvases = {};
  568. this._frameSelector = new WebInspector.StatusBarComboBox(this._dispatchViewUpdatedEvent.bind(this));
  569. this._frameSelector.element.title = WebInspector.UIString("Frame containing the canvases to capture.");
  570. this._frameSelector.element.addStyleClass("hidden");
  571. WebInspector.runtimeModel.contextLists().forEach(this._addFrame, this);
  572. WebInspector.runtimeModel.addEventListener(WebInspector.RuntimeModel.Events.FrameExecutionContextListAdded, this._frameAdded, this);
  573. WebInspector.runtimeModel.addEventListener(WebInspector.RuntimeModel.Events.FrameExecutionContextListRemoved, this._frameRemoved, this);
  574. this._dispatcher = new WebInspector.CanvasDispatcher(this);
  575. this._canvasAgentEnabled = false;
  576. this._decorationElement = document.createElement("div");
  577. this._decorationElement.className = "profile-canvas-decoration";
  578. this._updateDecorationElement();
  579. }
  580. WebInspector.CanvasProfileType.TypeId = "CANVAS_PROFILE";
  581. WebInspector.CanvasProfileType.prototype = {
  582. get statusBarItems()
  583. {
  584. return [this._capturingModeSelector.element, this._frameSelector.element];
  585. },
  586. get buttonTooltip()
  587. {
  588. if (this._isSingleFrameMode())
  589. return WebInspector.UIString("Capture next canvas frame.");
  590. else
  591. return this._recording ? WebInspector.UIString("Stop capturing canvas frames.") : WebInspector.UIString("Start capturing canvas frames.");
  592. },
  593. /**
  594. * @override
  595. * @return {boolean}
  596. */
  597. buttonClicked: function()
  598. {
  599. if (!this._canvasAgentEnabled)
  600. return false;
  601. if (this._recording) {
  602. this._recording = false;
  603. this._stopFrameCapturing();
  604. } else if (this._isSingleFrameMode()) {
  605. this._recording = false;
  606. this._runSingleFrameCapturing();
  607. } else {
  608. this._recording = true;
  609. this._startFrameCapturing();
  610. }
  611. return this._recording;
  612. },
  613. _runSingleFrameCapturing: function()
  614. {
  615. var frameId = this._selectedFrameId();
  616. CanvasAgent.captureFrame(frameId, this._didStartCapturingFrame.bind(this, frameId));
  617. },
  618. _startFrameCapturing: function()
  619. {
  620. var frameId = this._selectedFrameId();
  621. CanvasAgent.startCapturing(frameId, this._didStartCapturingFrame.bind(this, frameId));
  622. },
  623. _stopFrameCapturing: function()
  624. {
  625. if (!this._lastProfileHeader)
  626. return;
  627. var profileHeader = this._lastProfileHeader;
  628. var traceLogId = profileHeader.traceLogId();
  629. this._lastProfileHeader = null;
  630. function didStopCapturing()
  631. {
  632. profileHeader._updateCapturingStatus();
  633. }
  634. CanvasAgent.stopCapturing(traceLogId, didStopCapturing.bind(this));
  635. },
  636. /**
  637. * @param {string|undefined} frameId
  638. * @param {?Protocol.Error} error
  639. * @param {CanvasAgent.TraceLogId} traceLogId
  640. */
  641. _didStartCapturingFrame: function(frameId, error, traceLogId)
  642. {
  643. if (error || this._lastProfileHeader && this._lastProfileHeader.traceLogId() === traceLogId)
  644. return;
  645. var profileHeader = new WebInspector.CanvasProfileHeader(this, WebInspector.UIString("Trace Log %d", this._nextProfileUid), this._nextProfileUid, traceLogId, frameId);
  646. ++this._nextProfileUid;
  647. this._lastProfileHeader = profileHeader;
  648. this.addProfile(profileHeader);
  649. profileHeader._updateCapturingStatus();
  650. },
  651. get treeItemTitle()
  652. {
  653. return WebInspector.UIString("CANVAS PROFILE");
  654. },
  655. get description()
  656. {
  657. return WebInspector.UIString("Canvas calls instrumentation");
  658. },
  659. /**
  660. * @override
  661. * @return {Element}
  662. */
  663. decorationElement: function()
  664. {
  665. return this._decorationElement;
  666. },
  667. /**
  668. * @override
  669. */
  670. _reset: function()
  671. {
  672. WebInspector.ProfileType.prototype._reset.call(this);
  673. this._nextProfileUid = 1;
  674. },
  675. /**
  676. * @override
  677. * @param {!WebInspector.ProfileHeader} profile
  678. */
  679. removeProfile: function(profile)
  680. {
  681. WebInspector.ProfileType.prototype.removeProfile.call(this, profile);
  682. if (this._recording && profile === this._lastProfileHeader)
  683. this._recording = false;
  684. },
  685. setRecordingProfile: function(isProfiling)
  686. {
  687. this._recording = isProfiling;
  688. },
  689. /**
  690. * @override
  691. * @param {string=} title
  692. * @return {!WebInspector.ProfileHeader}
  693. */
  694. createTemporaryProfile: function(title)
  695. {
  696. title = title || WebInspector.UIString("Capturing\u2026");
  697. return new WebInspector.CanvasProfileHeader(this, title);
  698. },
  699. /**
  700. * @override
  701. * @param {ProfilerAgent.ProfileHeader} profile
  702. * @return {!WebInspector.ProfileHeader}
  703. */
  704. createProfile: function(profile)
  705. {
  706. return new WebInspector.CanvasProfileHeader(this, profile.title, -1);
  707. },
  708. /**
  709. * @param {boolean=} forcePageReload
  710. */
  711. _updateDecorationElement: function(forcePageReload)
  712. {
  713. this._decorationElement.removeChildren();
  714. this._decorationElement.createChild("div", "warning-icon-small");
  715. this._decorationElement.appendChild(document.createTextNode(this._canvasAgentEnabled ? WebInspector.UIString("Canvas Profiler is enabled.") : WebInspector.UIString("Canvas Profiler is disabled.")));
  716. var button = this._decorationElement.createChild("button");
  717. button.type = "button";
  718. button.textContent = this._canvasAgentEnabled ? WebInspector.UIString("Disable") : WebInspector.UIString("Enable");
  719. button.addEventListener("click", this._onProfilerEnableButtonClick.bind(this, !this._canvasAgentEnabled), false);
  720. if (forcePageReload) {
  721. if (this._canvasAgentEnabled) {
  722. /**
  723. * @param {?Protocol.Error} error
  724. * @param {boolean} result
  725. */
  726. function hasUninstrumentedCanvasesCallback(error, result)
  727. {
  728. if (error || result)
  729. PageAgent.reload();
  730. }
  731. CanvasAgent.hasUninstrumentedCanvases(hasUninstrumentedCanvasesCallback.bind(this));
  732. } else {
  733. for (var frameId in this._framesWithCanvases) {
  734. if (this._framesWithCanvases.hasOwnProperty(frameId)) {
  735. PageAgent.reload();
  736. break;
  737. }
  738. }
  739. }
  740. }
  741. },
  742. /**
  743. * @param {boolean} enable
  744. */
  745. _onProfilerEnableButtonClick: function(enable)
  746. {
  747. if (this._canvasAgentEnabled === enable)
  748. return;
  749. /**
  750. * @param {?Protocol.Error} error
  751. */
  752. function callback(error)
  753. {
  754. if (error)
  755. return;
  756. this._canvasAgentEnabled = enable;
  757. this._updateDecorationElement(true);
  758. this._dispatchViewUpdatedEvent();
  759. }
  760. if (enable)
  761. CanvasAgent.enable(callback.bind(this));
  762. else
  763. CanvasAgent.disable(callback.bind(this));
  764. },
  765. /**
  766. * @return {boolean}
  767. */
  768. _isSingleFrameMode: function()
  769. {
  770. return !this._capturingModeSelector.selectedOption().value;
  771. },
  772. /**
  773. * @param {WebInspector.Event} event
  774. */
  775. _frameAdded: function(event)
  776. {
  777. var contextList = /** @type {WebInspector.FrameExecutionContextList} */ (event.data);
  778. this._addFrame(contextList);
  779. },
  780. /**
  781. * @param {WebInspector.FrameExecutionContextList} contextList
  782. */
  783. _addFrame: function(contextList)
  784. {
  785. var frameId = contextList.frameId;
  786. var option = document.createElement("option");
  787. option.text = contextList.displayName;
  788. option.title = contextList.url;
  789. option.value = frameId;
  790. this._frameOptions[frameId] = option;
  791. if (this._framesWithCanvases[frameId]) {
  792. this._frameSelector.addOption(option);
  793. this._dispatchViewUpdatedEvent();
  794. }
  795. },
  796. /**
  797. * @param {WebInspector.Event} event
  798. */
  799. _frameRemoved: function(event)
  800. {
  801. var contextList = /** @type {WebInspector.FrameExecutionContextList} */ (event.data);
  802. var frameId = contextList.frameId;
  803. var option = this._frameOptions[frameId];
  804. if (option && this._framesWithCanvases[frameId]) {
  805. this._frameSelector.removeOption(option);
  806. this._dispatchViewUpdatedEvent();
  807. }
  808. delete this._frameOptions[frameId];
  809. delete this._framesWithCanvases[frameId];
  810. },
  811. /**
  812. * @param {string} frameId
  813. */
  814. _contextCreated: function(frameId)
  815. {
  816. if (this._framesWithCanvases[frameId])
  817. return;
  818. this._framesWithCanvases[frameId] = true;
  819. var option = this._frameOptions[frameId];
  820. if (option) {
  821. this._frameSelector.addOption(option);
  822. this._dispatchViewUpdatedEvent();
  823. }
  824. },
  825. /**
  826. * @param {PageAgent.FrameId=} frameId
  827. * @param {CanvasAgent.TraceLogId=} traceLogId
  828. */
  829. _traceLogsRemoved: function(frameId, traceLogId)
  830. {
  831. var sidebarElementsToDelete = [];
  832. var sidebarElements = /** @type {!Array.<WebInspector.ProfileSidebarTreeElement>} */ ((this.treeElement && this.treeElement.children) || []);
  833. for (var i = 0, n = sidebarElements.length; i < n; ++i) {
  834. var header = /** @type {WebInspector.CanvasProfileHeader} */ (sidebarElements[i].profile);
  835. if (!header)
  836. continue;
  837. if (frameId && frameId !== header.frameId())
  838. continue;
  839. if (traceLogId && traceLogId !== header.traceLogId())
  840. continue;
  841. sidebarElementsToDelete.push(sidebarElements[i]);
  842. }
  843. for (var i = 0, n = sidebarElementsToDelete.length; i < n; ++i)
  844. sidebarElementsToDelete[i].ondelete();
  845. },
  846. /**
  847. * @return {string|undefined}
  848. */
  849. _selectedFrameId: function()
  850. {
  851. var option = this._frameSelector.selectedOption();
  852. return option ? option.value : undefined;
  853. },
  854. _dispatchViewUpdatedEvent: function()
  855. {
  856. this._frameSelector.element.enableStyleClass("hidden", this._frameSelector.size() <= 1);
  857. this.dispatchEventToListeners(WebInspector.ProfileType.Events.ViewUpdated);
  858. },
  859. /**
  860. * @override
  861. * @return {boolean}
  862. */
  863. isInstantProfile: function()
  864. {
  865. return this._isSingleFrameMode();
  866. },
  867. /**
  868. * @override
  869. * @return {boolean}
  870. */
  871. isEnabled: function()
  872. {
  873. return this._canvasAgentEnabled;
  874. },
  875. __proto__: WebInspector.ProfileType.prototype
  876. }
  877. /**
  878. * @constructor
  879. * @implements {CanvasAgent.Dispatcher}
  880. * @param {WebInspector.CanvasProfileType} profileType
  881. */
  882. WebInspector.CanvasDispatcher = function(profileType)
  883. {
  884. this._profileType = profileType;
  885. InspectorBackend.registerCanvasDispatcher(this);
  886. }
  887. WebInspector.CanvasDispatcher.prototype = {
  888. /**
  889. * @param {string} frameId
  890. */
  891. contextCreated: function(frameId)
  892. {
  893. this._profileType._contextCreated(frameId);
  894. },
  895. /**
  896. * @param {PageAgent.FrameId=} frameId
  897. * @param {CanvasAgent.TraceLogId=} traceLogId
  898. */
  899. traceLogsRemoved: function(frameId, traceLogId)
  900. {
  901. this._profileType._traceLogsRemoved(frameId, traceLogId);
  902. }
  903. }
  904. /**
  905. * @constructor
  906. * @extends {WebInspector.ProfileHeader}
  907. * @param {!WebInspector.CanvasProfileType} type
  908. * @param {string} title
  909. * @param {number=} uid
  910. * @param {CanvasAgent.TraceLogId=} traceLogId
  911. * @param {PageAgent.FrameId=} frameId
  912. */
  913. WebInspector.CanvasProfileHeader = function(type, title, uid, traceLogId, frameId)
  914. {
  915. WebInspector.ProfileHeader.call(this, type, title, uid);
  916. /** @type {CanvasAgent.TraceLogId} */
  917. this._traceLogId = traceLogId || "";
  918. this._frameId = frameId;
  919. this._alive = true;
  920. this._traceLogSize = 0;
  921. this._traceLogPlayer = traceLogId ? new WebInspector.CanvasTraceLogPlayerProxy(traceLogId) : null;
  922. }
  923. WebInspector.CanvasProfileHeader.prototype = {
  924. /**
  925. * @return {CanvasAgent.TraceLogId}
  926. */
  927. traceLogId: function()
  928. {
  929. return this._traceLogId;
  930. },
  931. /**
  932. * @return {WebInspector.CanvasTraceLogPlayerProxy}
  933. */
  934. traceLogPlayer: function()
  935. {
  936. return this._traceLogPlayer;
  937. },
  938. /**
  939. * @return {PageAgent.FrameId|undefined}
  940. */
  941. frameId: function()
  942. {
  943. return this._frameId;
  944. },
  945. /**
  946. * @override
  947. * @return {WebInspector.ProfileSidebarTreeElement}
  948. */
  949. createSidebarTreeElement: function()
  950. {
  951. return new WebInspector.ProfileSidebarTreeElement(this, WebInspector.UIString("Trace Log %d"), "profile-sidebar-tree-item");
  952. },
  953. /**
  954. * @override
  955. * @param {WebInspector.ProfilesPanel} profilesPanel
  956. */
  957. createView: function(profilesPanel)
  958. {
  959. return new WebInspector.CanvasProfileView(this);
  960. },
  961. /**
  962. * @override
  963. */
  964. dispose: function()
  965. {
  966. if (this._traceLogPlayer)
  967. this._traceLogPlayer.dispose();
  968. clearTimeout(this._requestStatusTimer);
  969. this._alive = false;
  970. },
  971. /**
  972. * @param {CanvasAgent.TraceLog=} traceLog
  973. */
  974. _updateCapturingStatus: function(traceLog)
  975. {
  976. if (!this.sidebarElement || !this._traceLogId)
  977. return;
  978. if (traceLog) {
  979. this._alive = traceLog.alive;
  980. this._traceLogSize = traceLog.totalAvailableCalls;
  981. }
  982. this.sidebarElement.subtitle = this._alive ? WebInspector.UIString("Capturing\u2026 %d calls", this._traceLogSize) : WebInspector.UIString("Captured %d calls", this._traceLogSize);
  983. this.sidebarElement.wait = this._alive;
  984. if (this._alive) {
  985. clearTimeout(this._requestStatusTimer);
  986. this._requestStatusTimer = setTimeout(this._requestCapturingStatus.bind(this), WebInspector.CanvasProfileView.TraceLogPollingInterval);
  987. }
  988. },
  989. _requestCapturingStatus: function()
  990. {
  991. /**
  992. * @param {?CanvasAgent.TraceLog} traceLog
  993. */
  994. function didReceiveTraceLog(traceLog)
  995. {
  996. if (!traceLog)
  997. return;
  998. this._alive = traceLog.alive;
  999. this._traceLogSize = traceLog.totalAvailableCalls;
  1000. this._updateCapturingStatus();
  1001. }
  1002. this._traceLogPlayer.getTraceLog(0, 0, didReceiveTraceLog.bind(this));
  1003. },
  1004. __proto__: WebInspector.ProfileHeader.prototype
  1005. }
  1006. WebInspector.CanvasProfileDataGridHelper = {
  1007. /**
  1008. * @param {!CanvasAgent.CallArgument} callArgument
  1009. * @return {!Element}
  1010. */
  1011. createCallArgumentElement: function(callArgument)
  1012. {
  1013. if (callArgument.enumName)
  1014. return WebInspector.CanvasProfileDataGridHelper.createEnumValueElement(callArgument.enumName, +callArgument.description);
  1015. var element = document.createElement("span");
  1016. element.className = "canvas-call-argument";
  1017. var description = callArgument.description;
  1018. if (callArgument.type === "string") {
  1019. const maxStringLength = 150;
  1020. element.createTextChild("\"");
  1021. element.createChild("span", "canvas-formatted-string").textContent = description.trimMiddle(maxStringLength);
  1022. element.createTextChild("\"");
  1023. element.__suppressPopover = (description.length <= maxStringLength && !/[\r\n]/.test(description));
  1024. if (!element.__suppressPopover)
  1025. element.__evalResult = WebInspector.RemoteObject.fromPrimitiveValue(description);
  1026. } else {
  1027. var type = callArgument.subtype || callArgument.type;
  1028. if (type) {
  1029. element.addStyleClass("canvas-formatted-" + type);
  1030. if (["null", "undefined", "boolean", "number"].indexOf(type) >= 0)
  1031. element.__suppressPopover = true;
  1032. }
  1033. element.textContent = description;
  1034. if (callArgument.remoteObject)
  1035. element.__evalResult = WebInspector.RemoteObject.fromPayload(callArgument.remoteObject);
  1036. }
  1037. if (callArgument.resourceId) {
  1038. element.addStyleClass("canvas-formatted-resource");
  1039. element.__resourceId = callArgument.resourceId;
  1040. }
  1041. return element;
  1042. },
  1043. /**
  1044. * @param {string} enumName
  1045. * @param {number} enumValue
  1046. * @return {!Element}
  1047. */
  1048. createEnumValueElement: function(enumName, enumValue)
  1049. {
  1050. var element = document.createElement("span");
  1051. element.className = "canvas-call-argument canvas-formatted-number";
  1052. element.textContent = enumName;
  1053. element.__evalResult = WebInspector.RemoteObject.fromPrimitiveValue(enumValue);
  1054. return element;
  1055. }
  1056. }
  1057. /**
  1058. * @extends {WebInspector.Object}
  1059. * @constructor
  1060. * @param {CanvasAgent.TraceLogId} traceLogId
  1061. */
  1062. WebInspector.CanvasTraceLogPlayerProxy = function(traceLogId)
  1063. {
  1064. this._traceLogId = traceLogId;
  1065. /** @type {!Object.<string, !CanvasAgent.ResourceState>} */
  1066. this._currentResourceStates = {};
  1067. /** @type {?CanvasAgent.ResourceId} */
  1068. this._defaultResourceId = null;
  1069. }
  1070. /** @enum {string} */
  1071. WebInspector.CanvasTraceLogPlayerProxy.Events = {
  1072. CanvasTraceLogReceived: "CanvasTraceLogReceived",
  1073. CanvasReplayStateChanged: "CanvasReplayStateChanged",
  1074. CanvasResourceStateReceived: "CanvasResourceStateReceived",
  1075. }
  1076. WebInspector.CanvasTraceLogPlayerProxy.prototype = {
  1077. /**
  1078. * @param {number|undefined} startOffset
  1079. * @param {number|undefined} maxLength
  1080. * @param {function(?CanvasAgent.TraceLog):void} userCallback
  1081. */
  1082. getTraceLog: function(startOffset, maxLength, userCallback)
  1083. {
  1084. /**
  1085. * @param {?Protocol.Error} error
  1086. * @param {CanvasAgent.TraceLog} traceLog
  1087. */
  1088. function callback(error, traceLog)
  1089. {
  1090. if (error || !traceLog) {
  1091. userCallback(null);
  1092. return;
  1093. }
  1094. userCallback(traceLog);
  1095. this.dispatchEventToListeners(WebInspector.CanvasTraceLogPlayerProxy.Events.CanvasTraceLogReceived, traceLog);
  1096. }
  1097. CanvasAgent.getTraceLog(this._traceLogId, startOffset, maxLength, callback.bind(this));
  1098. },
  1099. dispose: function()
  1100. {
  1101. this._currentResourceStates = {};
  1102. CanvasAgent.dropTraceLog(this._traceLogId);
  1103. this.dispatchEventToListeners(WebInspector.CanvasTraceLogPlayerProxy.Events.CanvasReplayStateChanged);
  1104. },
  1105. /**
  1106. * @param {?CanvasAgent.ResourceId} resourceId
  1107. * @param {function(?CanvasAgent.ResourceState):void} userCallback
  1108. */
  1109. getResourceState: function(resourceId, userCallback)
  1110. {
  1111. resourceId = resourceId || this._defaultResourceId;
  1112. if (!resourceId) {
  1113. userCallback(null); // Has not been replayed yet.
  1114. return;
  1115. }
  1116. if (this._currentResourceStates[resourceId]) {
  1117. userCallback(this._currentResourceStates[resourceId]);
  1118. return;
  1119. }
  1120. /**
  1121. * @param {?Protocol.Error} error
  1122. * @param {CanvasAgent.ResourceState} resourceState
  1123. */
  1124. function callback(error, resourceState)
  1125. {
  1126. if (error || !resourceState) {
  1127. userCallback(null);
  1128. return;
  1129. }
  1130. this._currentResourceStates[resourceId] = resourceState;
  1131. userCallback(resourceState);
  1132. this.dispatchEventToListeners(WebInspector.CanvasTraceLogPlayerProxy.Events.CanvasResourceStateReceived, resourceState);
  1133. }
  1134. CanvasAgent.getResourceState(this._traceLogId, resourceId, callback.bind(this));
  1135. },
  1136. /**
  1137. * @param {number} index
  1138. * @param {function(?CanvasAgent.ResourceState, number):void} userCallback
  1139. */
  1140. replayTraceLog: function(index, userCallback)
  1141. {
  1142. /**
  1143. * @param {?Protocol.Error} error
  1144. * @param {CanvasAgent.ResourceState} resourceState
  1145. * @param {number} replayTime
  1146. */
  1147. function callback(error, resourceState, replayTime)
  1148. {
  1149. this._currentResourceStates = {};
  1150. if (error || !resourceState) {
  1151. resourceState = null;
  1152. userCallback(null, replayTime);
  1153. } else {
  1154. this._defaultResourceId = resourceState.id;
  1155. this._currentResourceStates[resourceState.id] = resourceState;
  1156. userCallback(resourceState, replayTime);
  1157. }
  1158. this.dispatchEventToListeners(WebInspector.CanvasTraceLogPlayerProxy.Events.CanvasReplayStateChanged);
  1159. if (resourceState)
  1160. this.dispatchEventToListeners(WebInspector.CanvasTraceLogPlayerProxy.Events.CanvasResourceStateReceived, resourceState);
  1161. }
  1162. CanvasAgent.replayTraceLog(this._traceLogId, index, callback.bind(this));
  1163. },
  1164. clearResourceStates: function()
  1165. {
  1166. this._currentResourceStates = {};
  1167. this.dispatchEventToListeners(WebInspector.CanvasTraceLogPlayerProxy.Events.CanvasReplayStateChanged);
  1168. },
  1169. __proto__: WebInspector.Object.prototype
  1170. }