/* * Copyright (C) 2013-2016 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.DOMNodeDetailsSidebarPanel = class DOMNodeDetailsSidebarPanel extends WI.DOMDetailsSidebarPanel { constructor() { super("dom-node-details", WI.UIString("Node")); this._eventListenerGroupingMethodSetting = new WI.Setting("dom-node-event-listener-grouping-method", WI.DOMNodeDetailsSidebarPanel.EventListenerGroupingMethod.Event); this.element.classList.add("dom-node"); this._nodeRemoteObject = null; } // Public closed() { WI.domTreeManager.removeEventListener(null, null, this); super.closed(); } // Protected initialLayout() { super.initialLayout(); WI.domTreeManager.addEventListener(WI.DOMTreeManager.Event.AttributeModified, this._attributesChanged, this); WI.domTreeManager.addEventListener(WI.DOMTreeManager.Event.AttributeRemoved, this._attributesChanged, this); WI.domTreeManager.addEventListener(WI.DOMTreeManager.Event.CharacterDataModified, this._characterDataModified, this); WI.domTreeManager.addEventListener(WI.DOMTreeManager.Event.CustomElementStateChanged, this._customElementStateChanged, this); this._identityNodeTypeRow = new WI.DetailsSectionSimpleRow(WI.UIString("Type")); this._identityNodeNameRow = new WI.DetailsSectionSimpleRow(WI.UIString("Name")); this._identityNodeValueRow = new WI.DetailsSectionSimpleRow(WI.UIString("Value")); this._identityNodeContentSecurityPolicyHashRow = new WI.DetailsSectionSimpleRow(WI.UIString("CSP Hash")); var identityGroup = new WI.DetailsSectionGroup([this._identityNodeTypeRow, this._identityNodeNameRow, this._identityNodeValueRow, this._identityNodeContentSecurityPolicyHashRow]); var identitySection = new WI.DetailsSection("dom-node-identity", WI.UIString("Identity"), [identityGroup]); this._attributesDataGridRow = new WI.DetailsSectionDataGridRow(null, WI.UIString("No Attributes")); var attributesGroup = new WI.DetailsSectionGroup([this._attributesDataGridRow]); var attributesSection = new WI.DetailsSection("dom-node-attributes", WI.UIString("Attributes"), [attributesGroup]); this._propertiesRow = new WI.DetailsSectionRow; var propertiesGroup = new WI.DetailsSectionGroup([this._propertiesRow]); var propertiesSection = new WI.DetailsSection("dom-node-properties", WI.UIString("Properties"), [propertiesGroup]); let eventListenersFilterElement = useSVGSymbol("Images/FilterFieldGlyph.svg", "filter", WI.UIString("Grouping Method")); let eventListenersGroupMethodSelectElement = eventListenersFilterElement.appendChild(document.createElement("select")); eventListenersGroupMethodSelectElement.addEventListener("change", (event) => { this._eventListenerGroupingMethodSetting.value = eventListenersGroupMethodSelectElement.value; this._refreshEventListeners(); }); function createOption(text, value) { let optionElement = eventListenersGroupMethodSelectElement.appendChild(document.createElement("option")); optionElement.value = value; optionElement.textContent = text; } createOption(WI.UIString("Group by Event"), WI.DOMNodeDetailsSidebarPanel.EventListenerGroupingMethod.Event); createOption(WI.UIString("Group by Node"), WI.DOMNodeDetailsSidebarPanel.EventListenerGroupingMethod.Node); eventListenersGroupMethodSelectElement.value = this._eventListenerGroupingMethodSetting.value; this._eventListenersSectionGroup = new WI.DetailsSectionGroup; let eventListenersSection = new WI.DetailsSection("dom-node-event-listeners", WI.UIString("Event Listeners"), [this._eventListenersSectionGroup], eventListenersFilterElement); this.contentView.element.appendChild(identitySection.element); this.contentView.element.appendChild(attributesSection.element); this.contentView.element.appendChild(propertiesSection.element); this.contentView.element.appendChild(eventListenersSection.element); if (this._accessibilitySupported()) { this._accessibilityEmptyRow = new WI.DetailsSectionRow(WI.UIString("No Accessibility Information")); this._accessibilityNodeActiveDescendantRow = new WI.DetailsSectionSimpleRow(WI.UIString("Shared Focus")); this._accessibilityNodeBusyRow = new WI.DetailsSectionSimpleRow(WI.UIString("Busy")); this._accessibilityNodeCheckedRow = new WI.DetailsSectionSimpleRow(WI.UIString("Checked")); this._accessibilityNodeChildrenRow = new WI.DetailsSectionSimpleRow(WI.UIString("Children")); this._accessibilityNodeControlsRow = new WI.DetailsSectionSimpleRow(WI.UIString("Controls")); this._accessibilityNodeCurrentRow = new WI.DetailsSectionSimpleRow(WI.UIString("Current")); this._accessibilityNodeDisabledRow = new WI.DetailsSectionSimpleRow(WI.UIString("Disabled")); this._accessibilityNodeExpandedRow = new WI.DetailsSectionSimpleRow(WI.UIString("Expanded")); this._accessibilityNodeFlowsRow = new WI.DetailsSectionSimpleRow(WI.UIString("Flows")); this._accessibilityNodeFocusedRow = new WI.DetailsSectionSimpleRow(WI.UIString("Focused")); this._accessibilityNodeHeadingLevelRow = new WI.DetailsSectionSimpleRow(WI.UIString("Heading Level")); this._accessibilityNodehierarchyLevelRow = new WI.DetailsSectionSimpleRow(WI.UIString("Hierarchy Level")); this._accessibilityNodeIgnoredRow = new WI.DetailsSectionSimpleRow(WI.UIString("Ignored")); this._accessibilityNodeInvalidRow = new WI.DetailsSectionSimpleRow(WI.UIString("Invalid")); this._accessibilityNodeLiveRegionStatusRow = new WI.DetailsSectionSimpleRow(WI.UIString("Live")); this._accessibilityNodeMouseEventRow = new WI.DetailsSectionSimpleRow(""); this._accessibilityNodeLabelRow = new WI.DetailsSectionSimpleRow(WI.UIString("Label")); this._accessibilityNodeOwnsRow = new WI.DetailsSectionSimpleRow(WI.UIString("Owns")); this._accessibilityNodeParentRow = new WI.DetailsSectionSimpleRow(WI.UIString("Parent")); this._accessibilityNodePressedRow = new WI.DetailsSectionSimpleRow(WI.UIString("Pressed")); this._accessibilityNodeReadonlyRow = new WI.DetailsSectionSimpleRow(WI.UIString("Readonly")); this._accessibilityNodeRequiredRow = new WI.DetailsSectionSimpleRow(WI.UIString("Required")); this._accessibilityNodeRoleRow = new WI.DetailsSectionSimpleRow(WI.UIString("Role")); this._accessibilityNodeSelectedRow = new WI.DetailsSectionSimpleRow(WI.UIString("Selected")); this._accessibilityNodeSelectedChildrenRow = new WI.DetailsSectionSimpleRow(WI.UIString("Selected Items")); this._accessibilityGroup = new WI.DetailsSectionGroup([this._accessibilityEmptyRow]); var accessibilitySection = new WI.DetailsSection("dom-node-accessibility", WI.UIString("Accessibility"), [this._accessibilityGroup]); this.contentView.element.appendChild(accessibilitySection.element); } } layout() { super.layout(); if (!this.domNode) return; this._refreshIdentity(); this._refreshAttributes(); this._refreshProperties(); this._refreshEventListeners(); this._refreshAccessibility(); } sizeDidChange() { super.sizeDidChange(); // FIXME: Web Inspector: Convert DetailsSection classes to use View this._attributesDataGridRow.sizeDidChange(); } attached() { super.attached(); WI.DOMNode.addEventListener(WI.DOMNode.Event.EventListenersChanged, this._eventListenersChanged, this); } detached() { WI.DOMNode.removeEventListener(WI.DOMNode.Event.EventListenersChanged, this._eventListenersChanged, this); super.detached(); } // Private _accessibilitySupported() { return window.DOMAgent && DOMAgent.getAccessibilityPropertiesForNode; } _refreshIdentity() { const domNode = this.domNode; this._identityNodeTypeRow.value = this._nodeTypeDisplayName(); this._identityNodeNameRow.value = domNode.nodeNameInCorrectCase(); this._identityNodeValueRow.value = domNode.nodeValue(); this._identityNodeContentSecurityPolicyHashRow.value = domNode.contentSecurityPolicyHash(); } _refreshAttributes() { let domNode = this.domNode; if (!domNode || !domNode.hasAttributes()) { // Remove the DataGrid to show the placeholder text. this._attributesDataGridRow.dataGrid = null; return; } let dataGrid = this._attributesDataGridRow.dataGrid; if (!dataGrid) { const columns = { name: {title: WI.UIString("Name"), width: "30%"}, value: {title: WI.UIString("Value")}, }; dataGrid = this._attributesDataGridRow.dataGrid = new WI.DataGrid(columns); } dataGrid.removeChildren(); let attributes = domNode.attributes(); attributes.sort((a, b) => a.name.extendedLocaleCompare(b.name)); for (let attribute of attributes) { let dataGridNode = new WI.EditableDataGridNode(attribute); dataGridNode.addEventListener(WI.EditableDataGridNode.Event.ValueChanged, this._attributeNodeValueChanged, this); dataGrid.appendChild(dataGridNode); } dataGrid.updateLayoutIfNeeded(); } _refreshProperties() { if (this._nodeRemoteObject) { this._nodeRemoteObject.release(); this._nodeRemoteObject = null; } let domNode = this.domNode; RuntimeAgent.releaseObjectGroup(WI.DOMNodeDetailsSidebarPanel.PropertiesObjectGroupName); WI.RemoteObject.resolveNode(domNode, WI.DOMNodeDetailsSidebarPanel.PropertiesObjectGroupName).then((object) => { // Bail if the DOM node changed while we were waiting for the async response. if (this.domNode !== domNode) return; this._nodeRemoteObject = object; function inspectedPage_node_collectPrototypes() { // This builds an object with numeric properties. This is easier than dealing with arrays // with the way RemoteObject works. Start at 1 since we use parseInt later and parseInt // returns 0 for non-numeric strings make it ambiguous. var prototype = this; var result = []; var counter = 1; while (prototype) { result[counter++] = prototype; prototype = prototype.__proto__; } return result; } const args = undefined; const generatePreview = false; object.callFunction(inspectedPage_node_collectPrototypes, args, generatePreview, nodePrototypesReady.bind(this)); }); function nodePrototypesReady(error, object, wasThrown) { if (error || wasThrown || !object) return; // Bail if the DOM node changed while we were waiting for the async response. if (this.domNode !== domNode) return; object.deprecatedGetOwnProperties(fillSection.bind(this)); } function fillSection(prototypes) { if (!prototypes) return; // Bail if the DOM node changed while we were waiting for the async response. if (this.domNode !== domNode) return; let element = this._propertiesRow.element; element.removeChildren(); let propertyPath = new WI.PropertyPath(this._nodeRemoteObject, "node"); let initialSection = true; for (let i = 0; i < prototypes.length; ++i) { // The only values we care about are numeric, as assigned in collectPrototypes. if (!parseInt(prototypes[i].name, 10)) continue; let prototype = prototypes[i].value; let prototypeName = prototype.description; let title = prototypeName; if (/Prototype$/.test(title)) { prototypeName = prototypeName.replace(/Prototype$/, ""); title = prototypeName + WI.UIString(" (Prototype)"); } else if (title === "Object") title = title + WI.UIString(" (Prototype)"); let mode = initialSection ? WI.ObjectTreeView.Mode.Properties : WI.ObjectTreeView.Mode.PureAPI; let objectTree = new WI.ObjectTreeView(prototype, mode, propertyPath); objectTree.showOnlyProperties(); objectTree.setPrototypeNameOverride(prototypeName); let detailsSection = new WI.DetailsSection(prototype.description.hash + "-prototype-properties", title, null, null, true); detailsSection.groups[0].rows = [new WI.ObjectPropertiesDetailSectionRow(objectTree, detailsSection)]; element.appendChild(detailsSection.element); initialSection = false; } } } _refreshEventListeners() { var domNode = this.domNode; if (!domNode) return; function createEventListenerSection(title, eventListeners, options = {}) { let groups = eventListeners.map((eventListener) => new WI.EventListenerSectionGroup(eventListener, options)); const optionsElement = null; const defaultCollapsedSettingValue = true; let section = new WI.DetailsSection(`${title}-event-listener-section`, title, groups, optionsElement, defaultCollapsedSettingValue); section.element.classList.add("event-listener-section"); return section; } function generateGroupsByEvent(eventListeners) { let eventListenerTypes = new Map; for (let eventListener of eventListeners) { eventListener.node = WI.domTreeManager.nodeForId(eventListener.nodeId); let eventListenersForType = eventListenerTypes.get(eventListener.type); if (!eventListenersForType) eventListenerTypes.set(eventListener.type, eventListenersForType = []); eventListenersForType.push(eventListener); } let rows = []; let types = Array.from(eventListenerTypes.keys()); types.sort(); for (let type of types) rows.push(createEventListenerSection(type, eventListenerTypes.get(type), {hideType: true})); return rows; } function generateGroupsByNode(eventListeners) { let eventListenerNodes = new Map; for (let eventListener of eventListeners) { eventListener.node = WI.domTreeManager.nodeForId(eventListener.nodeId); let eventListenersForNode = eventListenerNodes.get(eventListener.node); if (!eventListenersForNode) eventListenerNodes.set(eventListener.node, eventListenersForNode = []); eventListenersForNode.push(eventListener); } let rows = []; let currentNode = domNode; do { let eventListenersForNode = eventListenerNodes.get(currentNode); if (!eventListenersForNode) continue; eventListenersForNode.sort((a, b) => a.type.toLowerCase().extendedLocaleCompare(b.type.toLowerCase())); rows.push(createEventListenerSection(currentNode.displayName, eventListenersForNode, {hideNode: true})); } while (currentNode = currentNode.parentNode); return rows; } function eventListenersCallback(error, eventListeners) { if (error) return; // Bail if the DOM node changed while we were waiting for the async response. if (this.domNode !== domNode) return; if (!eventListeners.length) { var emptyRow = new WI.DetailsSectionRow(WI.UIString("No Event Listeners")); emptyRow.showEmptyMessage(); this._eventListenersSectionGroup.rows = [emptyRow]; return; } switch (this._eventListenerGroupingMethodSetting.value) { case WI.DOMNodeDetailsSidebarPanel.EventListenerGroupingMethod.Event: this._eventListenersSectionGroup.rows = generateGroupsByEvent.call(this, eventListeners); break; case WI.DOMNodeDetailsSidebarPanel.EventListenerGroupingMethod.Node: this._eventListenersSectionGroup.rows = generateGroupsByNode.call(this, eventListeners); break; } } domNode.getEventListeners(eventListenersCallback.bind(this)); } _refreshAccessibility() { if (!this._accessibilitySupported()) return; var domNode = this.domNode; if (!domNode) return; var properties = {}; function booleanValueToLocalizedStringIfTrue(property) { if (properties[property]) return WI.UIString("Yes"); return ""; } function booleanValueToLocalizedStringIfPropertyDefined(property) { if (properties[property] !== undefined) { if (properties[property]) return WI.UIString("Yes"); else return WI.UIString("No"); } return ""; } function linkForNodeId(nodeId) { var link = null; if (nodeId !== undefined && typeof nodeId === "number") { var node = WI.domTreeManager.nodeForId(nodeId); if (node) link = WI.linkifyAccessibilityNodeReference(node); } return link; } function linkListForNodeIds(nodeIds) { if (!nodeIds) return null; const itemsToShow = 5; let hasLinks = false; let listItemCount = 0; let container = document.createElement("div"); container.classList.add("list-container"); let linkList = container.createChild("ul", "node-link-list"); let initiallyHiddenItems = []; for (let nodeId of nodeIds) { let node = WI.domTreeManager.nodeForId(nodeId); if (!node) continue; let link = WI.linkifyAccessibilityNodeReference(node); hasLinks = true; let li = linkList.createChild("li"); li.appendChild(link); if (listItemCount >= itemsToShow) { li.hidden = true; initiallyHiddenItems.push(li); } listItemCount++; } container.appendChild(linkList); if (listItemCount > itemsToShow) { let moreNodesButton = container.createChild("button", "expand-list-button"); moreNodesButton.textContent = WI.UIString("%d More\u2026").format(listItemCount - itemsToShow); moreNodesButton.addEventListener("click", () => { initiallyHiddenItems.forEach((element) => { element.hidden = false; }); moreNodesButton.remove(); }); } if (hasLinks) return container; return null; } function accessibilityPropertiesCallback(accessibilityProperties) { if (this.domNode !== domNode) return; // Make sure the current set of properties is available in the closure scope for the helper functions. properties = accessibilityProperties; if (accessibilityProperties && accessibilityProperties.exists) { var activeDescendantLink = linkForNodeId(accessibilityProperties.activeDescendantNodeId); var busy = booleanValueToLocalizedStringIfPropertyDefined("busy"); var checked = ""; if (accessibilityProperties.checked !== undefined) { if (accessibilityProperties.checked === DOMAgent.AccessibilityPropertiesChecked.True) checked = WI.UIString("Yes"); else if (accessibilityProperties.checked === DOMAgent.AccessibilityPropertiesChecked.Mixed) checked = WI.UIString("Mixed"); else // DOMAgent.AccessibilityPropertiesChecked.False checked = WI.UIString("No"); } // Accessibility tree children are not a 1:1 mapping with DOM tree children. var childNodeLinkList = linkListForNodeIds(accessibilityProperties.childNodeIds); var controlledNodeLinkList = linkListForNodeIds(accessibilityProperties.controlledNodeIds); var current = ""; switch (accessibilityProperties.current) { case DOMAgent.AccessibilityPropertiesCurrent.True: current = WI.UIString("True"); break; case DOMAgent.AccessibilityPropertiesCurrent.Page: current = WI.UIString("Page"); break; case DOMAgent.AccessibilityPropertiesCurrent.Location: current = WI.UIString("Location"); break; case DOMAgent.AccessibilityPropertiesCurrent.Step: current = WI.UIString("Step"); break; case DOMAgent.AccessibilityPropertiesCurrent.Date: current = WI.UIString("Date"); break; case DOMAgent.AccessibilityPropertiesCurrent.Time: current = WI.UIString("Time"); break; default: current = ""; } var disabled = booleanValueToLocalizedStringIfTrue("disabled"); var expanded = booleanValueToLocalizedStringIfPropertyDefined("expanded"); var flowedNodeLinkList = linkListForNodeIds(accessibilityProperties.flowedNodeIds); var focused = booleanValueToLocalizedStringIfPropertyDefined("focused"); var ignored = ""; if (accessibilityProperties.ignored) { ignored = WI.UIString("Yes"); if (accessibilityProperties.hidden) ignored = WI.UIString("%s (hidden)").format(ignored); else if (accessibilityProperties.ignoredByDefault) ignored = WI.UIString("%s (default)").format(ignored); } var invalid = ""; if (accessibilityProperties.invalid === DOMAgent.AccessibilityPropertiesInvalid.True) invalid = WI.UIString("Yes"); else if (accessibilityProperties.invalid === DOMAgent.AccessibilityPropertiesInvalid.Grammar) invalid = WI.UIString("Grammar"); else if (accessibilityProperties.invalid === DOMAgent.AccessibilityPropertiesInvalid.Spelling) invalid = WI.UIString("Spelling"); var label = accessibilityProperties.label; var liveRegionStatus = ""; var liveRegionStatusNode = null; var liveRegionStatusToken = accessibilityProperties.liveRegionStatus; switch (liveRegionStatusToken) { case DOMAgent.AccessibilityPropertiesLiveRegionStatus.Assertive: liveRegionStatus = WI.UIString("Assertive"); break; case DOMAgent.AccessibilityPropertiesLiveRegionStatus.Polite: liveRegionStatus = WI.UIString("Polite"); break; default: liveRegionStatus = ""; } if (liveRegionStatus) { var liveRegionRelevant = accessibilityProperties.liveRegionRelevant; // Append @aria-relevant values. E.g. "Live: Assertive (Additions, Text)". if (liveRegionRelevant && liveRegionRelevant.length) { // @aria-relevant="all" is exposed as ["additions","removals","text"], in order. // This order is controlled in WebCore and expected in WebInspectorUI. if (liveRegionRelevant.length === 3 && liveRegionRelevant[0] === DOMAgent.LiveRegionRelevant.Additions && liveRegionRelevant[1] === DOMAgent.LiveRegionRelevant.Removals && liveRegionRelevant[2] === DOMAgent.LiveRegionRelevant.Text) liveRegionRelevant = [WI.UIString("All Changes")]; else { // Reassign localized strings in place: ["additions","text"] becomes ["Additions","Text"]. liveRegionRelevant = liveRegionRelevant.map(function(value) { switch (value) { case DOMAgent.LiveRegionRelevant.Additions: return WI.UIString("Additions"); case DOMAgent.LiveRegionRelevant.Removals: return WI.UIString("Removals"); case DOMAgent.LiveRegionRelevant.Text: return WI.UIString("Text"); default: // If WebCore sends a new unhandled value, display as a String. return "\"" + value + "\""; } }); } liveRegionStatus += " (" + liveRegionRelevant.join(", ") + ")"; } // Clarify @aria-atomic if necessary. if (accessibilityProperties.liveRegionAtomic) { liveRegionStatusNode = document.createElement("div"); liveRegionStatusNode.className = "value-with-clarification"; liveRegionStatusNode.setAttribute("role", "text"); liveRegionStatusNode.append(liveRegionStatus); var clarificationNode = document.createElement("div"); clarificationNode.className = "clarification"; clarificationNode.append(WI.UIString("Region announced in its entirety.")); liveRegionStatusNode.appendChild(clarificationNode); } } var mouseEventNodeId = accessibilityProperties.mouseEventNodeId; var mouseEventTextValue = ""; var mouseEventNodeLink = null; if (mouseEventNodeId) { if (mouseEventNodeId === accessibilityProperties.nodeId) mouseEventTextValue = WI.UIString("Yes"); else mouseEventNodeLink = linkForNodeId(mouseEventNodeId); } var ownedNodeLinkList = linkListForNodeIds(accessibilityProperties.ownedNodeIds); // Accessibility tree parent is not a 1:1 mapping with the DOM tree parent. var parentNodeLink = linkForNodeId(accessibilityProperties.parentNodeId); var pressed = booleanValueToLocalizedStringIfPropertyDefined("pressed"); var readonly = booleanValueToLocalizedStringIfTrue("readonly"); var required = booleanValueToLocalizedStringIfPropertyDefined("required"); var role = accessibilityProperties.role; let hasPopup = accessibilityProperties.isPopupButton; let roleType = null; let buttonType = null; let buttonTypePopupString = WI.UIString("popup"); let buttonTypeToggleString = WI.UIString("toggle"); let buttonTypePopupToggleString = WI.UIString("popup, toggle"); if (role === "" || role === "unknown") role = WI.UIString("No matching ARIA role"); else if (role) { if (role === "button") { if (pressed) buttonType = buttonTypeToggleString; // In cases where an element is a toggle button, but it also has // aria-haspopup, we concatenate the button types. If it is just // a popup button, we only include "popup". if (hasPopup) buttonType = buttonType ? buttonTypePopupToggleString : buttonTypePopupString; } if (!domNode.getAttribute("role")) roleType = WI.UIString("default"); else if (buttonType || domNode.getAttribute("role") !== role) roleType = WI.UIString("computed"); if (buttonType && roleType) role = WI.UIString("%s (%s, %s)").format(role, buttonType, roleType); else if (roleType || buttonType) { let extraInfo = roleType || buttonType; role = WI.UIString("%s (%s)").format(role, extraInfo); } } var selected = booleanValueToLocalizedStringIfTrue("selected"); var selectedChildNodeLinkList = linkListForNodeIds(accessibilityProperties.selectedChildNodeIds); var headingLevel = accessibilityProperties.headingLevel; var hierarchyLevel = accessibilityProperties.hierarchyLevel; // Assign all the properties to their respective views. this._accessibilityNodeActiveDescendantRow.value = activeDescendantLink || ""; this._accessibilityNodeBusyRow.value = busy; this._accessibilityNodeCheckedRow.value = checked; this._accessibilityNodeChildrenRow.value = childNodeLinkList || ""; this._accessibilityNodeControlsRow.value = controlledNodeLinkList || ""; this._accessibilityNodeCurrentRow.value = current; this._accessibilityNodeDisabledRow.value = disabled; this._accessibilityNodeExpandedRow.value = expanded; this._accessibilityNodeFlowsRow.value = flowedNodeLinkList || ""; this._accessibilityNodeFocusedRow.value = focused; this._accessibilityNodeHeadingLevelRow.value = headingLevel || ""; this._accessibilityNodehierarchyLevelRow.value = hierarchyLevel || ""; this._accessibilityNodeIgnoredRow.value = ignored; this._accessibilityNodeInvalidRow.value = invalid; this._accessibilityNodeLabelRow.value = label; this._accessibilityNodeLiveRegionStatusRow.value = liveRegionStatusNode || liveRegionStatus; // Row label changes based on whether the value is a delegate node link. this._accessibilityNodeMouseEventRow.label = mouseEventNodeLink ? WI.UIString("Click Listener") : WI.UIString("Clickable"); this._accessibilityNodeMouseEventRow.value = mouseEventNodeLink || mouseEventTextValue; this._accessibilityNodeOwnsRow.value = ownedNodeLinkList || ""; this._accessibilityNodeParentRow.value = parentNodeLink || ""; this._accessibilityNodePressedRow.value = pressed; this._accessibilityNodeReadonlyRow.value = readonly; this._accessibilityNodeRequiredRow.value = required; this._accessibilityNodeRoleRow.value = role; this._accessibilityNodeSelectedRow.value = selected; this._accessibilityNodeSelectedChildrenRow.label = WI.UIString("Selected Items"); this._accessibilityNodeSelectedChildrenRow.value = selectedChildNodeLinkList || ""; if (selectedChildNodeLinkList && accessibilityProperties.selectedChildNodeIds.length === 1) this._accessibilityNodeSelectedChildrenRow.label = WI.UIString("Selected Item"); // Display order, not alphabetical as above. this._accessibilityGroup.rows = [ // Global properties for all elements. this._accessibilityNodeIgnoredRow, this._accessibilityNodeRoleRow, this._accessibilityNodeLabelRow, this._accessibilityNodeParentRow, this._accessibilityNodeActiveDescendantRow, this._accessibilityNodeSelectedChildrenRow, this._accessibilityNodeChildrenRow, this._accessibilityNodeOwnsRow, this._accessibilityNodeControlsRow, this._accessibilityNodeFlowsRow, this._accessibilityNodeMouseEventRow, this._accessibilityNodeFocusedRow, this._accessibilityNodeHeadingLevelRow, this._accessibilityNodehierarchyLevelRow, this._accessibilityNodeBusyRow, this._accessibilityNodeLiveRegionStatusRow, this._accessibilityNodeCurrentRow, // Properties exposed for all input-type elements. this._accessibilityNodeDisabledRow, this._accessibilityNodeInvalidRow, this._accessibilityNodeRequiredRow, // Role-specific properties. this._accessibilityNodeCheckedRow, this._accessibilityNodeExpandedRow, this._accessibilityNodePressedRow, this._accessibilityNodeReadonlyRow, this._accessibilityNodeSelectedRow ]; this._accessibilityEmptyRow.hideEmptyMessage(); } else { this._accessibilityGroup.rows = [this._accessibilityEmptyRow]; this._accessibilityEmptyRow.showEmptyMessage(); } } domNode.accessibilityProperties(accessibilityPropertiesCallback.bind(this)); } _eventListenersChanged(event) { if (event.target === this.domNode || event.target.isAncestor(this.domNode)) this._refreshEventListeners(); } _attributesChanged(event) { if (event.data.node !== this.domNode) return; this._refreshAttributes(); this._refreshAccessibility(); } _attributeNodeValueChanged(event) { let change = event.data; let data = event.target.data; if (change.columnIdentifier === "name") { this.domNode.removeAttribute(data[change.columnIdentifier], (error) => { this.domNode.setAttribute(change.value, `${change.value}="${data.value}"`); }); } else if (change.columnIdentifier === "value") this.domNode.setAttributeValue(data.name, change.value); } _characterDataModified(event) { if (event.data.node !== this.domNode) return; this._identityNodeValueRow.value = this.domNode.nodeValue(); } _customElementStateChanged(event) { if (event.data.node !== this.domNode) return; this._refreshIdentity(); } _nodeTypeDisplayName() { switch (this.domNode.nodeType()) { case Node.ELEMENT_NODE: { const nodeName = WI.UIString("Element"); const state = this._customElementState(); return state === null ? nodeName : `${nodeName} (${state})`; } case Node.TEXT_NODE: return WI.UIString("Text Node"); case Node.COMMENT_NODE: return WI.UIString("Comment"); case Node.DOCUMENT_NODE: return WI.UIString("Document"); case Node.DOCUMENT_TYPE_NODE: return WI.UIString("Document Type"); case Node.DOCUMENT_FRAGMENT_NODE: return WI.UIString("Document Fragment"); case Node.CDATA_SECTION_NODE: return WI.UIString("Character Data"); case Node.PROCESSING_INSTRUCTION_NODE: return WI.UIString("Processing Instruction"); default: console.error("Unknown DOM node type: ", this.domNode.nodeType()); return this.domNode.nodeType(); } } _customElementState() { const state = this.domNode.customElementState(); switch (state) { case WI.DOMNode.CustomElementState.Builtin: return null; case WI.DOMNode.CustomElementState.Custom: return WI.UIString("Custom"); case WI.DOMNode.CustomElementState.Waiting: return WI.UIString("Undefined custom element"); case WI.DOMNode.CustomElementState.Failed: return WI.UIString("Failed to upgrade"); } console.error("Unknown DOM custom element state: ", state); return null; } }; WI.DOMNodeDetailsSidebarPanel.EventListenerGroupingMethod = { Event: "event", Node: "node", }; WI.DOMNodeDetailsSidebarPanel.PropertiesObjectGroupName = "dom-node-details-sidebar-properties-object-group";