mirror of
https://github.com/puppeteer/puppeteer
synced 2024-06-14 14:02:48 +00:00
chore: private-ise src/Accessibility.ts (#5832)
Now the first pass of migrating to TypeScript is complete I'm going through the src files one by one to tidy up the public/private interfaces. Puppeteer used an underscore convention to denote privacy but violates this in some places; we should be strict with TypeScript's `public` and `private` keywords instead. This means we'll get nice TS errors if you try to refer to a private method/variable, and means when we swap to generating our TS docs the tooling knows what method(s) are public and therefore need to be documented vs private internals that don't.
This commit is contained in:
parent
ce097426af
commit
5343c7a18f
@ -51,13 +51,13 @@ interface SerializedAXNode {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export class Accessibility {
|
export class Accessibility {
|
||||||
_client: CDPSession;
|
private _client: CDPSession;
|
||||||
|
|
||||||
constructor(client: CDPSession) {
|
constructor(client: CDPSession) {
|
||||||
this._client = client;
|
this._client = client;
|
||||||
}
|
}
|
||||||
|
|
||||||
async snapshot(
|
public async snapshot(
|
||||||
options: { interestingOnly?: boolean; root?: ElementHandle } = {}
|
options: { interestingOnly?: boolean; root?: ElementHandle } = {}
|
||||||
): Promise<SerializedAXNode> {
|
): Promise<SerializedAXNode> {
|
||||||
const { interestingOnly = true, root = null } = options;
|
const { interestingOnly = true, root = null } = options;
|
||||||
@ -73,80 +73,74 @@ export class Accessibility {
|
|||||||
let needle = defaultRoot;
|
let needle = defaultRoot;
|
||||||
if (backendNodeId) {
|
if (backendNodeId) {
|
||||||
needle = defaultRoot.find(
|
needle = defaultRoot.find(
|
||||||
(node) => node._payload.backendDOMNodeId === backendNodeId
|
(node) => node.payload.backendDOMNodeId === backendNodeId
|
||||||
);
|
);
|
||||||
if (!needle) return null;
|
if (!needle) return null;
|
||||||
}
|
}
|
||||||
if (!interestingOnly) return serializeTree(needle)[0];
|
if (!interestingOnly) return this.serializeTree(needle)[0];
|
||||||
|
|
||||||
const interestingNodes = new Set<AXNode>();
|
const interestingNodes = new Set<AXNode>();
|
||||||
collectInterestingNodes(interestingNodes, defaultRoot, false);
|
this.collectInterestingNodes(interestingNodes, defaultRoot, false);
|
||||||
if (!interestingNodes.has(needle)) return null;
|
if (!interestingNodes.has(needle)) return null;
|
||||||
return serializeTree(needle, interestingNodes)[0];
|
return this.serializeTree(needle, interestingNodes)[0];
|
||||||
|
}
|
||||||
|
|
||||||
|
private serializeTree(
|
||||||
|
node: AXNode,
|
||||||
|
whitelistedNodes?: Set<AXNode>
|
||||||
|
): SerializedAXNode[] {
|
||||||
|
const children: SerializedAXNode[] = [];
|
||||||
|
for (const child of node.children)
|
||||||
|
children.push(...this.serializeTree(child, whitelistedNodes));
|
||||||
|
|
||||||
|
if (whitelistedNodes && !whitelistedNodes.has(node)) return children;
|
||||||
|
|
||||||
|
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);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* @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);
|
|
||||||
}
|
|
||||||
|
|
||||||
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;
|
|
||||||
|
|
||||||
const serializedNode = node.serialize();
|
|
||||||
if (children.length) serializedNode.children = children;
|
|
||||||
return [serializedNode];
|
|
||||||
}
|
|
||||||
|
|
||||||
class AXNode {
|
class AXNode {
|
||||||
_payload: Protocol.Accessibility.AXNode;
|
public payload: Protocol.Accessibility.AXNode;
|
||||||
_children: AXNode[] = [];
|
public children: AXNode[] = [];
|
||||||
_richlyEditable = false;
|
|
||||||
_editable = false;
|
private _richlyEditable = false;
|
||||||
_focusable = false;
|
private _editable = false;
|
||||||
_expanded = false;
|
private _focusable = false;
|
||||||
_hidden = false;
|
private _hidden = false;
|
||||||
_name: string;
|
private _name: string;
|
||||||
_role: string;
|
private _role: string;
|
||||||
_cachedHasFocusableChild?: boolean;
|
private _cachedHasFocusableChild?: boolean;
|
||||||
|
|
||||||
constructor(payload: Protocol.Accessibility.AXNode) {
|
constructor(payload: Protocol.Accessibility.AXNode) {
|
||||||
this._payload = payload;
|
this.payload = payload;
|
||||||
this._name = this._payload.name ? this._payload.name.value : '';
|
this._name = this.payload.name ? this.payload.name.value : '';
|
||||||
this._role = this._payload.role ? this._payload.role.value : 'Unknown';
|
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') {
|
if (property.name === 'editable') {
|
||||||
this._richlyEditable = property.value.value === 'richtext';
|
this._richlyEditable = property.value.value === 'richtext';
|
||||||
this._editable = true;
|
this._editable = true;
|
||||||
}
|
}
|
||||||
if (property.name === 'focusable') this._focusable = property.value.value;
|
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;
|
if (property.name === 'hidden') this._hidden = property.value.value;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
_isPlainTextField(): boolean {
|
private _isPlainTextField(): boolean {
|
||||||
if (this._richlyEditable) return false;
|
if (this._richlyEditable) return false;
|
||||||
if (this._editable) return true;
|
if (this._editable) return true;
|
||||||
return (
|
return (
|
||||||
@ -156,15 +150,15 @@ class AXNode {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
_isTextOnlyObject(): boolean {
|
private _isTextOnlyObject(): boolean {
|
||||||
const role = this._role;
|
const role = this._role;
|
||||||
return role === 'LineBreak' || role === 'text' || role === 'InlineTextBox';
|
return role === 'LineBreak' || role === 'text' || role === 'InlineTextBox';
|
||||||
}
|
}
|
||||||
|
|
||||||
_hasFocusableChild(): boolean {
|
private _hasFocusableChild(): boolean {
|
||||||
if (this._cachedHasFocusableChild === undefined) {
|
if (this._cachedHasFocusableChild === undefined) {
|
||||||
this._cachedHasFocusableChild = false;
|
this._cachedHasFocusableChild = false;
|
||||||
for (const child of this._children) {
|
for (const child of this.children) {
|
||||||
if (child._focusable || child._hasFocusableChild()) {
|
if (child._focusable || child._hasFocusableChild()) {
|
||||||
this._cachedHasFocusableChild = true;
|
this._cachedHasFocusableChild = true;
|
||||||
break;
|
break;
|
||||||
@ -174,17 +168,17 @@ class AXNode {
|
|||||||
return this._cachedHasFocusableChild;
|
return this._cachedHasFocusableChild;
|
||||||
}
|
}
|
||||||
|
|
||||||
find(predicate: (x: AXNode) => boolean): AXNode | null {
|
public find(predicate: (x: AXNode) => boolean): AXNode | null {
|
||||||
if (predicate(this)) return this;
|
if (predicate(this)) return this;
|
||||||
for (const child of this._children) {
|
for (const child of this.children) {
|
||||||
const result = child.find(predicate);
|
const result = child.find(predicate);
|
||||||
if (result) return result;
|
if (result) return result;
|
||||||
}
|
}
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
isLeafNode(): boolean {
|
public isLeafNode(): boolean {
|
||||||
if (!this._children.length) return true;
|
if (!this.children.length) return true;
|
||||||
|
|
||||||
// These types of objects may have children that we use as internal
|
// These types of objects may have children that we use as internal
|
||||||
// implementation details, but we want to expose them as leaves to platform
|
// implementation details, but we want to expose them as leaves to platform
|
||||||
@ -217,7 +211,7 @@ class AXNode {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
isControl(): boolean {
|
public isControl(): boolean {
|
||||||
switch (this._role) {
|
switch (this._role) {
|
||||||
case 'button':
|
case 'button':
|
||||||
case 'checkbox':
|
case 'checkbox':
|
||||||
@ -245,11 +239,7 @@ class AXNode {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
public isInteresting(insideControl: boolean): boolean {
|
||||||
* @param {boolean} insideControl
|
|
||||||
* @return {boolean}
|
|
||||||
*/
|
|
||||||
isInteresting(insideControl: boolean): boolean {
|
|
||||||
const role = this._role;
|
const role = this._role;
|
||||||
if (role === 'Ignored' || this._hidden) return false;
|
if (role === 'Ignored' || this._hidden) return false;
|
||||||
|
|
||||||
@ -264,14 +254,14 @@ class AXNode {
|
|||||||
return this.isLeafNode() && !!this._name;
|
return this.isLeafNode() && !!this._name;
|
||||||
}
|
}
|
||||||
|
|
||||||
serialize(): SerializedAXNode {
|
public serialize(): SerializedAXNode {
|
||||||
const properties = new Map<string, number | string | boolean>();
|
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);
|
properties.set(property.name.toLowerCase(), property.value.value);
|
||||||
if (this._payload.name) properties.set('name', this._payload.name.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.value) properties.set('value', this.payload.value.value);
|
||||||
if (this._payload.description)
|
if (this.payload.description)
|
||||||
properties.set('description', this._payload.description.value);
|
properties.set('description', this.payload.description.value);
|
||||||
|
|
||||||
const node: SerializedAXNode = {
|
const node: SerializedAXNode = {
|
||||||
role: this._role,
|
role: this._role,
|
||||||
@ -378,14 +368,13 @@ class AXNode {
|
|||||||
return node;
|
return node;
|
||||||
}
|
}
|
||||||
|
|
||||||
static createTree(payloads: Protocol.Accessibility.AXNode[]): AXNode {
|
public static createTree(payloads: Protocol.Accessibility.AXNode[]): AXNode {
|
||||||
/** @type {!Map<string, !AXNode>} */
|
const nodeById = new Map<string, AXNode>();
|
||||||
const nodeById = new Map();
|
|
||||||
for (const payload of payloads)
|
for (const payload of payloads)
|
||||||
nodeById.set(payload.nodeId, new AXNode(payload));
|
nodeById.set(payload.nodeId, new AXNode(payload));
|
||||||
for (const node of nodeById.values()) {
|
for (const node of nodeById.values()) {
|
||||||
for (const childId of node._payload.childIds || [])
|
for (const childId of node.payload.childIds || [])
|
||||||
node._children.push(nodeById.get(childId));
|
node.children.push(nodeById.get(childId));
|
||||||
}
|
}
|
||||||
return nodeById.values().next().value;
|
return nodeById.values().next().value;
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user