OLD | NEW |
1 // Copyright (c) 2011 The Chromium Authors. All rights reserved. | 1 // Copyright (c) 2011 The Chromium Authors. All rights reserved. |
2 // Use of this source code is governed by a BSD-style license that can be | 2 // Use of this source code is governed by a BSD-style license that can be |
3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
4 | 4 |
5 #include "chrome/browser/extensions/extension_event_router.h" | 5 #include "chrome/browser/extensions/extension_event_router.h" |
6 | 6 |
| 7 #include "base/command_line.h" |
7 #include "base/values.h" | 8 #include "base/values.h" |
8 #include "chrome/browser/extensions/extension_devtools_manager.h" | 9 #include "chrome/browser/extensions/extension_devtools_manager.h" |
| 10 #include "chrome/browser/extensions/extension_host.h" |
9 #include "chrome/browser/extensions/extension_processes_api.h" | 11 #include "chrome/browser/extensions/extension_processes_api.h" |
10 #include "chrome/browser/extensions/extension_processes_api_constants.h" | 12 #include "chrome/browser/extensions/extension_processes_api_constants.h" |
11 #include "chrome/browser/extensions/extension_service.h" | 13 #include "chrome/browser/extensions/extension_service.h" |
12 #include "chrome/browser/extensions/extension_tabs_module.h" | 14 #include "chrome/browser/extensions/extension_tabs_module.h" |
13 #include "chrome/browser/extensions/extension_webrequest_api.h" | 15 #include "chrome/browser/extensions/extension_webrequest_api.h" |
14 #include "chrome/browser/profiles/profile.h" | 16 #include "chrome/browser/profiles/profile.h" |
| 17 #include "chrome/common/chrome_switches.h" |
15 #include "chrome/common/extensions/extension.h" | 18 #include "chrome/common/extensions/extension.h" |
16 #include "chrome/common/extensions/extension_messages.h" | 19 #include "chrome/common/extensions/extension_messages.h" |
17 #include "content/browser/child_process_security_policy.h" | 20 #include "content/browser/child_process_security_policy.h" |
18 #include "content/browser/renderer_host/render_process_host.h" | 21 #include "content/browser/renderer_host/render_process_host.h" |
19 #include "content/common/notification_service.h" | 22 #include "content/common/notification_service.h" |
20 | 23 |
21 namespace { | 24 namespace { |
22 | 25 |
23 const char kDispatchEvent[] = "Event.dispatchJSON"; | 26 const char kDispatchEvent[] = "Event.dispatchJSON"; |
24 | 27 |
(...skipping 16 matching lines...) Expand all Loading... |
41 | 44 |
42 bool operator<(const EventListener& that) const { | 45 bool operator<(const EventListener& that) const { |
43 if (process < that.process) | 46 if (process < that.process) |
44 return true; | 47 return true; |
45 if (process == that.process && extension_id < that.extension_id) | 48 if (process == that.process && extension_id < that.extension_id) |
46 return true; | 49 return true; |
47 return false; | 50 return false; |
48 } | 51 } |
49 }; | 52 }; |
50 | 53 |
| 54 struct ExtensionEventRouter::ExtensionEvent { |
| 55 std::string extension_id; |
| 56 std::string event_name; |
| 57 std::string event_args; |
| 58 GURL event_url; |
| 59 Profile* restrict_to_profile; |
| 60 std::string cross_incognito_args; |
| 61 |
| 62 ExtensionEvent(const std::string& extension_id, |
| 63 const std::string& event_name, |
| 64 const std::string& event_args, |
| 65 const GURL& event_url, |
| 66 Profile* restrict_to_profile, |
| 67 const std::string& cross_incognito_args) |
| 68 : extension_id(extension_id), |
| 69 event_name(event_name), |
| 70 event_args(event_args), |
| 71 event_url(event_url), |
| 72 restrict_to_profile(restrict_to_profile), |
| 73 cross_incognito_args(cross_incognito_args) {} |
| 74 }; |
| 75 |
51 // static | 76 // static |
52 void ExtensionEventRouter::DispatchEvent(IPC::Message::Sender* ipc_sender, | 77 void ExtensionEventRouter::DispatchEvent(IPC::Message::Sender* ipc_sender, |
53 const std::string& extension_id, | 78 const std::string& extension_id, |
54 const std::string& event_name, | 79 const std::string& event_name, |
55 const std::string& event_args, | 80 const std::string& event_args, |
56 const GURL& event_url) { | 81 const GURL& event_url) { |
57 ListValue args; | 82 ListValue args; |
58 args.Set(0, Value::CreateStringValue(event_name)); | 83 args.Set(0, Value::CreateStringValue(event_name)); |
59 args.Set(1, Value::CreateStringValue(event_args)); | 84 args.Set(1, Value::CreateStringValue(event_args)); |
60 ipc_sender->Send(new ExtensionMsg_MessageInvoke(MSG_ROUTING_CONTROL, | 85 ipc_sender->Send(new ExtensionMsg_MessageInvoke(MSG_ROUTING_CONTROL, |
61 extension_id, kDispatchEvent, args, event_url)); | 86 extension_id, kDispatchEvent, args, event_url)); |
62 } | 87 } |
63 | 88 |
64 ExtensionEventRouter::ExtensionEventRouter(Profile* profile) | 89 ExtensionEventRouter::ExtensionEventRouter(Profile* profile) |
65 : profile_(profile), | 90 : profile_(profile), |
66 extension_devtools_manager_(profile->GetExtensionDevToolsManager()) { | 91 extension_devtools_manager_(profile->GetExtensionDevToolsManager()) { |
67 registrar_.Add(this, content::NOTIFICATION_RENDERER_PROCESS_TERMINATED, | 92 registrar_.Add(this, content::NOTIFICATION_RENDERER_PROCESS_TERMINATED, |
68 NotificationService::AllSources()); | 93 NotificationService::AllSources()); |
69 registrar_.Add(this, content::NOTIFICATION_RENDERER_PROCESS_CLOSED, | 94 registrar_.Add(this, content::NOTIFICATION_RENDERER_PROCESS_CLOSED, |
70 NotificationService::AllSources()); | 95 NotificationService::AllSources()); |
| 96 registrar_.Add(this, chrome::NOTIFICATION_EXTENSION_HOST_DID_STOP_LOADING, |
| 97 Source<Profile>(profile_)); |
| 98 // TODO(tessamac): also get notified for background page crash/failure. |
71 } | 99 } |
72 | 100 |
73 ExtensionEventRouter::~ExtensionEventRouter() { | 101 ExtensionEventRouter::~ExtensionEventRouter() {} |
74 } | |
75 | 102 |
76 void ExtensionEventRouter::AddEventListener( | 103 void ExtensionEventRouter::AddEventListener( |
77 const std::string& event_name, | 104 const std::string& event_name, |
78 RenderProcessHost* process, | 105 RenderProcessHost* process, |
79 const std::string& extension_id) { | 106 const std::string& extension_id) { |
80 EventListener listener(process, extension_id); | 107 EventListener listener(process, extension_id); |
81 DCHECK_EQ(listeners_[event_name].count(listener), 0u) << event_name; | 108 DCHECK_EQ(listeners_[event_name].count(listener), 0u) << event_name; |
82 listeners_[event_name].insert(listener); | 109 listeners_[event_name].insert(listener); |
83 | 110 |
84 if (extension_devtools_manager_.get()) | 111 if (extension_devtools_manager_.get()) |
(...skipping 50 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
135 return true; | 162 return true; |
136 } | 163 } |
137 return false; | 164 return false; |
138 } | 165 } |
139 | 166 |
140 void ExtensionEventRouter::DispatchEventToRenderers( | 167 void ExtensionEventRouter::DispatchEventToRenderers( |
141 const std::string& event_name, | 168 const std::string& event_name, |
142 const std::string& event_args, | 169 const std::string& event_args, |
143 Profile* restrict_to_profile, | 170 Profile* restrict_to_profile, |
144 const GURL& event_url) { | 171 const GURL& event_url) { |
145 DispatchEventImpl("", event_name, event_args, restrict_to_profile, "", | 172 linked_ptr<ExtensionEvent> event( |
146 event_url); | 173 new ExtensionEvent("", event_name, event_args, event_url, |
| 174 restrict_to_profile, "")); |
| 175 DispatchEventImpl(event, false); |
147 } | 176 } |
148 | 177 |
149 void ExtensionEventRouter::DispatchEventToExtension( | 178 void ExtensionEventRouter::DispatchEventToExtension( |
150 const std::string& extension_id, | 179 const std::string& extension_id, |
151 const std::string& event_name, | 180 const std::string& event_name, |
152 const std::string& event_args, | 181 const std::string& event_args, |
153 Profile* restrict_to_profile, | 182 Profile* restrict_to_profile, |
154 const GURL& event_url) { | 183 const GURL& event_url) { |
155 DCHECK(!extension_id.empty()); | 184 DCHECK(!extension_id.empty()); |
156 DispatchEventImpl(extension_id, event_name, event_args, restrict_to_profile, | 185 linked_ptr<ExtensionEvent> event( |
157 "", event_url); | 186 new ExtensionEvent(extension_id, event_name, event_args, event_url, |
| 187 restrict_to_profile, "")); |
| 188 DispatchEventImpl(event, false); |
158 } | 189 } |
159 | 190 |
160 void ExtensionEventRouter::DispatchEventsToRenderersAcrossIncognito( | 191 void ExtensionEventRouter::DispatchEventsToRenderersAcrossIncognito( |
161 const std::string& event_name, | 192 const std::string& event_name, |
162 const std::string& event_args, | 193 const std::string& event_args, |
163 Profile* restrict_to_profile, | 194 Profile* restrict_to_profile, |
164 const std::string& cross_incognito_args, | 195 const std::string& cross_incognito_args, |
165 const GURL& event_url) { | 196 const GURL& event_url) { |
166 DispatchEventImpl("", event_name, event_args, restrict_to_profile, | 197 linked_ptr<ExtensionEvent> event( |
167 cross_incognito_args, event_url); | 198 new ExtensionEvent("", event_name, event_args, event_url, |
| 199 restrict_to_profile, cross_incognito_args)); |
| 200 DispatchEventImpl(event, false); |
| 201 } |
| 202 |
| 203 bool ExtensionEventRouter::CanDispatchEventNow( |
| 204 const std::string& extension_id) { |
| 205 if (!CommandLine::ForCurrentProcess()->HasSwitch( |
| 206 switches::kEnableLazyBackgroundPages)) |
| 207 return true; |
| 208 |
| 209 if (extension_id.empty()) |
| 210 // TODO(tessamac): Create all background pages. Wait for all to be loaded? |
| 211 // or dispatch event to each extension when it's ready? |
| 212 return true; |
| 213 |
| 214 const Extension* extension = profile_->GetExtensionService()-> |
| 215 GetExtensionById(extension_id, false); // exclude disabled extensions |
| 216 if (extension && extension->background_url().is_valid()) { |
| 217 ExtensionProcessManager* pm = profile_->GetExtensionProcessManager(); |
| 218 if (!pm->GetBackgroundHostForExtension(extension)) { |
| 219 pm->CreateBackgroundHost(extension, extension->background_url()); |
| 220 return false; |
| 221 } |
| 222 } |
| 223 |
| 224 return true; |
168 } | 225 } |
169 | 226 |
170 void ExtensionEventRouter::DispatchEventImpl( | 227 void ExtensionEventRouter::DispatchEventImpl( |
171 const std::string& extension_id, | 228 const linked_ptr<ExtensionEvent>& event, bool was_pending) { |
172 const std::string& event_name, | |
173 const std::string& event_args, | |
174 Profile* restrict_to_profile, | |
175 const std::string& cross_incognito_args, | |
176 const GURL& event_url) { | |
177 if (!profile_) | 229 if (!profile_) |
178 return; | 230 return; |
179 | 231 |
180 // We don't expect to get events from a completely different profile. | 232 // We don't expect to get events from a completely different profile. |
181 DCHECK(!restrict_to_profile || profile_->IsSameProfile(restrict_to_profile)); | 233 DCHECK(!event->restrict_to_profile || |
| 234 profile_->IsSameProfile(event->restrict_to_profile)); |
182 | 235 |
183 ListenerMap::iterator it = listeners_.find(event_name); | 236 if (!CanDispatchEventNow(event->extension_id)) { |
| 237 // Events should not be made pending twice. This may happen if the |
| 238 // background page is shutdown before we finish dispatching pending events. |
| 239 CHECK(!was_pending); |
| 240 // TODO(tessamac): make sure Background Page notification doesn't |
| 241 // happen before the event is added to the pending list. |
| 242 AppendEvent(event); |
| 243 return; |
| 244 } |
| 245 |
| 246 ListenerMap::iterator it = listeners_.find(event->event_name); |
184 if (it == listeners_.end()) | 247 if (it == listeners_.end()) |
185 return; | 248 return; |
186 | 249 |
187 std::set<EventListener>& listeners = it->second; | 250 std::set<EventListener>& listeners = it->second; |
188 ExtensionService* service = profile_->GetExtensionService(); | 251 ExtensionService* service = profile_->GetExtensionService(); |
189 | 252 |
190 // Send the event only to renderers that are listening for it. | 253 // Send the event only to renderers that are listening for it. |
191 for (std::set<EventListener>::iterator listener = listeners.begin(); | 254 for (std::set<EventListener>::iterator listener = listeners.begin(); |
192 listener != listeners.end(); ++listener) { | 255 listener != listeners.end(); ++listener) { |
193 if (!ChildProcessSecurityPolicy::GetInstance()-> | 256 if (!ChildProcessSecurityPolicy::GetInstance()-> |
194 HasExtensionBindings(listener->process->id())) { | 257 HasExtensionBindings(listener->process->id())) { |
195 // Don't send browser-level events to unprivileged processes. | 258 // Don't send browser-level events to unprivileged processes. |
196 continue; | 259 continue; |
197 } | 260 } |
198 | 261 |
199 if (!extension_id.empty() && extension_id != listener->extension_id) | 262 if (!event->extension_id.empty() && |
| 263 event->extension_id != listener->extension_id) |
200 continue; | 264 continue; |
201 | 265 |
202 // Is this event from a different profile than the renderer (ie, an | 266 // Is this event from a different profile than the renderer (ie, an |
203 // incognito tab event sent to a normal process, or vice versa). | 267 // incognito tab event sent to a normal process, or vice versa). |
204 bool cross_incognito = restrict_to_profile && | 268 bool cross_incognito = event->restrict_to_profile && |
205 listener->process->browser_context() != restrict_to_profile; | 269 listener->process->browser_context() != event->restrict_to_profile; |
206 const Extension* extension = service->GetExtensionById( | 270 const Extension* extension = service->GetExtensionById( |
207 listener->extension_id, false); | 271 listener->extension_id, false); |
208 // Send the event with different arguments to extensions that can't | 272 // Send the event with different arguments to extensions that can't |
209 // cross incognito, if necessary. | 273 // cross incognito, if necessary. |
210 if (cross_incognito && !service->CanCrossIncognito(extension)) { | 274 if (cross_incognito && !service->CanCrossIncognito(extension)) { |
211 if (!cross_incognito_args.empty()) { | 275 if (!event->cross_incognito_args.empty()) { |
212 DispatchEvent(listener->process, listener->extension_id, | 276 DispatchEvent(listener->process, listener->extension_id, |
213 event_name, cross_incognito_args, event_url); | 277 event->event_name, event->cross_incognito_args, |
| 278 event->event_url); |
214 } | 279 } |
215 continue; | 280 continue; |
216 } | 281 } |
217 | 282 |
218 DispatchEvent(listener->process, listener->extension_id, | 283 DispatchEvent(listener->process, listener->extension_id, |
219 event_name, event_args, event_url); | 284 event->event_name, event->event_args, event->event_url); |
220 } | 285 } |
221 } | 286 } |
222 | 287 |
| 288 void ExtensionEventRouter::AppendEvent( |
| 289 const linked_ptr<ExtensionEvent>& event) { |
| 290 PendingEventsList* events_list = NULL; |
| 291 PendingEventsPerExtMap::iterator it = |
| 292 pending_events_.find(event->extension_id); |
| 293 if (it == pending_events_.end()) { |
| 294 events_list = new PendingEventsList(); |
| 295 pending_events_[event->extension_id] = |
| 296 linked_ptr<PendingEventsList>(events_list); |
| 297 } else { |
| 298 events_list = it->second.get(); |
| 299 } |
| 300 |
| 301 events_list->push_back(event); |
| 302 } |
| 303 |
| 304 void ExtensionEventRouter::DispatchPendingEvents( |
| 305 const std::string &extension_id) { |
| 306 // Find the list of pending events for this extension. |
| 307 PendingEventsPerExtMap::const_iterator map_it = |
| 308 pending_events_.find(extension_id); |
| 309 if (map_it == pending_events_.end()) |
| 310 return; |
| 311 |
| 312 PendingEventsList* events_list = map_it->second.get(); |
| 313 for (PendingEventsList::const_iterator it = events_list->begin(); |
| 314 it != events_list->end(); ++it) |
| 315 DispatchEventImpl(*it, true); |
| 316 |
| 317 // Delete list. |
| 318 events_list->clear(); |
| 319 pending_events_.erase(extension_id); |
| 320 } |
| 321 |
223 void ExtensionEventRouter::Observe(int type, | 322 void ExtensionEventRouter::Observe(int type, |
224 const NotificationSource& source, | 323 const NotificationSource& source, |
225 const NotificationDetails& details) { | 324 const NotificationDetails& details) { |
226 switch (type) { | 325 switch (type) { |
227 case content::NOTIFICATION_RENDERER_PROCESS_TERMINATED: | 326 case content::NOTIFICATION_RENDERER_PROCESS_TERMINATED: |
228 case content::NOTIFICATION_RENDERER_PROCESS_CLOSED: { | 327 case content::NOTIFICATION_RENDERER_PROCESS_CLOSED: { |
229 RenderProcessHost* renderer = Source<RenderProcessHost>(source).ptr(); | 328 RenderProcessHost* renderer = Source<RenderProcessHost>(source).ptr(); |
230 // Remove all event listeners associated with this renderer | 329 // Remove all event listeners associated with this renderer |
231 for (ListenerMap::iterator it = listeners_.begin(); | 330 for (ListenerMap::iterator it = listeners_.begin(); |
232 it != listeners_.end(); ) { | 331 it != listeners_.end(); ) { |
233 ListenerMap::iterator current_it = it++; | 332 ListenerMap::iterator current_it = it++; |
234 for (std::set<EventListener>::iterator jt = current_it->second.begin(); | 333 for (std::set<EventListener>::iterator jt = current_it->second.begin(); |
235 jt != current_it->second.end(); ) { | 334 jt != current_it->second.end(); ) { |
236 std::set<EventListener>::iterator current_jt = jt++; | 335 std::set<EventListener>::iterator current_jt = jt++; |
237 if (current_jt->process == renderer) { | 336 if (current_jt->process == renderer) { |
238 RemoveEventListener(current_it->first, | 337 RemoveEventListener(current_it->first, |
239 current_jt->process, | 338 current_jt->process, |
240 current_jt->extension_id); | 339 current_jt->extension_id); |
241 } | 340 } |
242 } | 341 } |
243 } | 342 } |
244 break; | 343 break; |
245 } | 344 } |
| 345 case chrome::NOTIFICATION_EXTENSION_HOST_DID_STOP_LOADING: { |
| 346 // TODO: dispatch events in queue. ExtensionHost is in the details. |
| 347 ExtensionHost* eh = Details<ExtensionHost>(details).ptr(); |
| 348 DispatchPendingEvents(eh->extension_id()); |
| 349 break; |
| 350 } |
| 351 // TODO(tessamac): if background page crashed/failed clear queue. |
246 default: | 352 default: |
247 NOTREACHED(); | 353 NOTREACHED(); |
248 return; | 354 return; |
249 } | 355 } |
250 } | 356 } |
OLD | NEW |