summaryrefslogtreecommitdiffstats
path: root/chrome
diff options
context:
space:
mode:
authorcreis@chromium.org <creis@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2012-03-02 18:06:53 +0000
committercreis@chromium.org <creis@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2012-03-02 18:06:53 +0000
commit7b54ca0d914878f39e784bc4fd2046286817b319 (patch)
treeec2dd9a2d387b70105b676ccbff7588ce6b57db9 /chrome
parentc503402a1b3b3f0592e08281de6053f34147fd77 (diff)
downloadchromium_src-7b54ca0d914878f39e784bc4fd2046286817b319.zip
chromium_src-7b54ca0d914878f39e784bc4fd2046286817b319.tar.gz
chromium_src-7b54ca0d914878f39e784bc4fd2046286817b319.tar.bz2
Allow apps with background pages to request process-per-app-instance.
Requires setting background.allow_js_access to false in manifest. BUG=113444 TEST=Example hosted app has different processes in different tabs. Review URL: http://codereview.chromium.org/9508008 git-svn-id: svn://svn.chromium.org/chrome/trunk/src@124684 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'chrome')
-rw-r--r--chrome/browser/chrome_content_browser_client.cc34
-rw-r--r--chrome/browser/extensions/app_background_page_apitest.cc38
-rw-r--r--chrome/browser/extensions/app_process_apitest.cc184
-rw-r--r--chrome/common/extensions/extension.cc25
-rw-r--r--chrome/common/extensions/extension.h12
-rw-r--r--chrome/common/extensions/extension_constants.cc8
-rw-r--r--chrome/common/extensions/extension_constants.h5
-rw-r--r--chrome/common/extensions/extension_manifests_unittest.cc8
-rw-r--r--chrome/test/data/extensions/api_test/app_background_page/no_js/content_script.js18
-rw-r--r--chrome/test/data/extensions/api_test/app_background_page/no_js/manifest.json17
-rw-r--r--chrome/test/data/extensions/api_test/app_background_page/no_js/test.js61
-rw-r--r--chrome/test/data/extensions/api_test/app_process_background_instances/manifest.json22
-rw-r--r--chrome/test/data/extensions/api_test/app_process_background_instances/path1/empty.html1
-rw-r--r--chrome/test/data/extensions/api_test/app_process_background_instances/path2/empty.html1
-rw-r--r--chrome/test/data/extensions/manifest_tests/background_allow_no_js_access.json19
15 files changed, 354 insertions, 99 deletions
diff --git a/chrome/browser/chrome_content_browser_client.cc b/chrome/browser/chrome_content_browser_client.cc
index d86ad20..bc2bba12 100644
--- a/chrome/browser/chrome_content_browser_client.cc
+++ b/chrome/browser/chrome_content_browser_client.cc
@@ -454,15 +454,19 @@ bool ChromeContentBrowserClient::ShouldUseProcessPerSite(
return false;
// If the URL is part of a hosted app that does not have the background
- // permission, we want to give each instance its own process to improve
+ // permission, or that does not allow JavaScript access to the background
+ // page, we want to give each instance its own process to improve
// responsiveness.
- if (extension->GetType() == Extension::TYPE_HOSTED_APP &&
- !extension->HasAPIPermission(ExtensionAPIPermission::kBackground))
- return false;
+ if (extension->GetType() == Extension::TYPE_HOSTED_APP) {
+ if (!extension->HasAPIPermission(ExtensionAPIPermission::kBackground) ||
+ !extension->allow_background_js_access()) {
+ return false;
+ }
+ }
- // Hosted apps that have the background permission must use process per site,
- // since all instances can make synchronous calls to the background window.
- // Other extensions should use process per site as well.
+ // Hosted apps that have script access to their background page must use
+ // process per site, since all instances can make synchronous calls to the
+ // background window. Other extensions should use process per site as well.
return true;
}
@@ -1239,7 +1243,21 @@ bool ChromeContentBrowserClient::CanCreateWindow(
// the appropriate permission, fail the attempt.
if (container_type == WINDOW_CONTAINER_TYPE_BACKGROUND) {
ProfileIOData* io_data = ProfileIOData::FromResourceContext(context);
- return io_data->GetExtensionInfoMap()->SecurityOriginHasAPIPermission(
+ ExtensionInfoMap* map = io_data->GetExtensionInfoMap();
+
+ // If the opener is not allowed to script its background window, then return
+ // false so that the window.open call returns null. In this case, only
+ // the manifest is permitted to create a background window.
+ // Note: this use of GetExtensionOrAppByURL is safe but imperfect. It may
+ // return a recently installed Extension even if this CanCreateWindow call
+ // was made by an old copy of the page in a normal web process. That's ok,
+ // because the permission check below will still fail.
+ const Extension* extension = map->extensions().GetExtensionOrAppByURL(
+ ExtensionURLInfo(source_origin));
+ if (extension && !extension->allow_background_js_access())
+ return false;
+
+ return map->SecurityOriginHasAPIPermission(
source_origin, render_process_id, ExtensionAPIPermission::kBackground);
}
return true;
diff --git a/chrome/browser/extensions/app_background_page_apitest.cc b/chrome/browser/extensions/app_background_page_apitest.cc
index 9c9585b..32af291 100644
--- a/chrome/browser/extensions/app_background_page_apitest.cc
+++ b/chrome/browser/extensions/app_background_page_apitest.cc
@@ -143,6 +143,44 @@ IN_PROC_BROWSER_TEST_F(AppBackgroundPageApiTest, ManifestBackgroundPage) {
GetAppBackgroundContents(ASCIIToUTF16(extension->id())));
}
+IN_PROC_BROWSER_TEST_F(AppBackgroundPageApiTest, NoJsManifestBackgroundPage) {
+ host_resolver()->AddRule("a.com", "127.0.0.1");
+ ASSERT_TRUE(StartTestServer());
+
+ std::string app_manifest = base::StringPrintf(
+ "{"
+ " \"name\": \"App\","
+ " \"version\": \"0.1\","
+ " \"manifest_version\": 2,"
+ " \"app\": {"
+ " \"urls\": ["
+ " \"http://a.com/\""
+ " ],"
+ " \"launch\": {"
+ " \"web_url\": \"http://a.com:%d/\""
+ " }"
+ " },"
+ " \"permissions\": [\"background\"],"
+ " \"background\": {"
+ " \"page\": \"http://a.com:%d/bg.html\","
+ " \"allow_js_access\": false"
+ " }"
+ "}",
+ test_server()->host_port_pair().port(),
+ test_server()->host_port_pair().port());
+
+ FilePath app_dir;
+ ASSERT_TRUE(CreateApp(app_manifest, &app_dir));
+ ASSERT_TRUE(LoadExtension(app_dir));
+
+ // The background page should load, but window.open should return null.
+ const Extension* extension = GetSingleLoadedExtension();
+ ASSERT_TRUE(
+ BackgroundContentsServiceFactory::GetForProfile(browser()->profile())->
+ GetAppBackgroundContents(ASCIIToUTF16(extension->id())));
+ ASSERT_TRUE(RunExtensionTest("app_background_page/no_js")) << message_;
+}
+
IN_PROC_BROWSER_TEST_F(AppBackgroundPageApiTest, OpenTwoBackgroundPages) {
host_resolver()->AddRule("a.com", "127.0.0.1");
ASSERT_TRUE(StartTestServer());
diff --git a/chrome/browser/extensions/app_process_apitest.cc b/chrome/browser/extensions/app_process_apitest.cc
index 7f67045..7192af2 100644
--- a/chrome/browser/extensions/app_process_apitest.cc
+++ b/chrome/browser/extensions/app_process_apitest.cc
@@ -28,21 +28,6 @@
using content::NavigationController;
using content::WebContents;
-class AppApiTest : public ExtensionApiTest {
- protected:
- // Gets the base URL for files for a specific test, making sure that it uses
- // "localhost" as the hostname, since that is what the extent is declared
- // as in the test apps manifests.
- GURL GetTestBaseURL(std::string test_directory) {
- GURL::Replacements replace_host;
- std::string host_str("localhost"); // must stay in scope with replace_host
- replace_host.SetHostStr(host_str);
- GURL base_url = test_server()->GetURL(
- "files/extensions/api_test/" + test_directory + "/");
- return base_url.ReplaceComponents(replace_host);
- }
-};
-
// Simulates a page calling window.open on an URL, and waits for the navigation.
static void WindowOpenHelper(Browser* browser,
RenderViewHost* opener_host,
@@ -87,12 +72,97 @@ static void NavigateTabHelper(WebContents* contents, const GURL& url) {
EXPECT_EQ(url, contents->GetController().GetLastCommittedEntry()->GetURL());
}
+class AppApiTest : public ExtensionApiTest {
+ protected:
+ // Gets the base URL for files for a specific test, making sure that it uses
+ // "localhost" as the hostname, since that is what the extent is declared
+ // as in the test apps manifests.
+ GURL GetTestBaseURL(std::string test_directory) {
+ GURL::Replacements replace_host;
+ std::string host_str("localhost"); // must stay in scope with replace_host
+ replace_host.SetHostStr(host_str);
+ GURL base_url = test_server()->GetURL(
+ "files/extensions/api_test/" + test_directory + "/");
+ return base_url.ReplaceComponents(replace_host);
+ }
+
+ // Pass flags to make testing apps easier.
+ void SetUpCommandLine(CommandLine* command_line) {
+ ExtensionApiTest::SetUpCommandLine(command_line);
+ CommandLine::ForCurrentProcess()->AppendSwitch(
+ switches::kDisablePopupBlocking);
+ CommandLine::ForCurrentProcess()->AppendSwitch(
+ switches::kAllowHTTPBackgroundPage);
+ }
+
+ // Helper function to test that independent tabs of the named app are loaded
+ // into separate processes.
+ void TestAppInstancesHelper(std::string app_name) {
+ LOG(INFO) << "Start of test.";
+
+ extensions::ProcessMap* process_map =
+ browser()->profile()->GetExtensionService()->process_map();
+
+ host_resolver()->AddRule("*", "127.0.0.1");
+ ASSERT_TRUE(test_server()->Start());
+
+ ASSERT_TRUE(LoadExtension(
+ test_data_dir_.AppendASCII(app_name)));
+
+ // Open two tabs in the app, one outside it.
+ GURL base_url = GetTestBaseURL(app_name);
+
+ // Test both opening a URL in a new tab, and opening a tab and then
+ // navigating it. Either way, app tabs should be considered extension
+ // processes, but they have no elevated privileges and thus should not
+ // have WebUI bindings.
+ ui_test_utils::NavigateToURLWithDisposition(
+ browser(), base_url.Resolve("path1/empty.html"), NEW_FOREGROUND_TAB,
+ ui_test_utils::BROWSER_TEST_WAIT_FOR_NAVIGATION);
+ LOG(INFO) << "Nav 1.";
+ EXPECT_TRUE(process_map->Contains(
+ browser()->GetWebContentsAt(1)->GetRenderProcessHost()->GetID()));
+ EXPECT_FALSE(browser()->GetWebContentsAt(1)->GetWebUI());
+
+ ui_test_utils::WindowedNotificationObserver tab_added_observer(
+ content::NOTIFICATION_TAB_ADDED,
+ content::NotificationService::AllSources());
+ browser()->NewTab();
+ tab_added_observer.Wait();
+ LOG(INFO) << "New tab.";
+ ui_test_utils::NavigateToURL(browser(),
+ base_url.Resolve("path2/empty.html"));
+ LOG(INFO) << "Nav 2.";
+ EXPECT_TRUE(process_map->Contains(
+ browser()->GetWebContentsAt(2)->GetRenderProcessHost()->GetID()));
+ EXPECT_FALSE(browser()->GetWebContentsAt(2)->GetWebUI());
+
+ // We should have opened 2 new extension tabs. Including the original blank
+ // tab, we now have 3 tabs. The two app tabs should not be in the same
+ // process, since they do not have the background permission. (Thus, we
+ // want to separate them to improve responsiveness.)
+ ASSERT_EQ(3, browser()->tab_count());
+ RenderViewHost* host1 = browser()->GetWebContentsAt(1)->GetRenderViewHost();
+ RenderViewHost* host2 = browser()->GetWebContentsAt(2)->GetRenderViewHost();
+ EXPECT_NE(host1->process(), host2->process());
+
+ // Opening tabs with window.open should keep the page in the opener's
+ // process.
+ ASSERT_EQ(1u, BrowserList::GetBrowserCount(browser()->profile()));
+ WindowOpenHelper(browser(), host1,
+ base_url.Resolve("path1/empty.html"), true);
+ LOG(INFO) << "WindowOpenHelper 1.";
+ WindowOpenHelper(browser(), host2,
+ base_url.Resolve("path2/empty.html"), true);
+ LOG(INFO) << "End of test.";
+ }
+};
+
+// Tests that hosted apps with the background permission get a process-per-app
+// model, since all pages need to be able to script the background page.
IN_PROC_BROWSER_TEST_F(AppApiTest, AppProcess) {
LOG(INFO) << "Start of test.";
- CommandLine::ForCurrentProcess()->AppendSwitch(
- switches::kDisablePopupBlocking);
-
extensions::ProcessMap* process_map =
browser()->profile()->GetExtensionService()->process_map();
@@ -200,71 +270,19 @@ IN_PROC_BROWSER_TEST_F(AppApiTest, AppProcess) {
// Test that hosted apps without the background permission use a process per app
// instance model, such that separate instances are in separate processes.
IN_PROC_BROWSER_TEST_F(AppApiTest, AppProcessInstances) {
- LOG(INFO) << "Start of test.";
-
- CommandLine::ForCurrentProcess()->AppendSwitch(
- switches::kDisablePopupBlocking);
-
- extensions::ProcessMap* process_map =
- browser()->profile()->GetExtensionService()->process_map();
-
- host_resolver()->AddRule("*", "127.0.0.1");
- ASSERT_TRUE(test_server()->Start());
-
- ASSERT_TRUE(LoadExtension(
- test_data_dir_.AppendASCII("app_process_instances")));
-
- // Open two tabs in the app, one outside it.
- GURL base_url = GetTestBaseURL("app_process_instances");
-
- // Test both opening a URL in a new tab, and opening a tab and then navigating
- // it. Either way, app tabs should be considered extension processes, but
- // they have no elevated privileges and thus should not have WebUI bindings.
- ui_test_utils::NavigateToURLWithDisposition(
- browser(), base_url.Resolve("path1/empty.html"), NEW_FOREGROUND_TAB,
- ui_test_utils::BROWSER_TEST_WAIT_FOR_NAVIGATION);
- LOG(INFO) << "Nav 1.";
- EXPECT_TRUE(process_map->Contains(
- browser()->GetWebContentsAt(1)->GetRenderProcessHost()->GetID()));
- EXPECT_FALSE(browser()->GetWebContentsAt(1)->GetWebUI());
-
- ui_test_utils::WindowedNotificationObserver tab_added_observer(
- content::NOTIFICATION_TAB_ADDED,
- content::NotificationService::AllSources());
- browser()->NewTab();
- tab_added_observer.Wait();
- LOG(INFO) << "New tab.";
- ui_test_utils::NavigateToURL(browser(), base_url.Resolve("path2/empty.html"));
- LOG(INFO) << "Nav 2.";
- EXPECT_TRUE(process_map->Contains(
- browser()->GetWebContentsAt(2)->GetRenderProcessHost()->GetID()));
- EXPECT_FALSE(browser()->GetWebContentsAt(2)->GetWebUI());
-
- // We should have opened 2 new extension tabs. Including the original blank
- // tab, we now have 3 tabs. The two app tabs should not be in the same
- // process, since they do not have the background permission. (Thus, we want
- // to separate them to improve responsiveness.)
- ASSERT_EQ(3, browser()->tab_count());
- RenderViewHost* host1 = browser()->GetWebContentsAt(1)->GetRenderViewHost();
- RenderViewHost* host2 = browser()->GetWebContentsAt(2)->GetRenderViewHost();
- EXPECT_NE(host1->process(), host2->process());
+ TestAppInstancesHelper("app_process_instances");
+}
- // Opening tabs with window.open should keep the page in the opener's process.
- ASSERT_EQ(1u, BrowserList::GetBrowserCount(browser()->profile()));
- WindowOpenHelper(browser(), host1,
- base_url.Resolve("path1/empty.html"), true);
- LOG(INFO) << "WindowOpenHelper 1.";
- WindowOpenHelper(browser(), host2,
- base_url.Resolve("path2/empty.html"), true);
- LOG(INFO) << "End of test.";
+// Test that hosted apps with the background permission but that set
+// allow_js_access to false also use a process per app instance model.
+// Separate instances should be in separate processes.
+IN_PROC_BROWSER_TEST_F(AppApiTest, AppProcessBackgroundInstances) {
+ TestAppInstancesHelper("app_process_background_instances");
}
// Tests that bookmark apps do not use the app process model and are treated
// like normal web pages instead. http://crbug.com/104636.
IN_PROC_BROWSER_TEST_F(AppApiTest, BookmarkAppGetsNormalProcess) {
- CommandLine::ForCurrentProcess()->AppendSwitch(
- switches::kDisablePopupBlocking);
-
ExtensionService* service = browser()->profile()->GetExtensionService();
extensions::ProcessMap* process_map = service->process_map();
@@ -345,9 +363,6 @@ IN_PROC_BROWSER_TEST_F(AppApiTest, BookmarkAppGetsNormalProcess) {
#define MAYBE_AppProcessRedirectBack AppProcessRedirectBack
#endif
IN_PROC_BROWSER_TEST_F(AppApiTest, MAYBE_AppProcessRedirectBack) {
- CommandLine::ForCurrentProcess()->AppendSwitch(
- switches::kDisablePopupBlocking);
-
host_resolver()->AddRule("*", "127.0.0.1");
ASSERT_TRUE(test_server()->Start());
@@ -388,9 +403,6 @@ IN_PROC_BROWSER_TEST_F(AppApiTest, MAYBE_AppProcessRedirectBack) {
// Ensure that reloading a URL after installing or uninstalling it as an app
// correctly swaps the process. (http://crbug.com/80621)
IN_PROC_BROWSER_TEST_F(AppApiTest, ReloadIntoAppProcess) {
- CommandLine::ForCurrentProcess()->AppendSwitch(
- switches::kDisablePopupBlocking);
-
extensions::ProcessMap* process_map =
browser()->profile()->GetExtensionService()->process_map();
@@ -478,9 +490,6 @@ IN_PROC_BROWSER_TEST_F(AppApiTest, ReloadIntoAppProcess) {
// empty.html) results in the new window being in an app process. See
// http://crbug.com/89272 for more details.
IN_PROC_BROWSER_TEST_F(AppApiTest, OpenAppFromIframe) {
- CommandLine::ForCurrentProcess()->AppendSwitch(
- switches::kDisablePopupBlocking);
-
extensions::ProcessMap* process_map =
browser()->profile()->GetExtensionService()->process_map();
@@ -576,9 +585,6 @@ IN_PROC_BROWSER_TEST_F(AppApiTest, OpenAppFromExtension) {
// missing special permissions and should be scriptable from the iframe.
// See http://crbug.com/92669 for more details.
IN_PROC_BROWSER_TEST_F(AppApiTest, OpenWebPopupFromWebIframe) {
- CommandLine::ForCurrentProcess()->AppendSwitch(
- switches::kDisablePopupBlocking);
-
extensions::ProcessMap* process_map =
browser()->profile()->GetExtensionService()->process_map();
diff --git a/chrome/common/extensions/extension.cc b/chrome/common/extensions/extension.cc
index dd9fb92..2c1cfc5 100644
--- a/chrome/common/extensions/extension.cc
+++ b/chrome/common/extensions/extension.cc
@@ -1515,6 +1515,27 @@ bool Extension::LoadBackgroundPersistent(
return true;
}
+bool Extension::LoadBackgroundAllowJsAccess(
+ const ExtensionAPIPermissionSet& api_permissions,
+ string16* error) {
+ Value* allow_js_access = NULL;
+ if (!manifest_->Get(keys::kBackgroundAllowJsAccess, &allow_js_access))
+ return true;
+
+ if (!allow_js_access->IsType(Value::TYPE_BOOLEAN) ||
+ !allow_js_access->GetAsBoolean(&allow_background_js_access_)) {
+ *error = ASCIIToUTF16(errors::kInvalidBackgroundAllowJsAccess);
+ return false;
+ }
+
+ if (!has_background_page()) {
+ *error = ASCIIToUTF16(errors::kInvalidBackgroundAllowJsAccessNoPage);
+ return false;
+ }
+
+ return true;
+}
+
// static
bool Extension::IsTrustedId(const std::string& id) {
// See http://b/4946060 for more details.
@@ -1528,6 +1549,7 @@ Extension::Extension(const FilePath& path,
offline_enabled_(false),
converted_from_user_script_(false),
background_page_persists_(true),
+ allow_background_js_access_(true),
manifest_(manifest.release()),
is_storage_isolated_(false),
launch_container_(extension_misc::LAUNCH_TAB),
@@ -2325,6 +2347,9 @@ bool Extension::InitFromValue(int flags, string16* error) {
if (!LoadBackgroundPersistent(api_permissions, error))
return false;
+ if (!LoadBackgroundAllowJsAccess(api_permissions, error))
+ return false;
+
if (manifest_->HasKey(keys::kDefaultLocale)) {
if (!manifest_->GetString(keys::kDefaultLocale, &default_locale_) ||
!l10n_util::IsValidLocaleSyntax(default_locale_)) {
diff --git a/chrome/common/extensions/extension.h b/chrome/common/extensions/extension.h
index 0f789eb..9558d21 100644
--- a/chrome/common/extensions/extension.h
+++ b/chrome/common/extensions/extension.h
@@ -557,6 +557,9 @@ class Extension : public base::RefCountedThreadSafe<Extension> {
bool has_background_page() const {
return background_url_.is_valid() || !background_scripts_.empty();
}
+ bool allow_background_js_access() const {
+ return allow_background_js_access_;
+ }
const std::vector<std::string>& background_scripts() const {
return background_scripts_;
}
@@ -704,6 +707,9 @@ class Extension : public base::RefCountedThreadSafe<Extension> {
bool LoadBackgroundPersistent(
const ExtensionAPIPermissionSet& api_permissions,
string16* error);
+ bool LoadBackgroundAllowJsAccess(
+ const ExtensionAPIPermissionSet& api_permissions,
+ string16* error);
// Helper method that loads a UserScript object from a
// dictionary in the content_script list of the manifest.
@@ -857,6 +863,12 @@ class Extension : public base::RefCountedThreadSafe<Extension> {
// load on-demand (when it needs to handle an event). Defaults to true.
bool background_page_persists_;
+ // True if the background page can be scripted by pages of the app or
+ // extension, in which case all such pages must run in the same process.
+ // False if such pages are not permitted to script the background page,
+ // allowing them to run in different processes.
+ bool allow_background_js_access_;
+
// Optional URL to a page for setting options/preferences.
GURL options_url_;
diff --git a/chrome/common/extensions/extension_constants.cc b/chrome/common/extensions/extension_constants.cc
index c50c2e5..868dc3c 100644
--- a/chrome/common/extensions/extension_constants.cc
+++ b/chrome/common/extensions/extension_constants.cc
@@ -16,10 +16,11 @@ const char kAllFrames[] = "all_frames";
const char kAltKey[] = "altKey";
const char kApp[] = "app";
const char kBackground[] = "background";
+const char kBackgroundAllowJsAccess[] = "background.allow_js_access";
const char kBackgroundPage[] = "background.page";
const char kBackgroundPageLegacy[] = "background_page";
-const char kBackgroundScripts[] = "background.scripts";
const char kBackgroundPersistent[] = "background.persistent";
+const char kBackgroundScripts[] = "background.scripts";
const char kBrowserAction[] = "browser_action";
const char kChromeURLOverrides[] = "chrome_url_overrides";
const char kCommands[] = "commands";
@@ -179,6 +180,11 @@ const char kInvalidAllFrames[] =
"Invalid value for 'content_scripts[*].all_frames'.";
const char kInvalidBackground[] =
"Invalid value for 'background_page'.";
+const char kInvalidBackgroundAllowJsAccess[] =
+ "Invalid value for 'background.allow_js_access'.";
+const char kInvalidBackgroundAllowJsAccessNoPage[] =
+ "Must specify one of background.page or background.scripts to use"
+ " background.allow_js_access.";
const char kInvalidBackgroundCombination[] =
"The background.page and background.scripts properties cannot be used at "
"the same time.";
diff --git a/chrome/common/extensions/extension_constants.h b/chrome/common/extensions/extension_constants.h
index ca87caa..4ceb45e 100644
--- a/chrome/common/extensions/extension_constants.h
+++ b/chrome/common/extensions/extension_constants.h
@@ -17,10 +17,11 @@ namespace extension_manifest_keys {
extern const char kAltKey[];
extern const char kApp[];
extern const char kBackground[];
+ extern const char kBackgroundAllowJsAccess[];
extern const char kBackgroundPage[];
extern const char kBackgroundPageLegacy[];
- extern const char kBackgroundScripts[];
extern const char kBackgroundPersistent[];
+ extern const char kBackgroundScripts[];
extern const char kBrowserAction[];
extern const char kBrowseURLs[];
extern const char kChromeURLOverrides[];
@@ -162,6 +163,8 @@ namespace extension_manifest_errors {
extern const char kFeatureNotAllowed[];
extern const char kInvalidAllFrames[];
extern const char kInvalidBackground[];
+ extern const char kInvalidBackgroundAllowJsAccess[];
+ extern const char kInvalidBackgroundAllowJsAccessNoPage[];
extern const char kInvalidBackgroundCombination[];
extern const char kInvalidBackgroundScript[];
extern const char kInvalidBackgroundScripts[];
diff --git a/chrome/common/extensions/extension_manifests_unittest.cc b/chrome/common/extensions/extension_manifests_unittest.cc
index e1d108f..23e3847 100644
--- a/chrome/common/extensions/extension_manifests_unittest.cc
+++ b/chrome/common/extensions/extension_manifests_unittest.cc
@@ -1037,6 +1037,7 @@ TEST_F(ExtensionManifestTest, BackgroundPage) {
LoadAndExpectSuccess("background_page.json"));
ASSERT_TRUE(extension);
EXPECT_EQ("/foo.html", extension->GetBackgroundURL().path());
+ EXPECT_TRUE(extension->allow_background_js_access());
std::string error;
scoped_ptr<DictionaryValue> manifest(
@@ -1077,6 +1078,13 @@ TEST_F(ExtensionManifestTest, BackgroundScripts) {
errors::kInvalidBackgroundCombination);
}
+TEST_F(ExtensionManifestTest, BackgroundAllowNoJsAccess) {
+ scoped_refptr<Extension> extension;
+ extension = LoadAndExpectSuccess("background_allow_no_js_access.json");
+ ASSERT_TRUE(extension);
+ EXPECT_FALSE(extension->allow_background_js_access());
+}
+
TEST_F(ExtensionManifestTest, PageActionManifestVersion2) {
scoped_refptr<Extension> extension(
LoadAndExpectSuccess("page_action_manifest_version_2.json"));
diff --git a/chrome/test/data/extensions/api_test/app_background_page/no_js/content_script.js b/chrome/test/data/extensions/api_test/app_background_page/no_js/content_script.js
new file mode 100644
index 0000000..ba714c6
--- /dev/null
+++ b/chrome/test/data/extensions/api_test/app_background_page/no_js/content_script.js
@@ -0,0 +1,18 @@
+// 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.
+
+var scriptMessageEvent = document.createEvent("Event");
+scriptMessageEvent.initEvent('scriptMessage', true, true);
+
+var pageToScriptTunnel = document.getElementById("pageToScriptTunnel");
+pageToScriptTunnel.addEventListener("scriptMessage", function() {
+ var data = JSON.parse(pageToScriptTunnel.innerText);
+ chrome.extension.sendRequest(data);
+});
+
+chrome.extension.onRequest.addListener(function(request) {
+ var scriptToPageTunnel = document.getElementById("scriptToPageTunnel");
+ scriptToPageTunnel.innerText = JSON.stringify(request);
+ scriptToPageTunnel.dispatchEvent(scriptMessageEvent);
+});
diff --git a/chrome/test/data/extensions/api_test/app_background_page/no_js/manifest.json b/chrome/test/data/extensions/api_test/app_background_page/no_js/manifest.json
new file mode 100644
index 0000000..25fa056
--- /dev/null
+++ b/chrome/test/data/extensions/api_test/app_background_page/no_js/manifest.json
@@ -0,0 +1,17 @@
+{
+ "name": "app_background_page/no_js",
+ "version": "0.1",
+ "manifest_version": 2,
+ "description": "Tests that window.open returns null without JS access to background page.",
+ "background": {
+ "scripts": ["test.js"]
+ },
+ "permissions": ["tabs", "http://a.com/*"],
+ "content_scripts": [
+ {
+ "matches": ["http://a.com/*"],
+ "js": ["content_script.js"],
+ "run_at": "document_end"
+ }
+ ]
+}
diff --git a/chrome/test/data/extensions/api_test/app_background_page/no_js/test.js b/chrome/test/data/extensions/api_test/app_background_page/no_js/test.js
new file mode 100644
index 0000000..ef9332b
--- /dev/null
+++ b/chrome/test/data/extensions/api_test/app_background_page/no_js/test.js
@@ -0,0 +1,61 @@
+// 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.
+
+// This test checks that setting allow_js_access to false is effective:
+// - A background page is opened via the manifest (which is verified by the
+// AppBackgroundPageApiTest.NoJsManifestBackgroundPage code).
+// - A live (web-extent) web page is loaded (a.html), which tries to opens a
+// background page. This fails because allow_js_access is false.
+
+var pagePrefix =
+ 'http://a.com:PORT/files/extensions/api_test/app_background_page/common';
+
+// Dispatch "tunneled" functions from the live web pages to this testing page.
+chrome.extension.onRequest.addListener(function(request) {
+ window[request.name](request.args);
+});
+
+// At no point should a window be created that contains the background page
+// (bg.html).
+chrome.tabs.onUpdated.addListener(function(tabId, changeInfo, tab) {
+ if (tab.url.match("bg\.html$")) {
+ chrome.test.notifyFail("popup opened instead of background page");
+ }
+});
+
+// Start the test by opening the first page in the app. This will try to create
+// a background page whose name is "bg", but it should not replace the
+// background page created by the manifest (named "background").
+window.onload = function() {
+ // We wait for window.onload before getting the test config. If the
+ // config is requested before onload, then sometimes onload has already
+ // fired by the time chrome.test.getConfig()'s callback runs.
+ chrome.test.getConfig(function(config) {
+ var aUrl =
+ pagePrefix.replace(/PORT/, config.testServer.port) + '/a.html';
+ chrome.tabs.create({ 'url': aUrl });
+ });
+}
+
+// Background page opened.
+function onBackgroundPageLoaded() {
+ // The window.open call in a.html should not succeed.
+ chrome.test.notifyFail("Background page unexpectedly loaded.");
+}
+
+function onBackgroundPagePermissionDenied() {
+ // a.html will call this if it receives null from window.open, as we expect.
+ chrome.test.notifyPass();
+}
+
+// A second background page opened.
+function onBackgroundPageResponded() {
+ chrome.test.notifyFail("onBackgroundPageResponded called unexpectedly");
+}
+
+// The background counter check found an unexpected value (most likely caused
+// by an unwanted navigation).
+function onCounterError() {
+ chrome.test.notifyFail("checkCounter found an unexpected value");
+}
diff --git a/chrome/test/data/extensions/api_test/app_process_background_instances/manifest.json b/chrome/test/data/extensions/api_test/app_process_background_instances/manifest.json
new file mode 100644
index 0000000..9ee5d40
--- /dev/null
+++ b/chrome/test/data/extensions/api_test/app_process_background_instances/manifest.json
@@ -0,0 +1,22 @@
+{
+ "name": "app_process_background_instances",
+ "version": "0.1",
+ "manifest_version": 2,
+ "description": "Tests that apps with no background JS are not consolidated.",
+ "app": {
+ "urls": [
+ "http://localhost/files/extensions/api_test/app_process_background_instances/path1",
+ "http://localhost/files/extensions/api_test/app_process_background_instances/path2"
+ ],
+ "launch": {
+ "web_url": "http://localhost/files/extensions/api_test/app_process_background_instances/path1/empty.html"
+ }
+ },
+ "permissions": [
+ "background"
+ ],
+ "background": {
+ "page": "http://localhost/files/extensions/api_test/app_process_background_instances/path1/empty.html",
+ "allow_js_access": false
+ }
+}
diff --git a/chrome/test/data/extensions/api_test/app_process_background_instances/path1/empty.html b/chrome/test/data/extensions/api_test/app_process_background_instances/path1/empty.html
new file mode 100644
index 0000000..d3cdf0a
--- /dev/null
+++ b/chrome/test/data/extensions/api_test/app_process_background_instances/path1/empty.html
@@ -0,0 +1 @@
+<title>Unmodified</title>
diff --git a/chrome/test/data/extensions/api_test/app_process_background_instances/path2/empty.html b/chrome/test/data/extensions/api_test/app_process_background_instances/path2/empty.html
new file mode 100644
index 0000000..d3cdf0a
--- /dev/null
+++ b/chrome/test/data/extensions/api_test/app_process_background_instances/path2/empty.html
@@ -0,0 +1 @@
+<title>Unmodified</title>
diff --git a/chrome/test/data/extensions/manifest_tests/background_allow_no_js_access.json b/chrome/test/data/extensions/manifest_tests/background_allow_no_js_access.json
new file mode 100644
index 0000000..340b090
--- /dev/null
+++ b/chrome/test/data/extensions/manifest_tests/background_allow_no_js_access.json
@@ -0,0 +1,19 @@
+{
+ "name": "test",
+ "version": "1",
+ "app": {
+ "urls": [
+ "https://www.google.com/mail/"
+ ],
+ "launch": {
+ "web_url": "https://www.google.com/mail/"
+ }
+ },
+ "permissions": [
+ "background"
+ ],
+ "background": {
+ "page": "https://www.google.com/mail/foo.html",
+ "allow_js_access": false
+ }
+}