UNPKG

debug-server-next

Version:

Dev server for hippy-core.

132 lines (131 loc) 5.87 kB
// Copyright 2020 The Chromium Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. import * as Common from '../../core/common/common.js'; import * as SDK from '../../core/sdk/sdk.js'; import * as TreeOutline from '../../ui/components/tree_outline/tree_outline.js'; import * as UI from '../../ui/legacy/legacy.js'; import * as AccessibilityTreeUtils from './AccessibilityTreeUtils.js'; export class AccessibilityTreeView extends UI.Widget.VBox { accessibilityTreeComponent = new TreeOutline.TreeOutline.TreeOutline(); treeData = []; toggleButton; accessibilityModel = null; rootAXNode = null; selectedTreeNode = null; inspectedDOMNode = null; constructor(toggleButton) { super(); // toggleButton is bound to a click handler on ElementsPanel to switch between the DOM tree // and accessibility tree views. this.toggleButton = toggleButton; this.contentElement.appendChild(this.toggleButton); this.contentElement.appendChild(this.accessibilityTreeComponent); // The DOM tree and accessibility are kept in sync as much as possible, so // on node selection, update the currently inspected node and reveal in the // DOM tree. this.accessibilityTreeComponent.addEventListener('itemselected', (event) => { const evt = event; const axNode = evt.data.node.treeNodeData; if (!axNode.isDOMNode()) { return; } const deferredNode = axNode.deferredDOMNode(); if (deferredNode) { deferredNode.resolve(domNode => { if (domNode && domNode.nodeName() === '#document') { return; } Common.Revealer.reveal(domNode, true /* omitFocus */); }); } // Highlight the node as well, for keyboard navigation. axNode.highlightDOMNode(); }); this.accessibilityTreeComponent.addEventListener('itemmouseover', (event) => { const evt = event; evt.data.node.treeNodeData.highlightDOMNode(); }); this.accessibilityTreeComponent.addEventListener('itemmouseout', () => { SDK.OverlayModel.OverlayModel.hideDOMNodeHighlight(); }); } wasShown() { if (this.selectedTreeNode) { this.accessibilityTreeComponent.expandToAndSelectTreeNode(this.selectedTreeNode); } } setAccessibilityModel(model) { this.accessibilityModel = model; this.refreshAccessibilityTree(); } async refreshAccessibilityTree() { if (!this.accessibilityModel) { return; } const root = await this.accessibilityModel.requestRootNode(); if (!root) { return; } this.rootAXNode = root; this.treeData = [AccessibilityTreeUtils.sdkNodeToAXTreeNode(this.rootAXNode)]; this.accessibilityTreeComponent.data = { defaultRenderer: (node) => AccessibilityTreeUtils.accessibilityNodeRenderer(node), tree: this.treeData, }; this.accessibilityTreeComponent.expandRecursively(2); this.selectedTreeNode = this.treeData[0]; } // Given a selected DOM node, asks the model to load the missing subtree from the root to the // selected node and then re-renders the tree. async loadSubTreeIntoAccessibilityModel(selectedNode) { if (!this.accessibilityModel) { return; } this.inspectedDOMNode = selectedNode; // If this node has been loaded previously, the accessibility tree will return it's cached node. // Eventually we'll need some mechanism for forcing it to fetch a new node when we are subscribing // for updates, but TBD later. // EG for a backend tree like: // // A* // B // C // D // E // Where only A is already loaded into the model, calling requestAndLoadSubTreeToNode(C) will // load [A, B, D, C] into the model, and return C. const inspectedAXNode = await this.accessibilityModel.requestAndLoadSubTreeToNode(selectedNode); if (!inspectedAXNode) { return; } this.accessibilityTreeComponent.data = { defaultRenderer: (node) => AccessibilityTreeUtils.accessibilityNodeRenderer(node), tree: this.treeData, }; // These nodes require a special case, as they don't have an unignored node in the // accessibility tree. Someone inspecting these in the DOM is probably expecting to // be focused on the root WebArea of the accessibility tree. // TODO(meredithl): Fix for when the inspected node is ingored in the accessibility // tree. Eg, inspecting <head> in the DOM tree. if (selectedNode.nodeName() === 'BODY' || selectedNode.nodeName() === 'HTML') { this.accessibilityTreeComponent.expandToAndSelectTreeNode(this.treeData[0]); this.selectedTreeNode = this.treeData[0]; return; } this.selectedTreeNode = AccessibilityTreeUtils.sdkNodeToAXTreeNode(inspectedAXNode); this.accessibilityTreeComponent.expandToAndSelectTreeNode(this.selectedTreeNode); } // Selected node in the DOM has changed, and the corresponding accessibility node may be // unloaded. async selectedNodeChanged(inspectedNode) { // We only want to do this when the AccessibilityTree is visible. if (!this._visible) { return; } if (inspectedNode === this.inspectedDOMNode) { return; } await this.loadSubTreeIntoAccessibilityModel(inspectedNode); } }