UNPKG

chrome-devtools-frontend

Version:
157 lines (137 loc) 6.07 kB
// Copyright 2015 The Chromium Authors // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. /* eslint-disable rulesdir/no-imperative-dom-api */ import type * as Common from '../../core/common/common.js'; import * as Root from '../../core/root/root.js'; import * as SDK from '../../core/sdk/sdk.js'; import * as UI from '../../ui/legacy/legacy.js'; import {AXNodeSubPane} from './AccessibilityNodeView.js'; import {ARIAAttributesPane} from './ARIAAttributesView.js'; import {AXBreadcrumbsPane} from './AXBreadcrumbsPane.js'; import {SourceOrderPane} from './SourceOrderView.js'; let accessibilitySidebarViewInstance: AccessibilitySidebarView; export class AccessibilitySidebarView extends UI.ThrottledWidget.ThrottledWidget { #node: SDK.DOMModel.DOMNode|null; #axNode: SDK.AccessibilityModel.AccessibilityNode|null; private skipNextPullNode: boolean; private readonly sidebarPaneStack: UI.View.ViewLocation; private readonly breadcrumbsSubPane: AXBreadcrumbsPane; private readonly ariaSubPane: ARIAAttributesPane; private readonly axNodeSubPane: AXNodeSubPane; private readonly sourceOrderSubPane: SourceOrderPane; private constructor(throttlingTimeout?: number) { super(false /* useShadowDom */, throttlingTimeout); this.element.classList.add('accessibility-sidebar-view'); this.#node = null; this.#axNode = null; this.skipNextPullNode = false; this.sidebarPaneStack = UI.ViewManager.ViewManager.instance().createStackLocation(); this.breadcrumbsSubPane = new AXBreadcrumbsPane(this); void this.sidebarPaneStack.showView(this.breadcrumbsSubPane); this.ariaSubPane = new ARIAAttributesPane(); void this.sidebarPaneStack.showView(this.ariaSubPane); this.axNodeSubPane = new AXNodeSubPane(); void this.sidebarPaneStack.showView(this.axNodeSubPane); this.sourceOrderSubPane = new SourceOrderPane(); void this.sidebarPaneStack.showView(this.sourceOrderSubPane); this.sidebarPaneStack.widget().show(this.element); UI.Context.Context.instance().addFlavorChangeListener(SDK.DOMModel.DOMNode, this.pullNode, this); this.pullNode(); } static instance(opts?: { forceNew: boolean, throttlingTimeout: number, }): AccessibilitySidebarView { if (!accessibilitySidebarViewInstance || opts?.forceNew) { accessibilitySidebarViewInstance = new AccessibilitySidebarView(opts?.throttlingTimeout); } return accessibilitySidebarViewInstance; } node(): SDK.DOMModel.DOMNode|null { return this.#node; } axNode(): SDK.AccessibilityModel.AccessibilityNode|null { return this.#axNode; } setNode(node: SDK.DOMModel.DOMNode|null, fromAXTree?: boolean): void { this.skipNextPullNode = Boolean(fromAXTree); this.#node = node; this.update(); } accessibilityNodeCallback(axNode: SDK.AccessibilityModel.AccessibilityNode|null): void { if (!axNode) { return; } this.#axNode = axNode; if (axNode.isDOMNode()) { void this.sidebarPaneStack.showView(this.ariaSubPane, this.axNodeSubPane); } else { this.sidebarPaneStack.removeView(this.ariaSubPane); } this.axNodeSubPane.setAXNode(axNode); this.breadcrumbsSubPane.setAXNode(axNode); } override async doUpdate(): Promise<void> { const node = this.node(); this.axNodeSubPane.setNode(node); this.ariaSubPane.setNode(node); this.breadcrumbsSubPane.setNode(node); void this.sourceOrderSubPane.setNodeAsync(node); if (!node) { return; } const accessibilityModel = node.domModel().target().model(SDK.AccessibilityModel.AccessibilityModel); if (!accessibilityModel) { return; } if (!Root.Runtime.experiments.isEnabled('full-accessibility-tree')) { accessibilityModel.clear(); } await accessibilityModel.requestPartialAXTree(node); this.accessibilityNodeCallback(accessibilityModel.axNodeForDOMNode(node)); } override wasShown(): void { super.wasShown(); // Pull down the latest date for this node. void this.doUpdate(); SDK.TargetManager.TargetManager.instance().addModelListener( SDK.DOMModel.DOMModel, SDK.DOMModel.Events.AttrModified, this.onNodeChange, this, {scoped: true}); SDK.TargetManager.TargetManager.instance().addModelListener( SDK.DOMModel.DOMModel, SDK.DOMModel.Events.AttrRemoved, this.onNodeChange, this, {scoped: true}); SDK.TargetManager.TargetManager.instance().addModelListener( SDK.DOMModel.DOMModel, SDK.DOMModel.Events.CharacterDataModified, this.onNodeChange, this, {scoped: true}); SDK.TargetManager.TargetManager.instance().addModelListener( SDK.DOMModel.DOMModel, SDK.DOMModel.Events.ChildNodeCountUpdated, this.onNodeChange, this, {scoped: true}); } override willHide(): void { SDK.TargetManager.TargetManager.instance().removeModelListener( SDK.DOMModel.DOMModel, SDK.DOMModel.Events.AttrModified, this.onNodeChange, this); SDK.TargetManager.TargetManager.instance().removeModelListener( SDK.DOMModel.DOMModel, SDK.DOMModel.Events.AttrRemoved, this.onNodeChange, this); SDK.TargetManager.TargetManager.instance().removeModelListener( SDK.DOMModel.DOMModel, SDK.DOMModel.Events.CharacterDataModified, this.onNodeChange, this); SDK.TargetManager.TargetManager.instance().removeModelListener( SDK.DOMModel.DOMModel, SDK.DOMModel.Events.ChildNodeCountUpdated, this.onNodeChange, this); } private pullNode(): void { if (this.skipNextPullNode) { this.skipNextPullNode = false; return; } this.setNode(UI.Context.Context.instance().flavor(SDK.DOMModel.DOMNode)); } private onNodeChange( event: Common.EventTarget.EventTargetEvent<{node: SDK.DOMModel.DOMNode, name: string}|SDK.DOMModel.DOMNode>): void { if (!this.node()) { return; } const data = event.data; const node = (data instanceof SDK.DOMModel.DOMNode ? data : data.node); if (this.node() !== node) { return; } this.update(); } }