Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

chore: private-ise src/Accessibility.ts #5832

Merged
merged 1 commit into from May 7, 2020
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
137 changes: 63 additions & 74 deletions src/Accessibility.ts
Expand Up @@ -51,13 +51,13 @@ interface SerializedAXNode {
}

export class Accessibility {
_client: CDPSession;
private _client: CDPSession;

constructor(client: CDPSession) {
this._client = client;
}

async snapshot(
public async snapshot(
options: { interestingOnly?: boolean; root?: ElementHandle } = {}
): Promise<SerializedAXNode> {
const { interestingOnly = true, root = null } = options;
Expand All @@ -73,80 +73,74 @@ export class Accessibility {
let needle = defaultRoot;
if (backendNodeId) {
needle = defaultRoot.find(
(node) => node._payload.backendDOMNodeId === backendNodeId
(node) => node.payload.backendDOMNodeId === backendNodeId
);
if (!needle) return null;
}
if (!interestingOnly) return serializeTree(needle)[0];
if (!interestingOnly) return this.serializeTree(needle)[0];

const interestingNodes = new Set<AXNode>();
collectInterestingNodes(interestingNodes, defaultRoot, false);
this.collectInterestingNodes(interestingNodes, defaultRoot, false);
if (!interestingNodes.has(needle)) return null;
return serializeTree(needle, interestingNodes)[0];
return this.serializeTree(needle, interestingNodes)[0];
}
}

/**
* @param {!Set<!AXNode>} collection
* @param {!AXNode} node
* @param {boolean} insideControl
*/
function collectInterestingNodes(
collection: Set<AXNode>,
node: AXNode,
insideControl: boolean
): void {
if (node.isInteresting(insideControl)) collection.add(node);
if (node.isLeafNode()) return;
insideControl = insideControl || node.isControl();
for (const child of node._children)
collectInterestingNodes(collection, child, insideControl);
}
private serializeTree(
node: AXNode,
whitelistedNodes?: Set<AXNode>
): SerializedAXNode[] {
const children: SerializedAXNode[] = [];
for (const child of node.children)
children.push(...this.serializeTree(child, whitelistedNodes));

function serializeTree(
node: AXNode,
whitelistedNodes?: Set<AXNode>
): SerializedAXNode[] {
const children: SerializedAXNode[] = [];
for (const child of node._children)
children.push(...serializeTree(child, whitelistedNodes));
if (whitelistedNodes && !whitelistedNodes.has(node)) return children;

if (whitelistedNodes && !whitelistedNodes.has(node)) return children;
const serializedNode = node.serialize();
if (children.length) serializedNode.children = children;
return [serializedNode];
}

const serializedNode = node.serialize();
if (children.length) serializedNode.children = children;
return [serializedNode];
private collectInterestingNodes(
collection: Set<AXNode>,
node: AXNode,
insideControl: boolean
): void {
if (node.isInteresting(insideControl)) collection.add(node);
if (node.isLeafNode()) return;
insideControl = insideControl || node.isControl();
for (const child of node.children)
this.collectInterestingNodes(collection, child, insideControl);
}
}

class AXNode {
_payload: Protocol.Accessibility.AXNode;
_children: AXNode[] = [];
_richlyEditable = false;
_editable = false;
_focusable = false;
_expanded = false;
_hidden = false;
_name: string;
_role: string;
_cachedHasFocusableChild?: boolean;
public payload: Protocol.Accessibility.AXNode;
public children: AXNode[] = [];

private _richlyEditable = false;
private _editable = false;
private _focusable = false;
private _hidden = false;
private _name: string;
private _role: string;
private _cachedHasFocusableChild?: boolean;

constructor(payload: Protocol.Accessibility.AXNode) {
this._payload = payload;
this._name = this._payload.name ? this._payload.name.value : '';
this._role = this._payload.role ? this._payload.role.value : 'Unknown';
this.payload = payload;
this._name = this.payload.name ? this.payload.name.value : '';
this._role = this.payload.role ? this.payload.role.value : 'Unknown';

for (const property of this._payload.properties || []) {
for (const property of this.payload.properties || []) {
if (property.name === 'editable') {
this._richlyEditable = property.value.value === 'richtext';
this._editable = true;
}
if (property.name === 'focusable') this._focusable = property.value.value;
if (property.name === 'expanded') this._expanded = property.value.value;
if (property.name === 'hidden') this._hidden = property.value.value;
}
}

_isPlainTextField(): boolean {
private _isPlainTextField(): boolean {
if (this._richlyEditable) return false;
if (this._editable) return true;
return (
Expand All @@ -156,15 +150,15 @@ class AXNode {
);
}

_isTextOnlyObject(): boolean {
private _isTextOnlyObject(): boolean {
const role = this._role;
return role === 'LineBreak' || role === 'text' || role === 'InlineTextBox';
}

_hasFocusableChild(): boolean {
private _hasFocusableChild(): boolean {
if (this._cachedHasFocusableChild === undefined) {
this._cachedHasFocusableChild = false;
for (const child of this._children) {
for (const child of this.children) {
if (child._focusable || child._hasFocusableChild()) {
this._cachedHasFocusableChild = true;
break;
Expand All @@ -174,17 +168,17 @@ class AXNode {
return this._cachedHasFocusableChild;
}

find(predicate: (x: AXNode) => boolean): AXNode | null {
public find(predicate: (x: AXNode) => boolean): AXNode | null {
if (predicate(this)) return this;
for (const child of this._children) {
for (const child of this.children) {
const result = child.find(predicate);
if (result) return result;
}
return null;
}

isLeafNode(): boolean {
if (!this._children.length) return true;
public isLeafNode(): boolean {
if (!this.children.length) return true;

// These types of objects may have children that we use as internal
// implementation details, but we want to expose them as leaves to platform
Expand Down Expand Up @@ -217,7 +211,7 @@ class AXNode {
return false;
}

isControl(): boolean {
public isControl(): boolean {
switch (this._role) {
case 'button':
case 'checkbox':
Expand Down Expand Up @@ -245,11 +239,7 @@ class AXNode {
}
}

/**
* @param {boolean} insideControl
* @return {boolean}
*/
isInteresting(insideControl: boolean): boolean {
public isInteresting(insideControl: boolean): boolean {
const role = this._role;
if (role === 'Ignored' || this._hidden) return false;

Expand All @@ -264,14 +254,14 @@ class AXNode {
return this.isLeafNode() && !!this._name;
}

serialize(): SerializedAXNode {
public serialize(): SerializedAXNode {
const properties = new Map<string, number | string | boolean>();
for (const property of this._payload.properties || [])
for (const property of this.payload.properties || [])
properties.set(property.name.toLowerCase(), property.value.value);
if (this._payload.name) properties.set('name', this._payload.name.value);
if (this._payload.value) properties.set('value', this._payload.value.value);
if (this._payload.description)
properties.set('description', this._payload.description.value);
if (this.payload.name) properties.set('name', this.payload.name.value);
if (this.payload.value) properties.set('value', this.payload.value.value);
if (this.payload.description)
properties.set('description', this.payload.description.value);

const node: SerializedAXNode = {
role: this._role,
Expand Down Expand Up @@ -378,14 +368,13 @@ class AXNode {
return node;
}

static createTree(payloads: Protocol.Accessibility.AXNode[]): AXNode {
/** @type {!Map<string, !AXNode>} */
const nodeById = new Map();
public static createTree(payloads: Protocol.Accessibility.AXNode[]): AXNode {
const nodeById = new Map<string, AXNode>();
for (const payload of payloads)
nodeById.set(payload.nodeId, new AXNode(payload));
for (const node of nodeById.values()) {
for (const childId of node._payload.childIds || [])
node._children.push(nodeById.get(childId));
for (const childId of node.payload.childIds || [])
node.children.push(nodeById.get(childId));
}
return nodeById.values().next().value;
}
Expand Down