Make BackgroundContentsService start up BackgroundContents with a delay, as for ExtensionHosts.

extensions::ProcessManager starts ExtensionHosts asynchronously with a queue to avoid using too much resources at startup; make BackgroundContentsService use the same queue implementation. (ExtensionHostQueue now accepts an interface that both BackgroundContentsService and ExtensionHost share.)

This significantly reduces the time BackgroundContentsService contributes to ExtensionService startup (75th percentile was ~300ms, according to Extensions.BackgroundContentsServiceStartupTime).

BUG=47236

Review URL: https://codereview.chromium.org/933423003

Cr-Commit-Position: refs/heads/master@{#317480}
diff --git a/chrome/browser/background/background_contents.cc b/chrome/browser/background/background_contents.cc
index 7a1150b3..bf94280 100644
--- a/chrome/browser/background/background_contents.cc
+++ b/chrome/browser/background/background_contents.cc
@@ -16,6 +16,9 @@
 #include "content/public/browser/session_storage_namespace.h"
 #include "content/public/browser/site_instance.h"
 #include "content/public/browser/web_contents.h"
+#include "extensions/browser/extension_host_delegate.h"
+#include "extensions/browser/extension_host_queue.h"
+#include "extensions/browser/extensions_browser_client.h"
 #include "extensions/browser/view_type_utils.h"
 #include "ui/gfx/geometry/rect.h"
 
@@ -29,7 +32,9 @@
     Delegate* delegate,
     const std::string& partition_id,
     content::SessionStorageNamespace* session_storage_namespace)
-    : delegate_(delegate) {
+    : delegate_(delegate),
+      extension_host_delegate_(extensions::ExtensionsBrowserClient::Get()
+                                   ->CreateExtensionHostDelegate()) {
   profile_ = Profile::FromBrowserContext(
       site_instance->GetBrowserContext());
 
@@ -82,12 +87,19 @@
       chrome::NOTIFICATION_BACKGROUND_CONTENTS_DELETED,
       content::Source<Profile>(profile_),
       content::Details<BackgroundContents>(this));
+
+  extension_host_delegate_->GetExtensionHostQueue()->Remove(this);
 }
 
 const GURL& BackgroundContents::GetURL() const {
   return web_contents_.get() ? web_contents_->GetURL() : GURL::EmptyGURL();
 }
 
+void BackgroundContents::CreateRenderViewSoon(const GURL& url) {
+  initial_url_ = url;
+  extension_host_delegate_->GetExtensionHostQueue()->Add(this);
+}
+
 void BackgroundContents::CloseContents(WebContents* source) {
   content::NotificationService::current()->Notify(
       chrome::NOTIFICATION_BACKGROUND_CONTENTS_CLOSED,
@@ -160,3 +172,9 @@
       break;
   }
 }
+
+void BackgroundContents::CreateRenderViewNow() {
+  web_contents()->GetController().LoadURL(initial_url_, content::Referrer(),
+                                          ui::PAGE_TRANSITION_LINK,
+                                          std::string());
+}
diff --git a/chrome/browser/background/background_contents.h b/chrome/browser/background/background_contents.h
index e7345e3..b4a94c4 100644
--- a/chrome/browser/background/background_contents.h
+++ b/chrome/browser/background/background_contents.h
@@ -12,7 +12,9 @@
 #include "content/public/browser/notification_registrar.h"
 #include "content/public/browser/web_contents_delegate.h"
 #include "content/public/browser/web_contents_observer.h"
+#include "extensions/browser/deferred_start_render_host.h"
 #include "ui/base/window_open_disposition.h"
+#include "url/gurl.h"
 
 class Profile;
 
@@ -21,10 +23,15 @@
 class SiteInstance;
 };
 
+namespace extensions {
+class ExtensionHostDelegate;
+}
+
 // This class maintains a WebContents used in the background. It can host a
 // renderer, but does not have any visible display.
 // TODO(atwilson): Unify this with background pages; http://crbug.com/77790
