Introduce background.scripts feature for extension manifests.

This optimizes for the common use case where background pages
just include a reference to one or more script files and no
additional HTML.

BUG=107791


Review URL: http://codereview.chromium.org/9150008

git-svn-id: svn://svn.chromium.org/chrome/trunk/src@117110 0039d316-1c4b-4281-b951-d872f2087c98
diff --git a/chrome/browser/automation/testing_automation_provider.cc b/chrome/browser/automation/testing_automation_provider.cc
index fd810cf..1d422ef 100644
--- a/chrome/browser/automation/testing_automation_provider.cc
+++ b/chrome/browser/automation/testing_automation_provider.cc
@@ -4473,7 +4473,7 @@
     extension_value->SetString("public_key", extension->public_key());
     extension_value->SetString("description", extension->description());
     extension_value->SetString("background_url",
-                               extension->background_url().spec());
+                               extension->GetBackgroundURL().spec());
     extension_value->SetString("options_url",
                                extension->options_url().spec());
     extension_value->Set("host_permissions",
diff --git a/chrome/browser/background/background_contents_service.cc b/chrome/browser/background/background_contents_service.cc
index 5072851f..f1308eb8 100644
--- a/chrome/browser/background/background_contents_service.cc
+++ b/chrome/browser/background/background_contents_service.cc
@@ -1,4 +1,4 @@
-// Copyright (c) 2011 The Chromium Authors. All rights reserved.
+// Copyright (c) 2012 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.
 
@@ -239,7 +239,7 @@
       if (extension_service) {
         const Extension* extension =
             extension_service->GetExtensionById(UTF16ToUTF8(appid), false);
-        if (extension && extension->background_url().is_valid())
+        if (extension && extension->has_background_page())
           break;
       }
       RegisterBackgroundContents(bgcontents);
@@ -250,7 +250,7 @@
           content::Details<const Extension>(details).ptr();
       Profile* profile = content::Source<Profile>(source).ptr();
       if (extension->is_hosted_app() &&
-          extension->background_url().is_valid()) {
+          extension->has_background_page()) {
         // If there is a background page specified in the manifest for a hosted
         // app, then blow away registered urls in the pref.
         ShutdownAssociatedBackgroundContents(ASCIIToUTF16(extension->id()));
@@ -260,7 +260,7 @@
           // Now load the manifest-specified background page. If service isn't
           // ready, then the background page will be loaded from the
           // EXTENSIONS_READY callback.
-          LoadBackgroundContents(profile, extension->background_url(),
+          LoadBackgroundContents(profile, extension->GetBackgroundURL(),
               ASCIIToUTF16("background"), UTF8ToUTF16(extension->id()));
         }
       }
@@ -314,7 +314,7 @@
           // BackgroundContents in place.
           const Extension* extension =
               content::Details<UnloadedExtensionInfo>(details)->extension;
-          if (extension->background_url().is_valid())
+          if (extension->has_background_page())
             ShutdownAssociatedBackgroundContents(ASCIIToUTF16(extension->id()));
           break;
         }
@@ -378,9 +378,9 @@
   const Extension* extension =
       profile->GetExtensionService()->GetExtensionById(extension_id, false);
   DCHECK(!extension || extension->is_hosted_app());
