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"
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
{
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
,
56 void AutomationEventRouter::RegisterListenerWithDesktopPermission(
57 const ExtensionId
& extension_id
,
58 int listener_process_id
,
59 int listener_routing_id
) {
60 Register(extension_id
,
63 0 /* desktop tree ID */,
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()) {
81 content::RenderProcessHost
* rph
=
82 content::RenderProcessHost::FromID(listener
.process_id
);
83 rph
->Send(new ExtensionMsg_AccessibilityEvent(listener
.routing_id
,
85 listener
.is_active_profile
));
89 void AutomationEventRouter::DispatchTreeDestroyedEvent(
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
,
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
,
114 auto iter
= std::find_if(
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();
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
);
140 iter
->desktop
= true;
143 void AutomationEventRouter::Observe(
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
) {
153 content::RenderProcessHost
* rph
=
154 content::Source
<content::RenderProcessHost
>(source
).ptr();
155 int process_id
= rph
->GetID();
160 [process_id
](const AutomationListener
& item
) {
161 return item
.process_id
== process_id
;
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_
);
186 listener
.is_active_profile
= true;
191 } // namespace extensions