/* * Copyright (C) 2013-2015 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. */ WebInspector.DOMNodeDetailsSidebarPanel = class DOMNodeDetailsSidebarPanel extends WebInspector.DOMDetailsSidebarPanel { constructor() { super("dom-node-details", WebInspector.UIString("Node"), WebInspector.UIString("Node")); WebInspector.domTreeManager.addEventListener(WebInspector.DOMTreeManager.Event.AttributeModified, this._attributesChanged, this); WebInspector.domTreeManager.addEventListener(WebInspector.DOMTreeManager.Event.AttributeRemoved, this._attributesChanged, this); WebInspector.domTreeManager.addEventListener(WebInspector.DOMTreeManager.Event.CharacterDataModified, this._characterDataModified, this); this.element.classList.add("dom-node"); this._identityNodeTypeRow = new WebInspector.DetailsSectionSimpleRow(WebInspector.UIString("Type")); this._identityNodeNameRow = new WebInspector.DetailsSectionSimpleRow(WebInspector.UIString("Name")); this._identityNodeValueRow = new WebInspector.DetailsSectionSimpleRow(WebInspector.UIString("Value")); var identityGroup = new WebInspector.DetailsSectionGroup([this._identityNodeTypeRow, this._identityNodeNameRow, this._identityNodeValueRow]); var identitySection = new WebInspector.DetailsSection("dom-node-identity", WebInspector.UIString("Identity"), [identityGroup]); this._attributesDataGridRow = new WebInspector.DetailsSectionDataGridRow(null, WebInspector.UIString("No Attributes")); var attributesGroup = new WebInspector.DetailsSectionGroup([this._attributesDataGridRow]); var attributesSection = new WebInspector.DetailsSection("dom-node-attributes", WebInspector.UIString("Attributes"), [attributesGroup]); this._propertiesRow = new WebInspector.DetailsSectionRow; var propertiesGroup = new WebInspector.DetailsSectionGroup([this._propertiesRow]); var propertiesSection = new WebInspector.DetailsSection("dom-node-properties", WebInspector.UIString("Properties"), [propertiesGroup]); this._eventListenersSectionGroup = new WebInspector.DetailsSectionGroup; var eventListenersSection = new WebInspector.DetailsSection("dom-node-event-listeners", WebInspector.UIString("Event Listeners"), [this._eventListenersSectionGroup]); 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 WebInspector.DetailsSectionRow(WebInspector.UIString("No Accessibility Information")); this._accessibilityNodeActiveDescendantRow = new WebInspector.DetailsSectionSimpleRow(WebInspector.UIString("Shared Focus")); this._accessibilityNodeBusyRow = new WebInspector.DetailsSectionSimpleRow(WebInspector.UIString("Busy")); this._accessibilityNodeCheckedRow = new WebInspector.DetailsSectionSimpleRow(WebInspector.UIString("Checked")); this._accessibilityNodeChildrenRow = new WebInspector.DetailsSectionSimpleRow(WebInspector.UIString("Children")); this._accessibilityNodeControlsRow = new WebInspector.DetailsSectionSimpleRow(WebInspector.UIString("Controls")); this._accessibilityNodeCurrentRow = new WebInspector.DetailsSectionSimpleRow(WebInspector.UIString("Current")); this._accessibilityNodeDisabledRow = new WebInspector.DetailsSectionSimpleRow(WebInspector.UIString("Disabled")); this._accessibilityNodeExpandedRow = new WebInspector.DetailsSectionSimpleRow(WebInspector.UIString("Expanded")); this._accessibilityNodeFlowsRow = new WebInspector.DetailsSectionSimpleRow(WebInspector.UIString("Flows")); this._accessibilityNodeFocusedRow = new WebInspector.DetailsSectionSimpleRow(WebInspector.UIString("Focused")); this._accessibilityNodeIgnoredRow = new WebInspector.DetailsSectionSimpleRow(WebInspector.UIString("Ignored")); this._accessibilityNodeInvalidRow = new WebInspector.DetailsSectionSimpleRow(WebInspector.UIString("Invalid")); this._accessibilityNodeLiveRegionStatusRow = new WebInspector.DetailsSectionSimpleRow(WebInspector.UIString("Live")); this._accessibilityNodeMouseEventRow = new WebInspector.DetailsSectionSimpleRow(""); this._accessibilityNodeLabelRow = new WebInspector.DetailsSectionSimpleRow(WebInspector.UIString("Label")); this._accessibilityNodeOwnsRow = new WebInspector.DetailsSectionSimpleRow(WebInspector.UIString("Owns")); this._accessibilityNodeParentRow = new WebInspector.DetailsSectionSimpleRow(WebInspector.UIString("Parent")); this._accessibilityNodePressedRow = new WebInspector.DetailsSectionSimpleRow(WebInspector.UIString("Pressed")); this._accessibilityNodeReadonlyRow = new WebInspector.DetailsSectionSimpleRow(WebInspector.UIString("Readonly")); this._accessibilityNodeRequiredRow = new WebInspector.DetailsSectionSimpleRow(WebInspector.UIString("Required")); this._accessibilityNodeRoleRow = new WebInspector.DetailsSectionSimpleRow(WebInspector.UIString("Role")); this._accessibilityNodeSelectedRow = new WebInspector.DetailsSectionSimpleRow(WebInspector.UIString("Selected")); this._accessibilityNodeSelectedChildrenRow = new WebInspector.DetailsSectionSimpleRow(WebInspector.UIString("Selected Items")); this._accessibilityGroup = new WebInspector.DetailsSectionGroup([this._accessibilityEmptyRow]); var accessibilitySection = new WebInspector.DetailsSection("dom-node-accessibility", WebInspector.UIString("Accessibility"), [this._accessibilityGroup]); this.contentView.element.appendChild(accessibilitySection.element); } } // Public refresh() { var domNode = this.domNode; if (!domNode) return; this._identityNodeTypeRow.value = this._nodeTypeDisplayName(); this._identityNodeNameRow.value = domNode.nodeNameInCorrectCase(); this._identityNodeValueRow.value = domNode.nodeValue(); this._refreshAttributes(); this._refreshProperties(); this._refreshEventListeners(); this._refreshAccessibility(); } // Private _accessibilitySupported() { return window.DOMAgent && DOMAgent.getAccessibilityPropertiesForNode; } _refreshAttributes() { this._attributesDataGridRow.dataGrid = this._createAttributesDataGrid(); } _refreshProperties() { var domNode = this.domNode; if (!domNode) return; RuntimeAgent.releaseObjectGroup(WebInspector.DOMNodeDetailsSidebarPanel.PropertiesObjectGroupName); WebInspector.RemoteObject.resolveNode(domNode, WebInspector.DOMNodeDetailsSidebarPanel.PropertiesObjectGroupName, nodeResolved.bind(this)); function nodeResolved(object) { if (!object) return; // Bail if the DOM node changed while we were waiting for the async response. if (this.domNode !== domNode) return; function 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; } object.callFunction(collectPrototypes, undefined, false, nodePrototypesReady.bind(this)); object.release(); } 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; var element = this._propertiesRow.element; element.removeChildren(); // Get array of prototype user-friendly names. for (var 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; var prototype = prototypes[i].value; var title = prototype.description; if (title.match(/Prototype$/)) title = title.replace(/Prototype$/, WebInspector.UIString(" (Prototype)")); else if (title === "Object") title = title + WebInspector.UIString(" (Prototype)"); // FIXME: Web Inspector: Node Details Sidebar Properties Section has "undefined" for all prototype properties var objectTree = new WebInspector.ObjectTreeView(prototype, WebInspector.ObjectTreeView.Mode.Properties); objectTree.showOnlyProperties(); var detailsSection = new WebInspector.DetailsSection(prototype.description.hash + "-prototype-properties", title, null, null, true); detailsSection.groups[0].rows = [new WebInspector.DetailsSectionPropertiesRow(objectTree)]; element.appendChild(detailsSection.element); } } } _refreshEventListeners() { var domNode = this.domNode; if (!domNode) return; domNode.eventListeners(eventListenersCallback.bind(this)); 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; var eventListenerTypes = []; var eventListenerSections = {}; for (var i = 0; i < eventListeners.length; ++i) { var eventListener = eventListeners[i]; eventListener.node = WebInspector.domTreeManager.nodeForId(eventListener.nodeId); var type = eventListener.type; var section = eventListenerSections[type]; if (!section) { section = new WebInspector.EventListenerSection(type, domNode.id); eventListenerSections[type] = section; eventListenerTypes.push(type); } section.addListener(eventListener); } if (!eventListenerTypes.length) { var emptyRow = new WebInspector.DetailsSectionRow(WebInspector.UIString("No Event Listeners")); emptyRow.showEmptyMessage(); this._eventListenersSectionGroup.rows = [emptyRow]; return; } eventListenerTypes.sort(); var rows = []; for (var i = 0; i < eventListenerTypes.length; ++i) rows.push(eventListenerSections[eventListenerTypes[i]]); this._eventListenersSectionGroup.rows = rows; } } _refreshAccessibility() { if (!this._accessibilitySupported()) return; var domNode = this.domNode; if (!domNode) return; var properties = {}; function booleanValueToLocalizedStringIfTrue(property) { if (properties[property]) return WebInspector.UIString("Yes"); return ""; } function booleanValueToLocalizedStringIfPropertyDefined(property) { if (properties[property] !== undefined) { if (properties[property]) return WebInspector.UIString("Yes"); else return WebInspector.UIString("No"); } return ""; } function linkForNodeId(nodeId) { var link = null; if (nodeId !== undefined && typeof nodeId === "number") { var node = WebInspector.domTreeManager.nodeForId(nodeId); if (node) link = WebInspector.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 = WebInspector.domTreeManager.nodeForId(nodeId); if (!node) continue; let link = WebInspector.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 = WebInspector.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 = WebInspector.UIString("Yes"); else if (accessibilityProperties.checked === DOMAgent.AccessibilityPropertiesChecked.Mixed) checked = WebInspector.UIString("Mixed"); else // DOMAgent.AccessibilityPropertiesChecked.False checked = WebInspector.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 = WebInspector.UIString("True"); break; case DOMAgent.AccessibilityPropertiesCurrent.Page: current = WebInspector.UIString("Page"); break; case DOMAgent.AccessibilityPropertiesCurrent.Location: current = WebInspector.UIString("Location"); break; case DOMAgent.AccessibilityPropertiesCurrent.Step: current = WebInspector.UIString("Step"); break; case DOMAgent.AccessibilityPropertiesCurrent.Date: current = WebInspector.UIString("Date"); break; case DOMAgent.AccessibilityPropertiesCurrent.Time: current = WebInspector.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 = WebInspector.UIString("Yes"); if (accessibilityProperties.hidden) ignored = WebInspector.UIString("%s (hidden)").format(ignored); else if (accessibilityProperties.ignoredByDefault) ignored = WebInspector.UIString("%s (default)").format(ignored); } var invalid = ""; if (accessibilityProperties.invalid === DOMAgent.AccessibilityPropertiesInvalid.True) invalid = WebInspector.UIString("Yes"); else if (accessibilityProperties.invalid === DOMAgent.AccessibilityPropertiesInvalid.Grammar) invalid = WebInspector.UIString("Grammar"); else if (accessibilityProperties.invalid === DOMAgent.AccessibilityPropertiesInvalid.Spelling) invalid = WebInspector.UIString("Spelling"); var label = accessibilityProperties.label; var liveRegionStatus = ""; var liveRegionStatusNode = null; var liveRegionStatusToken = accessibilityProperties.liveRegionStatus; switch(liveRegionStatusToken) { case DOMAgent.AccessibilityPropertiesLiveRegionStatus.Assertive: liveRegionStatus = WebInspector.UIString("Assertive"); break; case DOMAgent.AccessibilityPropertiesLiveRegionStatus.Polite: liveRegionStatus = WebInspector.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 = [WebInspector.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 WebInspector.UIString("Additions"); case DOMAgent.LiveRegionRelevant.Removals: return WebInspector.UIString("Removals"); case DOMAgent.LiveRegionRelevant.Text: return WebInspector.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(WebInspector.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 = WebInspector.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; if (role === "" || role === "unknown") role = WebInspector.UIString("No exact ARIA role match."); else if (role) { if (!domNode.getAttribute("role")) role = WebInspector.UIString("%s (default)").format(role); else if (domNode.getAttribute("role") !== role) role = WebInspector.UIString("%s (computed)").format(role); } var selected = booleanValueToLocalizedStringIfTrue("selected"); var selectedChildNodeLinkList = linkListForNodeIds(accessibilityProperties.selectedChildNodeIds); // 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._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 ? WebInspector.UIString("Click Listener") : WebInspector.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 = WebInspector.UIString("Selected Items"); this._accessibilityNodeSelectedChildrenRow.value = selectedChildNodeLinkList || ""; if (selectedChildNodeLinkList && accessibilityProperties.selectedChildNodeIds.length === 1) this._accessibilityNodeSelectedChildrenRow.label = WebInspector.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._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)); } _attributesChanged(event) { if (event.data.node !== this.domNode) return; this._refreshAttributes(); this._refreshAccessibility(); } _characterDataModified(event) { if (event.data.node !== this.domNode) return; this._identityNodeValueRow.value = this.domNode.nodeValue(); } _nodeTypeDisplayName() { switch (this.domNode.nodeType()) { case Node.ELEMENT_NODE: return WebInspector.UIString("Element"); case Node.TEXT_NODE: return WebInspector.UIString("Text Node"); case Node.COMMENT_NODE: return WebInspector.UIString("Comment"); case Node.DOCUMENT_NODE: return WebInspector.UIString("Document"); case Node.DOCUMENT_TYPE_NODE: return WebInspector.UIString("Document Type"); case Node.DOCUMENT_FRAGMENT_NODE: return WebInspector.UIString("Document Fragment"); case Node.CDATA_SECTION_NODE: return WebInspector.UIString("Character Data"); case Node.PROCESSING_INSTRUCTION_NODE: return WebInspector.UIString("Processing Instruction"); default: console.error("Unknown DOM node type: ", this.domNode.nodeType()); return this.domNode.nodeType(); } } _createAttributesDataGrid() { var domNode = this.domNode; if (!domNode || !domNode.hasAttributes()) return null; var columns = {name: {title: WebInspector.UIString("Name"), width: "30%"}, value: {title: WebInspector.UIString("Value")}}; var dataGrid = new WebInspector.DataGrid(columns); var attributes = domNode.attributes(); for (var i = 0; i < attributes.length; ++i) { var attribute = attributes[i]; var node = new WebInspector.DataGridNode({name: attribute.name, value: attribute.value || ""}, false); dataGrid.appendChild(node); } return dataGrid; } }; WebInspector.DOMNodeDetailsSidebarPanel.PropertiesObjectGroupName = "dom-node-details-sidebar-properties-object-group";