-  if (extension && extension->background_url().is_valid()) {
+  if (extension && extension->has_background_page()) {
     LoadBackgroundContents(profile,
-                           extension->background_url(),
+                           extension->GetBackgroundURL(),
                            ASCIIToUTF16("background"),
                            UTF8ToUTF16(extension->id()));
     return;
@@ -425,10 +425,9 @@
   ExtensionSet::const_iterator iter = extensions->begin();
   for (; iter != extensions->end(); ++iter) {
     const Extension* extension = *iter;
-    if (extension->is_hosted_app() &&
-        extension->background_url().is_valid()) {
+    if (extension->is_hosted_app() && extension->has_background_page()) {
       LoadBackgroundContents(profile,
-                             extension->background_url(),
+                             extension->GetBackgroundURL(),
                              ASCIIToUTF16("background"),
                              UTF8ToUTF16(extension->id()));
     }
diff --git a/chrome/browser/extensions/background_scripts_apitest.cc b/chrome/browser/extensions/background_scripts_apitest.cc
new file mode 100644
index 0000000..292f6bc
--- /dev/null
+++ b/chrome/browser/extensions/background_scripts_apitest.cc
@@ -0,0 +1,9 @@
+// Copyright (c) 2012 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/extension_apitest.h"
+
+IN_PROC_BROWSER_TEST_F(ExtensionApiTest, BackgroundScripts) {
+  ASSERT_TRUE(RunExtensionTest("background_scripts")) << message_;
+}
diff --git a/chrome/browser/extensions/extension_event_router.cc b/chrome/browser/extensions/extension_event_router.cc
index d2f1179..f18ec66 100644
--- a/chrome/browser/extensions/extension_event_router.cc
+++ b/chrome/browser/extensions/extension_event_router.cc
@@ -224,10 +224,10 @@
 
   const Extension* extension = profile_->GetExtensionService()->
       GetExtensionById(extension_id, false);  // exclude disabled extensions
-  if (extension && extension->background_url().is_valid()) {
+  if (extension && extension->has_background_page()) {
     ExtensionProcessManager* pm = profile_->GetExtensionProcessManager();
     if (!pm->GetBackgroundHostForExtension(extension_id)) {
-      pm->CreateBackgroundHost(extension, extension->background_url());
+      pm->CreateBackgroundHost(extension, extension->GetBackgroundURL());
       return false;
     }
   }
diff --git a/chrome/browser/extensions/extension_process_manager.cc b/chrome/browser/extensions/extension_process_manager.cc
index 99f9753..6b91f33 100644
--- a/chrome/browser/extensions/extension_process_manager.cc
+++ b/chrome/browser/extensions/extension_process_manager.cc
@@ -69,8 +69,9 @@
   // 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());
+      extension->has_background_page()) {
+    manager->CreateBackgroundHost(extension, extension->GetBackgroundURL());
+  }
 }
 
 static void CreateBackgroundHostsForProfileStartup(
diff --git a/chrome/browser/extensions/extension_protocols.cc b/chrome/browser/extensions/extension_protocols.cc
index 9221c5c5..d0a03c5 100644
--- a/chrome/browser/extensions/extension_protocols.cc
+++ b/chrome/browser/extensions/extension_protocols.cc
@@ -1,4 +1,4 @@
-// Copyright (c) 2011 The Chromium Authors. All rights reserved.
+// Copyright (c) 2012 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.
 
@@ -12,6 +12,7 @@
 #include "base/message_loop.h"
 #include "base/path_service.h"
 #include "base/string_util.h"
+#include "base/stringprintf.h"
 #include "base/threading/thread_restrictions.h"
 #include "build/build_config.h"
 #include "chrome/browser/extensions/extension_info_map.h"
@@ -101,6 +102,42 @@
   net::HttpResponseInfo response_info_;
 };
 
+class GeneratedBackgroundPageJob : public net::URLRequestSimpleJob {
+ public:
+  GeneratedBackgroundPageJob(net::URLRequest* request,
+                             const scoped_refptr<const Extension> extension,
+                             const std::string& content_security_policy)
+      : net::URLRequestSimpleJob(request),
+        extension_(extension) {
+    response_info_.headers = BuildHttpHeaders(content_security_policy);
+  }
+
+  // Overridden from URLRequestSimpleJob:
+  virtual bool GetData(std::string* mime_type,
+                       std::string* charset,
+                       std::string* data) const OVERRIDE {
+    *mime_type = "text/html";
+    *charset = "utf-8";
+
+    *data = "<!DOCTYPE html>\n<body>\n";
+    for (size_t i = 0; i < extension_->background_scripts().size(); ++i) {
+      *data += "<script src=\"";
+      *data += extension_->background_scripts()[i];
+      *data += "\"></script>\n";
+    }
+
+    return true;
+  }
+
+  virtual void GetResponseInfo(net::HttpResponseInfo* info) {
+    *info = response_info_;
+  }
+
+ private:
+  scoped_refptr<const Extension> extension_;
+  net::HttpResponseInfo response_info_;
+};
+
 class URLRequestExtensionJob : public net::URLRequestFileJob {
  public:
   URLRequestExtensionJob(net::URLRequest* request,
@@ -221,6 +258,13 @@
   if (extension)
     content_security_policy = extension->content_security_policy();
 
+  std::string path = request->url().path();
+  if (path.size() > 1 &&
+      path.substr(1) == extension_filenames::kGeneratedBackgroundPageFilename) {
+    return new GeneratedBackgroundPageJob(
+        request, extension, content_security_policy);
+  }
+
   FilePath resources_path;
   if (PathService::Get(chrome::DIR_RESOURCES, &resources_path) &&
       directory_path.DirName() == resources_path) {
diff --git a/chrome/browser/extensions/extension_service.cc b/chrome/browser/extensions/extension_service.cc
index 655a80e..90920de2 100644
--- a/chrome/browser/extensions/extension_service.cc
+++ b/chrome/browser/extensions/extension_service.cc
@@ -2474,12 +2474,12 @@
 }
 
 bool ExtensionService::IsBackgroundPageReady(const Extension* extension) {
-  return (extension->background_url().is_empty() ||
+  return (!extension->has_background_page() ||
           extension_runtime_data_[extension->id()].background_page_ready);
 }
 
 void ExtensionService::SetBackgroundPageReady(const Extension* extension) {
-  DCHECK(!extension->background_url().is_empty());
+  DCHECK(extension->has_background_page());
   extension_runtime_data_[extension->id()].background_page_ready = true;
   content::NotificationService::current()->Notify(
       chrome::NOTIFICATION_EXTENSION_BACKGROUND_PAGE_READY,
diff --git a/chrome/browser/extensions/extension_service_unittest.cc b/chrome/browser/extensions/extension_service_unittest.cc
index 7494b0a..b582dfb7 100644
--- a/chrome/browser/extensions/extension_service_unittest.cc
+++ b/chrome/browser/extensions/extension_service_unittest.cc
@@ -1,4 +1,4 @@
-// Copyright (c) 2011 The Chromium Authors. All rights reserved.
+// Copyright (c) 2012 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.
 
@@ -1097,7 +1097,7 @@
   EXPECT_EQ(std::string("My extension 2"), loaded_[1]->name());
   EXPECT_EQ(std::string(""), loaded_[1]->description());
   EXPECT_EQ(loaded_[1]->GetResourceURL("background.html"),
-            loaded_[1]->background_url());
+            loaded_[1]->GetBackgroundURL());
   EXPECT_EQ(0u, loaded_[1]->content_scripts().size());
   // We don't parse the plugins section on Chrome OS.
 #if defined(OS_CHROMEOS)
diff --git a/chrome/browser/extensions/network_delay_listener.cc b/chrome/browser/extensions/network_delay_listener.cc
index c417e1c..11574e7 100644
--- a/chrome/browser/extensions/network_delay_listener.cc
+++ b/chrome/browser/extensions/network_delay_listener.cc
@@ -1,4 +1,4 @@
-// Copyright (c) 2011 The Chromium Authors. All rights reserved.
+// Copyright (c) 2012 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.
 
@@ -129,7 +129,7 @@
       // We only wait for background pages to load. If the extension has no
       // background page, ignore it.
       if (service->extension_prefs()->DelaysNetworkRequests(extension->id()) &&
-          !extension->background_url().is_empty()) {
+          extension->has_background_page()) {
         BrowserThread::PostTask(
             BrowserThread::IO, FROM_HERE,
             base::Bind(&NetworkDelayListener::OnExtensionPending,