Commit d52f0f68 authored by David Tseng's avatar David Tseng Committed by Commit Bot

Adds support for EventFrom action in desktop tree

This support allows Switch Access to ignore focus events on omnibox it triggers tiself.

Fixed: 1113669

Change-Id: I8c6f94bfc6c3547e114d5fbe408af9ed6cfdd1ad
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2373223
Commit-Queue: David Tseng <dtseng@chromium.org>
Reviewed-by: default avatarDominic Mazzoni <dmazzoni@chromium.org>
Cr-Commit-Position: refs/heads/master@{#801598}
parent b282cc3f
......@@ -55,7 +55,8 @@ void AutomationManagerAura::Enable() {
// Send this event immediately to push the initial desktop tree state.
pending_events_.push_back({current_tree_->GetRoot()->GetUniqueId(),
ax::mojom::Event::kLoadComplete, -1});
ax::mojom::Event::kLoadComplete, -1,
is_performing_action_});
SendPendingEvents();
// Intentionally not reset at shutdown since we cannot rely on the shutdown
// ordering of two base::Singletons.
......@@ -106,6 +107,14 @@ void AutomationManagerAura::HandleAlert(const std::string& text) {
void AutomationManagerAura::PerformAction(const ui::AXActionData& data) {
CHECK(enabled_);
base::AutoReset<bool> reset_is_performing_action(&is_performing_action_,
true);
// Exclude the do default action, which can trigger too many important events
// that should not be ignored by clients like focus.
if (data.action == ax::mojom::Action::kDoDefault)
is_performing_action_ = false;
// Unlike all of the other actions, a hit test requires determining the
// node to perform the action on first.
if (data.action == ax::mojom::Action::kHitTest) {
......@@ -168,7 +177,8 @@ void AutomationManagerAura::Reset(bool reset_serializer) {
void AutomationManagerAura::PostEvent(int id,
ax::mojom::Event event_type,
int action_request_id) {
pending_events_.push_back({id, event_type, action_request_id});
pending_events_.push_back(
{id, event_type, action_request_id, is_performing_action_});
if (processing_posted_)
return;
......@@ -214,6 +224,8 @@ void AutomationManagerAura::SendPendingEvents() {
ui::AXEvent event;
event.id = aura_obj->GetUniqueId();
event.event_type = event_type;
if (event_copy.is_performing_action)
event.event_from = ax::mojom::EventFrom::kAction;
event.action_request_id = event_copy.action_request_id;
events.push_back(event);
}
......
......@@ -82,6 +82,7 @@ class AutomationManagerAura : public ui::AXActionHandler,
FRIEND_TEST_ALL_PREFIXES(AutomationManagerAuraBrowserTest, ScrollView);
FRIEND_TEST_ALL_PREFIXES(AutomationManagerAuraBrowserTest, WebAppearsOnce);
FRIEND_TEST_ALL_PREFIXES(AutomationManagerAuraBrowserTest, EventFromAction);
AutomationManagerAura();
~AutomationManagerAura() override;
......@@ -120,6 +121,7 @@ class AutomationManagerAura : public ui::AXActionHandler,
int id;
ax::mojom::Event event_type;
int action_request_id;
bool is_performing_action;
};
std::vector<Event> pending_events_;
......@@ -132,6 +134,8 @@ class AutomationManagerAura : public ui::AXActionHandler,
std::unique_ptr<views::AXAuraObjCache> cache_;
bool is_performing_action_ = false;
DISALLOW_COPY_AND_ASSIGN(AutomationManagerAura);
};
......
......@@ -92,6 +92,13 @@ class AutomationEventWaiter : public ui::AXEventBundleSink {
run_loop_ = std::make_unique<base::RunLoop>();
}
std::unique_ptr<ui::AXEvent> WaitForEvent(ax::mojom::Event event_type) {
event_type_to_wait_for_ = event_type;
run_loop_->Run();
run_loop_ = std::make_unique<base::RunLoop>();
return std::move(most_recent_event_);
}
bool WasNodeIdFocused(int node_id) {
for (size_t i = 0; i < focused_node_ids_.size(); i++)
if (node_id == focused_node_ids_[i])
......@@ -105,18 +112,36 @@ class AutomationEventWaiter : public ui::AXEventBundleSink {
std::vector<ui::AXTreeUpdate> updates,
const gfx::Point& mouse_location,
std::vector<ui::AXEvent> events) override {
if (node_id_to_wait_for_ != -1) {
for (const ui::AXTreeUpdate& update : updates) {
int focused_node_id = update.tree_data.focus_id;
focused_node_ids_.push_back(focused_node_id);
if (focused_node_id == node_id_to_wait_for_)
if (focused_node_id == node_id_to_wait_for_) {
node_id_to_wait_for_ = -1;
run_loop_->Quit();
}
}
}
if (event_type_to_wait_for_ == ax::mojom::Event::kNone)
return;
for (const ui::AXEvent& event : events) {
if (event.event_type == event_type_to_wait_for_) {
most_recent_event_ = std::make_unique<ui::AXEvent>(event);
event_type_to_wait_for_ = ax::mojom::Event::kNone;
run_loop_->Quit();
}
}
}
std::unique_ptr<base::RunLoop> run_loop_;
int node_id_to_wait_for_ = 0;
int node_id_to_wait_for_ = -1;
std::vector<int> focused_node_ids_;
std::unique_ptr<ui::AXEvent> most_recent_event_;
ax::mojom::Event event_type_to_wait_for_ = ax::mojom::Event::kNone;
DISALLOW_COPY_AND_ASSIGN(AutomationEventWaiter);
};
......@@ -305,3 +330,55 @@ IN_PROC_BROWSER_TEST_F(AutomationManagerAuraBrowserTest, ScrollView) {
EXPECT_EQ(50, node_data.GetIntAttribute(ax::mojom::IntAttribute::kScrollX));
EXPECT_EQ(315, node_data.GetIntAttribute(ax::mojom::IntAttribute::kScrollY));
}
IN_PROC_BROWSER_TEST_F(AutomationManagerAuraBrowserTest, EventFromAction) {
auto cache = std::make_unique<views::AXAuraObjCache>();
auto* cache_ptr = cache.get();
AutomationManagerAura* manager = AutomationManagerAura::GetInstance();
manager->set_ax_aura_obj_cache_for_testing(std::move(cache));
manager->Enable();
views::Widget* widget = new views::Widget;
views::Widget::InitParams params(views::Widget::InitParams::TYPE_WINDOW);
params.bounds = {0, 0, 200, 200};
widget->Init(std::move(params));
widget->Show();
widget->Activate();
cache_ptr->set_focused_widget_for_testing(widget);
views::View* view1 = new views::View();
view1->GetViewAccessibility().OverrideName("view1");
view1->SetFocusBehavior(views::View::FocusBehavior::ALWAYS);
widget->GetRootView()->AddChildView(view1);
views::View* view2 = new views::View();
view2->SetFocusBehavior(views::View::FocusBehavior::ALWAYS);
view2->GetViewAccessibility().OverrideName("view2");
widget->GetRootView()->AddChildView(view2);
views::AXAuraObjWrapper* wrapper2 = cache_ptr->GetOrCreate(view2);
AutomationEventWaiter waiter;
// Focus view1, simulating the non-accessibility action, block until we get an
// accessibility event that shows this view is focused.
view1->RequestFocus();
auto event_from_views = waiter.WaitForEvent(ax::mojom::Event::kFocus);
ASSERT_NE(nullptr, event_from_views.get());
EXPECT_EQ(ax::mojom::EventFrom::kNone, event_from_views->event_from);
// Focus view2, simulating the accessibility action, block until we get an
// accessibility event that shows this view is focused.
ui::AXActionData action_data;
action_data.action = ax::mojom::Action::kFocus;
action_data.target_tree_id = manager->current_tree_.get()->tree_id_for_test();
action_data.target_node_id = wrapper2->GetUniqueId();
manager->PerformAction(action_data);
auto event_from_action = waiter.WaitForEvent(ax::mojom::Event::kFocus);
ASSERT_NE(nullptr, event_from_action.get());
EXPECT_EQ(ax::mojom::EventFrom::kAction, event_from_action->event_from);
cache_ptr->set_focused_widget_for_testing(nullptr);
AddFailureOnWidgetAccessibilityError(widget);
}
......@@ -36,6 +36,7 @@
-AutofillProviderBrowserTest.FrameDetachedOnFormlessSubmission
-AutofillProviderBrowserTestWithSkipFlagOn.InferredLabelChangeNotImpactFormComparing
-AutomationApiTest.ForceLayout
-AutomationManagerAuraBrowserTest.EventFromAction
-AutoplayExtensionBrowserTest.AutoplayAllowedInIframe
-BluetoothApiTest.*
-BluetoothLowEnergyApiTest.*
......
......@@ -3,6 +3,7 @@
# Failed:
-All/PopupBrowserTest.MoveClampedToCurrentDisplay/0
-All/PopupBrowserTest.MoveClampedToCurrentDisplay/1
-AutomationManagerAuraBrowserTest.EventFromAction
-AutomationManagerAuraBrowserTest.TransientFocusChangesAreSuppressed
-BrowserActionApiTest.BrowserActionOpenPopupOnPopup
-BrowserViewTest.F6CyclesThroughCaptionBubbleToo
......
......@@ -61,6 +61,8 @@ class VIEWS_EXPORT AXTreeSourceViews
// Useful for debugging.
std::string ToString(views::AXAuraObjWrapper* root, std::string prefix);
const ui::AXTreeID tree_id_for_test() const { return tree_id_; }
private:
// The top-level object to use for the AX tree. See class comment.
AXAuraObjWrapper* const root_ = nullptr;
......
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