-class BackgroundContents : public content::WebContentsDelegate,
+class BackgroundContents : public extensions::DeferredStartRenderHost,
+                           public content::WebContentsDelegate,
                            public content::WebContentsObserver,
                            public content::NotificationObserver {
  public:
@@ -56,6 +63,9 @@
   content::WebContents* web_contents() const { return web_contents_.get(); }
   virtual const GURL& GetURL() const;
 
+  // Adds this BackgroundContents to the queue of RenderViews to create.
+  void CreateRenderViewSoon(const GURL& url);
+
   // content::WebContentsDelegate implementation:
   void CloseContents(content::WebContents* source) override;
   bool ShouldSuppressDialogs(content::WebContents* source) override;
@@ -81,13 +91,22 @@
   BackgroundContents();
 
  private:
+  // DeferredStartRenderHost implementation:
+  void CreateRenderViewNow() override;
+
   // The delegate for this BackgroundContents.
   Delegate* delegate_;
 
+  // Delegate for choosing an ExtensionHostQueue.
+  scoped_ptr<extensions::ExtensionHostDelegate> extension_host_delegate_;
+
   Profile* profile_;
   scoped_ptr<content::WebContents> web_contents_;
   content::NotificationRegistrar registrar_;
 
+  // The initial URL to load.
+  GURL initial_url_;
+
   DISALLOW_COPY_AND_ASSIGN(BackgroundContents);
 };
 
diff --git a/chrome/browser/background/background_contents_service.cc b/chrome/browser/background/background_contents_service.cc
index 34f8446b..9cb6447 100644
--- a/chrome/browser/background/background_contents_service.cc
+++ b/chrome/browser/background/background_contents_service.cc
@@ -634,10 +634,7 @@
       std::string(),
       NULL);
 
-  // TODO(atwilson): Create RenderViews asynchronously to avoid increasing
-  // startup latency (http://crbug.com/47236).
-  contents->web_contents()->GetController().LoadURL(
-      url, content::Referrer(), ui::PAGE_TRANSITION_LINK, std::string());
+  contents->CreateRenderViewSoon(url);
 }
 
 BackgroundContents* BackgroundContentsService::CreateBackgroundContents(
diff --git a/chrome/browser/extensions/chrome_extension_host_delegate.cc b/chrome/browser/extensions/chrome_extension_host_delegate.cc
index 8cb4ec6..6a1af5e 100644
--- a/chrome/browser/extensions/chrome_extension_host_delegate.cc
+++ b/chrome/browser/extensions/chrome_extension_host_delegate.cc
@@ -4,6 +4,7 @@
 
 #include "chrome/browser/extensions/chrome_extension_host_delegate.h"
 
+#include "base/lazy_instance.h"
 #include "chrome/browser/extensions/chrome_extension_web_contents_observer.h"
 #include "chrome/browser/extensions/extension_service.h"
 #include "chrome/browser/extensions/extension_tab_util.h"
@@ -12,6 +13,7 @@
 #include "components/app_modal/javascript_dialog_manager.h"
 #include "extensions/browser/extension_host.h"
 #include "extensions/browser/extension_system.h"
+#include "extensions/browser/serial_extension_host_queue.h"
 
 namespace extensions {
 
@@ -66,4 +68,11 @@
           web_contents, security_origin, type, extension);
 }
 
+static base::LazyInstance<SerialExtensionHostQueue> g_queue =
+    LAZY_INSTANCE_INITIALIZER;
+
+ExtensionHostQueue* ChromeExtensionHostDelegate::GetExtensionHostQueue() const {
+  return g_queue.Pointer();
+}
+
 }  // namespace extensions
diff --git a/chrome/browser/extensions/chrome_extension_host_delegate.h b/chrome/browser/extensions/chrome_extension_host_delegate.h
index 0ba69555..c34b0d82 100644
--- a/chrome/browser/extensions/chrome_extension_host_delegate.h
+++ b/chrome/browser/extensions/chrome_extension_host_delegate.h
@@ -32,6 +32,7 @@
                                   const GURL& security_origin,
                                   content::MediaStreamType type,
                                   const Extension* extension) override;
+  ExtensionHostQueue* GetExtensionHostQueue() const override;
 };
 
 }  // namespace extensions