123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528 |
- /*
- * Copyright (C) 2013 Google Inc. All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions are
- * met:
- *
- * * Redistributions of source code must retain the above copyright
- * notice, this list of conditions and the following disclaimer.
- * * Redistributions in binary form must reproduce the above
- * copyright notice, this list of conditions and the following disclaimer
- * in the documentation and/or other materials provided with the
- * distribution.
- * * Neither the name of Google Inc. nor the names of its
- * contributors may be used to endorse or promote products derived from
- * this software without specific prior written permission.
- *
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
- * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
- * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
- * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
- * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
- * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
- * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
- * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
- * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
- * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
- * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- */
- /**
- * @constructor
- * @extends {WebInspector.View}
- * @param {WebInspector.CanvasTraceLogPlayerProxy} traceLogPlayer
- */
- WebInspector.CanvasReplayStateView = function(traceLogPlayer)
- {
- WebInspector.View.call(this);
- this.registerRequiredCSS("canvasProfiler.css");
- this.element.addStyleClass("canvas-replay-state-view");
- this._traceLogPlayer = traceLogPlayer;
- var controlsContainer = this.element.createChild("div", "status-bar");
- this._prevButton = this._createControlButton(controlsContainer, "canvas-replay-state-prev", WebInspector.UIString("Previous resource."), this._onResourceNavigationClick.bind(this, false));
- this._nextButton = this._createControlButton(controlsContainer, "canvas-replay-state-next", WebInspector.UIString("Next resource."), this._onResourceNavigationClick.bind(this, true));
- this._createControlButton(controlsContainer, "canvas-replay-state-refresh", WebInspector.UIString("Refresh."), this._onStateRefreshClick.bind(this));
- this._resourceSelector = new WebInspector.StatusBarComboBox(this._onReplayResourceChanged.bind(this));
- this._currentOption = this._resourceSelector.createOption(WebInspector.UIString("<auto>"), WebInspector.UIString("Show state of the last replayed resource."), "");
- controlsContainer.appendChild(this._resourceSelector.element);
- /** @type {!Object.<string, string>} */
- this._resourceIdToDescription = {};
- /** @type {!Object.<string, !Object.<string, boolean>>} */
- this._gridNodesExpandedState = {};
- /** @type {!Object.<string, {scrollTop:number, scrollLeft:number}>} */
- this._gridScrollPositions = {};
- /** @type {?CanvasAgent.ResourceId} */
- this._currentResourceId = null;
- /** @type {!Array.<!Element>} */
- this._prevOptionsStack = [];
- /** @type {!Array.<!Element>} */
- this._nextOptionsStack = [];
- /** @type {!Array.<!WebInspector.DataGridNode>} */
- this._highlightedGridNodes = [];
- var columns = [
- {title: WebInspector.UIString("Name"), sortable: false, width: "50%", disclosure: true},
- {title: WebInspector.UIString("Value"), sortable: false, width: "50%"}
- ];
- this._stateGrid = new WebInspector.DataGrid(columns);
- this._stateGrid.element.addStyleClass("fill");
- this._stateGrid.show(this.element);
- this._traceLogPlayer.addEventListener(WebInspector.CanvasTraceLogPlayerProxy.Events.CanvasReplayStateChanged, this._onReplayResourceChanged, this);
- this._traceLogPlayer.addEventListener(WebInspector.CanvasTraceLogPlayerProxy.Events.CanvasTraceLogReceived, this._onCanvasTraceLogReceived, this);
- this._traceLogPlayer.addEventListener(WebInspector.CanvasTraceLogPlayerProxy.Events.CanvasResourceStateReceived, this._onCanvasResourceStateReceived, this);
- this._updateButtonsEnabledState();
- }
- WebInspector.CanvasReplayStateView.prototype = {
- /**
- * @param {string} resourceId
- */
- selectResource: function(resourceId)
- {
- if (resourceId === this._resourceSelector.selectedOption().value)
- return;
- var option = this._resourceSelector.selectElement().firstChild;
- for (var index = 0; option; ++index, option = option.nextSibling) {
- if (resourceId === option.value) {
- this._resourceSelector.setSelectedIndex(index);
- this._onReplayResourceChanged();
- break;
- }
- }
- },
- /**
- * @param {Element} parent
- * @param {string} className
- * @param {string} title
- * @param {function(this:WebInspector.CanvasProfileView)} clickCallback
- * @return {!WebInspector.StatusBarButton}
- */
- _createControlButton: function(parent, className, title, clickCallback)
- {
- var button = new WebInspector.StatusBarButton(title, className + " canvas-replay-button");
- parent.appendChild(button.element);
- button.makeLongClickEnabled();
- button.addEventListener("click", clickCallback, this);
- button.addEventListener("longClickDown", clickCallback, this);
- button.addEventListener("longClickPress", clickCallback, this);
- return button;
- },
- /**
- * @param {boolean} forward
- */
- _onResourceNavigationClick: function(forward)
- {
- var newOption = forward ? this._nextOptionsStack.pop() : this._prevOptionsStack.pop();
- if (!newOption)
- return;
- (forward ? this._prevOptionsStack : this._nextOptionsStack).push(this._currentOption);
- this._isNavigationButton = true;
- this.selectResource(newOption.value);
- delete this._isNavigationButton;
- this._updateButtonsEnabledState();
- },
- _onStateRefreshClick: function()
- {
- this._traceLogPlayer.clearResourceStates();
- },
- _updateButtonsEnabledState: function()
- {
- this._prevButton.setEnabled(this._prevOptionsStack.length > 0);
- this._nextButton.setEnabled(this._nextOptionsStack.length > 0);
- },
- _updateCurrentOption: function()
- {
- const maxStackSize = 256;
- var selectedOption = this._resourceSelector.selectedOption();
- if (this._currentOption === selectedOption)
- return;
- if (!this._isNavigationButton) {
- this._prevOptionsStack.push(this._currentOption);
- this._nextOptionsStack = [];
- if (this._prevOptionsStack.length > maxStackSize)
- this._prevOptionsStack.shift();
- this._updateButtonsEnabledState();
- }
- this._currentOption = selectedOption;
- },
- /**
- * @param {!CanvasAgent.TraceLog} traceLog
- */
- _collectResourcesFromTraceLog: function(traceLog)
- {
- /** @type {!Array.<!CanvasAgent.CallArgument>} */
- var collectedResources = [];
- var calls = traceLog.calls;
- for (var i = 0, n = calls.length; i < n; ++i) {
- var call = calls[i];
- var args = call.arguments || [];
- for (var j = 0; j < args.length; ++j)
- this._collectResourceFromCallArgument(args[j], collectedResources);
- this._collectResourceFromCallArgument(call.result, collectedResources);
- this._collectResourceFromCallArgument(call.value, collectedResources);
- }
- var contexts = traceLog.contexts;
- for (var i = 0, n = contexts.length; i < n; ++i)
- this._collectResourceFromCallArgument(contexts[i], collectedResources);
- this._addCollectedResourcesToSelector(collectedResources);
- },
- /**
- * @param {!CanvasAgent.ResourceState} resourceState
- */
- _collectResourcesFromResourceState: function(resourceState)
- {
- /** @type {!Array.<!CanvasAgent.CallArgument>} */
- var collectedResources = [];
- this._collectResourceFromResourceStateDescriptors(resourceState.descriptors, collectedResources);
- this._addCollectedResourcesToSelector(collectedResources);
- },
- /**
- * @param {Array.<!CanvasAgent.ResourceStateDescriptor>|undefined} descriptors
- * @param {!Array.<!CanvasAgent.CallArgument>} output
- */
- _collectResourceFromResourceStateDescriptors: function(descriptors, output)
- {
- if (!descriptors)
- return;
- for (var i = 0, n = descriptors.length; i < n; ++i) {
- var descriptor = descriptors[i];
- this._collectResourceFromCallArgument(descriptor.value, output);
- this._collectResourceFromResourceStateDescriptors(descriptor.values, output);
- }
- },
- /**
- * @param {CanvasAgent.CallArgument|undefined} argument
- * @param {!Array.<!CanvasAgent.CallArgument>} output
- */
- _collectResourceFromCallArgument: function(argument, output)
- {
- if (!argument)
- return;
- var resourceId = argument.resourceId;
- if (!resourceId || this._resourceIdToDescription[resourceId])
- return;
- this._resourceIdToDescription[resourceId] = argument.description;
- output.push(argument);
- },
- /**
- * @param {!Array.<!CanvasAgent.CallArgument>} collectedResources
- */
- _addCollectedResourcesToSelector: function(collectedResources)
- {
- if (!collectedResources.length)
- return;
- /**
- * @param {!CanvasAgent.CallArgument} arg1
- * @param {!CanvasAgent.CallArgument} arg2
- * @return {number}
- */
- function comparator(arg1, arg2)
- {
- var a = arg1.description;
- var b = arg2.description;
- return String.naturalOrderComparator(a, b);
- }
- collectedResources.sort(comparator);
- var selectElement = this._resourceSelector.selectElement();
- var currentOption = selectElement.firstChild;
- currentOption = currentOption.nextSibling; // Skip the "<auto>" option.
- for (var i = 0, n = collectedResources.length; i < n; ++i) {
- var argument = collectedResources[i];
- while (currentOption && String.naturalOrderComparator(currentOption.text, argument.description) < 0)
- currentOption = currentOption.nextSibling;
- var option = this._resourceSelector.createOption(argument.description, WebInspector.UIString("Show state of this resource."), argument.resourceId);
- if (currentOption)
- selectElement.insertBefore(option, currentOption);
- }
- },
- _onReplayResourceChanged: function()
- {
- this._updateCurrentOption();
- var selectedResourceId = this._resourceSelector.selectedOption().value;
- /**
- * @param {?CanvasAgent.ResourceState} resourceState
- */
- function didReceiveResourceState(resourceState)
- {
- if (selectedResourceId !== this._resourceSelector.selectedOption().value)
- return;
- this._showResourceState(resourceState);
- }
- this._traceLogPlayer.getResourceState(selectedResourceId, didReceiveResourceState.bind(this));
- },
- /**
- * @param {WebInspector.Event} event
- */
- _onCanvasTraceLogReceived: function(event)
- {
- var traceLog = /** @type {CanvasAgent.TraceLog} */ (event.data);
- if (traceLog)
- this._collectResourcesFromTraceLog(traceLog);
- },
- /**
- * @param {WebInspector.Event} event
- */
- _onCanvasResourceStateReceived: function(event)
- {
- var resourceState = /** @type {CanvasAgent.ResourceState} */ (event.data);
- if (resourceState)
- this._collectResourcesFromResourceState(resourceState);
- },
- /**
- * @param {?CanvasAgent.ResourceState} resourceState
- */
- _showResourceState: function(resourceState)
- {
- this._saveExpandedState();
- this._saveScrollState();
- var rootNode = this._stateGrid.rootNode();
- if (!resourceState) {
- this._currentResourceId = null;
- this._updateDataGridHighlights([]);
- rootNode.removeChildren();
- return;
- }
- var nodesToHighlight = [];
- var nameToOldGridNodes = {};
- /**
- * @param {!Object} map
- * @param {WebInspector.DataGridNode=} node
- */
- function populateNameToNodesMap(map, node)
- {
- if (!node)
- return;
- for (var i = 0, child; child = node.children[i]; ++i) {
- var item = {
- node: child,
- children: {}
- };
- map[child.name] = item;
- populateNameToNodesMap(item.children, child);
- }
- }
- populateNameToNodesMap(nameToOldGridNodes, rootNode);
- rootNode.removeChildren();
- /**
- * @param {!CanvasAgent.ResourceStateDescriptor} d1
- * @param {!CanvasAgent.ResourceStateDescriptor} d2
- * @return {number}
- */
- function comparator(d1, d2)
- {
- var hasChildren1 = !!d1.values;
- var hasChildren2 = !!d2.values;
- if (hasChildren1 !== hasChildren2)
- return hasChildren1 ? 1 : -1;
- return String.naturalOrderComparator(d1.name, d2.name);
- }
- /**
- * @param {Array.<!CanvasAgent.ResourceStateDescriptor>|undefined} descriptors
- * @param {!WebInspector.DataGridNode} parent
- * @param {Object=} nameToOldChildren
- */
- function appendResourceStateDescriptors(descriptors, parent, nameToOldChildren)
- {
- descriptors = descriptors || [];
- descriptors.sort(comparator);
- var oldChildren = nameToOldChildren || {};
- for (var i = 0, n = descriptors.length; i < n; ++i) {
- var descriptor = descriptors[i];
- var childNode = this._createDataGridNode(descriptor);
- parent.appendChild(childNode);
- var oldChildrenItem = oldChildren[childNode.name] || {};
- var oldChildNode = oldChildrenItem.node;
- if (!oldChildNode || oldChildNode.element.textContent !== childNode.element.textContent)
- nodesToHighlight.push(childNode);
- appendResourceStateDescriptors.call(this, descriptor.values, childNode, oldChildrenItem.children);
- }
- }
- appendResourceStateDescriptors.call(this, resourceState.descriptors, rootNode, nameToOldGridNodes);
- var shouldHighlightChanges = (this._resourceKindId(this._currentResourceId) === this._resourceKindId(resourceState.id));
- this._currentResourceId = resourceState.id;
- this._restoreExpandedState();
- this._updateDataGridHighlights(shouldHighlightChanges ? nodesToHighlight : []);
- this._restoreScrollState();
- },
- /**
- * @param {!Array.<!WebInspector.DataGridNode>} nodes
- */
- _updateDataGridHighlights: function(nodes)
- {
- for (var i = 0, n = this._highlightedGridNodes.length; i < n; ++i) {
- var node = this._highlightedGridNodes[i];
- node.element.removeStyleClass("canvas-grid-node-highlighted");
- }
- this._highlightedGridNodes = nodes;
- for (var i = 0, n = this._highlightedGridNodes.length; i < n; ++i) {
- var node = this._highlightedGridNodes[i];
- node.element.addStyleClass("canvas-grid-node-highlighted");
- node.reveal();
- }
- },
- /**
- * @param {?CanvasAgent.ResourceId} resourceId
- * @return {string}
- */
- _resourceKindId: function(resourceId)
- {
- var description = (resourceId && this._resourceIdToDescription[resourceId]) || "";
- return description.replace(/\d+/g, "");
- },
- /**
- * @param {function(!WebInspector.DataGridNode, string):void} callback
- */
- _forEachGridNode: function(callback)
- {
- /**
- * @param {!WebInspector.DataGridNode} node
- * @param {string} key
- */
- function processRecursively(node, key)
- {
- for (var i = 0, child; child = node.children[i]; ++i) {
- var childKey = key + "#" + child.name;
- callback(child, childKey);
- processRecursively(child, childKey);
- }
- }
- processRecursively(this._stateGrid.rootNode(), "");
- },
- _saveExpandedState: function()
- {
- if (!this._currentResourceId)
- return;
- var expandedState = {};
- var key = this._resourceKindId(this._currentResourceId);
- this._gridNodesExpandedState[key] = expandedState;
- /**
- * @param {!WebInspector.DataGridNode} node
- * @param {string} key
- */
- function callback(node, key)
- {
- if (node.expanded)
- expandedState[key] = true;
- }
- this._forEachGridNode(callback);
- },
- _restoreExpandedState: function()
- {
- if (!this._currentResourceId)
- return;
- var key = this._resourceKindId(this._currentResourceId);
- var expandedState = this._gridNodesExpandedState[key];
- if (!expandedState)
- return;
- /**
- * @param {!WebInspector.DataGridNode} node
- * @param {string} key
- */
- function callback(node, key)
- {
- if (expandedState[key])
- node.expand();
- }
- this._forEachGridNode(callback);
- },
- _saveScrollState: function()
- {
- if (!this._currentResourceId)
- return;
- var key = this._resourceKindId(this._currentResourceId);
- this._gridScrollPositions[key] = {
- scrollTop: this._stateGrid.scrollContainer.scrollTop,
- scrollLeft: this._stateGrid.scrollContainer.scrollLeft
- };
- },
- _restoreScrollState: function()
- {
- if (!this._currentResourceId)
- return;
- var key = this._resourceKindId(this._currentResourceId);
- var scrollState = this._gridScrollPositions[key];
- if (!scrollState)
- return;
- this._stateGrid.scrollContainer.scrollTop = scrollState.scrollTop;
- this._stateGrid.scrollContainer.scrollLeft = scrollState.scrollLeft;
- },
- /**
- * @param {!CanvasAgent.ResourceStateDescriptor} descriptor
- * @return {!WebInspector.DataGridNode}
- */
- _createDataGridNode: function(descriptor)
- {
- var name = descriptor.name;
- var callArgument = descriptor.value;
- /** @type {!Element|string} */
- var valueElement = callArgument ? WebInspector.CanvasProfileDataGridHelper.createCallArgumentElement(callArgument) : "";
- /** @type {!Element|string} */
- var nameElement = name;
- if (typeof descriptor.enumValueForName !== "undefined")
- nameElement = WebInspector.CanvasProfileDataGridHelper.createEnumValueElement(name, +descriptor.enumValueForName);
- if (descriptor.isArray && descriptor.values) {
- if (typeof nameElement === "string")
- nameElement += "[" + descriptor.values.length + "]";
- else {
- var element = document.createElement("span");
- element.appendChild(nameElement);
- element.createTextChild("[" + descriptor.values.length + "]");
- nameElement = element;
- }
- }
- var data = {};
- data[0] = nameElement;
- data[1] = valueElement;
- var node = new WebInspector.DataGridNode(data);
- node.selectable = false;
- node.name = name;
- return node;
- },
- __proto__: WebInspector.View.prototype
- }
|