blob: dbf7eec95d7a35e4bb2dc4824099c6bd46e0b1c0 [file] [log] [blame]
// Copyright 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.
const AutomationEvent = require('automationEvent').AutomationEvent;
const automationInternal = getInternalApi('automationInternal');
const AutomationTreeCache = require('automationTreeCache').AutomationTreeCache;
const exceptionHandler = require('uncaught_exception_handler');
const natives = requireNative('automationInternal');
const IsInteractPermitted = natives.IsInteractPermitted;
/**
* @param {string} axTreeID The id of the accessibility tree.
* @return {?number} The id of the root node.
*/
const GetRootID = natives.GetRootID;
/**
* Similar to above, but may move to ancestor roots if the current tree
* has multiple roots.
* @param {string} axTreeID The id of the accessibility tree.
* @return {{treeID: string, nodeID: number}}
*/
const GetPublicRoot = natives.GetPublicRoot;
/**
* @param {string} axTreeID The id of the accessibility tree.
* @return {?string} The title of the document.
*/
const GetDocTitle = natives.GetDocTitle;
/**
* @param {string} axTreeID The id of the accessibility tree.
* @return {?string} The url of the document.
*/
const GetDocURL = natives.GetDocURL;
/**
* @param {string} axTreeID The id of the accessibility tree.
* @return {?boolean} True if the document has finished loading.
*/
const GetDocLoaded = natives.GetDocLoaded;
/**
* @param {string} axTreeID The id of the accessibility tree.
* @return {?number} The loading progress, from 0.0 to 1.0 (fully loaded).
*/
const GetDocLoadingProgress = natives.GetDocLoadingProgress;
/**
* @param {string} axTreeID The id of the accessibility tree.
* @return {boolean} Whether the selection's anchor comes after its focus in the
* accessibility tree.
*/
const GetIsSelectionBackward = natives.GetIsSelectionBackward;
/**
* @param {string} axTreeID The id of the accessibility tree.
* @return {?number} The ID of the selection anchor object.
*/
const GetAnchorObjectID = natives.GetAnchorObjectID;
/**
* @param {string} axTreeID The id of the accessibility tree.
* @return {?number} The selection anchor offset.
*/
const GetAnchorOffset = natives.GetAnchorOffset;
/**
* @param {string} axTreeID The id of the accessibility tree.
* @return {?string} The selection anchor affinity.
*/
const GetAnchorAffinity = natives.GetAnchorAffinity;
/**
* @param {string} axTreeID The id of the accessibility tree.
* @return {?number} The ID of the selection focus object.
*/
const GetFocusObjectID = natives.GetFocusObjectID;
/**
* @param {string} axTreeID The id of the accessibility tree.
* @return {?number} The selection focus offset.
*/
const GetFocusOffset = natives.GetFocusOffset;
/**
* @param {string} axTreeID The id of the accessibility tree.
* @return {?string} The selection focus affinity.
*/
const GetFocusAffinity = natives.GetFocusAffinity;
/**
* The start of the selection always comes before its end in the accessibility
* tree.
* @param {string} axTreeID The id of the accessibility tree.
* @return {?number} The ID of the object at the start of the
* selection.
*/
const GetSelectionStartObjectID = natives.GetSelectionStartObjectID;
/**
* The start of the selection always comes before its end in the accessibility
* tree.
* @param {string} axTreeID The id of the accessibility tree.
* @return {?number} The offset at the start of the selection.
*/
const GetSelectionStartOffset = natives.GetSelectionStartOffset;
/**
* The start of the selection always comes before its end in the accessibility
* tree.
* @param {string} axTreeID The id of the accessibility tree.
* @return {?string} The affinity at the start of the selection.
*/
const GetSelectionStartAffinity = natives.GetSelectionStartAffinity;
/**
* The end of the selection always comes after its start in the accessibility
* tree.
* @param {string} axTreeID The id of the accessibility tree.
* @return {?number} The ID of the object at the end of the selection.
*/
const GetSelectionEndObjectID = natives.GetSelectionEndObjectID;
/**
* The end of the selection always comes after its start in the accessibility
* tree.
* @param {string} axTreeID The id of the accessibility tree.
* @return {?number} The offset at the end of the selection.
*/
const GetSelectionEndOffset = natives.GetSelectionEndOffset;
/**
* The end of the selection always comes after its start in the accessibility
* tree.
* @param {string} axTreeID The id of the accessibility tree.
* @return {?string} The affinity at the end of the selection.
*/
const GetSelectionEndAffinity = natives.GetSelectionEndAffinity;
/**
* @param {string} axTreeID The id of the accessibility tree.
* @param {number} nodeID The id of a node.
* @return {?number} The id of the node's parent, or undefined if it's the
* root of its tree or if the tree or node wasn't found.
*/
const GetParentID = natives.GetParentID;
/**
* @param {string} axTreeID The id of the accessibility tree.
* @param {number} nodeID The id of a node.
* @return {?number} The number of children of the node, or undefined if
* the tree or node wasn't found.
*/
const GetChildCount = natives.GetChildCount;
/**
* @param {string} axTreeID The id of the accessibility tree.
* @param {number} nodeID The id of a node.
* @param {number} childIndex An index of a child of this node.
* @return {?number} The id of the child at the given index, or undefined
* if the tree or node or child at that index wasn't found.
*/
const GetChildIDAtIndex = natives.GetChildIDAtIndex;
/**
* @param {string} axTreeID The id of the accessibility tree.
* @param {number} nodeID The id of a node.
* @return {?number} The ids of the children of the node, or undefined
* if the tree or node wasn't found.
*/
const GetChildIds = natives.GetChildIDs;
/**
* @param {string} axTreeID The id of the accessibility tree.
* @param {number} nodeID The id of a node.
* @return {?Object} An object mapping html attributes to values.
*/
const GetHtmlAttributes = natives.GetHtmlAttributes;
/**
* @param {string} axTreeID The id of the accessibility tree.
* @param {number} nodeID The id of a node.
* @return {?number} The index of this node in its parent, or undefined if
* the tree or node or node parent wasn't found.
*/
const GetIndexInParent = natives.GetIndexInParent;
/**
* @param {string} axTreeID The id of the accessibility tree.
* @param {number} nodeID The id of a node.
* @return {?Object} An object with a string key for every state flag set,
* or undefined if the tree or node or node parent wasn't found.
*/
const GetState = natives.GetState;
/**
* @param {string} axTreeID The id of the accessibility tree.
* @param {number} nodeID The id of a node.
* @return {string} The restriction, one of
* "disabled", "readOnly" or undefined if enabled or other object not disabled
*/
const GetRestriction = natives.GetRestriction;
/**
* @param {string} axTreeID The id of the accessibility tree.
* @param {number} nodeID The id of a node.
* @return {string} The checked state, as undefined, "true", "false" or "mixed".
*/
const GetChecked = natives.GetChecked;
/**
* @param {string} axTreeID The id of the accessibility tree.
* @param {number} nodeID The id of a node.
* @return {string} The role of the node, or undefined if the tree or
* node wasn't found.
*/
const GetRole = natives.GetRole;
/**
* @param {string} axTreeID The id of the accessibility tree.
* @param {number} nodeID The id of a node.
* @return {?automation.Rect} The location of the node, or undefined if
* the tree or node wasn't found.
*/
const GetLocation = natives.GetLocation;
/**
* @param {string} axTreeID The id of the accessibility tree.
* @param {number} nodeID The id of a node.
* @param {number} startIndex The start index of the range.
* @param {number} endIndex The end index of the range.
* @param {boolean} clipped Whether the bounds are clipped to ancestors.
* @return {?automation.Rect} The bounding box of the subrange of this node,
* or the location if there are no subranges, or undefined if
* the tree or node wasn't found.
*/
const GetBoundsForRange = natives.GetBoundsForRange;
/**
* @param {number} left The left location of the text range.
* @param {number} top The top location of the text range.
* @param {number} width The width of text range.
* @param {number} height The height of the text range.
* @param {number} requestID The request id associated with the query
* for this range.
* @return {?automation.Rect} The bounding box of the subrange of this node,
* specified by arguments provided to the function.
*/
const ComputeGlobalBounds = natives.ComputeGlobalBounds;
/**
* @param {string} axTreeID The id of the accessibility tree.
* @param {number} nodeID The id of a node.
* @return {?automation.Rect} The unclipped location of the node, or
* undefined if the tree or node wasn't found.
*/
const GetUnclippedLocation = natives.GetUnclippedLocation;
/**
* @param {string} axTreeID The id of the accessibility tree.
* @param {number} nodeID The id of a node.
* @return {!Array<number>} The text offset where each line starts, or an empty
* array if this node has no text content, or undefined if the tree or node
* was not found.
*/
const GetLineStartOffsets =
requireNative('automationInternal').GetLineStartOffsets;
/**
* @param {string} axTreeID The id of the accessibility tree.
* @param {number} nodeID The id of the node.
* @return {?string} The computed name of this node.
*/
const GetName = natives.GetName;
/**
* @param {string} axTreeID The id of the accessibility tree.
* @param {number} nodeID The id of a node.
* @param {string} attr The name of a string attribute.
* @return {?string} The value of this attribute, or undefined if the tree,
* node, or attribute wasn't found.
*/
const GetStringAttribute = natives.GetStringAttribute;
/**
* @param {string} axTreeID The id of the accessibility tree.
* @param {number} nodeID The id of a node.
* @param {string} attr The name of an attribute.
* @return {?boolean} The value of this attribute, or undefined if the tree,
* node, or attribute wasn't found.
*/
const GetBoolAttribute = natives.GetBoolAttribute;
/**
* @param {string} axTreeID The id of the accessibility tree.
* @param {number} nodeID The id of a node.
* @param {string} attr The name of an attribute.
* @return {?number} The value of this attribute, or undefined if the tree,
* node, or attribute wasn't found.
*/
const GetIntAttribute = natives.GetIntAttribute;
/**
* @param {string} axTreeID The id of the accessibility tree.
* @param {number} nodeID The id of a node.
* @param {string} attr The name of an attribute.
* @return {?Array<number>} The ids of nodes who have a relationship pointing
* to |nodeID| (a reverse relationship).
*/
const GetIntAttributeReverseRelations = natives.GetIntAttributeReverseRelations;
/**
* @param {string} axTreeID The id of the accessibility tree.
* @param {number} nodeID The id of a node.
* @param {string} attr The name of an attribute.
* @return {?number} The value of this attribute, or undefined if the tree,
* node, or attribute wasn't found.
*/
const GetFloatAttribute = natives.GetFloatAttribute;
/**
* @param {string} axTreeID The id of the accessibility tree.
* @param {number} nodeID The id of a node.
* @param {string} attr The name of an attribute.
* @return {?Array<number>} The value of this attribute, or undefined
* if the tree, node, or attribute wasn't found.
*/
const GetIntListAttribute = natives.GetIntListAttribute;
/**
* @param {string} axTreeID The id of the accessibility tree.
* @param {number} nodeID The id of a node.
* @param {string} attr The name of an attribute.
* @return {?Array<number>} The ids of nodes who have a relationship pointing
* to |nodeID| (a reverse relationship).
*/
const GetIntListAttributeReverseRelations =
natives.GetIntListAttributeReverseRelations;
/**
* @param {string} axTreeID The id of the accessibility tree.
* @param {number} nodeID The id of a node.
* @param {string} attr The name of an HTML attribute.
* @return {?string} The value of this attribute, or undefined if the tree,
* node, or attribute wasn't found.
*/
const GetHtmlAttribute = natives.GetHtmlAttribute;
/**
* @param {string} axTreeID The id of the accessibility tree.
* @param {number} nodeID The id of a node.
* @return {automation.NameFromType} The source of the node's name.
*/
const GetNameFrom = natives.GetNameFrom;
/**
* @param {string} axTreeID The id of the accessibility tree.
* @param {number} nodeID The id of a node.
* @return {automation.DescriptionFromType} The node description source.
*/
const GetDescriptionFrom = natives.GetDescriptionFrom;
/**
* @param {string} axTreeID The id of the accessibility tree.
* @param {number} nodeID The id of a node.
* @return {?string} The image annotation status, which may
* include the annotation itself if completed successfully.
*/
const GetImageAnnotation = natives.GetImageAnnotation;
/**
* @param {string} axTreeID The id of the accessibility tree.
* @param {number} nodeID The id of a node.
* @return {boolean}
*/
const GetBold = natives.GetBold;
/**
* @param {string} axTreeID The id of the accessibility tree.
* @param {number} nodeID The id of a node.
* @return {boolean}
*/
const GetItalic = natives.GetItalic;
/**
* @param {string} axTreeID The id of the accessibility tree.
* @param {number} nodeID The id of a node.
* @return {boolean}
*/
const GetUnderline = natives.GetUnderline;
/**
* @param {string} axTreeID The id of the accessibility tree.
* @param {number} nodeID The id of a node.
* @return {boolean}
*/
const GetLineThrough = natives.GetLineThrough;
/**
* @param {string} axTreeID The id of the accessibility tree.
* @param {number} nodeID The id of a node.
* @return {?Array<automation.CustomAction>} List of custom actions of the
* node.
*/
const GetCustomActions = natives.GetCustomActions;
/**
* @param {string} axTreeID The id of the accessibility tree.
* @param {number} nodeID The id of a node.
* @return {?Array<string>} List of standard actions of the node.
*/
const GetStandardActions = natives.GetStandardActions;
/**
* @param {string} axTreeID The id of the accessibility tree.
* @param {number} nodeID The id of a node.
* @return {automation.NameFromType} The source of the node's name.
*/
const GetDefaultActionVerb = natives.GetDefaultActionVerb;
/**
* @param {string} axTreeID The id of the accessibility tree.
* @param {number} nodeID The id of a node.
* @return {automation.HasPopup}
*/
const GetHasPopup = natives.GetHasPopup;
/**
* @param {string} axTreeID The id of the accessibility tree.
* @param {number} nodeID The id of a node.
* @return {automation.AriaCurrentState}
*/
const GetAriaCurrentState = natives.GetAriaCurrentState;
/**
* @param {string} axTreeID The id of the accessibility tree.
* @param {number} nodeID The id of a node.
* @param {string} searchStr
* @param {boolean} backward
* @return {{treeId: string, nodeId: number}}
*/
const GetNextTextMatch = natives.GetNextTextMatch;
/**
* @param {string} axTreeID The id of the accessibility tree.
* @param {number} nodeID The id of a node.
* @return {?Array<number>} A list of column header ids.
*/
const GetTableCellColumnHeaders = natives.GetTableCellColumnHeaders;
/**
* @param {string} axTreeID The id of the accessibility tree.
* @param {number} nodeID The id of a node.
* @return {?Array<number>} A list of row header ids.
*/
const GetTableCellRowHeaders = natives.GetTableCellRowHeaders;
/**
* @param {string} axTreeID The id of the accessibility tree.
* @param {number} nodeID The id of a node.
* @return {number} Column index for this cell.
*/
const GetTableCellColumnIndex = natives.GetTableCellColumnIndex;
/**
* @param {string} axTreeID The id of the accessibility tree.
* @param {number} nodeID The id of a node.
* @return {number} Row index for this cell.
*/
const GetTableCellRowIndex = natives.GetTableCellRowIndex;
/**
* @param {string} axTreeID The id of the accessibility tree.
* @param {number} nodeID The id of a node.
* @return {number} Column index for this cell.
*/
const GetTableCellAriaColumnIndex = natives.GetTableCellAriaColumnIndex;
/**
* @param {string} axTreeID The id of the accessibility tree.
* @param {number} nodeID The id of a node.
* @return {number} Row index for this cell.
*/
const GetTableCellAriaRowIndex = natives.GetTableCellAriaRowIndex;
/**
* @param {string} axTreeID The id of the accessibility tree.
* @param {number} nodeID The id of a node.
* @return {number} column count for this cell's table. 0 if not in a table.
*/
const GetTableColumnCount = natives.GetTableColumnCount;
/**
* @param {string} axTreeID The id of the accessibility tree.
* @param {number} nodeID The id of a node.
* @return {number} Row count for this cell's table. 0 if not in a table.
*/
const GetTableRowCount = natives.GetTableRowCount;
/**
* @param {string} axTreeId The id of the accessibility tree.
* @param {number} nodeID The id of a node.
* @return {string} Detected language for this node.
*/
const GetDetectedLanguage = natives.GetDetectedLanguage;
/**
* @param {string} axTreeId The id of the accessibility tree.
* @param {number} nodeID The id of a node.
* @param {string} attr The name of the string attribute.
* @return {!Array<{startIndex: number, endIndex: number, language: string,
* probability: number}>}
*/
const GetLanguageAnnotationForStringAttribute =
natives.GetLanguageAnnotationForStringAttribute;
/**
* @param {string} axTreeID The id of the accessibility tree.
* @param {number} nodeID The id of a node.
* @return {!Array<number>}
*/
const GetWordStartOffsets = natives.GetWordStartOffsets;
/**
* @param {string} axTreeID The id of the accessibility tree.
* @param {number} nodeID The id of a node.
* @return {!Array<number>}
*/
const GetWordEndOffsets = natives.GetWordEndOffsets;
/**
* @param {string} axTreeID The id of the accessibility tree.
* @param {number} nodeID The id of a node.
* @return {!Array<number>}
*/
const GetSentenceStartOffsets = natives.GetSentenceStartOffsets;
/**
* @param {string} axTreeID The id of the accessibility tree.
* @param {number} nodeID The id of a node.
* @return {!Array<number>}
*/
const GetSentenceEndOffsets = natives.GetSentenceEndOffsets;
/**
* @param {string} axTreeID The id of the accessibility tree.
* @param {number} nodeID The id of a node.
*/
const SetAccessibilityFocus = natives.SetAccessibilityFocus;
/**
* @param {string} axTreeID The id of the accessibility tree.
* @param {number} nodeID The id of a node.
* @param {string} eventType
*/
const EventListenerAdded = natives.EventListenerAdded;
/**
* @param {string} axTreeID The id of the accessibility tree.
* @param {number} nodeID The id of a node.
* @param {string} eventType
*/
const EventListenerRemoved = natives.EventListenerRemoved;
/**
* @param {string} axTreeID The id of the accessibility tree.
* @param {number} nodeID The id of a node.
* @return {Array}
*/
const GetMarkers = natives.GetMarkers;
/**
* @param {string} axTreeID The id of the accessibility tree.
* @param {number} nodeID The id of a node.
* @param {number} offset
* @param {boolean} isUpstream
* @return {!Object}
*/
const CreateAutomationPosition = natives.CreateAutomationPosition;
/**
* @param {string} axTreeID The id of the accessibility tree.
* @param {number} nodeID The id of a node.
* @return {string} The sort direction.
*/
const GetSortDirection = natives.GetSortDirection;
/**
* @param {string} axTreeId The id of the accessibility tree.
* @param {number} nodeID The id of a node.
* @return {string} .
*/
const GetValue = natives.GetValue;
const logging = requireNative('logging');
const utils = require('utils');
/**
* A single node in the Automation tree.
* @param {AutomationRootNodeImpl} root The root of the tree.
* @constructor
*/
function AutomationNodeImpl(root) {
this.rootImpl = root;
this.listeners = {__proto__: null};
}
AutomationNodeImpl.prototype = {
__proto__: null,
treeID: '',
id: -1,
isRootNode: false,
detach: function() {
this.rootImpl = null;
this.listeners = {__proto__: null};
},
get root() {
const info = GetPublicRoot(this.treeID);
if (!info) {
return null;
}
return AutomationRootNodeImpl.getNodeFromTree(info.treeId, info.nodeId) ||
null;
},
get parent() {
const info = GetParentID(this.treeID, this.id);
if (info) {
return AutomationRootNodeImpl.getNodeFromTree(info.treeId, info.nodeId);
}
},
get htmlAttributes() {
return GetHtmlAttributes(this.treeID, this.id) || {};
},
get state() {
return GetState(this.treeID, this.id) || {};
},
get role() {
return GetRole(this.treeID, this.id);
},
get restriction() {
return GetRestriction(this.treeID, this.id);
},
get checked() {
return GetChecked(this.treeID, this.id);
},
get location() {
return GetLocation(this.treeID, this.id);
},
boundsForRange: function(startIndex, endIndex, callback) {
this.boundsForRangeInternal_(
startIndex, endIndex, true /* clipped */, callback);
},
unclippedBoundsForRange: function(startIndex, endIndex, callback) {
this.boundsForRangeInternal_(
startIndex, endIndex, false /* clipped */, callback);
},
boundsForRangeInternal_: function(startIndex, endIndex, clipped, callback) {
const errorMessage = clipped ?
'Error with bounds for range callback' :
'Error with unclipped bounds for range callback';
if (!this.rootImpl) {
return;
}
// Not yet initialized.
if (this.rootImpl.treeID === undefined || this.id === undefined) {
return;
}
if (!callback) {
return;
}
if (!GetBoolAttribute(this.treeID, this.id, 'supportsTextLocation')) {
try {
callback(GetBoundsForRange(
this.treeID, this.id, startIndex, endIndex, clipped /* clipped */));
return;
} catch (e) {
logging.WARNING(errorMessage + e);
}
return;
}
this.performAction_(
'getTextLocation', {startIndex: startIndex, endIndex: endIndex},
callback);
return;
},
get sortDirection() {
return GetSortDirection(this.treeID, this.id);
},
get value() {
return GetValue(this.treeID, this.id);
},
get unclippedLocation() {
let result = GetUnclippedLocation(this.treeID, this.id);
if (result === undefined) {
result = GetLocation(this.treeID, this.id);
}
return result;
},
get indexInParent() {
return GetIndexInParent(this.treeID, this.id);
},
get lineStartOffsets() {
return GetLineStartOffsets(this.treeID, this.id);
},
get childTree() {
const childTreeID = GetStringAttribute(this.treeID, this.id, 'childTreeId');
if (childTreeID) {
return AutomationRootNodeImpl.get(childTreeID);
}
},
get firstChild() {
if (GetChildCount(this.treeID, this.id) == 0) {
return undefined;
}
const info = GetChildIDAtIndex(this.treeID, this.id, 0);
if (info) {
return AutomationRootNodeImpl.getNodeFromTree(info.treeId, info.nodeId);
}
},
get lastChild() {
const count = GetChildCount(this.treeID, this.id);
if (count == 0) {
return;
}
const info = GetChildIDAtIndex(this.treeID, this.id, count - 1);
if (info) {
return AutomationRootNodeImpl.getNodeFromTree(info.treeId, info.nodeId);
}
},
get children() {
const info = GetChildIds(this.treeID, this.id);
if (!info) {
return [];
}
const children = [];
for (let i = 0; i < info.nodeIds.length; ++i) {
const childID = info.nodeIds[i];
const child =
AutomationRootNodeImpl.getNodeFromTree(info.treeId, childID);
if (child) {
$Array.push(children, child);
}
}
return children;
},
get previousSibling() {
let parent = this.parent;
if (!parent) {
return undefined;
}
parent = privates(parent).impl;
const indexInParent = GetIndexInParent(this.treeID, this.id);
const info = GetChildIDAtIndex(parent.treeID, parent.id, indexInParent - 1);
if (info) {
return AutomationRootNodeImpl.getNodeFromTree(info.treeId, info.nodeId);
}
},
get nextSibling() {
let parent = this.parent;
if (!parent) {
return undefined;
}
parent = privates(parent).impl;
const indexInParent = GetIndexInParent(this.treeID, this.id);
const info = GetChildIDAtIndex(parent.treeID, parent.id, indexInParent + 1);
if (info) {
return AutomationRootNodeImpl.getNodeFromTree(info.treeId, info.nodeId);
}
},
get nameFrom() {
return GetNameFrom(this.treeID, this.id);
},
get name() {
return GetName(this.treeID, this.id);
},
get descriptionFrom() {
return GetDescriptionFrom(this.treeID, this.id);
},
get imageAnnotation() {
return GetImageAnnotation(this.treeID, this.id);
},
get bold() {
return GetBold(this.treeID, this.id);
},
get italic() {
return GetItalic(this.treeID, this.id);
},
get underline() {
return GetUnderline(this.treeID, this.id);
},
get lineThrough() {
return GetLineThrough(this.treeID, this.id);
},
get detectedLanguage() {
return GetDetectedLanguage(this.treeID, this.id);
},
languageAnnotationForStringAttribute: function(attributeName) {
return GetLanguageAnnotationForStringAttribute(this.treeID,
this.id, attributeName);
},
get customActions() {
return GetCustomActions(this.treeID, this.id);
},
get standardActions() {
return GetStandardActions(this.treeID, this.id);
},
get defaultActionVerb() {
return GetDefaultActionVerb(this.treeID, this.id);
},
get hasPopup() {
return GetHasPopup(this.treeID, this.id);
},
get ariaCurrentState() {
return GetAriaCurrentState(this.treeID, this.id);
},
get tableCellColumnHeaders() {
const ids = GetTableCellColumnHeaders(this.treeID, this.id);
if (ids && this.rootImpl) {
const result = [];
for (let i = 0; i < ids.length; i++) {
result.push(this.rootImpl.get(ids[i]));
}
return result;
}
},
get tableCellRowHeaders() {
const ids = GetTableCellRowHeaders(this.treeID, this.id);
if (ids && this.rootImpl) {
const result = [];
for (let i = 0; i < ids.length; i++) {
result.push(this.rootImpl.get(ids[i]));
}
return result;
}
},
get tableCellColumnIndex() {
return GetTableCellColumnIndex(this.treeID, this.id);
},
get tableCellRowIndex() {
return GetTableCellRowIndex(this.treeID, this.id);
},
get tableCellAriaColumnIndex() {
return GetTableCellAriaColumnIndex(this.treeID, this.id);
},
get tableCellAriaRowIndex() {
return GetTableCellAriaRowIndex(this.treeID, this.id);
},
get tableColumnCount() {
return GetTableColumnCount(this.treeID, this.id);
},
get tableRowCount() {
return GetTableRowCount(this.treeID, this.id);
},
get nonInlineTextWordStarts() {
return GetWordStartOffsets(this.treeID, this.id);
},
get nonInlineTextWordEnds() {
return GetWordEndOffsets(this.treeID, this.id);
},
get sentenceStarts() {
return GetSentenceStartOffsets(this.treeID, this.id);
},
get sentenceEnds() {
return GetSentenceEndOffsets(this.treeID, this.id);
},
get markers() {
return GetMarkers(this.treeID, this.id);
},
createPosition: function(offset, opt_isUpstream) {
const nativePosition = CreateAutomationPosition(
this.treeID, this.id, offset, !!opt_isUpstream);
// Attach a getter for the node, which is only available in js.
Object.defineProperty(nativePosition, 'node', {
get: function() {
const tree =
AutomationTreeCache.idToAutomationRootNode[nativePosition.treeID];
if (!tree) {
return null;
}
return privates(tree).impl.get(nativePosition.anchorID);
}
});
return nativePosition;
},
doDefault: function() {
this.performAction_('doDefault');
},
focus: function() {
this.performAction_('focus');
},
getImageData: function(maxWidth, maxHeight) {
this.performAction_('getImageData',
{ maxWidth: maxWidth,
maxHeight: maxHeight });
},
hitTest: function(x, y, eventToFire) {
this.hitTestInternal(x, y, eventToFire);
},
hitTestWithReply: function(x, y, opt_callback) {
this.hitTestInternal(x, y, 'hitTestResult', opt_callback);
},
hitTestInternal: function(x, y, eventToFire, opt_callback) {
// Convert from global to tree-relative coordinates.
const location = GetLocation(this.treeID, GetRootID(this.treeID));
this.performAction_('hitTest',
{ x: Math.floor(x - location.left),
y: Math.floor(y - location.top),
eventToFire: eventToFire },
opt_callback);
},
makeVisible: function() {
this.performAction_('scrollToMakeVisible');
},
performCustomAction: function(customActionId) {
this.performAction_('customAction', { customActionID: customActionId });
},
performStandardAction: function(action) {
const standardActions = GetStandardActions(this.treeID, this.id);
if (!standardActions ||
!standardActions.find(item => action == item)) {
throw 'Inapplicable action for node: ' + action;
}
this.performAction_(action);
},
replaceSelectedText: function(value) {
if (this.state.editable) {
this.performAction_('replaceSelectedText', { value: value});
}
},
resumeMedia: function() {
this.performAction_('resumeMedia');
},
scrollBackward: function(opt_callback) {
this.performAction_('scrollBackward', {}, opt_callback);
},
scrollForward: function(opt_callback) {
this.performAction_('scrollForward', {}, opt_callback);
},
scrollUp: function(opt_callback) {
this.performAction_('scrollUp', {}, opt_callback);
},
scrollDown: function(opt_callback) {
this.performAction_('scrollDown', {}, opt_callback);
},
scrollLeft: function(opt_callback) {
this.performAction_('scrollLeft', {}, opt_callback);
},
scrollRight: function(opt_callback) {
this.performAction_('scrollRight', {}, opt_callback);
},
scrollToPoint: function(x, y) {
this.performAction_('scrollToPoint', {x, y});
},
setScrollOffset: function(x, y) {
this.performAction_('setScrollOffset', {x, y});
},
setAccessibilityFocus: function() {
SetAccessibilityFocus(this.treeID, this.id);
},
setSelection: function(startIndex, endIndex) {
if (this.state.editable) {
this.performAction_('setSelection',
{ focusNodeID: this.id,
anchorOffset: startIndex,
focusOffset: endIndex });
}
},
setSequentialFocusNavigationStartingPoint: function() {
this.performAction_('setSequentialFocusNavigationStartingPoint');
},
setValue: function(value) {
if (this.state.editable) {
this.performAction_('setValue', { value: value});
}
},
showContextMenu: function() {
this.performAction_('showContextMenu');
},
startDuckingMedia: function() {
this.performAction_('startDuckingMedia');
},
stopDuckingMedia: function() {
this.performAction_('stopDuckingMedia');
},
suspendMedia: function() {
this.performAction_('suspendMedia');
},
domQuerySelector: function(selector, callback) {
if (!this.rootImpl) {
callback();
}
automationInternal.querySelector(
{ treeID: this.rootImpl.treeID,
automationNodeID: this.id,
selector: selector },
$Function.bind(this.domQuerySelectorCallback_, this, callback));
},
find: function(params) {
return this.findInternal_(params);
},
findAll: function(params) {
return this.findInternal_(params, []);
},
matches: function(params) {
return this.matchInternal_(params);
},
getNextTextMatch: function(searchStr, backward) {
const info = GetNextTextMatch(this.treeID, this.id, searchStr, backward);
if (!info) {
return;
}
const impl = privates(AutomationRootNodeImpl.get(info.treeId)).impl;
if (impl) {
return impl.get(info.nodeId);
}
},
addEventListener: function(eventType, callback, capture) {
this.removeEventListener(eventType, callback);
if (!this.listeners[eventType]) {
this.listeners[eventType] = [];
}
// Calling EventListenerAdded will also validate the args
// and throw an exception it's not a valid event type, so no invalid event
// type/listener gets enqueued.
EventListenerAdded(this.treeID, this.id, eventType);
$Array.push(this.listeners[eventType], {
__proto__: null,
callback: callback,
capture: !!capture,
});
},
// TODO(dtseng/aboxhall): Check this impl against spec.
removeEventListener: function(eventType, callback) {
if (this.listeners[eventType]) {
const listeners = this.listeners[eventType];
for (let i = 0; i < listeners.length; i++) {
if (callback === listeners[i].callback) {
$Array.splice(listeners, i, 1);
}
}
if (listeners.length == 0) {
EventListenerRemoved(this.treeID, this.id, eventType);
}
}
},
toJSON: function() {
return { treeID: this.treeID,
id: this.id,
role: this.role,
attributes: this.attributes };
},
dispatchEvent: function(
eventType, eventFrom, eventFromAction, mouseX, mouseY, intents) {
const path = [];
let parent = this.parent;
while (parent) {
$Array.push(path, parent);
parent = parent.parent;
}
const event = new AutomationEvent(
eventType, this.wrapper, eventFrom, eventFromAction, mouseX, mouseY,
intents);
// Dispatch the event through the propagation path in three phases:
// - capturing: starting from the root and going down to the target's parent
// - targeting: dispatching the event on the target itself
// - bubbling: starting from the target's parent, going back up to the root.
// At any stage, a listener may call stopPropagation() on the event, which
// will immediately stop event propagation through this path.
if (this.dispatchEventAtCapturing_(event, path)) {
if (this.dispatchEventAtTargeting_(event, path)) {
this.dispatchEventAtBubbling_(event, path);
}
}
},
toString: function() {
let parentID = GetParentID(this.treeID, this.id);
parentID = parentID ? parentID.nodeId : null;
const childTreeID = GetStringAttribute(this.treeID, this.id, 'childTreeId');
const count = GetChildCount(this.treeID, this.id);
const childIDs = [];
for (let i = 0; i < count; ++i) {
const childID = GetChildIDAtIndex(this.treeID, this.id, i).nodeId;
$Array.push(childIDs, childID);
}
const name = GetName(this.treeID, this.id);
let result = 'node id=' + this.id + ' role=' + this.role +
' state=' + $JSON.stringify(this.state) + ' parentID=' + parentID +
' childIds=' + $JSON.stringify(childIDs);
if (childTreeID) {
result += ' childTreeID=' + childTreeID;
}
if (name) {
result += ' name=' + name;
}
if (this.className) {
result += ' className=' + this.className;
}
return result;
},
dispatchEventAtCapturing_: function(event, path) {
privates(event).impl.eventPhase = Event.CAPTURING_PHASE;
for (let i = path.length - 1; i >= 0; i--) {
this.fireEventListeners_(path[i], event);
if (privates(event).impl.propagationStopped) {
return false;
}
}
return true;
},
dispatchEventAtTargeting_: function(event) {
privates(event).impl.eventPhase = Event.AT_TARGET;
this.fireEventListeners_(this.wrapper, event);
return !privates(event).impl.propagationStopped;
},
dispatchEventAtBubbling_: function(event, path) {
privates(event).impl.eventPhase = Event.BUBBLING_PHASE;
for (let i = 0; i < path.length; i++) {
this.fireEventListeners_(path[i], event);
if (privates(event).impl.propagationStopped) {
return false;
}
}
return true;
},
fireEventListeners_: function(node, event) {
const nodeImpl = privates(node).impl;
if (!nodeImpl.rootImpl) {
return;
}
const originalListeners = nodeImpl.listeners[event.type];
if (!originalListeners) {
return;
}
// Make a copy of the original listeners since calling any of them can cause
// the list to be modified.
const listeners = [];
for (let i = 0; i < originalListeners.length; i++) {
listeners.push(originalListeners[i]);
}
const eventPhase = event.eventPhase;
for (let i = 0; i < listeners.length; i++) {
if (eventPhase == Event.CAPTURING_PHASE && !listeners[i].capture) {
continue;
}
if (eventPhase == Event.BUBBLING_PHASE && listeners[i].capture) {
continue;
}
try {
listeners[i].callback(event);
} catch (e) {
exceptionHandler.handle('Error in event handler for ' + event.type +
' during phase ' + eventPhase, e);
}
}
},
performAction_: function(actionType, opt_args, opt_callback) {
if (!this.rootImpl) {
return;
}
// Not yet initialized.
if (this.rootImpl.treeID === undefined ||
this.id === undefined) {
return;
}
// Check permissions.
if (!IsInteractPermitted()) {
throw new Error(actionType + ' requires {"desktop": true} or' +
' {"interact": true} in the "automation" manifest key.');
}
let requestID = -1;
if (opt_callback) {
requestID = this.rootImpl.addActionResultCallback(opt_callback);
}
automationInternal.performAction({ treeID: this.rootImpl.treeID,
automationNodeID: this.id,
actionType: actionType,
requestID: requestID},
opt_args || {});
},
domQuerySelectorCallback_: function(userCallback, resultAutomationNodeID) {
// resultAutomationNodeID could be zero or undefined or (unlikely) null;
// they all amount to the same thing here, which is that no node was
// returned.
if (!resultAutomationNodeID || !this.rootImpl) {
userCallback(null);
return;
}
const resultNode = this.rootImpl.get(resultAutomationNodeID);
if (!resultNode) {
logging.WARNING('Query selector result not in tree: ' +
resultAutomationNodeID);
userCallback(null);
}
userCallback(resultNode);
},
findInternal_: function(params, opt_results) {
let result = null;
this.forAllDescendants_(function(node) {
if (privates(node).impl.matchInternal_(params)) {
if (opt_results) {
$Array.push(opt_results, node);
} else {
result = node;
}
return !opt_results;
}
});
if (opt_results) {
return opt_results;
}
return result;
},
/**
* Executes a closure for all of this node's descendants, in pre-order.
* Early-outs if the closure returns true.
* @param {Function(AutomationNode):boolean} closure Closure to be executed
* for each node. Return true to early-out the traversal.
*/
forAllDescendants_: function(closure) {
const stack = $Array.reverse(this.wrapper.children);
while (stack.length > 0) {
const node = $Array.pop(stack);
if (closure(node)) {
return;
}
const children = node.children;
for (let i = children.length - 1; i >= 0; i--) {
$Array.push(stack, children[i]);
}
}
},
matchInternal_: function(params) {
if ($Object.keys(params).length === 0) {
return false;
}
if ('role' in params && this.role != params.role) {
return false;
}
if ('state' in params) {
for (const state in params.state) {
if (params.state[state] != (state in this.state)) {
return false;
}
}
}
if ('attributes' in params) {
for (const attribute in params.attributes) {
const attrValue = params.attributes[attribute];
if (typeof attrValue != 'object') {
if (this[attribute] !== attrValue) {
return false;
}
} else if (attrValue instanceof $RegExp.self) {
if (typeof this[attribute] != 'string') {
return false;
}
if (!attrValue.test(this[attribute])) {
return false;
}
} else {
// TODO(aboxhall): handle intlist case.
return false;
}
}
}
return true;
}
};
const stringAttributes = [
'accessKey',
'appId',
'ariaInvalidValue',
'autoComplete',
'checkedStateDescription',
'className',
'containerLiveRelevant',
'containerLiveStatus',
'description',
'display',
'fontFamily',
'htmlTag',
'imageDataUrl',
'innerHtml',
'language',
'liveRelevant',
'liveStatus',
'placeholder',
'roleDescription',
'tooltip',
'url'
];
const boolAttributes = [
'busy', 'clickable', 'containerLiveAtomic', 'containerLiveBusy',
'nonAtomicTextFieldRoot', 'liveAtomic', 'modal', 'notUserSelectableStyle',
'scrollable', 'selected', 'supportsTextLocation'
];
const intAttributes = [
'backgroundColor', 'color', 'colorValue', 'hierarchicalLevel', 'posInSet',
'scrollX', 'scrollXMax', 'scrollXMin', 'scrollY', 'scrollYMax', 'scrollYMin',
'setSize', 'tableCellColumnSpan', 'tableCellRowSpan', 'ariaColumnCount',
'ariaRowCount', 'textSelEnd', 'textSelStart'
];
// Int attribute, relation property to expose, reverse relation to expose.
const nodeRefAttributes = [
['activedescendantId', 'activeDescendant', 'activeDescendantFor'],
['errormessageId', 'errorMessage', 'errorMessageFor'],
['inPageLinkTargetId', 'inPageLinkTarget', null],
['nextFocusId', 'nextFocus', null], ['nextOnLineId', 'nextOnLine', null],
['previousFocusId', 'previousFocus', null],
['previousOnLineId', 'previousOnLine', null],
['tableColumnHeaderId', 'tableColumnHeader', null],
['tableHeaderId', 'tableHeader', null],
['tableRowHeaderId', 'tableRowHeader', null]
];
const intListAttributes = ['wordEnds', 'wordStarts'];
// Intlist attribute, relation property to expose, reverse relation to expose.
const nodeRefListAttributes = [
['controlsIds', 'controls', 'controlledBy'],
['describedbyIds', 'describedBy', 'descriptionFor'],
['detailsIds', 'details', 'detailsFor'], ['flowtoIds', 'flowTo', 'flowFrom'],
['labelledbyIds', 'labelledBy', 'labelFor']
];
const floatAttributes =
['fontSize', 'maxValueForRange', 'minValueForRange', 'valueForRange'];
const htmlAttributes = [['type', 'inputType']];
const publicAttributes = [];
$Array.forEach(stringAttributes, function(attributeName) {
$Array.push(publicAttributes, attributeName);
$Object.defineProperty(AutomationNodeImpl.prototype, attributeName, {
__proto__: null,
get: function() {
return GetStringAttribute(this.treeID, this.id, attributeName);
}
});
});
$Array.forEach(boolAttributes, function(attributeName) {
$Array.push(publicAttributes, attributeName);
$Object.defineProperty(AutomationNodeImpl.prototype, attributeName, {
__proto__: null,
get: function() {
return GetBoolAttribute(this.treeID, this.id, attributeName);
}
});
});
$Array.forEach(intAttributes, function(attributeName) {
$Array.push(publicAttributes, attributeName);
$Object.defineProperty(AutomationNodeImpl.prototype, attributeName, {
__proto__: null,
get: function() {
return GetIntAttribute(this.treeID, this.id, attributeName);
}
});
});
$Array.forEach(nodeRefAttributes, function(params) {
const srcAttributeName = params[0];
const dstAttributeName = params[1];
const dstReverseAttributeName = params[2];
$Array.push(publicAttributes, dstAttributeName);
$Object.defineProperty(AutomationNodeImpl.prototype, dstAttributeName, {
__proto__: null,
get: function() {
const id = GetIntAttribute(this.treeID, this.id, srcAttributeName);
if (id && this.rootImpl) {
return this.rootImpl.get(id);
} else {
return undefined;
}
}
});
if (dstReverseAttributeName) {
$Array.push(publicAttributes, dstReverseAttributeName);
$Object.defineProperty(
AutomationNodeImpl.prototype, dstReverseAttributeName, {
__proto__: null,
get: function() {
const ids = GetIntAttributeReverseRelations(
this.treeID, this.id, srcAttributeName);
if (!ids || !this.rootImpl) {
return undefined;
}
const result = [];
for (let i = 0; i < ids.length; ++i) {
const node = this.rootImpl.get(ids[i]);
if (node) {
$Array.push(result, node);
}
}
return result;
}
});
}
});
$Array.forEach(intListAttributes, function(attributeName) {
$Array.push(publicAttributes, attributeName);
$Object.defineProperty(AutomationNodeImpl.prototype, attributeName, {
__proto__: null,
get: function() {
return GetIntListAttribute(this.treeID, this.id, attributeName);
}
});
});
$Array.forEach(nodeRefListAttributes, function(params) {
const srcAttributeName = params[0];
const dstAttributeName = params[1];
const dstReverseAttributeName = params[2];
$Array.push(publicAttributes, dstAttributeName);
$Object.defineProperty(AutomationNodeImpl.prototype, dstAttributeName, {
__proto__: null,
get: function() {
const ids = GetIntListAttribute(this.treeID, this.id, srcAttributeName);
if (!ids || !this.rootImpl) {
return undefined;
}
const result = [];
for (let i = 0; i < ids.length; ++i) {
const node = this.rootImpl.get(ids[i]);
if (node) {
$Array.push(result, node);
}
}
return result;
}
});
if (dstReverseAttributeName) {
$Array.push(publicAttributes, dstReverseAttributeName);
$Object.defineProperty(
AutomationNodeImpl.prototype, dstReverseAttributeName, {
__proto__: null,
get: function() {
const ids = GetIntListAttributeReverseRelations(
this.treeID, this.id, srcAttributeName);
if (!ids || !this.rootImpl) {
return undefined;
}
const result = [];
for (let i = 0; i < ids.length; ++i) {
const node = this.rootImpl.get(ids[i]);
if (node) {
$Array.push(result, node);
}
}
return result;
}
});
}
});
$Array.forEach(floatAttributes, function(attributeName) {
$Array.push(publicAttributes, attributeName);
$Object.defineProperty(AutomationNodeImpl.prototype, attributeName, {
__proto__: null,
get: function() {
return GetFloatAttribute(this.treeID, this.id, attributeName);
}
});
});
$Array.forEach(htmlAttributes, function(params) {
const srcAttributeName = params[0];
const dstAttributeName = params[1];
$Array.push(publicAttributes, dstAttributeName);
$Object.defineProperty(AutomationNodeImpl.prototype, dstAttributeName, {
__proto__: null,
get: function() {
return GetHtmlAttribute(this.treeID, this.id, srcAttributeName);
}
});
});
/**
* AutomationRootNode.
*
* An AutomationRootNode is the javascript end of an AXTree living in the
* browser. AutomationRootNode handles unserializing incremental updates from
* the source AXTree. Each update contains node data that form a complete tree
* after applying the update.
*
* A brief note about ids used through this class. The source AXTree assigns
* unique ids per node and we use these ids to build a hash to the actual
* AutomationNode object.
* Thus, tree traversals amount to a lookup in our hash.
*
* The tree itself is identified by the accessibility tree id of the
* renderer widget host.
* @constructor
*/
function AutomationRootNodeImpl(treeID) {
$Function.call(AutomationNodeImpl, this, this);
this.treeID = treeID;
this.axNodeDataCache_ = {__proto__: null};
}
utils.defineProperty(AutomationRootNodeImpl, 'get', function(treeID) {
const result = AutomationTreeCache.idToAutomationRootNode[treeID];
return result || undefined;
});
utils.defineProperty(AutomationRootNodeImpl, 'getOrCreate', function(treeID) {
if (AutomationTreeCache.idToAutomationRootNode[treeID]) {
return AutomationTreeCache.idToAutomationRootNode[treeID];
}
const result = new AutomationRootNode(treeID);
AutomationTreeCache.idToAutomationRootNode[treeID] = result;
return result;
});
utils.defineProperty(
AutomationRootNodeImpl, 'getNodeFromTree', function(treeId, nodeId) {
const tree = AutomationRootNodeImpl.get(treeId);
if (!tree) {
return;
}
const impl = privates(tree).impl;
if (impl) {
return impl.get(nodeId);
}
});
utils.defineProperty(AutomationRootNodeImpl, 'destroy', function(treeID) {
delete AutomationTreeCache.idToAutomationRootNode[treeID];
});
/**
* A counter keeping track of IDs to use for mapping action requests to
* their callback function.
*/
AutomationRootNodeImpl.actionRequestCounter = 0;
/**
* A map from a request ID to the corresponding callback function to call
* when the action response event is received.
*/
AutomationRootNodeImpl.actionRequestIDToCallback = {};
AutomationRootNodeImpl.prototype = {
__proto__: AutomationNodeImpl.prototype,
/**
* @type {boolean}
*/
isRootNode: true,
/**
* @type {string}
*/
treeID: '',
/**
* A map from id to AutomationNode.
* @type {Object<number, AutomationNode>}
* @private
*/
axNodeDataCache_: null,
get id() {
const result = GetRootID(this.treeID);
// Don't return undefined, because the id is often passed directly
// as an argument to a native binding that expects only a valid number.
if (result === undefined) {
return -1;
}
return result;
},
get docUrl() {
return GetDocURL(this.treeID);
},
get docTitle() {
return GetDocTitle(this.treeID);
},
get docLoaded() {
return GetDocLoaded(this.treeID);
},
get docLoadingProgress() {
return GetDocLoadingProgress(this.treeID);
},
get isSelectionBackward() {
return GetIsSelectionBackward(this.treeID);
},
get anchorObject() {
const id = GetAnchorObjectID(this.treeID);
if (id && id != -1) {
return this.get(id);
}
return undefined;
},
get anchorOffset() {
const id = GetAnchorObjectID(this.treeID);
if (id && id != -1) {
return GetAnchorOffset(this.treeID);
}
return undefined;
},
get anchorAffinity() {
const id = GetAnchorObjectID(this.treeID);
if (id && id != -1) {
return GetAnchorAffinity(this.treeID);
}
return undefined;
},
get focusObject() {
const id = GetFocusObjectID(this.treeID);
if (id && id != -1) {
return this.get(id);
}
return undefined;
},
get focusOffset() {
const id = GetFocusObjectID(this.treeID);
if (id && id != -1) {
return GetFocusOffset(this.treeID);
}
return undefined;
},
get focusAffinity() {
const id = GetFocusObjectID(this.treeID);
if (id && id != -1) {
return GetFocusAffinity(this.treeID);
}
return undefined;
},
get selectionStartObject() {
const id = GetSelectionStartObjectID(this.treeID);
if (id && id != -1) {
return this.get(id);
}
return undefined;
},
get selectionStartOffset() {
const id = GetSelectionStartObjectID(this.treeID);
if (id && id != -1) {
return GetSelectionStartOffset(this.treeID);
}
return undefined;
},
get selectionStartAffinity() {
const id = GetSelectionStartObjectID(this.treeID);
if (id && id != -1) {
return GetSelectionStartAffinity(this.treeID);
}
return undefined;
},
get selectionEndObject() {
const id = GetSelectionEndObjectID(this.treeID);
if (id && id != -1) {
return this.get(id);
}
return undefined;
},
get selectionEndOffset() {
const id = GetSelectionEndObjectID(this.treeID);
if (id && id != -1) {
return GetSelectionEndOffset(this.treeID);
}
return undefined;
},
get selectionEndAffinity() {
const id = GetSelectionEndObjectID(this.treeID);
if (id && id != -1) {
return GetSelectionEndAffinity(this.treeID);
}
return undefined;
},
get: function(id) {
if (id == undefined) {
return undefined;
}
if (id == this.id) {
return this.wrapper;
}
let obj = this.axNodeDataCache_[id];
if (obj) {
return obj;
}
// Validate the backing AXTree has the specified node.
if (!GetRole(this.treeID, id)) {
return;
}
obj = new AutomationNode(this);
privates(obj).impl.treeID = this.treeID;
privates(obj).impl.id = id;
this.axNodeDataCache_[id] = obj;
return obj;
},
remove: function(id) {
if (this.axNodeDataCache_[id]) {
privates(this.axNodeDataCache_[id]).impl.detach();
}
delete this.axNodeDataCache_[id];
},
destroy: function() {
for (const id in this.axNodeDataCache_) {
this.remove(id);
}
this.detach();
},
onAccessibilityEvent: function(eventParams) {
const targetNode = this.get(eventParams.targetID);
if (targetNode) {
const targetNodeImpl = privates(targetNode).impl;
targetNodeImpl.dispatchEvent(
eventParams.eventType, eventParams.eventFrom,
eventParams.eventFromAction, eventParams.mouseX, eventParams.mouseY,
eventParams.intents);
if (eventParams.actionRequestID != -1) {
this.onActionResult(eventParams.actionRequestID, targetNode);
}
} else {
logging.WARNING('Got ' + eventParams.eventType +
' event on unknown node: ' + eventParams.targetID +
'; this: ' + this.id);
}
return true;
},
addActionResultCallback: function(callback) {
AutomationRootNodeImpl.actionRequestIDToCallback[
++AutomationRootNodeImpl.actionRequestCounter] = callback;
return AutomationRootNodeImpl.actionRequestCounter;
},
onGetTextLocationResult: function(textLocationParams) {
const requestID = textLocationParams.requestID;
if (requestID in AutomationRootNodeImpl.actionRequestIDToCallback) {
const callback =
AutomationRootNodeImpl.actionRequestIDToCallback[requestID];
try {
if (textLocationParams.result) {
callback(ComputeGlobalBounds(
this.treeID, textLocationParams.nodeID, textLocationParams.left,
textLocationParams.top, textLocationParams.width,
textLocationParams.height));
} else {
callback(undefined);
}
} catch (e) {
logging.WARNING('Error with onGetTextLocationResult callback:' + e);
}
delete AutomationRootNodeImpl.actionRequestIDToCallback[requestID];
}
},
onActionResult: function(requestID, result) {
if (requestID in AutomationRootNodeImpl.actionRequestIDToCallback) {
AutomationRootNodeImpl.actionRequestIDToCallback[requestID](result);
delete AutomationRootNodeImpl.actionRequestIDToCallback[requestID];
}
},
toString: function() {
function toStringInternal(nodeImpl, indent) {
if (!nodeImpl) {
return '';
}
let output = '';
if (nodeImpl.isRootNode) {
output += indent + 'tree id=' + nodeImpl.treeID + '\n';
}
output += indent +
$Function.call(AutomationNodeImpl.prototype.toString, nodeImpl) + '\n';
indent += ' ';
const children = nodeImpl.children;
for (let i = 0; i < children.length; ++i) {
output += toStringInternal(privates(children[i]).impl, indent);
}
return output;
}
return toStringInternal(this, '');
},
};
function AutomationNode() {
privates(AutomationNode).constructPrivate(this, arguments);
}
utils.expose(AutomationNode, AutomationNodeImpl, {
functions: [
'addEventListener',
'boundsForRange',
'createPosition',
'doDefault',
'domQuerySelector',
'find',
'findAll',
'focus',
'getImageData',
'getNextTextMatch',
'hitTest',
'hitTestWithReply',
'languageAnnotationForStringAttribute',
'makeVisible',
'matches',
'performCustomAction',
'performStandardAction',
'removeEventListener',
'replaceSelectedText',
'resumeMedia',
'scrollBackward',
'scrollDown',
'scrollForward',
'scrollLeft',
'scrollRight',
'scrollToPoint',
'scrollUp',
'setAccessibilityFocus',
'setScrollOffset',
'setSelection',
'setSequentialFocusNavigationStartingPoint',
'setValue',
'showContextMenu',
'startDuckingMedia',
'stopDuckingMedia',
'suspendMedia',
'toString',
'unclippedBoundsForRange'
],
readonly: $Array.concat(
publicAttributes,
[
'ariaCurrentState',
'bold',
'checked',
'children',
'customActions',
'defaultActionVerb',
'descriptionFrom',
'detectedLanguage',
'firstChild',
'hasPopup',
'htmlAttributes',
'imageAnnotation',
'indexInParent',
'isRootNode',
'italic',
'lastChild',
'lineStartOffsets',
'lineThrough',
'location',
'markers',
'name',
'nameFrom',
'nextSibling',
'nonInlineTextWordEnds',
'nonInlineTextWordStarts',
'parent',
'previousSibling',
'restriction',
'role',
'root',
'sortDirection',
'standardActions',
'state',
'sentenceStarts',
'sentenceEnds',
'tableCellAriaColumnIndex',
'tableCellAriaRowIndex',
'tableCellColumnHeaders',
'tableCellColumnIndex',
'tableCellRowHeaders',
'tableCellRowIndex',
'tableColumnCount',
'tableRowCount',
'unclippedLocation',
'underline',
'value',
]),
});
function AutomationRootNode() {
privates(AutomationRootNode).constructPrivate(this, arguments);
}
utils.expose(AutomationRootNode, AutomationRootNodeImpl, {
superclass: AutomationNode,
readonly: [
'docTitle',
'docUrl',
'docLoaded',
'docLoadingProgress',
'isSelectionBackward',
'anchorObject',
'anchorOffset',
'anchorAffinity',
'focusObject',
'focusOffset',
'focusAffinity',
'selectionStartObject',
'selectionStartOffset',
'selectionStartAffinity',
'selectionEndObject',
'selectionEndOffset',
'selectionEndAffinity',
],
});
utils.defineProperty(AutomationRootNode, 'get', function(treeID) {
return AutomationRootNodeImpl.get(treeID);
});
utils.defineProperty(AutomationRootNode, 'getOrCreate', function(treeID) {
return AutomationRootNodeImpl.getOrCreate(treeID);
});
utils.defineProperty(AutomationRootNode, 'destroy', function(treeID) {
AutomationRootNodeImpl.destroy(treeID);
});
exports.$set('AutomationNode', AutomationNode);
exports.$set('AutomationRootNode', AutomationRootNode);