Chromium Code Reviews| 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()), |
| 92 ALLOW_THIS_IN_INITIALIZER_LIST(task_factory_(this)) { | |
| 67 registrar_.Add(this, content::NOTIFICATION_RENDERER_PROCESS_TERMINATED, | 93 registrar_.Add(this, content::NOTIFICATION_RENDERER_PROCESS_TERMINATED, |
| 68 NotificationService::AllSources()); | 94 NotificationService::AllSources()); |
| 69 registrar_.Add(this, content::NOTIFICATION_RENDERER_PROCESS_CLOSED, | 95 registrar_.Add(this, content::NOTIFICATION_RENDERER_PROCESS_CLOSED, |
| 70 NotificationService::AllSources()); | 96 NotificationService::AllSources()); |
| 97 registrar_.Add(this, chrome::NOTIFICATION_EXTENSION_HOST_DID_STOP_LOADING, | |
| 98 Source<Profile>(profile_)); | |
| 99 // TODO(tessamac): also get notified for background page crash/failure. | |
| 71 } | 100 } |
| 72 | 101 |
| 73 ExtensionEventRouter::~ExtensionEventRouter() { | 102 ExtensionEventRouter::~ExtensionEventRouter() {} |
| 74 } | |
| 75 | 103 |
| 76 void ExtensionEventRouter::AddEventListener( | 104 void ExtensionEventRouter::AddEventListener( |
| 77 const std::string& event_name, | 105 const std::string& event_name, |
| 78 RenderProcessHost* process, | 106 RenderProcessHost* process, |
| 79 const std::string& extension_id) { | 107 const std::string& extension_id) { |
| 80 EventListener listener(process, extension_id); | 108 EventListener listener(process, extension_id); |
| 81 DCHECK_EQ(listeners_[event_name].count(listener), 0u) << event_name; | 109 DCHECK_EQ(listeners_[event_name].count(listener), 0u) << event_name; |
| 82 listeners_[event_name].insert(listener); | 110 listeners_[event_name].insert(listener); |
| 83 | 111 |
| 84 if (extension_devtools_manager_.get()) | 112 if (extension_devtools_manager_.get()) |
| (...skipping 50 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 135 return true; | 163 return true; |
| 136 } | 164 } |
| 137 return false; | 165 return false; |
| 138 } | 166 } |
| 139 | 167 |
| 140 void ExtensionEventRouter::DispatchEventToRenderers( | 168 void ExtensionEventRouter::DispatchEventToRenderers( |
| 141 const std::string& event_name, | 169 const std::string& event_name, |
| 142 const std::string& event_args, | 170 const std::string& event_args, |
| 143 Profile* restrict_to_profile, | 171 Profile* restrict_to_profile, |
| 144 const GURL& event_url) { | 172 const GURL& event_url) { |
| 145 DispatchEventImpl("", event_name, event_args, restrict_to_profile, "", | 173 ExtensionEvent event("", event_name, event_args, event_url, |
| 146 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 ExtensionEvent event(extension_id, event_name, event_args, event_url, |
| 157 "", event_url); | 186 restrict_to_profile, ""); |
| 187 DispatchEventImpl(event, false); | |
| 158 } | 188 } |
| 159 | 189 |
| 160 void ExtensionEventRouter::DispatchEventsToRenderersAcrossIncognito( | 190 void ExtensionEventRouter::DispatchEventsToRenderersAcrossIncognito( |
| 161 const std::string& event_name, | 191 const std::string& event_name, |
| 162 const std::string& event_args, | 192 const std::string& event_args, |
| 163 Profile* restrict_to_profile, | 193 Profile* restrict_to_profile, |
| 164 const std::string& cross_incognito_args, | 194 const std::string& cross_incognito_args, |
| 165 const GURL& event_url) { | 195 const GURL& event_url) { |
| 166 DispatchEventImpl("", event_name, event_args, restrict_to_profile, | 196 ExtensionEvent event("", event_name, event_args, event_url, |
| 167 cross_incognito_args, event_url); | 197 restrict_to_profile, cross_incognito_args); |
| 198 DispatchEventImpl(event, false); | |
| 199 } | |
| 200 | |
| 201 bool ExtensionEventRouter::CanDispatchEventNow( | |
| 202 const std::string& extension_id) { | |
| 203 if (!CommandLine::ForCurrentProcess()->HasSwitch( | |
| 204 switches::kEnableLazyBackgroundPages)) | |
| 205 return true; | |
| 206 | |
| 207 if (extension_id.empty()) | |
| 208 // TODO(tessamac): Create all background pages. Wait for all to be loaded? | |
| 209 // or dispatch event to each extension when it's ready? | |
| 210 return true; | |
| 211 | |
| 212 const Extension* extension = profile_->GetExtensionService()-> | |
| 213 GetExtensionById(extension_id, false); // exclude disabled extensions | |
| 214 if (extension && extension->background_url().is_valid()) { | |
| 215 ExtensionProcessManager* pm = profile_->GetExtensionProcessManager(); | |
| 216 if (!pm->GetBackgroundHostForExtension(extension)) { | |
| 217 pm->CreateBackgroundHost(extension, extension->background_url()); | |
| 218 return false; | |
| 219 } | |
| 220 } | |
| 221 | |
| 222 return true; | |
| 168 } | 223 } |
| 169 | 224 |
| 170 void ExtensionEventRouter::DispatchEventImpl( | 225 void ExtensionEventRouter::DispatchEventImpl( |
| 171 const std::string& extension_id, | 226 const 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_) | 227 if (!profile_) |
| 178 return; | 228 return; |
| 179 | 229 |
| 180 // We don't expect to get events from a completely different profile. | 230 // We don't expect to get events from a completely different profile. |
| 181 DCHECK(!restrict_to_profile || profile_->IsSameProfile(restrict_to_profile)); | 231 DCHECK(!event.restrict_to_profile || |
| 232 profile_->IsSameProfile(event.restrict_to_profile)); | |
| 182 | 233 |
| 183 ListenerMap::iterator it = listeners_.find(event_name); | 234 if (!CanDispatchEventNow(event.extension_id)) { |
| 235 // Events should not be made pending twice. This may happen if the | |
| 236 // background page is shutdown before we finish dispatching pending events. | |
| 237 CHECK(!was_pending); | |
|
Matt Perry
2011/08/26 00:13:00
Since it can actually happen in a legit case, don'
Tessa MacDuff
2011/08/26 01:12:53
Actually it should never happen. I feel pretty co
| |
| 238 // TODO(tessamac): make sure Background Page notification doesn't | |
| 239 // happen before the event is added to the pending list. | |
| 240 AppendEvent(event); | |
| 241 return; | |
| 242 } | |
| 243 | |
| 244 ListenerMap::iterator it = listeners_.find(event.event_name); | |
| 184 if (it == listeners_.end()) | 245 if (it == listeners_.end()) |
| 185 return; | 246 return; |
| 186 | 247 |
| 187 std::set<EventListener>& listeners = it->second; | 248 std::set<EventListener>& listeners = it->second; |
| 188 ExtensionService* service = profile_->GetExtensionService(); | 249 ExtensionService* service = profile_->GetExtensionService(); |
| 189 | 250 |
| 190 // Send the event only to renderers that are listening for it. | 251 // Send the event only to renderers that are listening for it. |
| 191 for (std::set<EventListener>::iterator listener = listeners.begin(); | 252 for (std::set<EventListener>::iterator listener = listeners.begin(); |
| 192 listener != listeners.end(); ++listener) { | 253 listener != listeners.end(); ++listener) { |
| 193 if (!ChildProcessSecurityPolicy::GetInstance()-> | 254 if (!ChildProcessSecurityPolicy::GetInstance()-> |
| 194 HasExtensionBindings(listener->process->id())) { | 255 HasExtensionBindings(listener->process->id())) { |
| 195 // Don't send browser-level events to unprivileged processes. | 256 // Don't send browser-level events to unprivileged processes. |
| 196 continue; | 257 continue; |
| 197 } | 258 } |
| 198 | 259 |
| 199 if (!extension_id.empty() && extension_id != listener->extension_id) | 260 if (!event.extension_id.empty() && |
| 261 event.extension_id != listener->extension_id) | |
| 200 continue; | 262 continue; |
| 201 | 263 |
| 202 // Is this event from a different profile than the renderer (ie, an | 264 // Is this event from a different profile than the renderer (ie, an |
| 203 // incognito tab event sent to a normal process, or vice versa). | 265 // incognito tab event sent to a normal process, or vice versa). |
| 204 bool cross_incognito = restrict_to_profile && | 266 bool cross_incognito = event.restrict_to_profile && |
| 205 listener->process->browser_context() != restrict_to_profile; | 267 listener->process->browser_context() != event.restrict_to_profile; |
| 206 const Extension* extension = service->GetExtensionById( | 268 const Extension* extension = service->GetExtensionById( |
| 207 listener->extension_id, false); | 269 listener->extension_id, false); |
| 208 // Send the event with different arguments to extensions that can't | 270 // Send the event with different arguments to extensions that can't |
| 209 // cross incognito, if necessary. | 271 // cross incognito, if necessary. |
| 210 if (cross_incognito && !service->CanCrossIncognito(extension)) { | 272 if (cross_incognito && !service->CanCrossIncognito(extension)) { |
| 211 if (!cross_incognito_args.empty()) { | 273 if (!event.cross_incognito_args.empty()) { |
| 212 DispatchEvent(listener->process, listener->extension_id, | 274 DispatchEvent(listener->process, listener->extension_id, |
| 213 event_name, cross_incognito_args, event_url); | 275 event.event_name, event.cross_incognito_args, |
| 276 event.event_url); | |
| 214 } | 277 } |
| 215 continue; | 278 continue; |
| 216 } | 279 } |
| 217 | 280 |
| 218 DispatchEvent(listener->process, listener->extension_id, | 281 DispatchEvent(listener->process, listener->extension_id, |
| 219 event_name, event_args, event_url); | 282 event.event_name, event.event_args, event.event_url); |
| 220 } | 283 } |
| 221 } | 284 } |
| 222 | 285 |
| 286 void ExtensionEventRouter::AppendEvent(const ExtensionEvent& pending_event) { | |
| 287 PendingTasksList* tasks_list = NULL; | |
| 288 PendingTasksPerExtMap::iterator it = | |
| 289 pending_tasks_.find(pending_event.extension_id); | |
| 290 if (it == pending_tasks_.end()) { | |
| 291 tasks_list = new PendingTasksList(); | |
| 292 pending_tasks_[pending_event.extension_id] = | |
| 293 linked_ptr<PendingTasksList>(tasks_list); | |
| 294 } else { | |
| 295 tasks_list = it->second.get(); | |
| 296 } | |
| 297 | |
| 298 tasks_list->push_back(linked_ptr<Task>( | |
| 299 task_factory_.NewRunnableMethod(&ExtensionEventRouter::DispatchEventImpl, | |
| 300 pending_event, true))); | |
| 301 } | |
| 302 | |
| 303 void ExtensionEventRouter::DispatchPendingEvents( | |
| 304 const std::string &extension_id) { | |
| 305 // Find the list of pending tasks for this extension. | |
| 306 PendingTasksPerExtMap::const_iterator map_it = | |
| 307 pending_tasks_.find(extension_id); | |
| 308 if (map_it == pending_tasks_.end()) | |
| 309 return; | |
| 310 | |
| 311 PendingTasksList* tasks_list = map_it->second.get(); | |
| 312 for (PendingTasksList::const_iterator it = tasks_list->begin(); | |
| 313 it != tasks_list->end(); ++it) | |
| 314 it->get()->Run(); | |
| 315 | |
| 316 // Delete list. | |
| 317 tasks_list->clear(); | |
| 318 pending_tasks_.erase(extension_id); | |
| 319 } | |
| 320 | |
| 223 void ExtensionEventRouter::Observe(int type, | 321 void ExtensionEventRouter::Observe(int type, |
| 224 const NotificationSource& source, | 322 const NotificationSource& source, |
| 225 const NotificationDetails& details) { | 323 const NotificationDetails& details) { |
| 226 switch (type) { | 324 switch (type) { |
| 227 case content::NOTIFICATION_RENDERER_PROCESS_TERMINATED: | 325 case content::NOTIFICATION_RENDERER_PROCESS_TERMINATED: |
| 228 case content::NOTIFICATION_RENDERER_PROCESS_CLOSED: { | 326 case content::NOTIFICATION_RENDERER_PROCESS_CLOSED: { |
| 229 RenderProcessHost* renderer = Source<RenderProcessHost>(source).ptr(); | 327 RenderProcessHost* renderer = Source<RenderProcessHost>(source).ptr(); |
| 230 // Remove all event listeners associated with this renderer | 328 // Remove all event listeners associated with this renderer |
| 231 for (ListenerMap::iterator it = listeners_.begin(); | 329 for (ListenerMap::iterator it = listeners_.begin(); |
| 232 it != listeners_.end(); ) { | 330 it != listeners_.end(); ) { |
| 233 ListenerMap::iterator current_it = it++; | 331 ListenerMap::iterator current_it = it++; |
| 234 for (std::set<EventListener>::iterator jt = current_it->second.begin(); | 332 for (std::set<EventListener>::iterator jt = current_it->second.begin(); |
| 235 jt != current_it->second.end(); ) { | 333 jt != current_it->second.end(); ) { |
| 236 std::set<EventListener>::iterator current_jt = jt++; | 334 std::set<EventListener>::iterator current_jt = jt++; |
| 237 if (current_jt->process == renderer) { | 335 if (current_jt->process == renderer) { |
| 238 RemoveEventListener(current_it->first, | 336 RemoveEventListener(current_it->first, |
| 239 current_jt->process, | 337 current_jt->process, |
| 240 current_jt->extension_id); | 338 current_jt->extension_id); |
| 241 } | 339 } |
| 242 } | 340 } |
| 243 } | 341 } |
| 244 break; | 342 break; |
| 245 } | 343 } |
| 344 case chrome::NOTIFICATION_EXTENSION_HOST_DID_STOP_LOADING: { | |
| 345 // TODO: dispatch events in queue. ExtensionHost is in the details. | |
| 346 ExtensionHost* eh = Details<ExtensionHost>(details).ptr(); | |
| 347 DispatchPendingEvents(eh->extension_id()); | |
| 348 break; | |
| 349 } | |
| 350 // TODO(tessamac): if background page crashed/failed clear queue. | |
| 246 default: | 351 default: |
| 247 NOTREACHED(); | 352 NOTREACHED(); |
| 248 return; | 353 return; |
| 249 } | 354 } |
| 250 } | 355 } |
| OLD | NEW |