Commit 62f95525 authored by David Tseng's avatar David Tseng Committed by Commit Bot

Add chrome.automation.GeneratedEventType

This change plumbs through the idea of a generated event type directly onto the AutomationEvent interface. GeneratedEvents are those defined in ui/accessibility/ax_event_generator.h.

This is a straight-forward way for chrome.automation to make use of the underlying data.

Note also that future authors can add additional events to chrome.automation.GeneratedEventType without touching ax::mojom::Event or ui::AXEventGenerator::Event. We only enforce that all ui::AXEventGenerator::Event types are in chrome.automation.GeneratedEventType, but not the reverse.

See the fixed bug for an application of this change.

Test: manually and browser_tests --gtest_filter=ChromeVox*.SortDirection.
Fixed: 1112983
AX-relnotes: adds support for aria-sort output in ChromeVox.

Change-Id: I27a34e988ce7ca8988ab2ee177e9022b7bc54610
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2353704
Commit-Queue: David Tseng <dtseng@chromium.org>
Reviewed-by: default avatarDominic Mazzoni <dmazzoni@chromium.org>
Reviewed-by: default avatarAkihiro Ota <akihiroota@chromium.org>
Cr-Commit-Position: refs/heads/master@{#801041}
parent 4eeea5a6
...@@ -2860,3 +2860,35 @@ TEST_F('ChromeVoxBackgroundTest', 'PopupButtonExpanded', function() { ...@@ -2860,3 +2860,35 @@ TEST_F('ChromeVoxBackgroundTest', 'PopupButtonExpanded', function() {
.replay(); .replay();
}); });
}); });
TEST_F('ChromeVoxBackgroundTest', 'SortDirection', function() {
const mockFeedback = this.createMockFeedback();
this.runWithLoadedTree(
`
<table border="1">
<th aria-sort="ascending"><button id="sort">Date</button></th>
<tr><td>1/2/20</td></tr>
<tr><td>2/2/20</td></tr>
</table>
<script>
let ascending = true;
const sortButton = document.getElementById('sort');
sortButton.addEventListener('click', () => {
ascending = !ascending;
sortButton.parentElement.setAttribute(
'aria-sort', ascending ? 'ascending' : 'descending');
});
</script>
`,
function(root) {
const sortButton = root.find({role: RoleType.BUTTON});
mockFeedback.expectSpeech('Button', 'Ascending sort')
.call(sortButton.doDefault.bind(sortButton))
.expectSpeech('Descending sort')
.call(sortButton.doDefault.bind(sortButton))
.expectSpeech('Ascending sort')
.call(sortButton.doDefault.bind(sortButton))
.expectSpeech('Descending sort')
.replay();
});
});
...@@ -1777,6 +1777,25 @@ Output = class { ...@@ -1777,6 +1777,25 @@ Output = class {
if (node.errorMessage) { if (node.errorMessage) {
ret.push({outputFormat: '$node(errorMessage)'}); ret.push({outputFormat: '$node(errorMessage)'});
} }
// Provide a hint for sort direction.
let sortDirectionNode = node;
while (sortDirectionNode && sortDirectionNode != sortDirectionNode.root) {
if (!sortDirectionNode.sortDirection) {
sortDirectionNode = sortDirectionNode.parent;
continue;
}
if (sortDirectionNode.sortDirection ===
chrome.automation.SortDirectionType.ASCENDING) {
ret.push({msgId: 'sort_ascending'});
} else if (
sortDirectionNode.sortDirection ===
chrome.automation.SortDirectionType.DESCENDING) {
ret.push({msgId: 'sort_descending'});
}
break;
}
return ret; return ret;
} }
......
...@@ -54,6 +54,18 @@ RangeAutomationHandler = class extends BaseAutomationHandler { ...@@ -54,6 +54,18 @@ RangeAutomationHandler = class extends BaseAutomationHandler {
newRange.start.node, newRange.end.node) || newRange.start.node, newRange.end.node) ||
newRange.start.node; newRange.start.node;
// Some re-targeting is needed for cases like tables.
let retarget = this.node_;
while (retarget && retarget != retarget.root) {
// Table headers require retargeting for events because they often have
// event types we care about e.g. sort direction.
if (retarget.role == RoleType.COLUMN_HEADER ||
retarget.role == RoleType.ROW_HEADER) {
this.node_ = retarget;
break;
}
retarget = retarget.parent;
}
this.addListener_( this.addListener_(
EventType.ARIA_ATTRIBUTE_CHANGED, this.onAriaAttributeChanged); EventType.ARIA_ATTRIBUTE_CHANGED, this.onAriaAttributeChanged);
this.addListener_(EventType.AUTOCORRECTION_OCCURED, this.onEventIfInRange); this.addListener_(EventType.AUTOCORRECTION_OCCURED, this.onEventIfInRange);
...@@ -131,6 +143,24 @@ RangeAutomationHandler = class extends BaseAutomationHandler { ...@@ -131,6 +143,24 @@ RangeAutomationHandler = class extends BaseAutomationHandler {
return; return;
} }
// Report attribute changes for specific generated events.
if (evt.generatedType ==
chrome.automation.GeneratedEventType.SORT_CHANGED) {
let msgId;
if (evt.target.sortDirection ==
chrome.automation.SortDirectionType.ASCENDING) {
msgId = 'sort_ascending';
} else if (
evt.target.sortDirection ==
chrome.automation.SortDirectionType.DESCENDING) {
msgId = 'sort_descending';
}
if (msgId) {
new Output().withString(Msgs.getMsg(msgId)).go();
}
return;
}
// Only report attribute changes on some *Option roles if it is selected. // Only report attribute changes on some *Option roles if it is selected.
if ((evt.target.role == RoleType.MENU_LIST_OPTION || if ((evt.target.role == RoleType.MENU_LIST_OPTION ||
evt.target.role == RoleType.LIST_BOX_OPTION) && evt.target.role == RoleType.LIST_BOX_OPTION) &&
......
...@@ -2990,5 +2990,11 @@ ...@@ -2990,5 +2990,11 @@
<message desc="Describes the media play/pause key on a Chromebook. No associated UI with this string which is spoken using text-to-speech." name="IDS_CHROMEVOX_MEDIA_PLAY_PAUSE" is_accessibility_with_no_ui="true"> <message desc="Describes the media play/pause key on a Chromebook. No associated UI with this string which is spoken using text-to-speech." name="IDS_CHROMEVOX_MEDIA_PLAY_PAUSE" is_accessibility_with_no_ui="true">
Media Play/Pause Media Play/Pause
</message> </message>
<message desc="Describes a table header sorted in ascending order. No associated UI with this string which is spoken using text-to-speech." name="IDS_CHROMEVOX_SORT_ASCENDING" is_accessibility_with_no_ui="true">
Ascending sort
</message>
<message desc="Describes a table header sorted in descending order. No associated UI with this string which is spoken using text-to-speech." name="IDS_CHROMEVOX_SORT_DESCENDING" is_accessibility_with_no_ui="true">
Descending sort
</message>
</grit-part> </grit-part>
...@@ -486,6 +486,67 @@ enum EventMoveDirectionType { ...@@ -486,6 +486,67 @@ enum EventMoveDirectionType {
backward backward
}; };
// A sort applied to a table row or column header.
enum SortDirectionType {
unsorted,
ascending,
descending,
other
};
// A set of events inferred by Chrome based upon the tree and node data.
enum GeneratedEventType {
accessKeyChanged,
atomicChanged,
autoCompleteChanged,
busyChanged,
classNameChanged,
collapsed,
describedByChanged,
descriptionChanged,
dropeffectChanged,
enabledChanged,
expanded,
focusChanged,
flowFromChanged,
flowToChanged,
grabbedChanged,
haspopupChanged,
hierarchicalLevelChanged,
ignoredChanged,
imageAnnotationChanged,
keyShortcutsChanged,
labeledByChanged,
languageChanged,
layoutInvalidated, // fired when aria-busy goes false
liveRegionNodeChanged, // fired on a node within a live region.
liveRelevantChanged,
liveStatusChanged,
menuItemSelected,
multilineStateChanged,
multiselectableStateChanged,
nameChanged,
objectAttributeChanged,
otherAttributeChanged,
placeholderChanged,
portalActivated,
positionInSetChanged,
relatedNodeChanged,
readonlyChanged,
requiredStateChanged,
roleChanged,
scrollHorizontalPositionChanged,
scrollVerticalPositionChanged,
selectedChanged,
setSizeChanged,
sortChanged,
subtreeCreated,
textAttributeChanged,
valueMaxChanged,
valueMinChanged,
valueStepChanged
};
dictionary Rect { dictionary Rect {
long left; long left;
long top; long top;
...@@ -557,6 +618,9 @@ enum EventMoveDirectionType { ...@@ -557,6 +618,9 @@ enum EventMoveDirectionType {
// The type of the event. // The type of the event.
EventType type; EventType type;
// The type of the generated event, if any.
GeneratedEventType? generatedType;
// The source of this event. // The source of this event.
DOMString eventFrom; DOMString eventFrom;
...@@ -1173,6 +1237,9 @@ enum EventMoveDirectionType { ...@@ -1173,6 +1237,9 @@ enum EventMoveDirectionType {
// the root node, this will be undefined. // the root node, this will be undefined.
long? indexInParent; long? indexInParent;
// The sort direction of this node.
SortDirectionType sortDirection;
// //
// Actions. // Actions.
// //
......
...@@ -193,6 +193,7 @@ api::automation::EventType ToAutomationEvent( ...@@ -193,6 +193,7 @@ api::automation::EventType ToAutomationEvent(
case ui::AXEventGenerator::Event::NAME_CHANGED: case ui::AXEventGenerator::Event::NAME_CHANGED:
case ui::AXEventGenerator::Event::ROLE_CHANGED: case ui::AXEventGenerator::Event::ROLE_CHANGED:
case ui::AXEventGenerator::Event::SELECTED_CHANGED: case ui::AXEventGenerator::Event::SELECTED_CHANGED:
case ui::AXEventGenerator::Event::SORT_CHANGED:
case ui::AXEventGenerator::Event::STATE_CHANGED: case ui::AXEventGenerator::Event::STATE_CHANGED:
return api::automation::EVENT_TYPE_ARIAATTRIBUTECHANGED; return api::automation::EVENT_TYPE_ARIAATTRIBUTECHANGED;
...@@ -227,7 +228,6 @@ api::automation::EventType ToAutomationEvent( ...@@ -227,7 +228,6 @@ api::automation::EventType ToAutomationEvent(
case ui::AXEventGenerator::Event::READONLY_CHANGED: case ui::AXEventGenerator::Event::READONLY_CHANGED:
case ui::AXEventGenerator::Event::REQUIRED_STATE_CHANGED: case ui::AXEventGenerator::Event::REQUIRED_STATE_CHANGED:
case ui::AXEventGenerator::Event::SET_SIZE_CHANGED: case ui::AXEventGenerator::Event::SET_SIZE_CHANGED:
case ui::AXEventGenerator::Event::SORT_CHANGED:
case ui::AXEventGenerator::Event::SUBTREE_CREATED: case ui::AXEventGenerator::Event::SUBTREE_CREATED:
case ui::AXEventGenerator::Event::TEXT_ATTRIBUTE_CHANGED: case ui::AXEventGenerator::Event::TEXT_ATTRIBUTE_CHANGED:
case ui::AXEventGenerator::Event::VALUE_MAX_CHANGED: case ui::AXEventGenerator::Event::VALUE_MAX_CHANGED:
...@@ -241,6 +241,144 @@ api::automation::EventType ToAutomationEvent( ...@@ -241,6 +241,144 @@ api::automation::EventType ToAutomationEvent(
return api::automation::EVENT_TYPE_NONE; return api::automation::EVENT_TYPE_NONE;
} }
// Convert from ui::AXEventGenerator::Event to
// api::automation::GeneratedEventType.
api::automation::GeneratedEventType ToAutomationGeneratedEvent(
ui::AXEventGenerator::Event event_type) {
// Note for future changes:
// Please add a corresponding definition of the AXeventGenerator::Event enum
// to extensions/common/api/automation.idl if the generated event is an
// attribute or something not mirrored in ax::mojom::Event.
switch (event_type) {
case ui::AXEventGenerator::Event::ACCESS_KEY_CHANGED:
return api::automation::GENERATED_EVENT_TYPE_ACCESSKEYCHANGED;
case ui::AXEventGenerator::Event::ATOMIC_CHANGED:
return api::automation::GENERATED_EVENT_TYPE_ATOMICCHANGED;
case ui::AXEventGenerator::Event::AUTO_COMPLETE_CHANGED:
return api::automation::GENERATED_EVENT_TYPE_AUTOCOMPLETECHANGED;
case ui::AXEventGenerator::Event::BUSY_CHANGED:
return api::automation::GENERATED_EVENT_TYPE_BUSYCHANGED;
case ui::AXEventGenerator::Event::CLASS_NAME_CHANGED:
return api::automation::GENERATED_EVENT_TYPE_CLASSNAMECHANGED;
case ui::AXEventGenerator::Event::COLLAPSED:
return api::automation::GENERATED_EVENT_TYPE_COLLAPSED;
case ui::AXEventGenerator::Event::DESCRIBED_BY_CHANGED:
return api::automation::GENERATED_EVENT_TYPE_DESCRIBEDBYCHANGED;
case ui::AXEventGenerator::Event::DESCRIPTION_CHANGED:
return api::automation::GENERATED_EVENT_TYPE_DESCRIPTIONCHANGED;
case ui::AXEventGenerator::Event::DROPEFFECT_CHANGED:
return api::automation::GENERATED_EVENT_TYPE_DROPEFFECTCHANGED;
case ui::AXEventGenerator::Event::ENABLED_CHANGED:
return api::automation::GENERATED_EVENT_TYPE_ENABLEDCHANGED;
case ui::AXEventGenerator::Event::EXPANDED:
return api::automation::GENERATED_EVENT_TYPE_EXPANDED;
case ui::AXEventGenerator::Event::FOCUS_CHANGED:
return api::automation::GENERATED_EVENT_TYPE_FOCUSCHANGED;
case ui::AXEventGenerator::Event::FLOW_FROM_CHANGED:
return api::automation::GENERATED_EVENT_TYPE_FLOWFROMCHANGED;
case ui::AXEventGenerator::Event::FLOW_TO_CHANGED:
return api::automation::GENERATED_EVENT_TYPE_FLOWTOCHANGED;
case ui::AXEventGenerator::Event::GRABBED_CHANGED:
return api::automation::GENERATED_EVENT_TYPE_GRABBEDCHANGED;
case ui::AXEventGenerator::Event::HASPOPUP_CHANGED:
return api::automation::GENERATED_EVENT_TYPE_HASPOPUPCHANGED;
case ui::AXEventGenerator::Event::HIERARCHICAL_LEVEL_CHANGED:
return api::automation::GENERATED_EVENT_TYPE_HIERARCHICALLEVELCHANGED;
case ui::AXEventGenerator::Event::IGNORED_CHANGED:
return api::automation::GENERATED_EVENT_TYPE_IGNOREDCHANGED;
case ui::AXEventGenerator::Event::IMAGE_ANNOTATION_CHANGED:
return api::automation::GENERATED_EVENT_TYPE_IMAGEANNOTATIONCHANGED;
case ui::AXEventGenerator::Event::KEY_SHORTCUTS_CHANGED:
return api::automation::GENERATED_EVENT_TYPE_KEYSHORTCUTSCHANGED;
case ui::AXEventGenerator::Event::LABELED_BY_CHANGED:
return api::automation::GENERATED_EVENT_TYPE_LABELEDBYCHANGED;
case ui::AXEventGenerator::Event::LANGUAGE_CHANGED:
return api::automation::GENERATED_EVENT_TYPE_LANGUAGECHANGED;
case ui::AXEventGenerator::Event::LAYOUT_INVALIDATED:
return api::automation::GENERATED_EVENT_TYPE_LAYOUTINVALIDATED;
case ui::AXEventGenerator::Event::LIVE_REGION_NODE_CHANGED:
return api::automation::GENERATED_EVENT_TYPE_LIVEREGIONNODECHANGED;
case ui::AXEventGenerator::Event::LIVE_RELEVANT_CHANGED:
return api::automation::GENERATED_EVENT_TYPE_LIVERELEVANTCHANGED;
case ui::AXEventGenerator::Event::LIVE_STATUS_CHANGED:
return api::automation::GENERATED_EVENT_TYPE_LIVESTATUSCHANGED;
case ui::AXEventGenerator::Event::MENU_ITEM_SELECTED:
return api::automation::GENERATED_EVENT_TYPE_MENUITEMSELECTED;
case ui::AXEventGenerator::Event::MULTILINE_STATE_CHANGED:
return api::automation::GENERATED_EVENT_TYPE_MULTILINESTATECHANGED;
case ui::AXEventGenerator::Event::MULTISELECTABLE_STATE_CHANGED:
return api::automation::GENERATED_EVENT_TYPE_MULTISELECTABLESTATECHANGED;
case ui::AXEventGenerator::Event::NAME_CHANGED:
return api::automation::GENERATED_EVENT_TYPE_NAMECHANGED;
case ui::AXEventGenerator::Event::OBJECT_ATTRIBUTE_CHANGED:
return api::automation::GENERATED_EVENT_TYPE_OBJECTATTRIBUTECHANGED;
case ui::AXEventGenerator::Event::OTHER_ATTRIBUTE_CHANGED:
return api::automation::GENERATED_EVENT_TYPE_OTHERATTRIBUTECHANGED;
case ui::AXEventGenerator::Event::PLACEHOLDER_CHANGED:
return api::automation::GENERATED_EVENT_TYPE_PLACEHOLDERCHANGED;
case ui::AXEventGenerator::Event::PORTAL_ACTIVATED:
return api::automation::GENERATED_EVENT_TYPE_PORTALACTIVATED;
case ui::AXEventGenerator::Event::POSITION_IN_SET_CHANGED:
return api::automation::GENERATED_EVENT_TYPE_POSITIONINSETCHANGED;
case ui::AXEventGenerator::Event::RELATED_NODE_CHANGED:
return api::automation::GENERATED_EVENT_TYPE_RELATEDNODECHANGED;
case ui::AXEventGenerator::Event::READONLY_CHANGED:
return api::automation::GENERATED_EVENT_TYPE_READONLYCHANGED;
case ui::AXEventGenerator::Event::REQUIRED_STATE_CHANGED:
return api::automation::GENERATED_EVENT_TYPE_REQUIREDSTATECHANGED;
case ui::AXEventGenerator::Event::ROLE_CHANGED:
return api::automation::GENERATED_EVENT_TYPE_ROLECHANGED;
case ui::AXEventGenerator::Event::SCROLL_HORIZONTAL_POSITION_CHANGED:
return api::automation::
GENERATED_EVENT_TYPE_SCROLLHORIZONTALPOSITIONCHANGED;
case ui::AXEventGenerator::Event::SCROLL_VERTICAL_POSITION_CHANGED:
return api::automation::
GENERATED_EVENT_TYPE_SCROLLVERTICALPOSITIONCHANGED;
case ui::AXEventGenerator::Event::SELECTED_CHANGED:
return api::automation::GENERATED_EVENT_TYPE_SELECTEDCHANGED;
case ui::AXEventGenerator::Event::SET_SIZE_CHANGED:
return api::automation::GENERATED_EVENT_TYPE_SETSIZECHANGED;
case ui::AXEventGenerator::Event::SORT_CHANGED:
return api::automation::GENERATED_EVENT_TYPE_SORTCHANGED;
case ui::AXEventGenerator::Event::SUBTREE_CREATED:
return api::automation::GENERATED_EVENT_TYPE_SUBTREECREATED;
case ui::AXEventGenerator::Event::TEXT_ATTRIBUTE_CHANGED:
return api::automation::GENERATED_EVENT_TYPE_TEXTATTRIBUTECHANGED;
case ui::AXEventGenerator::Event::VALUE_MAX_CHANGED:
return api::automation::GENERATED_EVENT_TYPE_VALUEMAXCHANGED;
case ui::AXEventGenerator::Event::VALUE_MIN_CHANGED:
return api::automation::GENERATED_EVENT_TYPE_VALUEMINCHANGED;
case ui::AXEventGenerator::Event::VALUE_STEP_CHANGED:
return api::automation::GENERATED_EVENT_TYPE_VALUESTEPCHANGED;
// These do not have a mapping to automation either because they're
// already represented in ax::mojom::Event or they are not publically
// relevant.
case ui::AXEventGenerator::Event::ACTIVE_DESCENDANT_CHANGED:
case ui::AXEventGenerator::Event::ALERT:
case ui::AXEventGenerator::Event::CHECKED_STATE_CHANGED:
case ui::AXEventGenerator::Event::CHILDREN_CHANGED:
case ui::AXEventGenerator::Event::CONTROLS_CHANGED:
case ui::AXEventGenerator::Event::DOCUMENT_SELECTION_CHANGED:
case ui::AXEventGenerator::Event::DOCUMENT_TITLE_CHANGED:
case ui::AXEventGenerator::Event::INVALID_STATUS_CHANGED:
case ui::AXEventGenerator::Event::LIVE_REGION_CHANGED:
case ui::AXEventGenerator::Event::LIVE_REGION_CREATED:
case ui::AXEventGenerator::Event::LOAD_COMPLETE:
case ui::AXEventGenerator::Event::LOAD_START:
case ui::AXEventGenerator::Event::ROW_COUNT_CHANGED:
case ui::AXEventGenerator::Event::SELECTED_CHILDREN_CHANGED:
case ui::AXEventGenerator::Event::STATE_CHANGED:
case ui::AXEventGenerator::Event::VALUE_CHANGED:
case ui::AXEventGenerator::Event::WIN_IACCESSIBLE_STATE_CHANGED:
return api::automation::GENERATED_EVENT_TYPE_NONE;
}
NOTREACHED();
return api::automation::GENERATED_EVENT_TYPE_NONE;
}
} // namespace } // namespace
AutomationAXTreeWrapper::AutomationAXTreeWrapper( AutomationAXTreeWrapper::AutomationAXTreeWrapper(
...@@ -363,9 +501,10 @@ bool AutomationAXTreeWrapper::OnAccessibilityEvents( ...@@ -363,9 +501,10 @@ bool AutomationAXTreeWrapper::OnAccessibilityEvents(
generated_event.id = targeted_event.node->id(); generated_event.id = targeted_event.node->id();
generated_event.event_from = targeted_event.event_params.event_from; generated_event.event_from = targeted_event.event_params.event_from;
generated_event.event_intents = targeted_event.event_params.event_intents; generated_event.event_intents = targeted_event.event_params.event_intents;
owner_->SendAutomationEvent(event_bundle.tree_id, owner_->SendAutomationEvent(
event_bundle.mouse_location, generated_event, event_bundle.tree_id, event_bundle.mouse_location, generated_event,
event_type); event_type,
ToAutomationGeneratedEvent(targeted_event.event_params.event));
} }
} }
event_generator_.ClearEvents(); event_generator_.ClearEvents();
......
...@@ -1588,6 +1588,20 @@ void AutomationInternalCustomBindings::AddRoutes() { ...@@ -1588,6 +1588,20 @@ void AutomationInternalCustomBindings::AddRoutes() {
accessibility_focused_tree_id_ = tree_id; accessibility_focused_tree_id_ = tree_id;
tree_wrapper->SetAccessibilityFocus(node->id()); tree_wrapper->SetAccessibilityFocus(node->id());
}); });
RouteNodeIDFunction(
"GetSortDirection",
[](v8::Isolate* isolate, v8::ReturnValue<v8::Value> result,
AutomationAXTreeWrapper* tree_wrapper, ui::AXNode* node) {
if (node->HasIntAttribute(ax::mojom::IntAttribute::kSortDirection)) {
const std::string& sort_direction_str = ui::ToString(
static_cast<ax::mojom::SortDirection>(node->GetIntAttribute(
ax::mojom::IntAttribute::kSortDirection)));
result.Set(v8::String::NewFromUtf8(isolate,
sort_direction_str.c_str(),
v8::NewStringType::kNormal)
.ToLocalChecked());
}
});
RouteNodeIDPlusEventFunction( RouteNodeIDPlusEventFunction(
"EventListenerAdded", "EventListenerAdded",
[](v8::Isolate* isolate, v8::ReturnValue<v8::Value> result, [](v8::Isolate* isolate, v8::ReturnValue<v8::Value> result,
...@@ -2344,7 +2358,8 @@ void AutomationInternalCustomBindings::SendAutomationEvent( ...@@ -2344,7 +2358,8 @@ void AutomationInternalCustomBindings::SendAutomationEvent(
ui::AXTreeID tree_id, ui::AXTreeID tree_id,
const gfx::Point& mouse_location, const gfx::Point& mouse_location,
const ui::AXEvent& event, const ui::AXEvent& event,
api::automation::EventType event_type) { api::automation::EventType event_type,
api::automation::GeneratedEventType generated_event_type) {
AutomationAXTreeWrapper* tree_wrapper = AutomationAXTreeWrapper* tree_wrapper =
GetAutomationAXTreeWrapperFromTreeID(tree_id); GetAutomationAXTreeWrapperFromTreeID(tree_id);
if (!tree_wrapper) if (!tree_wrapper)
...@@ -2378,6 +2393,9 @@ void AutomationInternalCustomBindings::SendAutomationEvent( ...@@ -2378,6 +2393,9 @@ void AutomationInternalCustomBindings::SendAutomationEvent(
event_params.SetKey("targetID", base::Value(event.id)); event_params.SetKey("targetID", base::Value(event.id));
event_params.SetKey("eventType", event_params.SetKey("eventType",
base::Value(api::automation::ToString(event_type))); base::Value(api::automation::ToString(event_type)));
event_params.SetKey(
"generatedEventType",
base::Value(api::automation::ToString(generated_event_type)));
event_params.SetKey("eventFrom", base::Value(ui::ToString(event.event_from))); event_params.SetKey("eventFrom", base::Value(ui::ToString(event.event_from)));
event_params.SetKey("actionRequestID", base::Value(event.action_request_id)); event_params.SetKey("actionRequestID", base::Value(event.action_request_id));
event_params.SetKey("mouseX", base::Value(mouse_location.x())); event_params.SetKey("mouseX", base::Value(mouse_location.x()));
......
...@@ -83,10 +83,13 @@ class AutomationInternalCustomBindings : public ObjectBackedNativeHandler { ...@@ -83,10 +83,13 @@ class AutomationInternalCustomBindings : public ObjectBackedNativeHandler {
bool SendTreeChangeEvent(api::automation::TreeChangeType change_type, bool SendTreeChangeEvent(api::automation::TreeChangeType change_type,
ui::AXTree* tree, ui::AXTree* tree,
ui::AXNode* node); ui::AXNode* node);
void SendAutomationEvent(ui::AXTreeID tree_id, void SendAutomationEvent(
const gfx::Point& mouse_location, ui::AXTreeID tree_id,
const ui::AXEvent& event, const gfx::Point& mouse_location,
api::automation::EventType event_type); const ui::AXEvent& event,
api::automation::EventType event_type,
api::automation::GeneratedEventType generated_event_type =
api::automation::GENERATED_EVENT_TYPE_NONE);
void MaybeSendFocusAndBlur( void MaybeSendFocusAndBlur(
AutomationAXTreeWrapper* tree, AutomationAXTreeWrapper* tree,
......
...@@ -558,7 +558,14 @@ var GetMarkers = natives.GetMarkers; ...@@ -558,7 +558,14 @@ var GetMarkers = natives.GetMarkers;
* @param {boolean} isUpstream * @param {boolean} isUpstream
* @return {!Object} * @return {!Object}
*/ */
var createAutomationPosition = natives.CreateAutomationPosition; var 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.
*/
var GetSortDirection = natives.GetSortDirection;
var logging = requireNative('logging'); var logging = requireNative('logging');
var utils = require('utils'); var utils = require('utils');
...@@ -662,6 +669,10 @@ AutomationNodeImpl.prototype = { ...@@ -662,6 +669,10 @@ AutomationNodeImpl.prototype = {
return; return;
}, },
get sortDirection() {
return GetSortDirection(this.treeID, this.id);
},
get unclippedLocation() { get unclippedLocation() {
var result = GetUnclippedLocation(this.treeID, this.id); var result = GetUnclippedLocation(this.treeID, this.id);
if (result === undefined) if (result === undefined)
...@@ -853,7 +864,7 @@ AutomationNodeImpl.prototype = { ...@@ -853,7 +864,7 @@ AutomationNodeImpl.prototype = {
}, },
createPosition: function(offset, opt_isUpstream) { createPosition: function(offset, opt_isUpstream) {
var nativePosition = createAutomationPosition( var nativePosition = CreateAutomationPosition(
this.treeID, this.id, offset, !!opt_isUpstream); this.treeID, this.id, offset, !!opt_isUpstream);
// Attach a getter for the node, which is only available in js. // Attach a getter for the node, which is only available in js.
...@@ -1068,7 +1079,8 @@ AutomationNodeImpl.prototype = { ...@@ -1068,7 +1079,8 @@ AutomationNodeImpl.prototype = {
attributes: this.attributes }; attributes: this.attributes };
}, },
dispatchEvent: function(eventType, eventFrom, mouseX, mouseY, intents) { dispatchEvent: function(
eventType, generatedEventType, eventFrom, mouseX, mouseY, intents) {
var path = []; var path = [];
var parent = this.parent; var parent = this.parent;
while (parent) { while (parent) {
...@@ -1076,6 +1088,7 @@ AutomationNodeImpl.prototype = { ...@@ -1076,6 +1088,7 @@ AutomationNodeImpl.prototype = {
parent = parent.parent; parent = parent.parent;
} }
var event = new AutomationEvent(eventType, this.wrapper, eventFrom); var event = new AutomationEvent(eventType, this.wrapper, eventFrom);
event.generatedType = generatedEventType;
event.mouseX = mouseX; event.mouseX = mouseX;
event.mouseY = mouseY; event.mouseY = mouseY;
event.intents = intents; event.intents = intents;
...@@ -1746,8 +1759,9 @@ AutomationRootNodeImpl.prototype = { ...@@ -1746,8 +1759,9 @@ AutomationRootNodeImpl.prototype = {
if (targetNode) { if (targetNode) {
var targetNodeImpl = privates(targetNode).impl; var targetNodeImpl = privates(targetNode).impl;
targetNodeImpl.dispatchEvent( targetNodeImpl.dispatchEvent(
eventParams.eventType, eventParams.eventFrom, eventParams.eventType, eventParams.generatedEventType,
eventParams.mouseX, eventParams.mouseY, eventParams.intents); eventParams.eventFrom, eventParams.mouseX, eventParams.mouseY,
eventParams.intents);
if (eventParams.actionRequestID != -1) { if (eventParams.actionRequestID != -1) {
this.onActionResult(eventParams.actionRequestID, targetNode); this.onActionResult(eventParams.actionRequestID, targetNode);
...@@ -1890,6 +1904,7 @@ utils.expose(AutomationNode, AutomationNodeImpl, { ...@@ -1890,6 +1904,7 @@ utils.expose(AutomationNode, AutomationNodeImpl, {
'restriction', 'restriction',
'role', 'role',
'root', 'root',
'sortDirection',
'standardActions', 'standardActions',
'state', 'state',
'tableCellAriaColumnIndex', 'tableCellAriaColumnIndex',
......
...@@ -489,6 +489,73 @@ chrome.automation.EventMoveDirectionType = { ...@@ -489,6 +489,73 @@ chrome.automation.EventMoveDirectionType = {
BACKWARD: 'backward', BACKWARD: 'backward',
}; };
/**
* @enum {string}
* @see https://developer.chrome.com/extensions/automation#type-SortDirectionType
*/
chrome.automation.SortDirectionType = {
UNSORTED: 'unsorted',
ASCENDING: 'ascending',
DESCENDING: 'descending',
OTHER: 'other',
};
/**
* @enum {string}
* @see https://developer.chrome.com/extensions/automation#type-GeneratedEventType
*/
chrome.automation.GeneratedEventType = {
ACCESS_KEY_CHANGED: 'accessKeyChanged',
ATOMIC_CHANGED: 'atomicChanged',
AUTO_COMPLETE_CHANGED: 'autoCompleteChanged',
BUSY_CHANGED: 'busyChanged',
CLASS_NAME_CHANGED: 'classNameChanged',
COLLAPSED: 'collapsed',
DESCRIBED_BY_CHANGED: 'describedByChanged',
DESCRIPTION_CHANGED: 'descriptionChanged',
DROPEFFECT_CHANGED: 'dropeffectChanged',
ENABLED_CHANGED: 'enabledChanged',
EXPANDED: 'expanded',
FOCUS_CHANGED: 'focusChanged',
FLOW_FROM_CHANGED: 'flowFromChanged',
FLOW_TO_CHANGED: 'flowToChanged',
GRABBED_CHANGED: 'grabbedChanged',
HASPOPUP_CHANGED: 'haspopupChanged',
HIERARCHICAL_LEVEL_CHANGED: 'hierarchicalLevelChanged',
IGNORED_CHANGED: 'ignoredChanged',
IMAGE_ANNOTATION_CHANGED: 'imageAnnotationChanged',
KEY_SHORTCUTS_CHANGED: 'keyShortcutsChanged',
LABELED_BY_CHANGED: 'labeledByChanged',
LANGUAGE_CHANGED: 'languageChanged',
LAYOUT_INVALIDATED: 'layoutInvalidated',
LIVE_REGION_NODE_CHANGED: 'liveRegionNodeChanged',
LIVE_RELEVANT_CHANGED: 'liveRelevantChanged',
LIVE_STATUS_CHANGED: 'liveStatusChanged',
MENU_ITEM_SELECTED: 'menuItemSelected',
MULTILINE_STATE_CHANGED: 'multilineStateChanged',
MULTISELECTABLE_STATE_CHANGED: 'multiselectableStateChanged',
NAME_CHANGED: 'nameChanged',
OBJECT_ATTRIBUTE_CHANGED: 'objectAttributeChanged',
OTHER_ATTRIBUTE_CHANGED: 'otherAttributeChanged',
PLACEHOLDER_CHANGED: 'placeholderChanged',
PORTAL_ACTIVATED: 'portalActivated',
POSITION_IN_SET_CHANGED: 'positionInSetChanged',
RELATED_NODE_CHANGED: 'relatedNodeChanged',
READONLY_CHANGED: 'readonlyChanged',
REQUIRED_STATE_CHANGED: 'requiredStateChanged',
ROLE_CHANGED: 'roleChanged',
SCROLL_HORIZONTAL_POSITION_CHANGED: 'scrollHorizontalPositionChanged',
SCROLL_VERTICAL_POSITION_CHANGED: 'scrollVerticalPositionChanged',
SELECTED_CHANGED: 'selectedChanged',
SET_SIZE_CHANGED: 'setSizeChanged',
SORT_CHANGED: 'sortChanged',
SUBTREE_CREATED: 'subtreeCreated',
TEXT_ATTRIBUTE_CHANGED: 'textAttributeChanged',
VALUE_MAX_CHANGED: 'valueMaxChanged',
VALUE_MIN_CHANGED: 'valueMinChanged',
VALUE_STEP_CHANGED: 'valueStepChanged',
};
/** /**
* @typedef {{ * @typedef {{
* left: number, * left: number,
...@@ -552,6 +619,13 @@ chrome.automation.AutomationEvent.prototype.target; ...@@ -552,6 +619,13 @@ chrome.automation.AutomationEvent.prototype.target;
*/ */
chrome.automation.AutomationEvent.prototype.type; chrome.automation.AutomationEvent.prototype.type;
/**
* The type of the generated event, if any.
* @type {(!chrome.automation.GeneratedEventType|undefined)}
* @see https://developer.chrome.com/extensions/automation#type-generatedType
*/
chrome.automation.AutomationEvent.prototype.generatedType;
/** /**
* The source of this event. * The source of this event.
* @type {string} * @type {string}
...@@ -1046,8 +1120,7 @@ chrome.automation.AutomationNode.prototype.boundsForRange = function(startIndex, ...@@ -1046,8 +1120,7 @@ chrome.automation.AutomationNode.prototype.boundsForRange = function(startIndex,
* @param {function(!chrome.automation.Rect): void} callback * @param {function(!chrome.automation.Rect): void} callback
* @see https://developer.chrome.com/extensions/automation#method-unclippedBoundsForRange * @see https://developer.chrome.com/extensions/automation#method-unclippedBoundsForRange
*/ */
chrome.automation.AutomationNode.prototype.unclippedBoundsForRange = function( chrome.automation.AutomationNode.prototype.unclippedBoundsForRange = function(startIndex, endIndex, callback) {};
startIndex, endIndex, callback) {};
/** /**
* The location (as a bounding box) of this node in global screen coordinates without applying any clipping from ancestors. * The location (as a bounding box) of this node in global screen coordinates without applying any clipping from ancestors.
...@@ -1944,6 +2017,13 @@ chrome.automation.AutomationNode.prototype.nextFocus; ...@@ -1944,6 +2017,13 @@ chrome.automation.AutomationNode.prototype.nextFocus;
*/ */
chrome.automation.AutomationNode.prototype.indexInParent; chrome.automation.AutomationNode.prototype.indexInParent;
/**
* The sort direction of this node.
* @type {!chrome.automation.SortDirectionType}
* @see https://developer.chrome.com/extensions/automation#type-sortDirection
*/
chrome.automation.AutomationNode.prototype.sortDirection;
/** /**
* Does the default action based on this node's role. This is generally the same * Does the default action based on this node's role. This is generally the same
* action that would result from clicking the node such as expanding a treeitem, * action that would result from clicking the node such as expanding a treeitem,
......
...@@ -155,6 +155,8 @@ def CheckEnumsMatch(input_api, output_api): ...@@ -155,6 +155,8 @@ def CheckEnumsMatch(input_api, output_api):
'EventTextBoundaryType', errs, output_api) 'EventTextBoundaryType', errs, output_api)
CheckMatchingEnum(ax_enums, 'MoveDirection', automation_enums, CheckMatchingEnum(ax_enums, 'MoveDirection', automation_enums,
'EventMoveDirectionType', errs, output_api) 'EventMoveDirectionType', errs, output_api)
CheckMatchingEnum(ax_enums, 'SortDirection', automation_enums,
'SortDirectionType', errs, output_api)
return errs return errs
# Given a full path to c++ header, return an array of the first static # Given a full path to c++ header, return an array of the first static
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment