diff options
author | tessamac@chromium.org <tessamac@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2011-08-31 16:27:44 +0000 |
---|---|---|
committer | tessamac@chromium.org <tessamac@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2011-08-31 16:27:44 +0000 |
commit | f72d0c685a4a4a94036fb5b638979d9e3546224d (patch) | |
tree | 9c05705557ed2e2fc12cf4726031d78c2c60d2a2 /chrome | |
parent | f258e97dc776069ee60028ca949b93b297300b3b (diff) | |
download | chromium_src-f72d0c685a4a4a94036fb5b638979d9e3546224d.zip chromium_src-f72d0c685a4a4a94036fb5b638979d9e3546224d.tar.gz chromium_src-f72d0c685a4a4a94036fb5b638979d9e3546224d.tar.bz2 |
Lazy creation of background pages with --enable-lazy-background-pages
BUG=81752
TEST=None
Review URL: http://codereview.chromium.org/7672009
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@98981 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'chrome')
-rw-r--r-- | chrome/app/generated_resources.grd | 6 | ||||
-rw-r--r-- | chrome/browser/about_flags.cc | 7 | ||||
-rw-r--r-- | chrome/browser/extensions/extension_event_router.cc | 150 | ||||
-rw-r--r-- | chrome/browser/extensions/extension_event_router.h | 31 | ||||
-rw-r--r-- | chrome/browser/extensions/extension_menu_manager_unittest.cc | 23 | ||||
-rw-r--r-- | chrome/browser/extensions/extension_process_manager.cc | 23 | ||||
-rw-r--r-- | chrome/common/chrome_switches.cc | 4 | ||||
-rw-r--r-- | chrome/common/chrome_switches.h | 1 |
8 files changed, 195 insertions, 50 deletions
diff --git a/chrome/app/generated_resources.grd b/chrome/app/generated_resources.grd index f5558d9..96bb3df 100644 --- a/chrome/app/generated_resources.grd +++ b/chrome/app/generated_resources.grd @@ -4324,6 +4324,12 @@ Keep your key file in a safe place. You will need it to create new versions of y <message name="IDS_FLAGS_CRXLESS_WEB_APPS_DESCRIPTION" desc="Description of the CRX-less web apps lab"> Enables support for installing Chrome apps that are deployed using a manifest file on a webpage, rather than by packaging the manifest and icons into a crx file. </message> + <message name="IDS_FLAGS_LAZY_BACKGROUND_PAGES_NAME" desc="Title of the flag to enable lazy loading of extension background pages."> + Lazy Background Pages + </message> + <message name="IDS_FLAGS_LAZY_BACKGROUND_PAGES_DESCRIPTION" desc="Description of the flag to enable lazy loading of extension background pages."> + Enables some extension background pages to be loaded when they are needed rather than when the extensions are first loaded. + </message> <message name="IDS_FLAGS_CONFLICTS_CHECK_NAME" desc="Title of the run conflicts check flag"> Check for known conflicts with 3rd party modules. </message> diff --git a/chrome/browser/about_flags.cc b/chrome/browser/about_flags.cc index 263f031..60b325d 100644 --- a/chrome/browser/about_flags.cc +++ b/chrome/browser/about_flags.cc @@ -126,6 +126,13 @@ const Experiment kExperiments[] = { SINGLE_VALUE_TYPE(switches::kEnableCrxlessWebApps) }, { + "lazy-background-pages", + IDS_FLAGS_LAZY_BACKGROUND_PAGES_NAME, + IDS_FLAGS_LAZY_BACKGROUND_PAGES_DESCRIPTION, + kOsAll, + SINGLE_VALUE_TYPE(switches::kEnableLazyBackgroundPages) + }, + { "ignore-gpu-blacklist", IDS_FLAGS_IGNORE_GPU_BLACKLIST_NAME, IDS_FLAGS_IGNORE_GPU_BLACKLIST_DESCRIPTION, diff --git a/chrome/browser/extensions/extension_event_router.cc b/chrome/browser/extensions/extension_event_router.cc index 7a9cb340..eb458c8 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, @@ -68,10 +93,12 @@ ExtensionEventRouter::ExtensionEventRouter(Profile* profile) 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 +169,10 @@ 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); + linked_ptr<ExtensionEvent> event( + new ExtensionEvent("", event_name, event_args, event_url, + restrict_to_profile, "")); + DispatchEventImpl(event, false); } void ExtensionEventRouter::DispatchEventToExtension( @@ -153,8 +182,10 @@ 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); + linked_ptr<ExtensionEvent> event( + new ExtensionEvent(extension_id, event_name, event_args, event_url, + restrict_to_profile, "")); + DispatchEventImpl(event, false); } void ExtensionEventRouter::DispatchEventsToRenderersAcrossIncognito( @@ -163,24 +194,56 @@ 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); + linked_ptr<ExtensionEvent> event( + new ExtensionEvent("", 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 linked_ptr<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); + // 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 +259,66 @@ 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 linked_ptr<ExtensionEvent>& event) { + PendingEventsList* events_list = NULL; + PendingEventsPerExtMap::iterator it = + pending_events_.find(event->extension_id); + if (it == pending_events_.end()) { + events_list = new PendingEventsList(); + pending_events_[event->extension_id] = + linked_ptr<PendingEventsList>(events_list); + } else { + events_list = it->second.get(); + } + + events_list->push_back(event); +} + +void ExtensionEventRouter::DispatchPendingEvents( + const std::string &extension_id) { + // Find the list of pending events for this extension. + PendingEventsPerExtMap::const_iterator map_it = + pending_events_.find(extension_id); + if (map_it == pending_events_.end()) + return; + + PendingEventsList* events_list = map_it->second.get(); + for (PendingEventsList::const_iterator it = events_list->begin(); + it != events_list->end(); ++it) + DispatchEventImpl(*it, true); + + // Delete list. + events_list->clear(); + pending_events_.erase(extension_id); +} + void ExtensionEventRouter::Observe(int type, const NotificationSource& source, const NotificationDetails& details) { @@ -243,6 +342,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; diff --git a/chrome/browser/extensions/extension_event_router.h b/chrome/browser/extensions/extension_event_router.h index d46e05f..99ca416 100644 --- a/chrome/browser/extensions/extension_event_router.h +++ b/chrome/browser/extensions/extension_event_router.h @@ -10,6 +10,7 @@ #include <set> #include <string> +#include "base/memory/linked_ptr.h" #include "base/memory/ref_counted.h" #include "content/common/notification_observer.h" #include "content/common/notification_registrar.h" @@ -65,7 +66,7 @@ class ExtensionEventRouter : public NotificationObserver { const GURL& event_url); // Same as above, except only send the event to the given extension. - void DispatchEventToExtension( + virtual void DispatchEventToExtension( const std::string& extension_id, const std::string& event_name, const std::string& event_args, @@ -87,15 +88,22 @@ class ExtensionEventRouter : public NotificationObserver { const GURL& event_url); protected: + // The details of an event to be dispatched. + struct ExtensionEvent; + // Shared by DispatchEvent*. If |extension_id| is empty, the event is // broadcast. - virtual void 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); + // An event that just came off the pending list may not be delayed again. + void DispatchEventImpl(const linked_ptr<ExtensionEvent>& event, + bool was_pending); + + // Dispatch may be delayed if the extension has a lazy background page. + bool CanDispatchEventNow(const std::string& extension_id); + + // Store the event so that it can be dispatched (in order received) + // when the background page is done loading. + void AppendEvent(const linked_ptr<ExtensionEvent>& event); + void DispatchPendingEvents(const std::string& extension_id); private: // An extension listening to an event. @@ -116,6 +124,13 @@ class ExtensionEventRouter : public NotificationObserver { typedef std::map<std::string, std::set<EventListener> > ListenerMap; ListenerMap listeners_; + // A map between an extension id and the queue of events pending + // the load of it's background page. + typedef std::vector<linked_ptr<ExtensionEvent> > PendingEventsList; + typedef std::map<std::string, + linked_ptr<PendingEventsList> > PendingEventsPerExtMap; + PendingEventsPerExtMap pending_events_; + DISALLOW_COPY_AND_ASSIGN(ExtensionEventRouter); }; diff --git a/chrome/browser/extensions/extension_menu_manager_unittest.cc b/chrome/browser/extensions/extension_menu_manager_unittest.cc index bb834cb..eef1afd 100644 --- a/chrome/browser/extensions/extension_menu_manager_unittest.cc +++ b/chrome/browser/extensions/extension_menu_manager_unittest.cc @@ -347,12 +347,12 @@ class MockExtensionEventRouter : public ExtensionEventRouter { explicit MockExtensionEventRouter(Profile* profile) : ExtensionEventRouter(profile) {} - MOCK_METHOD6(DispatchEventImpl, void(const std::string& extension_id, - const std::string& event_name, - const std::string& event_args, - Profile* source_profile, - const std::string& cross_incognito_args, - const GURL& event_url)); + MOCK_METHOD5(DispatchEventToExtension, void(const std::string& extension_id, + const std::string& event_name, + const std::string& event_args, + Profile* source_profile, + const GURL& event_url)); + private: DISALLOW_COPY_AND_ASSIGN(MockExtensionEventRouter); @@ -445,12 +445,11 @@ TEST_F(ExtensionMenuManagerTest, ExecuteCommand) { std::string event_args; std::string expected_event_name = "contextMenus"; EXPECT_CALL(*mock_event_router.get(), - DispatchEventImpl(item->extension_id(), - expected_event_name, - _, - &profile, - "", - GURL())) + DispatchEventToExtension(item->extension_id(), + expected_event_name, + _, + &profile, + GURL())) .Times(1) .WillOnce(SaveArg<2>(&event_args)); diff --git a/chrome/browser/extensions/extension_process_manager.cc b/chrome/browser/extensions/extension_process_manager.cc index f64624c..8069686 100644 --- a/chrome/browser/extensions/extension_process_manager.cc +++ b/chrome/browser/extensions/extension_process_manager.cc @@ -2,6 +2,7 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. +#include "base/command_line.h" #include "chrome/browser/extensions/extension_process_manager.h" #include "chrome/browser/ui/browser_window.h" @@ -14,6 +15,7 @@ #include "chrome/browser/profiles/profile.h" #include "chrome/browser/ui/browser.h" #include "chrome/common/chrome_notification_types.h" +#include "chrome/common/chrome_switches.h" #include "chrome/common/extensions/extension.h" #include "chrome/common/url_constants.h" #include "content/browser/site_instance.h" @@ -55,18 +57,20 @@ class IncognitoExtensionProcessManager : public ExtensionProcessManager { ExtensionProcessManager* original_manager_; }; -static void CreateBackgroundHost( +static void CreateBackgroundHostForExtensionLoad( ExtensionProcessManager* manager, const Extension* extension) { - // Start the process for the master page, if it exists. - if (extension->background_url().is_valid()) + // Start the process for the master page, if it exists and we're not lazy. + if (!CommandLine::ForCurrentProcess()->HasSwitch( + switches::kEnableLazyBackgroundPages) && + extension->background_url().is_valid()) manager->CreateBackgroundHost(extension, extension->background_url()); } -static void CreateBackgroundHosts( +static void CreateBackgroundHostsForProfileStartup( ExtensionProcessManager* manager, const ExtensionList* extensions) { for (ExtensionList::const_iterator extension = extensions->begin(); extension != extensions->end(); ++extension) { - CreateBackgroundHost(manager, *extension); + CreateBackgroundHostForExtensionLoad(manager, *extension); } } @@ -288,7 +292,7 @@ void ExtensionProcessManager::Observe(int type, const NotificationDetails& details) { switch (type) { case chrome::NOTIFICATION_EXTENSIONS_READY: { - CreateBackgroundHosts(this, + CreateBackgroundHostsForProfileStartup(this, Source<Profile>(source).ptr()->GetExtensionService()->extensions()); break; } @@ -298,7 +302,7 @@ void ExtensionProcessManager::Observe(int type, Source<Profile>(source).ptr()->GetExtensionService(); if (service->is_ready()) { const Extension* extension = Details<const Extension>(details).ptr(); - ::CreateBackgroundHost(this, extension); + ::CreateBackgroundHostForExtensionLoad(this, extension); } break; } @@ -470,6 +474,9 @@ void IncognitoExtensionProcessManager::Observe( const NotificationDetails& details) { switch (type) { case chrome::NOTIFICATION_BROWSER_WINDOW_READY: { + if (CommandLine::ForCurrentProcess()->HasSwitch( + switches::kEnableLazyBackgroundPages)) + break; // We want to spawn our background hosts as soon as the user opens an // incognito window. Watch for new browsers and create the hosts if // it matches our profile. @@ -482,7 +489,7 @@ void IncognitoExtensionProcessManager::Observe( Profile::FromBrowserContext(browsing_instance_->browser_context()); ExtensionService* service = profile->GetExtensionService(); if (service && service->is_ready()) - CreateBackgroundHosts(this, service->extensions()); + CreateBackgroundHostsForProfileStartup(this, service->extensions()); } break; } diff --git a/chrome/common/chrome_switches.cc b/chrome/common/chrome_switches.cc index 77a1942..930ca1a 100644 --- a/chrome/common/chrome_switches.cc +++ b/chrome/common/chrome_switches.cc @@ -472,6 +472,10 @@ const char kEnableIPCFuzzing[] = "enable-ipc-fuzzing"; // attempt to use the existing connection. const char kEnableIPPooling[] = "enable-ip-pooling"; +// Enables some extension background pages to be loaded when they are +// needed rather than when the extensions are first loaded. +const char kEnableLazyBackgroundPages[] = "enable-lazy-background-pages"; + // Enables MAC cookies in the network stack. These cookies use HMAC to // protect session state from passive network attackers. // http://tools.ietf.org/html/draft-hammer-oauth-v2-mac-token diff --git a/chrome/common/chrome_switches.h b/chrome/common/chrome_switches.h index c484cc2..3c87392 100644 --- a/chrome/common/chrome_switches.h +++ b/chrome/common/chrome_switches.h @@ -137,6 +137,7 @@ extern const char kEnableInlineWebstoreInstall[]; extern const char kEnableIPv6[]; extern const char kEnableIPCFuzzing[]; extern const char kEnableIPPooling[]; +extern const char kEnableLazyBackgroundPages[]; extern const char kEnableMacCookies[]; extern const char kEnableMemoryInfo[]; extern const char kEnableNaCl[]; |