@quick-game/cli
Version:
Command line interface for rapid qg development
306 lines • 10.4 kB
JavaScript
// Copyright (c) 2014 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 { DeferredDOMNode } from './DOMModel.js';
import { Capability } from './Target.js';
import { SDKModel } from './SDKModel.js';
// TODO(crbug.com/1167717): Make this a const enum again
// eslint-disable-next-line rulesdir/const_enum
export var CoreAxPropertyName;
(function (CoreAxPropertyName) {
CoreAxPropertyName["Name"] = "name";
CoreAxPropertyName["Description"] = "description";
CoreAxPropertyName["Value"] = "value";
CoreAxPropertyName["Role"] = "role";
})(CoreAxPropertyName || (CoreAxPropertyName = {}));
export class AccessibilityNode {
#accessibilityModelInternal;
#idInternal;
#backendDOMNodeIdInternal;
#deferredDOMNodeInternal;
#ignoredInternal;
#ignoredReasonsInternal;
#roleInternal;
#nameInternal;
#descriptionInternal;
#valueInternal;
#propertiesInternal;
#parentId;
#frameId;
#childIds;
constructor(accessibilityModel, payload) {
this.#accessibilityModelInternal = accessibilityModel;
this.#idInternal = payload.nodeId;
accessibilityModel.setAXNodeForAXId(this.#idInternal, this);
if (payload.backendDOMNodeId) {
accessibilityModel.setAXNodeForBackendDOMNodeId(payload.backendDOMNodeId, this);
this.#backendDOMNodeIdInternal = payload.backendDOMNodeId;
this.#deferredDOMNodeInternal = new DeferredDOMNode(accessibilityModel.target(), payload.backendDOMNodeId);
}
else {
this.#backendDOMNodeIdInternal = null;
this.#deferredDOMNodeInternal = null;
}
this.#ignoredInternal = payload.ignored;
if (this.#ignoredInternal && 'ignoredReasons' in payload) {
this.#ignoredReasonsInternal = payload.ignoredReasons;
}
this.#roleInternal = payload.role || null;
this.#nameInternal = payload.name || null;
this.#descriptionInternal = payload.description || null;
this.#valueInternal = payload.value || null;
this.#propertiesInternal = payload.properties || null;
this.#childIds = payload.childIds || null;
this.#parentId = payload.parentId || null;
if (payload.frameId && !payload.parentId) {
this.#frameId = payload.frameId;
accessibilityModel.setRootAXNodeForFrameId(payload.frameId, this);
}
else {
this.#frameId = null;
}
}
id() {
return this.#idInternal;
}
accessibilityModel() {
return this.#accessibilityModelInternal;
}
ignored() {
return this.#ignoredInternal;
}
ignoredReasons() {
return this.#ignoredReasonsInternal || null;
}
role() {
return this.#roleInternal || null;
}
coreProperties() {
const properties = [];
if (this.#nameInternal) {
properties.push({ name: CoreAxPropertyName.Name, value: this.#nameInternal });
}
if (this.#descriptionInternal) {
properties.push({ name: CoreAxPropertyName.Description, value: this.#descriptionInternal });
}
if (this.#valueInternal) {
properties.push({ name: CoreAxPropertyName.Value, value: this.#valueInternal });
}
return properties;
}
name() {
return this.#nameInternal || null;
}
description() {
return this.#descriptionInternal || null;
}
value() {
return this.#valueInternal || null;
}
properties() {
return this.#propertiesInternal || null;
}
parentNode() {
if (this.#parentId) {
return this.#accessibilityModelInternal.axNodeForId(this.#parentId);
}
return null;
}
isDOMNode() {
return Boolean(this.#backendDOMNodeIdInternal);
}
backendDOMNodeId() {
return this.#backendDOMNodeIdInternal;
}
deferredDOMNode() {
return this.#deferredDOMNodeInternal;
}
highlightDOMNode() {
const deferredNode = this.deferredDOMNode();
if (!deferredNode) {
return;
}
// Highlight node in page.
deferredNode.highlight();
}
children() {
if (!this.#childIds) {
return [];
}
const children = [];
for (const childId of this.#childIds) {
const child = this.#accessibilityModelInternal.axNodeForId(childId);
if (child) {
children.push(child);
}
}
return children;
}
numChildren() {
if (!this.#childIds) {
return 0;
}
return this.#childIds.length;
}
hasOnlyUnloadedChildren() {
if (!this.#childIds || !this.#childIds.length) {
return false;
}
return this.#childIds.every(id => this.#accessibilityModelInternal.axNodeForId(id) === null);
}
hasUnloadedChildren() {
if (!this.#childIds || !this.#childIds.length) {
return false;
}
return this.#childIds.some(id => this.#accessibilityModelInternal.axNodeForId(id) === null);
}
// Only the root node gets a frameId, so nodes have to walk up the tree to find their frameId.
getFrameId() {
return this.#frameId || this.parentNode()?.getFrameId() || null;
}
}
// TODO(crbug.com/1167717): Make this a const enum again
// eslint-disable-next-line rulesdir/const_enum
export var Events;
(function (Events) {
Events["TreeUpdated"] = "TreeUpdated";
})(Events || (Events = {}));
export class AccessibilityModel extends SDKModel {
agent;
#axIdToAXNode;
#backendDOMNodeIdToAXNode;
#frameIdToAXNode;
#pendingChildRequests;
#root;
constructor(target) {
super(target);
target.registerAccessibilityDispatcher(this);
this.agent = target.accessibilityAgent();
void this.resumeModel();
this.#axIdToAXNode = new Map();
this.#backendDOMNodeIdToAXNode = new Map();
this.#frameIdToAXNode = new Map();
this.#pendingChildRequests = new Map();
this.#root = null;
}
clear() {
this.#root = null;
this.#axIdToAXNode.clear();
this.#backendDOMNodeIdToAXNode.clear();
this.#frameIdToAXNode.clear();
}
async resumeModel() {
await this.agent.invoke_enable();
}
async suspendModel() {
await this.agent.invoke_disable();
}
async requestPartialAXTree(node) {
const { nodes } = await this.agent.invoke_getPartialAXTree({ nodeId: node.id, fetchRelatives: true });
if (!nodes) {
return;
}
const axNodes = [];
for (const payload of nodes) {
axNodes.push(new AccessibilityNode(this, payload));
}
}
loadComplete({ root }) {
this.clear();
this.#root = new AccessibilityNode(this, root);
this.dispatchEventToListeners(Events.TreeUpdated, { root: this.#root });
}
nodesUpdated({ nodes }) {
this.createNodesFromPayload(nodes);
this.dispatchEventToListeners(Events.TreeUpdated, {});
return;
}
createNodesFromPayload(payloadNodes) {
const accessibilityNodes = payloadNodes.map(node => {
const sdkNode = new AccessibilityNode(this, node);
return sdkNode;
});
return accessibilityNodes;
}
async requestRootNode(frameId) {
if (frameId && this.#frameIdToAXNode.has(frameId)) {
return this.#frameIdToAXNode.get(frameId);
}
if (!frameId && this.#root) {
return this.#root;
}
const { node } = await this.agent.invoke_getRootAXNode({ frameId });
if (!node) {
return;
}
return this.createNodesFromPayload([node])[0];
}
async requestAXChildren(nodeId, frameId) {
const parent = this.#axIdToAXNode.get(nodeId);
if (!parent) {
throw Error('Cannot request children before parent');
}
if (!parent.hasUnloadedChildren()) {
return parent.children();
}
const request = this.#pendingChildRequests.get(nodeId);
if (request) {
await request;
}
else {
const request = this.agent.invoke_getChildAXNodes({ id: nodeId, frameId });
this.#pendingChildRequests.set(nodeId, request);
const result = await request;
if (!result.getError()) {
this.createNodesFromPayload(result.nodes);
this.#pendingChildRequests.delete(nodeId);
}
}
return parent.children();
}
async requestAndLoadSubTreeToNode(node) {
// Node may have already been loaded, so don't bother requesting it again.
const result = [];
let ancestor = this.axNodeForDOMNode(node);
while (ancestor) {
result.push(ancestor);
const parent = ancestor.parentNode();
if (!parent) {
return result;
}
ancestor = parent;
}
const { nodes } = await this.agent.invoke_getAXNodeAndAncestors({ backendNodeId: node.backendNodeId() });
if (!nodes) {
return null;
}
const ancestors = this.createNodesFromPayload(nodes);
return ancestors;
}
axNodeForId(axId) {
return this.#axIdToAXNode.get(axId) || null;
}
setRootAXNodeForFrameId(frameId, axNode) {
this.#frameIdToAXNode.set(frameId, axNode);
}
axNodeForFrameId(frameId) {
return this.#frameIdToAXNode.get(frameId) ?? null;
}
setAXNodeForAXId(axId, axNode) {
this.#axIdToAXNode.set(axId, axNode);
}
axNodeForDOMNode(domNode) {
if (!domNode) {
return null;
}
return this.#backendDOMNodeIdToAXNode.get(domNode.backendNodeId()) ?? null;
}
setAXNodeForBackendDOMNodeId(backendDOMNodeId, axNode) {
this.#backendDOMNodeIdToAXNode.set(backendDOMNodeId, axNode);
}
getAgent() {
return this.agent;
}
}
SDKModel.register(AccessibilityModel, { capabilities: Capability.DOM, autostart: false });
//# sourceMappingURL=AccessibilityModel.js.map