0

Exposes computed node attributes to chrome.automation

In https://crrev.com/c/2953640 computed node data was exposed to some parts of the codebase.
Computed node data includes any attribute that could be computed on the browser side
instead of in Blink, such as the value attribute.
This patch exposes these attributes to the automation API.

R=dmazzoni@chromium.org, aleventhal@chromium.org

AX-Relnotes: n/a.
Bug: 123141
Change-Id: Icf05987e4341d05c675c61d1a61931d7075a5c2b
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2982020
Reviewed-by: Dominic Mazzoni <dmazzoni@chromium.org>
Commit-Queue: Nektarios Paisios <nektar@chromium.org>
Cr-Commit-Position: refs/heads/master@{#895845}
This commit is contained in:
Nektarios Paisios
2021-06-25 00:13:22 +00:00
committed by Chromium LUCI CQ
parent 75e0c1446e
commit b4339d24fb
3 changed files with 72 additions and 83 deletions

@ -184,15 +184,15 @@ bool AutomationAXTreeWrapper::OnAccessibilityEvents(
}
bool AutomationAXTreeWrapper::IsDesktopTree() const {
return tree_.root() ? tree_.root()->data().role == ax::mojom::Role::kDesktop
return tree_.root() ? tree_.root()->GetRole() == ax::mojom::Role::kDesktop
: false;
}
bool AutomationAXTreeWrapper::HasDeviceScaleFactor() const {
return tree_.root() ?
// These are views-backed trees.
tree_.root()->data().role != ax::mojom::Role::kDesktop &&
tree_.root()->data().role != ax::mojom::Role::kClient
tree_.root()->GetRole() != ax::mojom::Role::kDesktop &&
tree_.root()->GetRole() != ax::mojom::Role::kClient
: true;
}
@ -427,15 +427,15 @@ void AutomationAXTreeWrapper::OnNodeWillBeDeleted(ui::AXTree* tree,
deleted_node_ids_.push_back(node->id());
node_id_to_events_.erase(node->id());
if (node->data().HasStringAttribute(
if (node->HasStringAttribute(
ax::mojom::StringAttribute::kChildTreeNodeAppId)) {
GetAppIDToChildTreeNodeMap().erase(node->data().GetStringAttribute(
GetAppIDToChildTreeNodeMap().erase(node->GetStringAttribute(
ax::mojom::StringAttribute::kChildTreeNodeAppId));
}
if (node->data().HasStringAttribute(ax::mojom::StringAttribute::kAppId)) {
if (node->HasStringAttribute(ax::mojom::StringAttribute::kAppId)) {
const std::string& app_id =
node->data().GetStringAttribute(ax::mojom::StringAttribute::kAppId);
node->GetStringAttribute(ax::mojom::StringAttribute::kAppId);
GetAppIDToParentTreeNodeMap().erase(app_id);
all_parent_tree_node_app_ids_.erase(app_id);
}
@ -443,16 +443,16 @@ void AutomationAXTreeWrapper::OnNodeWillBeDeleted(ui::AXTree* tree,
void AutomationAXTreeWrapper::OnNodeCreated(ui::AXTree* tree,
ui::AXNode* node) {
if (node->data().HasStringAttribute(
if (node->HasStringAttribute(
ax::mojom::StringAttribute::kChildTreeNodeAppId)) {
GetAppIDToChildTreeNodeMap()[node->data().GetStringAttribute(
GetAppIDToChildTreeNodeMap()[node->GetStringAttribute(
ax::mojom::StringAttribute::kChildTreeNodeAppId)] = {
node->tree()->GetAXTreeID(), node->id()};
}
if (node->data().HasStringAttribute(ax::mojom::StringAttribute::kAppId)) {
if (node->HasStringAttribute(ax::mojom::StringAttribute::kAppId)) {
const std::string& app_id =
node->data().GetStringAttribute(ax::mojom::StringAttribute::kAppId);
node->GetStringAttribute(ax::mojom::StringAttribute::kAppId);
GetAppIDToParentTreeNodeMap()[app_id] = {node->tree()->GetAXTreeID(),
node->id()};
all_parent_tree_node_app_ids_.insert(app_id);

@ -850,7 +850,7 @@ void AutomationInternalCustomBindings::AddRoutes() {
RouteNodeIDFunction(
"GetRole", [](v8::Isolate* isolate, v8::ReturnValue<v8::Value> result,
AutomationAXTreeWrapper* tree_wrapper, ui::AXNode* node) {
const std::string& role_name = ui::ToString(node->data().role);
const std::string& role_name = ui::ToString(node->GetRole());
result.Set(v8::String::NewFromUtf8(isolate, role_name.c_str())
.ToLocalChecked());
});
@ -993,7 +993,7 @@ void AutomationInternalCustomBindings::AddRoutes() {
[this](v8::Isolate* isolate, v8::ReturnValue<v8::Value> result,
AutomationAXTreeWrapper* tree_wrapper, ui::AXNode* node, int start,
int end, bool clipped) {
if (node->data().role != ax::mojom::Role::kInlineTextBox)
if (node->GetRole() != ax::mojom::Role::kInlineTextBox)
return;
// Use character offsets to compute the local bounds of this subrange.
@ -1001,8 +1001,8 @@ void AutomationInternalCustomBindings::AddRoutes() {
node->data().relative_bounds.bounds.width(),
node->data().relative_bounds.bounds.height());
const std::string& name =
node->data().GetStringAttribute(ax::mojom::StringAttribute::kName);
std::vector<int> character_offsets = node->data().GetIntListAttribute(
node->GetStringAttribute(ax::mojom::StringAttribute::kName);
std::vector<int> character_offsets = node->GetIntListAttribute(
ax::mojom::IntListAttribute::kCharacterOffsets);
int len =
static_cast<int>(std::min(name.size(), character_offsets.size()));
@ -1070,10 +1070,10 @@ void AutomationInternalCustomBindings::AddRoutes() {
if (attribute == ax::mojom::StringAttribute::kFontFamily ||
attribute == ax::mojom::StringAttribute::kLanguage) {
attr_value = node->GetInheritedStringAttribute(attribute).c_str();
} else if (!node->data().HasStringAttribute(attribute)) {
} else if (!node->HasStringAttribute(attribute)) {
return;
} else {
attr_value = node->data().GetStringAttribute(attribute).c_str();
attr_value = node->GetStringAttribute(attribute).c_str();
}
result.Set(
@ -1087,7 +1087,7 @@ void AutomationInternalCustomBindings::AddRoutes() {
auto attribute =
ui::ParseAXEnum<ax::mojom::BoolAttribute>(attribute_name.c_str());
bool attr_value;
if (!node->data().GetBoolAttribute(attribute, &attr_value))
if (!node->GetBoolAttribute(attribute, &attr_value))
return;
result.Set(v8::Boolean::New(isolate, attr_value));
@ -1107,7 +1107,7 @@ void AutomationInternalCustomBindings::AddRoutes() {
} else if (attribute == ax::mojom::IntAttribute::kSetSize &&
node->GetSetSize()) {
attr_value = *node->GetSetSize();
} else if (!node->data().GetIntAttribute(attribute, &attr_value)) {
} else if (!node->GetIntAttribute(attribute, &attr_value)) {
return;
}
@ -1142,7 +1142,7 @@ void AutomationInternalCustomBindings::AddRoutes() {
ui::ParseAXEnum<ax::mojom::FloatAttribute>(attribute_name.c_str());
float attr_value;
if (!node->data().GetFloatAttribute(attribute, &attr_value))
if (!node->GetFloatAttribute(attribute, &attr_value))
return;
double intpart, fracpart;
@ -1158,10 +1158,10 @@ void AutomationInternalCustomBindings::AddRoutes() {
const std::string& attribute_name) {
auto attribute = ui::ParseAXEnum<ax::mojom::IntListAttribute>(
attribute_name.c_str());
if (!node->data().HasIntListAttribute(attribute))
if (!node->HasIntListAttribute(attribute))
return;
const std::vector<int32_t>& attr_value =
node->data().GetIntListAttribute(attribute);
node->GetIntListAttribute(attribute);
v8::Local<v8::Context> context = isolate->GetCurrentContext();
v8::Local<v8::Array> array_result(
@ -1199,8 +1199,7 @@ void AutomationInternalCustomBindings::AddRoutes() {
ui::AXTree* tree, ui::AXNode* node,
const std::string& attribute_name) {
std::string attribute_value;
if (!node->data().GetHtmlAttribute(attribute_name.c_str(),
&attribute_value))
if (!node->GetHtmlAttribute(attribute_name.c_str(), &attribute_value))
return;
result.Set(v8::String::NewFromUtf8(isolate, attribute_value.c_str())
@ -1210,8 +1209,7 @@ void AutomationInternalCustomBindings::AddRoutes() {
"GetNameFrom",
[](v8::Isolate* isolate, v8::ReturnValue<v8::Value> result,
AutomationAXTreeWrapper* tree_wrapper, ui::AXNode* node) {
ax::mojom::NameFrom name_from = static_cast<ax::mojom::NameFrom>(
node->data().GetIntAttribute(ax::mojom::IntAttribute::kNameFrom));
ax::mojom::NameFrom name_from = node->data().GetNameFrom();
const std::string& name_from_str = ui::ToString(name_from);
result.Set(v8::String::NewFromUtf8(isolate, name_from_str.c_str())
.ToLocalChecked());
@ -1220,21 +1218,18 @@ void AutomationInternalCustomBindings::AddRoutes() {
v8::ReturnValue<v8::Value> result,
AutomationAXTreeWrapper* tree_wrapper,
ui::AXNode* node) {
const ui::AXNodeData& node_data = node->data();
const char* name = nullptr;
if (node_data.role == ax::mojom::Role::kPortal &&
node_data.GetNameFrom() == ax::mojom::NameFrom::kNone) {
if (node->GetRole() == ax::mojom::Role::kPortal &&
node->data().GetNameFrom() == ax::mojom::NameFrom::kNone) {
if (GetRootOfChildTree(&node, &tree_wrapper)) {
name = node->data()
.GetStringAttribute(ax::mojom::StringAttribute::kName)
.c_str();
name =
node->GetStringAttribute(ax::mojom::StringAttribute::kName).c_str();
}
}
if (!name &&
node_data.HasStringAttribute(ax::mojom::StringAttribute::kName)) {
name = node_data.GetStringAttribute(ax::mojom::StringAttribute::kName)
.c_str();
if (!name && node->HasStringAttribute(ax::mojom::StringAttribute::kName)) {
name =
node->GetStringAttribute(ax::mojom::StringAttribute::kName).c_str();
}
if (name)
@ -1245,9 +1240,8 @@ void AutomationInternalCustomBindings::AddRoutes() {
[](v8::Isolate* isolate, v8::ReturnValue<v8::Value> result,
AutomationAXTreeWrapper* tree_wrapper, ui::AXNode* node) {
ax::mojom::DescriptionFrom description_from =
static_cast<ax::mojom::DescriptionFrom>(
node->data().GetIntAttribute(
ax::mojom::IntAttribute::kDescriptionFrom));
static_cast<ax::mojom::DescriptionFrom>(node->GetIntAttribute(
ax::mojom::IntAttribute::kDescriptionFrom));
std::string description_from_str = ui::ToString(description_from);
result.Set(
v8::String::NewFromUtf8(isolate, description_from_str.c_str())
@ -1283,22 +1277,21 @@ void AutomationInternalCustomBindings::AddRoutes() {
result.Set(v8::String::NewFromUtf8(isolate, status_string.c_str())
.ToLocalChecked());
});
RouteNodeIDFunction("GetSubscript", [](v8::Isolate* isolate,
v8::ReturnValue<v8::Value> result,
AutomationAXTreeWrapper* tree_wrapper,
ui::AXNode* node) {
bool value =
node->data().GetIntAttribute(ax::mojom::IntAttribute::kTextPosition) ==
static_cast<int32_t>(ax::mojom::TextPosition::kSubscript);
result.Set(v8::Boolean::New(isolate, value));
});
RouteNodeIDFunction(
"GetSubscript",
[](v8::Isolate* isolate, v8::ReturnValue<v8::Value> result,
AutomationAXTreeWrapper* tree_wrapper, ui::AXNode* node) {
bool value =
node->GetIntAttribute(ax::mojom::IntAttribute::kTextPosition) ==
static_cast<int32_t>(ax::mojom::TextPosition::kSubscript);
result.Set(v8::Boolean::New(isolate, value));
});
RouteNodeIDFunction(
"GetSuperscript",
[](v8::Isolate* isolate, v8::ReturnValue<v8::Value> result,
AutomationAXTreeWrapper* tree_wrapper, ui::AXNode* node) {
bool value =
node->data().GetIntAttribute(
ax::mojom::IntAttribute::kTextPosition) ==
node->GetIntAttribute(ax::mojom::IntAttribute::kTextPosition) ==
static_cast<int32_t>(ax::mojom::TextPosition::kSuperscript);
result.Set(v8::Boolean::New(isolate, value));
});
@ -1387,7 +1380,7 @@ void AutomationInternalCustomBindings::AddRoutes() {
[](v8::Isolate* isolate, v8::ReturnValue<v8::Value> result,
AutomationAXTreeWrapper* tree_wrapper, ui::AXNode* node) {
const std::vector<int32_t>& custom_action_ids =
node->data().GetIntListAttribute(
node->GetIntListAttribute(
ax::mojom::IntListAttribute::kCustomActionIds);
if (custom_action_ids.empty()) {
result.SetUndefined();
@ -1395,7 +1388,7 @@ void AutomationInternalCustomBindings::AddRoutes() {
}
const std::vector<std::string>& custom_action_descriptions =
node->data().GetStringListAttribute(
node->GetStringListAttribute(
ax::mojom::StringListAttribute::kCustomActionDescriptions);
if (custom_action_ids.size() != custom_action_descriptions.size()) {
NOTREACHED();
@ -1435,9 +1428,8 @@ void AutomationInternalCustomBindings::AddRoutes() {
// The doDefault action is implied by having a default action verb.
int default_action_verb =
static_cast<int>(ax::mojom::DefaultActionVerb::kNone);
if (node->data().GetIntAttribute(
ax::mojom::IntAttribute::kDefaultActionVerb,
&default_action_verb) &&
if (node->GetIntAttribute(ax::mojom::IntAttribute::kDefaultActionVerb,
&default_action_verb) &&
default_action_verb !=
static_cast<int>(ax::mojom::DefaultActionVerb::kNone)) {
standard_actions.push_back(ui::ToString(
@ -1446,7 +1438,7 @@ void AutomationInternalCustomBindings::AddRoutes() {
// Increment and decrement are available when the role is a slider or
// spin button.
auto role = node->data().role;
auto role = node->GetRole();
if (role == ax::mojom::Role::kSlider ||
role == ax::mojom::Role::kSpinButton) {
standard_actions.push_back(ui::ToString(
@ -1473,8 +1465,8 @@ void AutomationInternalCustomBindings::AddRoutes() {
[](v8::Isolate* isolate, v8::ReturnValue<v8::Value> result,
AutomationAXTreeWrapper* tree_wrapper, ui::AXNode* node) {
const ax::mojom::CheckedState checked_state =
static_cast<ax::mojom::CheckedState>(node->data().GetIntAttribute(
ax::mojom::IntAttribute::kCheckedState));
static_cast<ax::mojom::CheckedState>(
node->GetIntAttribute(ax::mojom::IntAttribute::kCheckedState));
if (checked_state != ax::mojom::CheckedState::kNone) {
const std::string& checked_str = ui::ToString(checked_state);
result.Set(v8::String::NewFromUtf8(isolate, checked_str.c_str())
@ -1498,9 +1490,8 @@ void AutomationInternalCustomBindings::AddRoutes() {
[](v8::Isolate* isolate, v8::ReturnValue<v8::Value> result,
AutomationAXTreeWrapper* tree_wrapper, ui::AXNode* node) {
ax::mojom::DefaultActionVerb default_action_verb =
static_cast<ax::mojom::DefaultActionVerb>(
node->data().GetIntAttribute(
ax::mojom::IntAttribute::kDefaultActionVerb));
static_cast<ax::mojom::DefaultActionVerb>(node->GetIntAttribute(
ax::mojom::IntAttribute::kDefaultActionVerb));
if (default_action_verb == ax::mojom::DefaultActionVerb::kNone)
return;
@ -1524,9 +1515,8 @@ void AutomationInternalCustomBindings::AddRoutes() {
[](v8::Isolate* isolate, v8::ReturnValue<v8::Value> result,
AutomationAXTreeWrapper* tree_wrapper, ui::AXNode* node) {
ax::mojom::AriaCurrentState current_state =
static_cast<ax::mojom::AriaCurrentState>(
node->data().GetIntAttribute(
ax::mojom::IntAttribute::kAriaCurrentState));
static_cast<ax::mojom::AriaCurrentState>(node->GetIntAttribute(
ax::mojom::IntAttribute::kAriaCurrentState));
if (current_state == ax::mojom::AriaCurrentState::kNone)
return;
const std::string& current_state_string = ui::ToString(current_state);
@ -1866,12 +1856,12 @@ bool AutomationInternalCustomBindings::GetFocusInternal(
// tree can have focus.
std::string child_tree_id_str;
std::string child_tree_node_app_id_str;
while ((focus->data().GetStringAttribute(
ax::mojom::StringAttribute::kChildTreeId, &child_tree_id_str) ||
focus->data().GetStringAttribute(
while ((focus->GetStringAttribute(ax::mojom::StringAttribute::kChildTreeId,
&child_tree_id_str) ||
focus->GetStringAttribute(
ax::mojom::StringAttribute::kChildTreeNodeAppId,
&child_tree_node_app_id_str)) &&
focus->data().role != ax::mojom::Role::kPortal) {
focus->GetRole() != ax::mojom::Role::kPortal) {
AutomationAXTreeWrapper* child_tree_wrapper = nullptr;
if (!child_tree_node_app_id_str.empty()) {
@ -2432,8 +2422,8 @@ bool AutomationInternalCustomBindings::SendTreeChangeEvent(
// Notify custom bindings when there's an unloaded tree; js will enable the
// renderer and wait for it to load.
std::string child_tree_id_str;
if (node->data().GetStringAttribute(ax::mojom::StringAttribute::kChildTreeId,
&child_tree_id_str)) {
if (node->GetStringAttribute(ax::mojom::StringAttribute::kChildTreeId,
&child_tree_id_str)) {
ui::AXTreeID child_tree_id = ui::AXTreeID::FromString(child_tree_id_str);
auto* tree_wrapper = GetAutomationAXTreeWrapperFromTreeID(child_tree_id);
if (!tree_wrapper || !tree_wrapper->tree()->data().loaded)
@ -2444,9 +2434,9 @@ bool AutomationInternalCustomBindings::SendTreeChangeEvent(
if (tree_change_observer_overall_filter_ &
(1
<< api::automation::TREE_CHANGE_OBSERVER_FILTER_LIVEREGIONTREECHANGES)) {
if (node->data().HasStringAttribute(
if (node->HasStringAttribute(
ax::mojom::StringAttribute::kContainerLiveStatus) ||
node->data().role == ax::mojom::Role::kAlert ||
node->GetRole() == ax::mojom::Role::kAlert ||
change_type == api::automation::TREE_CHANGE_TYPE_SUBTREEUPDATEEND) {
has_filter = true;
}
@ -2454,8 +2444,7 @@ bool AutomationInternalCustomBindings::SendTreeChangeEvent(
if (tree_change_observer_overall_filter_ &
(1 << api::automation::TREE_CHANGE_OBSERVER_FILTER_TEXTMARKERCHANGES)) {
if (node->data().HasIntListAttribute(
ax::mojom::IntListAttribute::kMarkerTypes))
if (node->HasIntListAttribute(ax::mojom::IntListAttribute::kMarkerTypes))
has_filter = true;
}
@ -2478,15 +2467,15 @@ bool AutomationInternalCustomBindings::SendTreeChangeEvent(
default:
continue;
case api::automation::TREE_CHANGE_OBSERVER_FILTER_LIVEREGIONTREECHANGES:
if (!node->data().HasStringAttribute(
if (!node->HasStringAttribute(
ax::mojom::StringAttribute::kContainerLiveStatus) &&
node->data().role != ax::mojom::Role::kAlert &&
node->GetRole() != ax::mojom::Role::kAlert &&
change_type != api::automation::TREE_CHANGE_TYPE_SUBTREEUPDATEEND) {
continue;
}
break;
case api::automation::TREE_CHANGE_OBSERVER_FILTER_TEXTMARKERCHANGES:
if (!node->data().HasIntListAttribute(
if (!node->HasIntListAttribute(
ax::mojom::IntListAttribute::kMarkerTypes))
continue;
break;

@ -137,7 +137,7 @@ TEST_F(AutomationInternalCustomBindingsTest, GetFocusOneTree) {
ASSERT_TRUE(focused_wrapper);
ASSERT_TRUE(focused_node);
EXPECT_EQ(desktop, focused_wrapper);
EXPECT_EQ(ax::mojom::Role::kButton, focused_node->data().role);
EXPECT_EQ(ax::mojom::Role::kButton, focused_node->GetRole());
// Push an update where we change the focus.
focused_wrapper = nullptr;
@ -148,7 +148,7 @@ TEST_F(AutomationInternalCustomBindingsTest, GetFocusOneTree) {
ASSERT_TRUE(focused_wrapper);
ASSERT_TRUE(focused_node);
EXPECT_EQ(desktop, focused_wrapper);
EXPECT_EQ(ax::mojom::Role::kDesktop, focused_node->data().role);
EXPECT_EQ(ax::mojom::Role::kDesktop, focused_node->GetRole());
// Push an update where we change the focus to nothing.
focused_wrapper = nullptr;
@ -221,7 +221,7 @@ TEST_F(AutomationInternalCustomBindingsTest,
ASSERT_TRUE(focused_node);
EXPECT_EQ(wrapper_1, focused_wrapper);
EXPECT_EQ(tree_1_id, focused_node->tree()->GetAXTreeID());
EXPECT_EQ(ax::mojom::Role::kButton, focused_node->data().role);
EXPECT_EQ(ax::mojom::Role::kButton, focused_node->GetRole());
// Push an update where we change the focus.
focused_wrapper = nullptr;
@ -235,7 +235,7 @@ TEST_F(AutomationInternalCustomBindingsTest,
ASSERT_TRUE(focused_node);
EXPECT_EQ(wrapper_2, focused_wrapper);
EXPECT_EQ(tree_2_id, focused_node->tree()->GetAXTreeID());
EXPECT_EQ(ax::mojom::Role::kButton, focused_node->data().role);
EXPECT_EQ(ax::mojom::Role::kButton, focused_node->GetRole());
}
TEST_F(AutomationInternalCustomBindingsTest,
@ -315,7 +315,7 @@ TEST_F(AutomationInternalCustomBindingsTest,
// This is an interesting inconsistency as this node is technically not in the
// app (which starts at the link in wrapper 1).
EXPECT_EQ(ax::mojom::Role::kButton, focused_node->data().role);
EXPECT_EQ(ax::mojom::Role::kButton, focused_node->GetRole());
// Push an update where we change the focus.
focused_wrapper = nullptr;
@ -329,7 +329,7 @@ TEST_F(AutomationInternalCustomBindingsTest,
ASSERT_TRUE(focused_node);
EXPECT_EQ(wrapper_2, focused_wrapper);
EXPECT_EQ(tree_2_id, focused_node->tree()->GetAXTreeID());
EXPECT_EQ(ax::mojom::Role::kButton, focused_node->data().role);
EXPECT_EQ(ax::mojom::Role::kButton, focused_node->GetRole());
}
TEST_F(AutomationInternalCustomBindingsTest, GetBoundsAppIdConstruction) {