Merge Chromium + Blink git repositories
[chromium-blink-merge.git] / chrome / browser / extensions / api / automation_internal / automation_event_router.cc
blobd3af41dd2a114a7a07ace8e552a25705edbe4e45
1 // Copyright 2015 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
5 #include "chrome/browser/extensions/api/automation_internal/automation_event_router.h"
7 #include <algorithm>
8 #include <string>
9 #include <utility>
11 #include "base/stl_util.h"
12 #include "base/values.h"
13 #include "chrome/browser/profiles/profile_manager.h"
14 #include "chrome/common/extensions/api/automation_internal.h"
15 #include "chrome/common/extensions/chrome_extension_messages.h"
16 #include "content/public/browser/notification_service.h"
17 #include "content/public/browser/notification_source.h"
18 #include "content/public/browser/notification_types.h"
19 #include "content/public/browser/render_process_host.h"
20 #include "extensions/browser/event_router.h"
21 #include "ui/accessibility/ax_enums.h"
22 #include "ui/accessibility/ax_node_data.h"
24 namespace extensions {
26 // static
27 AutomationEventRouter* AutomationEventRouter::GetInstance() {
28 return base::Singleton<
29 AutomationEventRouter,
30 base::LeakySingletonTraits<AutomationEventRouter>>::get();
33 AutomationEventRouter::AutomationEventRouter()
34 : active_profile_(ProfileManager::GetActiveUserProfile()) {
35 registrar_.Add(this, content::NOTIFICATION_RENDERER_PROCESS_TERMINATED,
36 content::NotificationService::AllBrowserContextsAndSources());
37 registrar_.Add(this, content::NOTIFICATION_RENDERER_PROCESS_CLOSED,
38 content::NotificationService::AllBrowserContextsAndSources());
41 AutomationEventRouter::~AutomationEventRouter() {
44 void AutomationEventRouter::RegisterListenerForOneTree(
45 const ExtensionId& extension_id,
46 int listener_process_id,
47 int listener_routing_id,
48 int source_ax_tree_id) {
49 Register(extension_id,
50 listener_process_id,
51 listener_routing_id,
52 source_ax_tree_id,
53 false);
56 void AutomationEventRouter::RegisterListenerWithDesktopPermission(
57 const ExtensionId& extension_id,
58 int listener_process_id,
59 int listener_routing_id) {
60 Register(extension_id,
61 listener_process_id,
62 listener_routing_id,
63 0 /* desktop tree ID */,
64 true);
67 void AutomationEventRouter::DispatchAccessibilityEvent(
68 const ExtensionMsg_AccessibilityEventParams& params) {
69 if (active_profile_ != ProfileManager::GetActiveUserProfile()) {
70 active_profile_ = ProfileManager::GetActiveUserProfile();
71 UpdateActiveProfile();
74 for (const auto& listener : listeners_) {
75 // Skip listeners that don't want to listen to this tree.
76 if (!listener.desktop &&
77 listener.tree_ids.find(params.tree_id) == listener.tree_ids.end()) {
78 continue;
81 content::RenderProcessHost* rph =
82 content::RenderProcessHost::FromID(listener.process_id);
83 rph->Send(new ExtensionMsg_AccessibilityEvent(listener.routing_id,
84 params,
85 listener.is_active_profile));
89 void AutomationEventRouter::DispatchTreeDestroyedEvent(
90 int tree_id,
91 content::BrowserContext* browser_context) {
92 scoped_ptr<base::ListValue> args(
93 api::automation_internal::OnAccessibilityTreeDestroyed::Create(tree_id));
94 scoped_ptr<Event> event(new Event(
95 events::AUTOMATION_INTERNAL_ON_ACCESSIBILITY_TREE_DESTROYED,
96 api::automation_internal::OnAccessibilityTreeDestroyed::kEventName,
97 args.Pass()));
98 event->restrict_to_browser_context = browser_context;
99 EventRouter::Get(browser_context)->BroadcastEvent(event.Pass());
102 AutomationEventRouter::AutomationListener::AutomationListener() {
105 AutomationEventRouter::AutomationListener::~AutomationListener() {
108 void AutomationEventRouter::Register(
109 const ExtensionId& extension_id,
110 int listener_process_id,
111 int listener_routing_id,
112 int ax_tree_id,
113 bool desktop) {
114 auto iter = std::find_if(
115 listeners_.begin(),
116 listeners_.end(),
117 [listener_process_id, listener_routing_id](
118 const AutomationListener& item) {
119 return (item.process_id == listener_process_id &&
120 item.routing_id == listener_routing_id);
123 // Add a new entry if we don't have one with that process and routing id.
124 if (iter == listeners_.end()) {
125 AutomationListener listener;
126 listener.extension_id = extension_id;
127 listener.routing_id = listener_routing_id;
128 listener.process_id = listener_process_id;
129 listener.desktop = desktop;
130 listener.tree_ids.insert(ax_tree_id);
131 listeners_.push_back(listener);
132 UpdateActiveProfile();
133 return;
136 // We have an entry with that process and routing id, so update the set of
137 // tree ids it wants to listen to, and update its desktop permission.
138 iter->tree_ids.insert(ax_tree_id);
139 if (desktop)
140 iter->desktop = true;
143 void AutomationEventRouter::Observe(
144 int type,
145 const content::NotificationSource& source,
146 const content::NotificationDetails& details) {
147 if (type != content::NOTIFICATION_RENDERER_PROCESS_TERMINATED &&
148 type != content::NOTIFICATION_RENDERER_PROCESS_CLOSED) {
149 NOTREACHED();
150 return;
153 content::RenderProcessHost* rph =
154 content::Source<content::RenderProcessHost>(source).ptr();
155 int process_id = rph->GetID();
156 listeners_.erase(
157 std::remove_if(
158 listeners_.begin(),
159 listeners_.end(),
160 [process_id](const AutomationListener& item) {
161 return item.process_id == process_id;
163 listeners_.end());
164 UpdateActiveProfile();
167 void AutomationEventRouter::UpdateActiveProfile() {
168 for (auto& listener : listeners_) {
169 #if defined(OS_CHROMEOS)
170 int extension_id_count = 0;
171 for (const auto& listener2 : listeners_) {
172 if (listener2.extension_id == listener.extension_id)
173 extension_id_count++;
175 content::RenderProcessHost* rph =
176 content::RenderProcessHost::FromID(listener.process_id);
178 // The purpose of is_active_profile is to ensure different instances of
179 // the same extension running in different profiles don't interfere with
180 // one another. If an automation extension is only running in one profile,
181 // always mark it as active. If it's running in two or more profiles,
182 // only mark one as active.
183 listener.is_active_profile = (extension_id_count == 1 ||
184 rph->GetBrowserContext() == active_profile_);
185 #else
186 listener.is_active_profile = true;
187 #endif
191 } // namespace extensions