diff options
Diffstat (limited to 'chromium/third_party/WebKit/Source/devtools/front_end/layers/PaintProfilerView.js')
-rw-r--r-- | chromium/third_party/WebKit/Source/devtools/front_end/layers/PaintProfilerView.js | 336 |
1 files changed, 336 insertions, 0 deletions
diff --git a/chromium/third_party/WebKit/Source/devtools/front_end/layers/PaintProfilerView.js b/chromium/third_party/WebKit/Source/devtools/front_end/layers/PaintProfilerView.js new file mode 100644 index 00000000000..1e3bf4ce1e6 --- /dev/null +++ b/chromium/third_party/WebKit/Source/devtools/front_end/layers/PaintProfilerView.js @@ -0,0 +1,336 @@ +/* + * 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 + * @param {function(string=)} showImageCallback + * @extends {WebInspector.HBox} + */ +WebInspector.PaintProfilerView = function(showImageCallback) +{ + WebInspector.View.call(this); + this.element.classList.add("paint-profiler-view"); + + this._showImageCallback = showImageCallback; + + this._canvas = this.element.createChild("canvas", "fill"); + this._context = this._canvas.getContext("2d"); + this._selectionWindow = new WebInspector.OverviewGrid.Window(this.element, this.element); + this._selectionWindow.addEventListener(WebInspector.OverviewGrid.Events.WindowChanged, this._onWindowChanged, this); + + this._innerBarWidth = 4 * window.devicePixelRatio; + this._minBarHeight = 4 * window.devicePixelRatio; + this._barPaddingWidth = 2 * window.devicePixelRatio; + this._outerBarWidth = this._innerBarWidth + this._barPaddingWidth; + + this._reset(); +} + +WebInspector.PaintProfilerView.Events = { + WindowChanged: "WindowChanged" +}; + +WebInspector.PaintProfilerView.prototype = { + onResize: function() + { + this._update(); + }, + + /** + * @param {?WebInspector.PaintProfilerSnapshot} snapshot + */ + setSnapshot: function(snapshot) + { + this._reset(); + this._snapshot = snapshot; + if (!this._snapshot) { + this._update(); + return; + } + snapshot.requestImage(null, null, this._showImageCallback); + snapshot.profile(onProfileDone.bind(this)); + /** + * @param {!Array.<!LayerTreeAgent.PaintProfile>=} profiles + * @this {WebInspector.PaintProfilerView} + */ + function onProfileDone(profiles) + { + this._profiles = profiles; + this._update(); + } + }, + + _update: function() + { + this._canvas.width = this.element.clientWidth * window.devicePixelRatio; + this._canvas.height = this.element.clientHeight * window.devicePixelRatio; + this._samplesPerBar = 0; + if (!this._profiles || !this._profiles.length) + return; + + var maxBars = Math.floor((this._canvas.width - 2 * this._barPaddingWidth) / this._outerBarWidth); + var sampleCount = this._profiles[0].length; + this._samplesPerBar = Math.ceil(sampleCount / maxBars); + var barCount = Math.floor(sampleCount / this._samplesPerBar); + + var maxBarTime = 0; + var barTimes = []; + for (var i = 0, lastBarIndex = 0, lastBarTime = 0; i < sampleCount;) { + for (var row = 0; row < this._profiles.length; row++) + lastBarTime += this._profiles[row][i]; + ++i; + if (i - lastBarIndex == this._samplesPerBar || i == sampleCount) { + // Normalize by total number of samples accumulated. + lastBarTime /= this._profiles.length * (i - lastBarIndex); + barTimes.push(lastBarTime); + if (lastBarTime > maxBarTime) + maxBarTime = lastBarTime; + lastBarTime = 0; + lastBarIndex = i; + } + } + const paddingHeight = 4 * window.devicePixelRatio; + var scale = (this._canvas.height - paddingHeight - this._minBarHeight) / maxBarTime; + this._context.fillStyle = "rgba(110, 180, 110, 0.7)"; + for (var i = 0; i < barTimes.length; ++i) + this._renderBar(i, barTimes[i] * scale + this._minBarHeight); + }, + + /** + * @param {number} index + * @param {number} height + */ + _renderBar: function(index, height) + { + var x = this._barPaddingWidth + index * this._outerBarWidth; + var y = this._canvas.height - height; + this._context.fillRect(x, y, this._innerBarWidth, height); + }, + + _onWindowChanged: function() + { + if (this._updateImageTimer) + return; + this._updateImageTimer = setTimeout(this._updateImage.bind(this), 100); + this.dispatchEventToListeners(WebInspector.PaintProfilerView.Events.WindowChanged); + }, + + /** + * @return {{left: number, right: number}} + */ + windowBoundaries: function() + { + var screenLeft = this._selectionWindow.windowLeft * this._canvas.width; + var screenRight = this._selectionWindow.windowRight * this._canvas.width; + var barLeft = Math.floor((screenLeft - this._barPaddingWidth) / this._outerBarWidth); + var barRight = Math.floor((screenRight - this._barPaddingWidth + this._innerBarWidth)/ this._outerBarWidth); + var stepLeft = Math.max(0, barLeft * this._samplesPerBar); + var stepRight = Math.min(barRight * this._samplesPerBar, this._profiles[0].length); + + return {left: stepLeft, right: stepRight}; + }, + + _updateImage: function() + { + delete this._updateImageTimer; + if (!this._profiles || !this._profiles.length) + return; + + var window = this.windowBoundaries(); + this._snapshot.requestImage(window.left, window.right, this._showImageCallback); + }, + + _reset: function() + { + this._snapshot = null; + this._profiles = null; + this._selectionWindow.reset(); + }, + + __proto__: WebInspector.HBox.prototype +}; + +/** + * @constructor + * @extends {WebInspector.VBox} + */ +WebInspector.PaintProfilerCommandLogView = function() +{ + WebInspector.VBox.call(this); + this.setMinimumSize(100, 25); + this.element.classList.add("outline-disclosure"); + var sidebarTreeElement = this.element.createChild("ol", "sidebar-tree"); + this.sidebarTree = new TreeOutline(sidebarTreeElement); + this._popoverHelper = new WebInspector.ObjectPopoverHelper(this.element, this._getHoverAnchor.bind(this), this._resolveObjectForPopover.bind(this), undefined, true); + this._reset(); +} + +WebInspector.PaintProfilerCommandLogView.prototype = { + /** + * @param {?WebInspector.PaintProfilerSnapshot} snapshot + */ + setSnapshot: function(snapshot) + { + this._reset(); + if (!snapshot) { + this.updateWindow(); + return; + } + snapshot.commandLog(onCommandLogDone.bind(this)); + + /** + * @param {!Array.<!Object>=} log + * @this {WebInspector.PaintProfilerCommandLogView} + */ + function onCommandLogDone(log) + { + this._log = log; + this.updateWindow(); + } + }, + + /** + * @param {number=} stepLeft + * @param {number=} stepRight + */ + updateWindow: function(stepLeft, stepRight) + { + var log = this._log; + stepLeft = stepLeft || 0; + stepRight = stepRight || log.length - 1; + this.sidebarTree.removeChildren(); + for (var i = stepLeft; i <= stepRight; ++i) { + var node = new WebInspector.LogTreeElement(log[i]); + this.sidebarTree.appendChild(node); + } + }, + + _reset: function() + { + this._log = []; + }, + + /** + * @param {!Element} target + * @return {!Element} + */ + _getHoverAnchor: function(target) + { + return /** @type {!Element} */ (target.enclosingNodeOrSelfWithNodeName("span")); + }, + + /** + * @param {!Element} element + * @param {function(!WebInspector.RemoteObject, boolean, !Element=):undefined} showCallback + */ + _resolveObjectForPopover: function(element, showCallback) + { + var liElement = element.enclosingNodeOrSelfWithNodeName("li"); + var logItem = liElement.treeElement.representedObject; + var obj = {"method": logItem.method}; + if (logItem.params) + obj.params = logItem.params; + showCallback(WebInspector.RemoteObject.fromLocalObject(obj), false); + }, + + __proto__: WebInspector.VBox.prototype +}; + +/** + * @constructor + * @param {!Object} logItem + * @extends {TreeElement} + */ +WebInspector.LogTreeElement = function(logItem) +{ + TreeElement.call(this, "", logItem); + this._update(); +} + +WebInspector.LogTreeElement.prototype = { + /** + * @param {!Object} param + * @param {string} name + * @return {string} + */ + _paramToString: function(param, name) + { + if (typeof param !== "object") + return typeof param === "string" && param.length > 100 ? name : JSON.stringify(param); + var str = ""; + var keyCount = 0; + for (var key in param) { + if (++keyCount > 4 || typeof param[key] === "object" || (typeof param[key] === "string" && param[key].length > 100)) + return name; + if (str) + str += ", "; + str += param[key]; + } + return str; + }, + + /** + * @param {!Object} params + * @return {string} + */ + _paramsToString: function(params) + { + var str = ""; + for (var key in params) { + if (str) + str += ", "; + str += this._paramToString(params[key], key); + } + return str; + }, + + _update: function() + { + var logItem = this.representedObject; + var title = document.createDocumentFragment(); + title.createChild("div", "selection"); + var span = title.createChild("span"); + var textContent = logItem.method; + if (logItem.params) + textContent += "(" + this._paramsToString(logItem.params) + ")"; + span.textContent = textContent; + this.title = title; + }, + + /** + * @param {boolean} hovered + */ + setHovered: function(hovered) + { + this.listItemElement.classList.toggle("hovered", hovered); + }, + + __proto__: TreeElement.prototype +}; |