blob: d993b05b201b986e5da56c860a617e852d3d70a6 [file] [log] [blame]
/*
* 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();
}
};