Reland: Refactor code that defers extension background page loading
src/extensions depends on chrome::NOTIFICATION_PROFILE_CREATED to support
deferred loading of extension background pages when the profile isn't ready
yet. This is a layering violation.
* Remove Chrome concepts like "browser window ready" and "profile created"
from ProcessManager. Introduce ProcessManagerDelegate with a general concept
of deferring background page loading.
* Consolidate all the tricky Chrome-specific background page loading rules into
ChromeProcessManagerDelegate. This keeps all the rules in one place. Annotate
each block of special case code with the bug that inspired it.
* Extend unit test coverage for ProcessManager.
This will make it easier to eliminate chrome::NOTIFICATION_PROFILE_DESTROYED
in ProcessManager in a later CL.
(Original CL https://codereview.chromium.org/381283002 broke valgrind bots
because it was initializing left-over BrowserContextKeyedServices from tests
running earlier in the same process.)
BUG=392658
TEST=unit_tests ProcessManagerTest, browser_tests ProcessManagerBrowserTest, manual
Review URL: https://codereview.chromium.org/408523005
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@284593 0039d316-1c4b-4281-b951-d872f2087c98
diff --git a/chrome/browser/extensions/chrome_extensions_browser_client.cc b/chrome/browser/extensions/chrome_extensions_browser_client.cc
index c66d0236..db2ae3b 100644
--- a/chrome/browser/extensions/chrome_extensions_browser_client.cc
+++ b/chrome/browser/extensions/chrome_extensions_browser_client.cc
@@ -21,7 +21,6 @@
#include "chrome/browser/external_protocol/external_protocol_handler.h"
#include "chrome/browser/profiles/profile.h"
#include "chrome/browser/profiles/profile_manager.h"
-#include "chrome/browser/ui/browser_finder.h"
#include "chrome/common/chrome_paths.h"
#include "chrome/common/chrome_switches.h"
#include "chrome/common/chrome_version_info.h"
@@ -42,12 +41,14 @@
#include "chrome/browser/extensions/activity_log/activity_log.h"
#include "chrome/browser/extensions/api/chrome_extensions_api_client.h"
#include "chrome/browser/extensions/api/content_settings/content_settings_service.h"
+#include "chrome/browser/extensions/chrome_process_manager_delegate.h"
#endif
namespace extensions {
ChromeExtensionsBrowserClient::ChromeExtensionsBrowserClient() {
#if defined(ENABLE_EXTENSIONS)
+ process_manager_delegate_.reset(new ChromeProcessManagerDelegate);
api_client_.reset(new ChromeExtensionsAPIClient);
#endif
// Only set if it hasn't already been set (e.g. by a test).
@@ -158,35 +159,15 @@
#endif
}
-bool ChromeExtensionsBrowserClient::DeferLoadingBackgroundHosts(
- content::BrowserContext* context) const {
- Profile* profile = static_cast<Profile*>(context);
-
- // The profile may not be valid yet if it is still being initialized.
- // In that case, defer loading, since it depends on an initialized profile.
- // http://crbug.com/222473
- if (!g_browser_process->profile_manager()->IsValidProfile(profile))
- return true;
-
-#if defined(OS_ANDROID)
- return false;
+ProcessManagerDelegate*
+ChromeExtensionsBrowserClient::GetProcessManagerDelegate() const {
+#if defined(ENABLE_EXTENSIONS)
+ return process_manager_delegate_.get();
#else
- // There are no browser windows open and the browser process was
- // started to show the app launcher.
- return chrome::GetTotalBrowserCountForProfile(profile) == 0 &&
- CommandLine::ForCurrentProcess()->HasSwitch(switches::kShowAppList);
+ return NULL;
#endif
}
-bool ChromeExtensionsBrowserClient::IsBackgroundPageAllowed(
- content::BrowserContext* context) const {
- // Returns true if current session is Guest mode session and current
- // browser context is *not* off-the-record. Such context is artificial and
- // background page shouldn't be created in it.
- return !static_cast<Profile*>(context)->IsGuestSession() ||
- context->IsOffTheRecord();
-}
-
scoped_ptr<ExtensionHostDelegate>
ChromeExtensionsBrowserClient::CreateExtensionHostDelegate() {
return scoped_ptr<ExtensionHostDelegate>(new ChromeExtensionHostDelegate);
diff --git a/chrome/browser/extensions/chrome_extensions_browser_client.h b/chrome/browser/extensions/chrome_extensions_browser_client.h
index 3c4a2c6..5957838 100644
--- a/chrome/browser/extensions/chrome_extensions_browser_client.h
+++ b/chrome/browser/extensions/chrome_extensions_browser_client.h
@@ -26,6 +26,7 @@
class ChromeComponentExtensionResourceManager;
class ChromeExtensionsAPIClient;
+class ChromeProcessManagerDelegate;
class ContentSettingsPrefsObserver;
// Implementation of extensions::BrowserClient for Chrome, which includes
@@ -76,10 +77,7 @@
virtual void GetEarlyExtensionPrefsObservers(
content::BrowserContext* context,
std::vector<ExtensionPrefsObserver*>* observers) const OVERRIDE;
- virtual bool DeferLoadingBackgroundHosts(
- content::BrowserContext* context) const OVERRIDE;
- virtual bool IsBackgroundPageAllowed(
- content::BrowserContext* context) const OVERRIDE;
+ virtual ProcessManagerDelegate* GetProcessManagerDelegate() const OVERRIDE;
virtual scoped_ptr<ExtensionHostDelegate> CreateExtensionHostDelegate()
OVERRIDE;
virtual bool DidVersionUpdate(content::BrowserContext* context) OVERRIDE;
@@ -102,6 +100,9 @@
ChromeNotificationObserver notification_observer_;
#if defined(ENABLE_EXTENSIONS)
+ // Support for ProcessManager.
+ scoped_ptr<ChromeProcessManagerDelegate> process_manager_delegate_;
+
// Client for API implementations.
scoped_ptr<ChromeExtensionsAPIClient> api_client_;
#endif
diff --git a/chrome/browser/extensions/chrome_notification_observer.cc b/chrome/browser/extensions/chrome_notification_observer.cc
index 5d966c8..c8e60163 100644
--- a/chrome/browser/extensions/chrome_notification_observer.cc
+++ b/chrome/browser/extensions/chrome_notification_observer.cc
@@ -4,59 +4,22 @@
#include "chrome/browser/extensions/chrome_notification_observer.h"
-#include "base/logging.h"
-#include "chrome/browser/chrome_notification_types.h"
-#include "chrome/browser/profiles/profile.h"
-#include "chrome/browser/ui/browser.h"
#include "chrome/common/extensions/features/feature_channel.h"
#include "content/public/browser/notification_service.h"
#include "content/public/browser/notification_types.h"
#include "content/public/browser/render_process_host.h"
-#include "extensions/browser/extension_system.h"
-#include "extensions/browser/process_manager.h"
#include "extensions/common/extension_messages.h"
namespace extensions {
ChromeNotificationObserver::ChromeNotificationObserver() {
registrar_.Add(this,
- chrome::NOTIFICATION_BROWSER_WINDOW_READY,
- content::NotificationService::AllSources());
- registrar_.Add(this,
content::NOTIFICATION_RENDERER_PROCESS_CREATED,
content::NotificationService::AllBrowserContextsAndSources());
}
ChromeNotificationObserver::~ChromeNotificationObserver() {}
-void ChromeNotificationObserver::OnBrowserWindowReady(Browser* browser) {
- Profile* profile = browser->profile();
- DCHECK(profile);
-
- // Inform the process manager for this profile that the window is ready.
- // We continue to observe the notification in case browser windows open for
- // a related incognito profile or other regular profiles.
- extensions::ProcessManager* manager =
- ExtensionSystem::Get(profile)->process_manager();
- if (!manager) // Tests may not have a process manager.
- return;
- DCHECK_EQ(profile, manager->GetBrowserContext());
- manager->OnBrowserWindowReady();
-
- // For incognito profiles also inform the original profile's process manager
- // that the window is ready. This will usually be a no-op because the
- // original profile's process manager should have been informed when the
- // non-incognito window opened.
- if (profile->IsOffTheRecord()) {
- Profile* original_profile = profile->GetOriginalProfile();
- extensions::ProcessManager* original_manager =
- ExtensionSystem::Get(original_profile)->process_manager();
- DCHECK(original_manager);
- DCHECK_EQ(original_profile, original_manager->GetBrowserContext());
- original_manager->OnBrowserWindowReady();
- }
-}
-
void ChromeNotificationObserver::OnRendererProcessCreated(
content::RenderProcessHost* process) {
// Extensions need to know the channel for API restrictions. Send the channel
@@ -68,12 +31,6 @@
const content::NotificationSource& source,
const content::NotificationDetails& details) {
switch (type) {
- case chrome::NOTIFICATION_BROWSER_WINDOW_READY: {
- Browser* browser = content::Source<Browser>(source).ptr();
- OnBrowserWindowReady(browser);
- break;
- }
-
case content::NOTIFICATION_RENDERER_PROCESS_CREATED: {
content::RenderProcessHost* process =
content::Source<content::RenderProcessHost>(source).ptr();
diff --git a/chrome/browser/extensions/chrome_notification_observer.h b/chrome/browser/extensions/chrome_notification_observer.h
index a31dc87..8045b156 100644
--- a/chrome/browser/extensions/chrome_notification_observer.h
+++ b/chrome/browser/extensions/chrome_notification_observer.h
@@ -25,8 +25,7 @@
ChromeNotificationObserver();
virtual ~ChromeNotificationObserver();
- // IPC message handlers:
- void OnBrowserWindowReady(Browser* browser);
+ // Notification handlers:
void OnRendererProcessCreated(content::RenderProcessHost* process);
// content::NotificationObserver overrides:
diff --git a/chrome/browser/extensions/chrome_process_manager_delegate.cc b/chrome/browser/extensions/chrome_process_manager_delegate.cc
new file mode 100644
index 0000000..12e0c16
--- /dev/null
+++ b/chrome/browser/extensions/chrome_process_manager_delegate.cc
@@ -0,0 +1,143 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "chrome/browser/extensions/chrome_process_manager_delegate.h"
+
+#include "base/command_line.h"
+#include "base/logging.h"
+#include "chrome/browser/browser_process.h"
+#include "chrome/browser/chrome_notification_types.h"
+#include "chrome/browser/profiles/profile.h"
+#include "chrome/browser/profiles/profile_manager.h"
+#include "chrome/browser/ui/browser.h"
+#include "content/public/browser/notification_service.h"
+#include "extensions/browser/extension_system.h"
+#include "extensions/browser/process_manager.h"
+#include "extensions/common/one_shot_event.h"
+
+#if !defined(OS_ANDROID)
+#include "chrome/browser/ui/browser_finder.h"
+#include "chrome/common/chrome_switches.h"
+#endif
+
+namespace extensions {
+
+ChromeProcessManagerDelegate::ChromeProcessManagerDelegate() {
+ registrar_.Add(this,
+ chrome::NOTIFICATION_BROWSER_WINDOW_READY,
+ content::NotificationService::AllSources());
+ registrar_.Add(this,
+ chrome::NOTIFICATION_PROFILE_CREATED,
+ content::NotificationService::AllSources());
+ // TODO(jamescook): Move observation of NOTIFICATION_PROFILE_DESTROYED here.
+ // http://crbug.com/392658
+}
+
+ChromeProcessManagerDelegate::~ChromeProcessManagerDelegate() {
+}
+
+bool ChromeProcessManagerDelegate::IsBackgroundPageAllowed(
+ content::BrowserContext* context) const {
+ Profile* profile = static_cast<Profile*>(context);
+
+ // Disallow if the current session is a Guest mode session but the current
+ // browser context is *not* off-the-record. Such context is artificial and
+ // background page shouldn't be created in it.
+ // http://crbug.com/329498
+ return !(profile->IsGuestSession() && !profile->IsOffTheRecord());
+}
+
+bool ChromeProcessManagerDelegate::DeferCreatingStartupBackgroundHosts(
+ content::BrowserContext* context) const {
+ Profile* profile = static_cast<Profile*>(context);
+
+ // The profile may not be valid yet if it is still being initialized.
+ // In that case, defer loading, since it depends on an initialized profile.
+ // Background hosts will be loaded later via NOTIFICATION_PROFILE_CREATED.
+ // http://crbug.com/222473
+ if (!g_browser_process->profile_manager()->IsValidProfile(profile))
+ return true;
+
+#if defined(OS_ANDROID)
+ return false;
+#else
+ // There are no browser windows open and the browser process was
+ // started to show the app launcher. Background hosts will be loaded later
+ // via NOTIFICATION_BROWSER_WINDOW_READY. http://crbug.com/178260
+ return chrome::GetTotalBrowserCountForProfile(profile) == 0 &&
+ CommandLine::ForCurrentProcess()->HasSwitch(switches::kShowAppList);
+#endif
+}
+
+void ChromeProcessManagerDelegate::Observe(
+ int type,
+ const content::NotificationSource& source,
+ const content::NotificationDetails& details) {
+ switch (type) {
+ case chrome::NOTIFICATION_BROWSER_WINDOW_READY: {
+ Browser* browser = content::Source<Browser>(source).ptr();
+ OnBrowserWindowReady(browser);
+ break;
+ }
+ case chrome::NOTIFICATION_PROFILE_CREATED: {
+ Profile* profile = content::Source<Profile>(source).ptr();
+ OnProfileCreated(profile);
+ break;
+ }
+
+ default:
+ NOTREACHED();
+ }
+}
+
+void ChromeProcessManagerDelegate::OnBrowserWindowReady(Browser* browser) {
+ Profile* profile = browser->profile();
+ DCHECK(profile);
+
+ // If the extension system isn't ready yet the background hosts will be
+ // created automatically when it is.
+ ExtensionSystem* system = ExtensionSystem::Get(profile);
+ if (!system->ready().is_signaled())
+ return;
+
+ // Inform the process manager for this profile that the window is ready.
+ // We continue to observe the notification in case browser windows open for
+ // a related incognito profile or other regular profiles.
+ ProcessManager* manager = system->process_manager();
+ if (!manager) // Tests may not have a process manager.
+ return;
+ DCHECK_EQ(profile, manager->GetBrowserContext());
+ manager->MaybeCreateStartupBackgroundHosts();
+
+ // For incognito profiles also inform the original profile's process manager
+ // that the window is ready. This will usually be a no-op because the
+ // original profile's process manager should have been informed when the
+ // non-incognito window opened.
+ if (profile->IsOffTheRecord()) {
+ Profile* original_profile = profile->GetOriginalProfile();
+ ProcessManager* original_manager =
+ ExtensionSystem::Get(original_profile)->process_manager();
+ DCHECK(original_manager);
+ DCHECK_EQ(original_profile, original_manager->GetBrowserContext());
+ original_manager->MaybeCreateStartupBackgroundHosts();
+ }
+}
+
+void ChromeProcessManagerDelegate::OnProfileCreated(Profile* profile) {
+ // Incognito profiles are handled by their original profile.
+ if (profile->IsOffTheRecord())
+ return;
+
+ // The profile can be created before the extension system is ready.
+ ProcessManager* manager = ExtensionSystem::Get(profile)->process_manager();
+ if (!manager)
+ return;
+
+ // The profile might have been initialized asynchronously (in parallel with
+ // extension system startup). Now that initialization is complete the
+ // ProcessManager can load deferred background pages.
+ manager->MaybeCreateStartupBackgroundHosts();
+}
+
+} // namespace extensions
diff --git a/chrome/browser/extensions/chrome_process_manager_delegate.h b/chrome/browser/extensions/chrome_process_manager_delegate.h
new file mode 100644
index 0000000..9bcc58a
--- /dev/null
+++ b/chrome/browser/extensions/chrome_process_manager_delegate.h
@@ -0,0 +1,50 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CHROME_BROWSER_EXTENSIONS_CHROME_PROCESS_MANAGER_DELEGATE_H_
+#define CHROME_BROWSER_EXTENSIONS_CHROME_PROCESS_MANAGER_DELEGATE_H_
+
+#include "base/compiler_specific.h"
+#include "base/macros.h"
+#include "content/public/browser/notification_observer.h"
+#include "content/public/browser/notification_registrar.h"
+#include "extensions/browser/process_manager_delegate.h"
+
+class Browser;
+class Profile;
+
+namespace extensions {
+
+// Support for ProcessManager. Controls cases where Chrome wishes to disallow
+// extension background pages or defer their creation.
+class ChromeProcessManagerDelegate : public ProcessManagerDelegate,
+ public content::NotificationObserver {
+ public:
+ ChromeProcessManagerDelegate();
+ virtual ~ChromeProcessManagerDelegate();
+
+ // ProcessManagerDelegate implementation:
+ virtual bool IsBackgroundPageAllowed(
+ content::BrowserContext* context) const OVERRIDE;
+ virtual bool DeferCreatingStartupBackgroundHosts(
+ content::BrowserContext* context) const OVERRIDE;
+
+ // content::NotificationObserver implementation:
+ virtual void Observe(int type,
+ const content::NotificationSource& source,
+ const content::NotificationDetails& details) OVERRIDE;
+
+ private:
+ // Notification handlers.
+ void OnBrowserWindowReady(Browser* browser);
+ void OnProfileCreated(Profile* profile);
+
+ content::NotificationRegistrar registrar_;
+
+ DISALLOW_COPY_AND_ASSIGN(ChromeProcessManagerDelegate);
+};
+
+} // namespace extensions
+
+#endif // CHROME_BROWSER_EXTENSIONS_CHROME_PROCESS_MANAGER_DELEGATE_H_