Commit 9760426c authored by James Cook's avatar James Cook Committed by Commit Bot

Add ui::AXEventBundleSink interface for accessibility

This breaks a dependency from AutomationManagerAura to the extensions
system. It should make AutomationManagerAura easier to test in unit
tests.

Bug: 910280
Test: browser_tests
Change-Id: I3e7036671d6e6140b6e19b72e7d6482e2fb6f376
Reviewed-on: https://chromium-review.googlesource.com/c/1357548Reviewed-by: default avatarDominic Mazzoni <dmazzoni@chromium.org>
Reviewed-by: default avatarDavid Tseng <dtseng@chromium.org>
Commit-Queue: James Cook <jamescook@chromium.org>
Cr-Commit-Position: refs/heads/master@{#613347}
parent 391671c4
...@@ -26,6 +26,10 @@ ...@@ -26,6 +26,10 @@
#include "ui/accessibility/ax_enums.mojom.h" #include "ui/accessibility/ax_enums.mojom.h"
#include "ui/accessibility/ax_node_data.h" #include "ui/accessibility/ax_node_data.h"
#if defined(USE_AURA)
#include "chrome/browser/ui/aura/accessibility/automation_manager_aura.h"
#endif
namespace extensions { namespace extensions {
// static // static
...@@ -41,6 +45,10 @@ AutomationEventRouter::AutomationEventRouter() ...@@ -41,6 +45,10 @@ AutomationEventRouter::AutomationEventRouter()
content::NotificationService::AllBrowserContextsAndSources()); content::NotificationService::AllBrowserContextsAndSources());
registrar_.Add(this, content::NOTIFICATION_RENDERER_PROCESS_CLOSED, registrar_.Add(this, content::NOTIFICATION_RENDERER_PROCESS_CLOSED,
content::NotificationService::AllBrowserContextsAndSources()); content::NotificationService::AllBrowserContextsAndSources());
#if defined(USE_AURA)
// Not reset because |this| is leaked.
AutomationManagerAura::GetInstance()->set_event_bundle_sink(this);
#endif
} }
AutomationEventRouter::~AutomationEventRouter() { AutomationEventRouter::~AutomationEventRouter() {
...@@ -208,6 +216,20 @@ void AutomationEventRouter::Register(const ExtensionId& extension_id, ...@@ -208,6 +216,20 @@ void AutomationEventRouter::Register(const ExtensionId& extension_id,
iter->desktop = true; iter->desktop = true;
} }
void AutomationEventRouter::DispatchAccessibilityEvents(
const ui::AXTreeID& tree_id,
std::vector<ui::AXTreeUpdate> updates,
const gfx::Point& mouse_location,
std::vector<ui::AXEvent> events) {
ExtensionMsg_AccessibilityEventBundleParams event_bundle;
event_bundle.tree_id = tree_id;
event_bundle.updates = std::move(updates);
event_bundle.mouse_location = mouse_location;
event_bundle.events = std::move(events);
DispatchAccessibilityEvents(event_bundle);
}
void AutomationEventRouter::Observe( void AutomationEventRouter::Observe(
int type, int type,
const content::NotificationSource& source, const content::NotificationSource& source,
......
...@@ -17,6 +17,7 @@ ...@@ -17,6 +17,7 @@
#include "content/public/browser/notification_registrar.h" #include "content/public/browser/notification_registrar.h"
#include "extensions/common/extension_id.h" #include "extensions/common/extension_id.h"
#include "extensions/common/extension_messages.h" #include "extensions/common/extension_messages.h"
#include "ui/accessibility/ax_event_bundle_sink.h"
#include "ui/accessibility/ax_tree_id.h" #include "ui/accessibility/ax_tree_id.h"
class Profile; class Profile;
...@@ -35,7 +36,8 @@ struct ExtensionMsg_AccessibilityLocationChangeParams; ...@@ -35,7 +36,8 @@ struct ExtensionMsg_AccessibilityLocationChangeParams;
namespace extensions { namespace extensions {
struct AutomationListener; struct AutomationListener;
class AutomationEventRouter : public content::NotificationObserver { class AutomationEventRouter : public ui::AXEventBundleSink,
public content::NotificationObserver {
public: public:
static AutomationEventRouter* GetInstance(); static AutomationEventRouter* GetInstance();
...@@ -95,6 +97,12 @@ class AutomationEventRouter : public content::NotificationObserver { ...@@ -95,6 +97,12 @@ class AutomationEventRouter : public content::NotificationObserver {
ui::AXTreeID source_ax_tree_id, ui::AXTreeID source_ax_tree_id,
bool desktop); bool desktop);
// ui::AXEventBundleSink:
void DispatchAccessibilityEvents(const ui::AXTreeID& tree_id,
std::vector<ui::AXTreeUpdate> updates,
const gfx::Point& mouse_location,
std::vector<ui::AXEvent> events) override;
// content::NotificationObserver interface. // content::NotificationObserver interface.
void Observe(int type, void Observe(int type,
const content::NotificationSource& source, const content::NotificationSource& source,
......
...@@ -8,13 +8,12 @@ ...@@ -8,13 +8,12 @@
#include "base/memory/singleton.h" #include "base/memory/singleton.h"
#include "build/build_config.h" #include "build/build_config.h"
#include "chrome/browser/extensions/api/automation_internal/automation_event_router.h"
#include "chrome/common/extensions/chrome_extension_messages.h"
#include "content/public/browser/render_frame_host.h" #include "content/public/browser/render_frame_host.h"
#include "extensions/common/extension_messages.h"
#include "ui/accessibility/ax_action_data.h" #include "ui/accessibility/ax_action_data.h"
#include "ui/accessibility/ax_enum_util.h" #include "ui/accessibility/ax_enum_util.h"
#include "ui/accessibility/ax_enums.mojom.h" #include "ui/accessibility/ax_enums.mojom.h"
#include "ui/accessibility/ax_event.h"
#include "ui/accessibility/ax_event_bundle_sink.h"
#include "ui/accessibility/ax_tree_id_registry.h" #include "ui/accessibility/ax_tree_id_registry.h"
#include "ui/accessibility/platform/aura_window_properties.h" #include "ui/accessibility/platform/aura_window_properties.h"
#include "ui/aura/env.h" #include "ui/aura/env.h"
...@@ -33,8 +32,6 @@ ...@@ -33,8 +32,6 @@
#include "ui/base/ui_base_features.h" #include "ui/base/ui_base_features.h"
#endif #endif
using extensions::AutomationEventRouter;
// static // static
AutomationManagerAura* AutomationManagerAura::GetInstance() { AutomationManagerAura* AutomationManagerAura::GetInstance() {
return base::Singleton<AutomationManagerAura>::get(); return base::Singleton<AutomationManagerAura>::get();
...@@ -198,16 +195,13 @@ void AutomationManagerAura::SendEvent(views::AXAuraObjWrapper* aura_obj, ...@@ -198,16 +195,13 @@ void AutomationManagerAura::SendEvent(views::AXAuraObjWrapper* aura_obj,
} }
processing_events_ = true; processing_events_ = true;
ExtensionMsg_AccessibilityEventBundleParams event_bundle; std::vector<ui::AXTreeUpdate> tree_updates;
event_bundle.tree_id = ui::DesktopAXTreeID();
event_bundle.mouse_location = aura::Env::GetInstance()->last_mouse_location();
ui::AXTreeUpdate update; ui::AXTreeUpdate update;
if (!current_tree_serializer_->SerializeChanges(aura_obj, &update)) { if (!current_tree_serializer_->SerializeChanges(aura_obj, &update)) {
LOG(ERROR) << "Unable to serialize one accessibility event."; LOG(ERROR) << "Unable to serialize one accessibility event.";
return; return;
} }
event_bundle.updates.push_back(update); tree_updates.push_back(update);
// Make sure the focused node is serialized. // Make sure the focused node is serialized.
views::AXAuraObjWrapper* focus = views::AXAuraObjWrapper* focus =
...@@ -215,9 +209,10 @@ void AutomationManagerAura::SendEvent(views::AXAuraObjWrapper* aura_obj, ...@@ -215,9 +209,10 @@ void AutomationManagerAura::SendEvent(views::AXAuraObjWrapper* aura_obj,
if (focus) { if (focus) {
ui::AXTreeUpdate focused_node_update; ui::AXTreeUpdate focused_node_update;
current_tree_serializer_->SerializeChanges(focus, &focused_node_update); current_tree_serializer_->SerializeChanges(focus, &focused_node_update);
event_bundle.updates.push_back(focused_node_update); tree_updates.push_back(focused_node_update);
} }
std::vector<ui::AXEvent> events;
// Fire the event on the node, but only if it's actually in the tree. // Fire the event on the node, but only if it's actually in the tree.
// Sometimes we get events fired on nodes with an ancestor that's // Sometimes we get events fired on nodes with an ancestor that's
// marked invisible, for example. In those cases we should still // marked invisible, for example. In those cases we should still
...@@ -227,14 +222,14 @@ void AutomationManagerAura::SendEvent(views::AXAuraObjWrapper* aura_obj, ...@@ -227,14 +222,14 @@ void AutomationManagerAura::SendEvent(views::AXAuraObjWrapper* aura_obj,
ui::AXEvent event; ui::AXEvent event;
event.id = aura_obj->GetUniqueId(); event.id = aura_obj->GetUniqueId();
event.event_type = event_type; event.event_type = event_type;
event_bundle.events.push_back(event); events.push_back(event);
} }
AutomationEventRouter* router = AutomationEventRouter::GetInstance(); if (event_bundle_sink_) {
router->DispatchAccessibilityEvents(event_bundle); event_bundle_sink_->DispatchAccessibilityEvents(
ui::DesktopAXTreeID(), std::move(tree_updates),
if (event_bundle_callback_for_testing_) aura::Env::GetInstance()->last_mouse_location(), std::move(events));
event_bundle_callback_for_testing_.Run(event_bundle); }
processing_events_ = false; processing_events_ = false;
auto pending_events_copy = pending_events_; auto pending_events_copy = pending_events_;
......
...@@ -27,6 +27,10 @@ template <typename T> ...@@ -27,6 +27,10 @@ template <typename T>
struct DefaultSingletonTraits; struct DefaultSingletonTraits;
} // namespace base } // namespace base
namespace ui {
class AXEventBundleSink;
}
namespace views { namespace views {
class AXAuraObjWrapper; class AXAuraObjWrapper;
class View; class View;
...@@ -35,8 +39,6 @@ class View; ...@@ -35,8 +39,6 @@ class View;
using AuraAXTreeSerializer = ui:: using AuraAXTreeSerializer = ui::
AXTreeSerializer<views::AXAuraObjWrapper*, ui::AXNodeData, ui::AXTreeData>; AXTreeSerializer<views::AXAuraObjWrapper*, ui::AXNodeData, ui::AXTreeData>;
struct ExtensionMsg_AccessibilityEventBundleParams;
// Manages a tree of automation nodes. // Manages a tree of automation nodes.
class AutomationManagerAura : public ui::AXHostDelegate, class AutomationManagerAura : public ui::AXHostDelegate,
public views::AXAuraObjCache::Delegate, public views::AXAuraObjCache::Delegate,
...@@ -67,10 +69,8 @@ class AutomationManagerAura : public ui::AXHostDelegate, ...@@ -67,10 +69,8 @@ class AutomationManagerAura : public ui::AXHostDelegate,
// views::AXEventObserver: // views::AXEventObserver:
void OnViewEvent(views::View* view, ax::mojom::Event event_type) override; void OnViewEvent(views::View* view, ax::mojom::Event event_type) override;
void set_event_bundle_callback_for_testing( void set_event_bundle_sink(ui::AXEventBundleSink* sink) {
base::RepeatingCallback<void(ExtensionMsg_AccessibilityEventBundleParams)> event_bundle_sink_ = sink;
callback) {
event_bundle_callback_for_testing_ = callback;
} }
protected: protected:
...@@ -113,8 +113,9 @@ class AutomationManagerAura : public ui::AXHostDelegate, ...@@ -113,8 +113,9 @@ class AutomationManagerAura : public ui::AXHostDelegate,
std::vector<std::pair<views::AXAuraObjWrapper*, ax::mojom::Event>> std::vector<std::pair<views::AXAuraObjWrapper*, ax::mojom::Event>>
pending_events_; pending_events_;
base::RepeatingCallback<void(ExtensionMsg_AccessibilityEventBundleParams)> // The handler for AXEvents (e.g. the extensions subsystem in production, or
event_bundle_callback_for_testing_; // a fake for tests).
ui::AXEventBundleSink* event_bundle_sink_ = nullptr;
base::WeakPtrFactory<AutomationManagerAura> weak_ptr_factory_; base::WeakPtrFactory<AutomationManagerAura> weak_ptr_factory_;
......
...@@ -6,7 +6,6 @@ ...@@ -6,7 +6,6 @@
#include "chrome/browser/ui/browser.h" #include "chrome/browser/ui/browser.h"
#include "chrome/browser/ui/browser_window.h" #include "chrome/browser/ui/browser_window.h"
#include "chrome/browser/ui/tabs/tab_strip_model.h" #include "chrome/browser/ui/tabs/tab_strip_model.h"
#include "chrome/common/extensions/chrome_extension_messages.h"
#include "chrome/test/base/in_process_browser_test.h" #include "chrome/test/base/in_process_browser_test.h"
#include "chrome/test/base/ui_test_utils.h" #include "chrome/test/base/ui_test_utils.h"
#include "chrome/test/views/accessibility_checker.h" #include "chrome/test/views/accessibility_checker.h"
...@@ -15,6 +14,7 @@ ...@@ -15,6 +14,7 @@
#include "content/public/browser/web_contents.h" #include "content/public/browser/web_contents.h"
#include "content/public/test/browser_test_utils.h" #include "content/public/test/browser_test_utils.h"
#include "extensions/common/extension_messages.h" #include "extensions/common/extension_messages.h"
#include "ui/accessibility/ax_event_bundle_sink.h"
#include "ui/accessibility/ax_node_data.h" #include "ui/accessibility/ax_node_data.h"
#include "ui/views/accessibility/ax_aura_obj_wrapper.h" #include "ui/views/accessibility/ax_aura_obj_wrapper.h"
#include "ui/views/accessibility/ax_tree_source_views.h" #include "ui/views/accessibility/ax_tree_source_views.h"
...@@ -47,16 +47,19 @@ void FindAllHostsOfWebContentsWithAXTreeID( ...@@ -47,16 +47,19 @@ void FindAllHostsOfWebContentsWithAXTreeID(
} }
} }
// A class that installs a test-only callback to handle automation events // A class that installs itself as the sink to handle automation event bundles
// from AutomationManagerAura, then waits until an automation event indicates // from AutomationManagerAura, then waits until an automation event indicates
// that a given node ID is focused. // that a given node ID is focused.
class AutomationEventWaiter { class AutomationEventWaiter : public ui::AXEventBundleSink {
public: public:
explicit AutomationEventWaiter(AutomationManagerAura* manager) AutomationEventWaiter() : run_loop_(std::make_unique<base::RunLoop>()) {
: run_loop_(std::make_unique<base::RunLoop>()), weak_factory_(this) { AutomationManagerAura::GetInstance()->set_event_bundle_sink(this);
manager->set_event_bundle_callback_for_testing( }
base::BindRepeating(&AutomationEventWaiter::EventBundleCallback,
weak_factory_.GetWeakPtr())); ~AutomationEventWaiter() override {
// Don't bother to reconnect to AutomationEventRouter because it's not
// relevant to the tests.
AutomationManagerAura::GetInstance()->set_event_bundle_sink(nullptr);
} }
// Returns immediately if the node with AXAuraObjCache ID |node_id| // Returns immediately if the node with AXAuraObjCache ID |node_id|
...@@ -79,11 +82,13 @@ class AutomationEventWaiter { ...@@ -79,11 +82,13 @@ class AutomationEventWaiter {
} }
private: private:
// Callback to intercept messages sent by AutomationManagerAura. // ui::AXEventBundleSink:
void EventBundleCallback( void DispatchAccessibilityEvents(const ui::AXTreeID& tree_id,
ExtensionMsg_AccessibilityEventBundleParams event_bundle) { std::vector<ui::AXTreeUpdate> updates,
for (size_t i = 0; i < event_bundle.updates.size(); ++i) { const gfx::Point& mouse_location,
int focused_node_id = event_bundle.updates[i].tree_data.focus_id; std::vector<ui::AXEvent> events) override {
for (const ui::AXTreeUpdate& update : updates) {
int focused_node_id = update.tree_data.focus_id;
focused_node_ids_.push_back(focused_node_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_)
run_loop_->QuitClosure().Run(); run_loop_->QuitClosure().Run();
...@@ -93,7 +98,6 @@ class AutomationEventWaiter { ...@@ -93,7 +98,6 @@ class AutomationEventWaiter {
std::unique_ptr<base::RunLoop> run_loop_; std::unique_ptr<base::RunLoop> run_loop_;
int node_id_to_wait_for_ = 0; int node_id_to_wait_for_ = 0;
std::vector<int> focused_node_ids_; std::vector<int> focused_node_ids_;
base::WeakPtrFactory<AutomationEventWaiter> weak_factory_;
DISALLOW_COPY_AND_ASSIGN(AutomationEventWaiter); DISALLOW_COPY_AND_ASSIGN(AutomationEventWaiter);
}; };
...@@ -174,7 +178,7 @@ IN_PROC_BROWSER_TEST_F(AutomationManagerAuraBrowserTest, ...@@ -174,7 +178,7 @@ IN_PROC_BROWSER_TEST_F(AutomationManagerAuraBrowserTest,
widget->GetRootView()->AddChildView(view3); widget->GetRootView()->AddChildView(view3);
views::AXAuraObjWrapper* wrapper3 = cache->GetOrCreate(view3); views::AXAuraObjWrapper* wrapper3 = cache->GetOrCreate(view3);
AutomationEventWaiter waiter(manager); AutomationEventWaiter waiter;
// Focus view1, then block until we get an accessibility event that // Focus view1, then block until we get an accessibility event that
// shows this view is focused. // shows this view is focused.
......
...@@ -2,9 +2,9 @@ ...@@ -2,9 +2,9 @@
# Use of this source code is governed by a BSD-style license that can be # Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file. # found in the LICENSE file.
import("//build/config/linux/pkg_config.gni")
import("//build/config/features.gni") import("//build/config/features.gni")
import("//build/config/jumbo.gni") import("//build/config/jumbo.gni")
import("//build/config/linux/pkg_config.gni")
import("//build/config/ui.gni") import("//build/config/ui.gni")
import("//mojo/public/tools/bindings/mojom.gni") import("//mojo/public/tools/bindings/mojom.gni")
import("//services/service_manager/public/service_manifest.gni") import("//services/service_manager/public/service_manifest.gni")
...@@ -39,6 +39,7 @@ jumbo_component("accessibility") { ...@@ -39,6 +39,7 @@ jumbo_component("accessibility") {
"ax_enum_util.h", "ax_enum_util.h",
"ax_event.cc", "ax_event.cc",
"ax_event.h", "ax_event.h",
"ax_event_bundle_sink.h",
"ax_event_generator.cc", "ax_event_generator.cc",
"ax_event_generator.h", "ax_event_generator.h",
"ax_export.h", "ax_export.h",
......
// Copyright 2018 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.
#ifndef UI_ACCESSIBILITY_AX_EVENT_BUNDLE_SINK_H_
#define UI_ACCESSIBILITY_AX_EVENT_BUNDLE_SINK_H_
#include <vector>
#include "ui/accessibility/ax_export.h"
#include "ui/accessibility/ax_tree_update.h"
namespace gfx {
class Point;
}
namespace ui {
struct AXEvent;
class AXTreeID;
// Interface for a consumer of groups of AXEvents.
class AX_EXPORT AXEventBundleSink {
public:
// |tree_id|: ID of the accessibility tree that the events apply to.
// |updates|: Zero or more updates to the accessibility tree to apply first.
// |mouse location|: Current mouse location in screen coordinates.
// |events|: Zero or more events to fire after the updates have been applied.
// Callers may wish to std::move() into the vector params to avoid copies.
virtual void DispatchAccessibilityEvents(const AXTreeID& tree_id,
std::vector<AXTreeUpdate> updates,
const gfx::Point& mouse_location,
std::vector<AXEvent> events) = 0;
protected:
virtual ~AXEventBundleSink() {}
};
} // namespace ui
#endif // UI_ACCESSIBILITY_AX_EVENT_BUNDLE_SINK_H_
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