Chromium Code Reviews| Index: chrome/browser/extensions/extension_event_router.cc |
| diff --git a/chrome/browser/extensions/extension_event_router.cc b/chrome/browser/extensions/extension_event_router.cc |
| index 7a9cb34065807cd76bf647eaab015609c542f117..b22476f96db2b1f45cf991737190cd3f2f221e4b 100644 |
| --- a/chrome/browser/extensions/extension_event_router.cc |
| +++ b/chrome/browser/extensions/extension_event_router.cc |
| @@ -4,14 +4,17 @@ |
| #include "chrome/browser/extensions/extension_event_router.h" |
| +#include "base/command_line.h" |
| #include "base/values.h" |
| #include "chrome/browser/extensions/extension_devtools_manager.h" |
| +#include "chrome/browser/extensions/extension_host.h" |
| #include "chrome/browser/extensions/extension_processes_api.h" |
| #include "chrome/browser/extensions/extension_processes_api_constants.h" |
| #include "chrome/browser/extensions/extension_service.h" |
| #include "chrome/browser/extensions/extension_tabs_module.h" |
| #include "chrome/browser/extensions/extension_webrequest_api.h" |
| #include "chrome/browser/profiles/profile.h" |
| +#include "chrome/common/chrome_switches.h" |
| #include "chrome/common/extensions/extension.h" |
| #include "chrome/common/extensions/extension_messages.h" |
| #include "content/browser/child_process_security_policy.h" |
| @@ -48,6 +51,28 @@ struct ExtensionEventRouter::EventListener { |
| } |
| }; |
| +struct ExtensionEventRouter::ExtensionEvent { |
| + std::string extension_id; |
| + std::string event_name; |
| + std::string event_args; |
| + GURL event_url; |
| + Profile* restrict_to_profile; |
| + std::string cross_incognito_args; |
| + |
| + ExtensionEvent(const std::string& extension_id, |
| + const std::string& event_name, |
| + const std::string& event_args, |
| + const GURL& event_url, |
| + Profile* restrict_to_profile, |
| + const std::string cross_incognito_args) |
| + : extension_id(extension_id), |
| + event_name(event_name), |
| + event_args(event_args), |
| + event_url(event_url), |
| + restrict_to_profile(restrict_to_profile), |
| + cross_incognito_args(cross_incognito_args) {} |
| +}; |
| + |
| // static |
| void ExtensionEventRouter::DispatchEvent(IPC::Message::Sender* ipc_sender, |
| const std::string& extension_id, |
| @@ -63,15 +88,18 @@ void ExtensionEventRouter::DispatchEvent(IPC::Message::Sender* ipc_sender, |
| ExtensionEventRouter::ExtensionEventRouter(Profile* profile) |
| : profile_(profile), |
| - extension_devtools_manager_(profile->GetExtensionDevToolsManager()) { |
| + extension_devtools_manager_(profile->GetExtensionDevToolsManager()), |
| + ALLOW_THIS_IN_INITIALIZER_LIST(task_factory_(this)) { |
| registrar_.Add(this, content::NOTIFICATION_RENDERER_PROCESS_TERMINATED, |
| NotificationService::AllSources()); |
| registrar_.Add(this, content::NOTIFICATION_RENDERER_PROCESS_CLOSED, |
| NotificationService::AllSources()); |
| + registrar_.Add(this, chrome::NOTIFICATION_EXTENSION_HOST_DID_STOP_LOADING, |
| + Source<Profile>(profile_)); |
| + // TODO(tessamac): also get notified for background page crash/failure. |
| } |
| -ExtensionEventRouter::~ExtensionEventRouter() { |
| -} |
| +ExtensionEventRouter::~ExtensionEventRouter() {} |
| void ExtensionEventRouter::AddEventListener( |
| const std::string& event_name, |
| @@ -142,8 +170,9 @@ void ExtensionEventRouter::DispatchEventToRenderers( |
| const std::string& event_args, |
| Profile* restrict_to_profile, |
| const GURL& event_url) { |
| - DispatchEventImpl("", event_name, event_args, restrict_to_profile, "", |
| - event_url); |
| + ExtensionEvent event("", event_name, event_args, event_url, |
| + restrict_to_profile, ""); |
| + DispatchEventImpl(event, false); |
| } |
| void ExtensionEventRouter::DispatchEventToExtension( |
| @@ -153,8 +182,9 @@ void ExtensionEventRouter::DispatchEventToExtension( |
| Profile* restrict_to_profile, |
| const GURL& event_url) { |
| DCHECK(!extension_id.empty()); |
| - DispatchEventImpl(extension_id, event_name, event_args, restrict_to_profile, |
| - "", event_url); |
| + ExtensionEvent event(extension_id, event_name, event_args, event_url, |
| + restrict_to_profile, ""); |
| + DispatchEventImpl(event, false); |
| } |
| void ExtensionEventRouter::DispatchEventsToRenderersAcrossIncognito( |
| @@ -163,24 +193,55 @@ void ExtensionEventRouter::DispatchEventsToRenderersAcrossIncognito( |
| Profile* restrict_to_profile, |
| const std::string& cross_incognito_args, |
| const GURL& event_url) { |
| - DispatchEventImpl("", event_name, event_args, restrict_to_profile, |
| - cross_incognito_args, event_url); |
| + ExtensionEvent event("", event_name, event_args, event_url, |
| + restrict_to_profile, cross_incognito_args); |
| + DispatchEventImpl(event, false); |
| +} |
| + |
| +bool ExtensionEventRouter::CanDispatchEventNow( |
| + const std::string& extension_id) { |
| + if (!CommandLine::ForCurrentProcess()->HasSwitch( |
| + switches::kEnableLazyBackgroundPages)) |
| + return true; |
| + |
| + if (extension_id.empty()) |
| + // TODO(tessamac): Create all background pages. Wait for all to be loaded? |
| + // or dispatch event to each extension when it's ready? |
| + return true; |
| + |
| + const Extension* extension = profile_->GetExtensionService()-> |
| + GetExtensionById(extension_id, false); // exclude disabled extensions |
| + if (extension && extension->background_url().is_valid()) { |
| + ExtensionProcessManager* pm = profile_->GetExtensionProcessManager(); |
| + if (!pm->GetBackgroundHostForExtension(extension)) { |
| + pm->CreateBackgroundHost(extension, extension->background_url()); |
| + return false; |
| + } |
| + } |
| + |
| + return true; |
| } |
| void ExtensionEventRouter::DispatchEventImpl( |
| - const std::string& extension_id, |
| - const std::string& event_name, |
| - const std::string& event_args, |
| - Profile* restrict_to_profile, |
| - const std::string& cross_incognito_args, |
| - const GURL& event_url) { |
| + const ExtensionEvent& event, bool was_pending) { |
| if (!profile_) |
| return; |
| // We don't expect to get events from a completely different profile. |
| - DCHECK(!restrict_to_profile || profile_->IsSameProfile(restrict_to_profile)); |
| + DCHECK(!event.restrict_to_profile || |
| + profile_->IsSameProfile(event.restrict_to_profile)); |
| + |
| + if (!CanDispatchEventNow(event.extension_id)) { |
| + // Events should not be made pending twice. This may happen if the |
| + // background page is shutdown before we finish dispatching pending events. |
| + 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
|
| + // TODO(tessamac): make sure Background Page notification doesn't |
| + // happen before the event is added to the pending list. |
| + AppendEvent(event); |
| + return; |
| + } |
| - ListenerMap::iterator it = listeners_.find(event_name); |
| + ListenerMap::iterator it = listeners_.find(event.event_name); |
| if (it == listeners_.end()) |
| return; |
| @@ -196,30 +257,67 @@ void ExtensionEventRouter::DispatchEventImpl( |
| continue; |
| } |
| - if (!extension_id.empty() && extension_id != listener->extension_id) |
| + if (!event.extension_id.empty() && |
| + event.extension_id != listener->extension_id) |
| continue; |
| // Is this event from a different profile than the renderer (ie, an |
| // incognito tab event sent to a normal process, or vice versa). |
| - bool cross_incognito = restrict_to_profile && |
| - listener->process->browser_context() != restrict_to_profile; |
| + bool cross_incognito = event.restrict_to_profile && |
| + listener->process->browser_context() != event.restrict_to_profile; |
| const Extension* extension = service->GetExtensionById( |
| listener->extension_id, false); |
| // Send the event with different arguments to extensions that can't |
| // cross incognito, if necessary. |
| if (cross_incognito && !service->CanCrossIncognito(extension)) { |
| - if (!cross_incognito_args.empty()) { |
| + if (!event.cross_incognito_args.empty()) { |
| DispatchEvent(listener->process, listener->extension_id, |
| - event_name, cross_incognito_args, event_url); |
| + event.event_name, event.cross_incognito_args, |
| + event.event_url); |
| } |
| continue; |
| } |
| DispatchEvent(listener->process, listener->extension_id, |
| - event_name, event_args, event_url); |
| + event.event_name, event.event_args, event.event_url); |
| } |
| } |
| +void ExtensionEventRouter::AppendEvent(const ExtensionEvent& pending_event) { |
| + PendingTasksList* tasks_list = NULL; |
| + PendingTasksPerExtMap::iterator it = |
| + pending_tasks_.find(pending_event.extension_id); |
| + if (it == pending_tasks_.end()) { |
| + tasks_list = new PendingTasksList(); |
| + pending_tasks_[pending_event.extension_id] = |
| + linked_ptr<PendingTasksList>(tasks_list); |
| + } else { |
| + tasks_list = it->second.get(); |
| + } |
| + |
| + tasks_list->push_back(linked_ptr<Task>( |
| + task_factory_.NewRunnableMethod(&ExtensionEventRouter::DispatchEventImpl, |
| + pending_event, true))); |
| +} |
| + |
| +void ExtensionEventRouter::DispatchPendingEvents( |
| + const std::string &extension_id) { |
| + // Find the list of pending tasks for this extension. |
| + PendingTasksPerExtMap::const_iterator map_it = |
| + pending_tasks_.find(extension_id); |
| + if (map_it == pending_tasks_.end()) |
| + return; |
| + |
| + PendingTasksList* tasks_list = map_it->second.get(); |
| + for (PendingTasksList::const_iterator it = tasks_list->begin(); |
| + it != tasks_list->end(); ++it) |
| + it->get()->Run(); |
| + |
| + // Delete list. |
| + tasks_list->clear(); |
| + pending_tasks_.erase(extension_id); |
| +} |
| + |
| void ExtensionEventRouter::Observe(int type, |
| const NotificationSource& source, |
| const NotificationDetails& details) { |
| @@ -243,6 +341,13 @@ void ExtensionEventRouter::Observe(int type, |
| } |
| break; |
| } |
| + case chrome::NOTIFICATION_EXTENSION_HOST_DID_STOP_LOADING: { |
| + // TODO: dispatch events in queue. ExtensionHost is in the details. |
| + ExtensionHost* eh = Details<ExtensionHost>(details).ptr(); |
| + DispatchPendingEvents(eh->extension_id()); |
| + break; |
| + } |
| + // TODO(tessamac): if background page crashed/failed clear queue. |
| default: |
| NOTREACHED(); |
| return; |