| /* |
| * Copyright (C) 2017 Apple 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: |
| * 1. Redistributions of source code must retain the above copyright |
| * notice, this list of conditions and the following disclaimer. |
| * 2. 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. |
| * |
| * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS 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 APPLE INC. OR ITS 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. |
| */ |
| |
| WI.CanvasDetailsSidebarPanel = class CanvasDetailsSidebarPanel extends WI.DetailsSidebarPanel |
| { |
| constructor() |
| { |
| super("canvas", WI.UIString("Canvas")); |
| |
| this.element.classList.add("canvas"); |
| |
| this._canvas = null; |
| this._node = null; |
| |
| this._sections = []; |
| this._emptyContentPlaceholder = null; |
| } |
| |
| // Public |
| |
| inspect(objects) |
| { |
| if (!(objects instanceof Array)) |
| objects = [objects]; |
| |
| objects = objects.map((object) => { |
| if (object instanceof WI.Recording) |
| return object.source; |
| if (object instanceof WI.ShaderProgram) |
| return object.canvas; |
| return object; |
| }); |
| |
| this.canvas = objects.find((object) => object instanceof WI.Canvas); |
| |
| return !!this.canvas; |
| } |
| |
| get canvas() |
| { |
| return this._canvas; |
| } |
| |
| set canvas(canvas) |
| { |
| if (canvas === this._canvas) |
| return; |
| |
| if (this._node) { |
| this._node.removeEventListener(WI.DOMNode.Event.AttributeModified, this._refreshSourceSection, this); |
| this._node.removeEventListener(WI.DOMNode.Event.AttributeRemoved, this._refreshSourceSection, this); |
| |
| this._node = null; |
| } |
| |
| if (this._canvas) { |
| this._canvas.removeEventListener(WI.Canvas.Event.MemoryChanged, this._canvasMemoryChanged, this); |
| this._canvas.removeEventListener(WI.Canvas.Event.ExtensionEnabled, this._refreshExtensionsSection, this); |
| this._canvas.removeEventListener(WI.Canvas.Event.ClientNodesChanged, this._refreshClientsSection, this); |
| } |
| |
| this._canvas = canvas || null; |
| |
| if (this._canvas) { |
| this._canvas.addEventListener(WI.Canvas.Event.MemoryChanged, this._canvasMemoryChanged, this); |
| this._canvas.addEventListener(WI.Canvas.Event.ExtensionEnabled, this._refreshExtensionsSection, this); |
| this._canvas.addEventListener(WI.Canvas.Event.ClientNodesChanged, this._refreshClientsSection, this); |
| } |
| |
| this.needsLayout(); |
| } |
| |
| // Protected |
| |
| initialLayout() |
| { |
| super.initialLayout(); |
| |
| this._nameRow = new WI.DetailsSectionSimpleRow(WI.UIString("Name")); |
| this._typeRow = new WI.DetailsSectionSimpleRow(WI.UIString("Type")); |
| this._memoryRow = new WI.DetailsSectionSimpleRow(WI.UIString("Memory")); |
| this._memoryRow.tooltip = WI.UIString("Memory usage of this canvas"); |
| |
| let identitySection = new WI.DetailsSection("canvas-details", WI.UIString("Identity")); |
| identitySection.groups = [new WI.DetailsSectionGroup([this._nameRow, this._typeRow, this._memoryRow])]; |
| this._sections.push(identitySection); |
| |
| this._nodeRow = new WI.DetailsSectionSimpleRow(WI.UIString("Node")); |
| this._cssCanvasRow = new WI.DetailsSectionSimpleRow(WI.UIString("CSS Canvas")); |
| this._widthRow = new WI.DetailsSectionSimpleRow(WI.UIString("Width")); |
| this._heightRow = new WI.DetailsSectionSimpleRow(WI.UIString("Height")); |
| this._detachedRow = new WI.DetailsSectionSimpleRow(WI.UIString("Detached")); |
| |
| let sourceSection = new WI.DetailsSection("canvas-source", WI.UIString("Source")); |
| sourceSection.groups = [new WI.DetailsSectionGroup([this._nodeRow, this._cssCanvasRow, this._widthRow, this._heightRow, this._detachedRow])]; |
| this._sections.push(sourceSection); |
| |
| this._attributesDataGridRow = new WI.DetailsSectionDataGridRow(null, WI.UIString("No Attributes")); |
| |
| this._attributesSection = new WI.DetailsSection("canvas-attributes", WI.UIString("Attributes")); |
| this._attributesSection.groups = [new WI.DetailsSectionGroup([this._attributesDataGridRow])]; |
| this._attributesSection.element.hidden = true; |
| this._sections.push(this._attributesSection); |
| |
| this._extensionsSection = new WI.DetailsSection("canvas-extensions", WI.UIString("Extensions")); |
| this._extensionsSection.element.hidden = true; |
| this._sections.push(this._extensionsSection); |
| |
| this._clientNodesRow = new WI.DetailsSectionSimpleRow(WI.UIString("Nodes")); |
| |
| this._clientsSection = new WI.DetailsSection("canvas-clients", WI.UIString("Clients")); |
| this._clientsSection.groups = [new WI.DetailsSectionGroup([this._clientNodesRow])]; |
| this._clientsSection.element.hidden = true; |
| this._sections.push(this._clientsSection); |
| |
| const selectable = false; |
| let backtraceTreeOutline = new WI.TreeOutline(selectable); |
| backtraceTreeOutline.disclosureButtons = false; |
| this._backtraceTreeController = new WI.CallFrameTreeController(backtraceTreeOutline); |
| |
| let backtraceRow = new WI.DetailsSectionRow; |
| backtraceRow.element.appendChild(backtraceTreeOutline.element); |
| this._backtraceSection = new WI.DetailsSection("canvas-backtrace", WI.UIString("Backtrace")); |
| this._backtraceSection.groups = [new WI.DetailsSectionGroup([backtraceRow])]; |
| |
| this._backtraceSection.element.hidden = true; |
| this._sections.push(this._backtraceSection); |
| |
| this._emptyContentPlaceholder = WI.createMessageTextView(WI.UIString("No Canvas Selected")); |
| } |
| |
| layout() |
| { |
| super.layout(); |
| |
| this.contentView.element.removeChildren(); |
| |
| if (!this._canvas) { |
| this.contentView.element.appendChild(this._emptyContentPlaceholder); |
| return; |
| } |
| |
| this.contentView.element.append(...this._sections.map(section => section.element)); |
| |
| this._refreshIdentitySection(); |
| this._refreshSourceSection(); |
| this._refreshAttributesSection(); |
| this._refreshExtensionsSection(); |
| this._refreshClientsSection(); |
| this._refreshBacktraceSection(); |
| } |
| |
| sizeDidChange() |
| { |
| super.sizeDidChange(); |
| |
| // FIXME: <https://webkit.org/b/152269> Web Inspector: Convert DetailsSection classes to use View |
| this._attributesDataGridRow.sizeDidChange(); |
| } |
| |
| // Private |
| |
| _refreshIdentitySection() |
| { |
| this._nameRow.value = this._canvas.displayName; |
| this._typeRow.value = WI.Canvas.displayNameForContextType(this._canvas.contextType); |
| this._formatMemoryRow(); |
| } |
| |
| _refreshSourceSection() |
| { |
| if (!this.didInitialLayout) |
| return; |
| |
| let hideNode = this._canvas.cssCanvasName || this._canvas.contextType === WI.Canvas.ContextType.WebGPU; |
| |
| this._nodeRow.value = hideNode ? null : emDash; |
| this._cssCanvasRow.value = this._canvas.cssCanvasName || null; |
| this._widthRow.value = emDash; |
| this._heightRow.value = emDash; |
| this._detachedRow.value = null; |
| |
| this._canvas.requestNode().then((node) => { |
| if (!node) { |
| this._nodeRow.value = null; |
| return; |
| } |
| |
| if (node !== this._node) { |
| if (this._node) { |
| this._node.removeEventListener(WI.DOMNode.Event.AttributeModified, this._refreshSourceSection, this); |
| this._node.removeEventListener(WI.DOMNode.Event.AttributeRemoved, this._refreshSourceSection, this); |
| |
| this._node = null; |
| } |
| |
| this._node = node; |
| |
| this._node.addEventListener(WI.DOMNode.Event.AttributeModified, this._refreshSourceSection, this); |
| this._node.addEventListener(WI.DOMNode.Event.AttributeRemoved, this._refreshSourceSection, this); |
| } |
| |
| if (!hideNode) { |
| this._nodeRow.value = WI.linkifyNodeReference(this._node); |
| |
| if (!this._node.parentNode) |
| this._detachedRow.value = WI.UIString("Yes"); |
| } |
| |
| let setRowValueIfValidAttributeValue = (row, attribute) => { |
| let value = Number(this._node.getAttribute(attribute)); |
| if (!Number.isInteger(value) || value < 0) |
| return false; |
| |
| row.value = value; |
| return true; |
| }; |
| |
| let validWidth = setRowValueIfValidAttributeValue(this._widthRow, "width"); |
| let validHeight = setRowValueIfValidAttributeValue(this._heightRow, "height"); |
| if (!validWidth || !validHeight) { |
| // Since the "width" and "height" properties of canvas elements are more than just |
| // attributes, we need to invoke the getter for each to get the actual value. |
| // - https://html.spec.whatwg.org/multipage/canvas.html#attr-canvas-width |
| // - https://html.spec.whatwg.org/multipage/canvas.html#attr-canvas-height |
| WI.RemoteObject.resolveNode(node).then((remoteObject) => { |
| function setRowValueToPropertyValue(row, property) { |
| remoteObject.getProperty(property, (error, result, wasThrown) => { |
| if (!error && result.type === "number") |
| row.value = `${result.value}px`; |
| }); |
| } |
| |
| setRowValueToPropertyValue(this._widthRow, "width"); |
| setRowValueToPropertyValue(this._heightRow, "height"); |
| |
| remoteObject.release(); |
| }); |
| } |
| }); |
| } |
| |
| _refreshAttributesSection() |
| { |
| let hasAttributes = !isEmptyObject(this._canvas.contextAttributes); |
| this._attributesSection.element.hidden = !hasAttributes; |
| if (!hasAttributes) |
| return; |
| |
| let dataGrid = this._attributesDataGridRow.dataGrid; |
| if (!dataGrid) { |
| dataGrid = this._attributesDataGridRow.dataGrid = new WI.DataGrid({ |
| name: {title: WI.UIString("Name")}, |
| value: {title: WI.UIString("Value"), width: "30%"}, |
| }); |
| } |
| |
| dataGrid.removeChildren(); |
| |
| for (let attribute in this._canvas.contextAttributes) { |
| let data = {name: attribute, value: this._canvas.contextAttributes[attribute]}; |
| let dataGridNode = new WI.DataGridNode(data); |
| dataGrid.appendChild(dataGridNode); |
| } |
| |
| dataGrid.updateLayoutIfNeeded(); |
| } |
| |
| _refreshExtensionsSection() |
| { |
| let hasEnabledExtensions = this._canvas.extensions.size > 0; |
| this._extensionsSection.element.hidden = !hasEnabledExtensions; |
| if (!hasEnabledExtensions) |
| return; |
| |
| let element = document.createElement("ul"); |
| for (let extension of this._canvas.extensions) { |
| let listElement = element.appendChild(document.createElement("li")); |
| listElement.textContent = extension; |
| } |
| this._extensionsSection.groups = [{element}]; |
| } |
| |
| _refreshClientsSection() |
| { |
| if (!this.didInitialLayout) |
| return; |
| |
| if (!this._canvas.cssCanvasName && this._canvas.contextType !== WI.Canvas.ContextType.WebGPU) { |
| this._clientsSection.element.hidden = true; |
| return; |
| } |
| |
| this._clientNodesRow.value = emDash; |
| |
| this._clientsSection.element.hidden = false; |
| |
| this._canvas.requestClientNodes((clientNodes) => { |
| if (!clientNodes.length) |
| return; |
| |
| let fragment = document.createDocumentFragment(); |
| for (let clientNode of clientNodes) |
| fragment.appendChild(WI.linkifyNodeReference(clientNode)); |
| this._clientNodesRow.value = fragment; |
| }); |
| } |
| |
| _refreshBacktraceSection() |
| { |
| let callFrames = this._canvas.backtrace; |
| this._backtraceTreeController.callFrames = callFrames; |
| this._backtraceSection.element.hidden = !callFrames.length; |
| } |
| |
| _formatMemoryRow() |
| { |
| if (!this.didInitialLayout) |
| return; |
| |
| if (!this._canvas.memoryCost || isNaN(this._canvas.memoryCost)) { |
| this._memoryRow.value = emDash; |
| return; |
| } |
| |
| this._memoryRow.value = Number.bytesToString(this._canvas.memoryCost); |
| } |
| |
| _canvasMemoryChanged(event) |
| { |
| this._formatMemoryRow(); |
| } |
| }; |