summaryrefslogtreecommitdiffstats
path: root/chrome
diff options
context:
space:
mode:
authorsadrul@chromium.org <sadrul@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2011-04-13 18:00:25 +0000
committersadrul@chromium.org <sadrul@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2011-04-13 18:00:25 +0000
commit7492275526897a62fe04400aa536dde5c51278d7 (patch)
tree187d8e83d349b975adf00c1f31d1c6e2630f4e7a /chrome
parent0fc357452454dbcf342314dfa828bdcb2dd560be (diff)
downloadchromium_src-7492275526897a62fe04400aa536dde5c51278d7.zip
chromium_src-7492275526897a62fe04400aa536dde5c51278d7.tar.gz
chromium_src-7492275526897a62fe04400aa536dde5c51278d7.tar.bz2
apps: Add 'background_page' support for hosted apps.
A hosted app can have a 'background_page' if it has 'background' 'permission'. The 'background_page' will launch immediately after the app is installed. BUG=77718 TEST=AppBackgroundPageApiTest.ManifestBackgroundPage, and existing tests. Review URL: http://codereview.chromium.org/6708100 git-svn-id: svn://svn.chromium.org/chrome/trunk/src@81438 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'chrome')
-rw-r--r--chrome/browser/background_contents_service.cc95
-rw-r--r--chrome/browser/background_contents_service.h10
-rw-r--r--chrome/browser/background_page_tracker.cc3
-rw-r--r--chrome/browser/extensions/app_background_page_apitest.cc39
-rw-r--r--chrome/browser/extensions/extension_process_manager.cc5
-rw-r--r--chrome/browser/renderer_host/browser_render_process_host.cc1
-rw-r--r--chrome/browser/tab_contents/render_view_host_delegate_helper.cc5
-rw-r--r--chrome/browser/task_manager/task_manager_resource_providers.cc3
-rw-r--r--chrome/common/chrome_switches.cc3
-rw-r--r--chrome/common/chrome_switches.h1
-rw-r--r--chrome/common/extensions/extension.cc52
-rw-r--r--chrome/common/extensions/extension_constants.cc8
-rw-r--r--chrome/common/extensions/extension_constants.h2
-rw-r--r--chrome/common/extensions/extension_file_util.cc6
-rw-r--r--chrome/common/extensions/extension_manifests_unittest.cc6
15 files changed, 209 insertions, 30 deletions
diff --git a/chrome/browser/background_contents_service.cc b/chrome/browser/background_contents_service.cc
index cacca3c..e5f2906 100644
--- a/chrome/browser/background_contents_service.cc
+++ b/chrome/browser/background_contents_service.cc
@@ -49,7 +49,7 @@ class CrashNotificationDelegate : public NotificationDelegate {
public:
CrashNotificationDelegate(Profile* profile, const Extension* extension)
: profile_(profile),
- is_app_(extension->is_app()),
+ is_hosted_app_(extension->is_hosted_app()),
extension_id_(extension->id()) {
}
@@ -63,7 +63,7 @@ class CrashNotificationDelegate : public NotificationDelegate {
void Close(bool by_user) {}
void Click() {
- if (is_app_) {
+ if (is_hosted_app_) {
profile_->GetBackgroundContentsService()->
LoadBackgroundContentsForExtension(profile_, extension_id_);
} else {
@@ -81,7 +81,7 @@ class CrashNotificationDelegate : public NotificationDelegate {
private:
Profile* profile_;
- bool is_app_;
+ bool is_hosted_app_;
std::string extension_id_;
DISALLOW_COPY_AND_ASSIGN(CrashNotificationDelegate);
@@ -89,7 +89,7 @@ class CrashNotificationDelegate : public NotificationDelegate {
void ShowBalloon(const Extension* extension, Profile* profile) {
string16 message = l10n_util::GetStringFUTF16(
- extension->is_app() ? IDS_BACKGROUND_CRASHED_APP_BALLOON_MESSAGE :
+ extension->is_hosted_app() ? IDS_BACKGROUND_CRASHED_APP_BALLOON_MESSAGE :
IDS_BACKGROUND_CRASHED_EXTENSION_BALLOON_MESSAGE,
UTF8ToUTF16(extension->name()));
string16 content_url = DesktopNotificationService::CreateDataUrl(
@@ -171,6 +171,11 @@ void BackgroundContentsService::StartObserving(Profile* profile) {
registrar_.Add(this, NotificationType::BACKGROUND_CONTENTS_NAVIGATED,
Source<Profile>(profile));
+ // Listen for new extension installs so that we can load any associated
+ // background page.
+ registrar_.Add(this, NotificationType::EXTENSION_LOADED,
+ Source<Profile>(profile));
+
// Track when the extensions crash so that the user can be notified
// about it, and the crashed contents can be restarted.
registrar_.Add(this, NotificationType::EXTENSION_PROCESS_TERMINATED,
@@ -198,6 +203,7 @@ void BackgroundContentsService::Observe(NotificationType type,
const NotificationDetails& details) {
switch (type.value) {
case NotificationType::EXTENSIONS_READY:
+ LoadBackgroundContentsFromManifests(Source<Profile>(source).ptr());
LoadBackgroundContentsFromPrefs(Source<Profile>(source).ptr());
break;
case NotificationType::BACKGROUND_CONTENTS_DELETED:
@@ -207,11 +213,46 @@ void BackgroundContentsService::Observe(NotificationType type,
DCHECK(IsTracked(Details<BackgroundContents>(details).ptr()));
UnregisterBackgroundContents(Details<BackgroundContents>(details).ptr());
break;
- case NotificationType::BACKGROUND_CONTENTS_NAVIGATED:
+ case NotificationType::BACKGROUND_CONTENTS_NAVIGATED: {
DCHECK(IsTracked(Details<BackgroundContents>(details).ptr()));
+
+ // Do not register in the pref if the extension has a manifest-specified
+ // background page.
+ BackgroundContents* bgcontents =
+ Details<BackgroundContents>(details).ptr();
+ Profile* profile = Source<Profile>(source).ptr();
+ const string16& appid = GetParentApplicationId(bgcontents);
+ ExtensionService* extension_service = profile->GetExtensionService();
+ // extension_service can be NULL when running tests.
+ if (extension_service) {
+ const Extension* extension =
+ extension_service->GetExtensionById(UTF16ToUTF8(appid), false);
+ if (extension && extension->background_url().is_valid())
+ break;
+ }
RegisterBackgroundContents(Details<BackgroundContents>(details).ptr());
break;
-
+ }
+ case NotificationType::EXTENSION_LOADED: {
+ const Extension* extension = Details<const Extension>(details).ptr();
+ Profile* profile = Source<Profile>(source).ptr();
+ if (extension->is_hosted_app() &&
+ extension->background_url().is_valid()) {
+ // 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()));
+
+ ExtensionService* service = profile->GetExtensionService();
+ if (service && service->is_ready()) {
+ // 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(),
+ ASCIIToUTF16("background"), UTF8ToUTF16(extension->id()));
+ }
+ }
+ break;
+ }
case NotificationType::EXTENSION_PROCESS_TERMINATED:
case NotificationType::BACKGROUND_CONTENTS_TERMINATED: {
Profile* profile = Source<Profile>(source).ptr();
@@ -247,9 +288,17 @@ void BackgroundContentsService::Observe(NotificationType type,
ASCIIToUTF16(
Details<UnloadedExtensionInfo>(details)->extension->id()));
break;
- case UnloadedExtensionInfo::UPDATE:
- // Leave BackgroundContents in place
+ case UnloadedExtensionInfo::UPDATE: {
+ // If there is a manifest specified background page, then shut it down
+ // here, since if the updated extension still has the background page,
+ // then it will be loaded from LOADED callback. Otherwise, leave
+ // BackgroundContents in place.
+ const Extension* extension =
+ Details<UnloadedExtensionInfo>(details)->extension;
+ if (extension->background_url().is_valid())
+ ShutdownAssociatedBackgroundContents(ASCIIToUTF16(extension->id()));
break;
+ }
default:
NOTREACHED();
ShutdownAssociatedBackgroundContents(
@@ -312,6 +361,19 @@ void BackgroundContentsService::LoadBackgroundContentsFromPrefs(
void BackgroundContentsService::LoadBackgroundContentsForExtension(
Profile* profile,
const std::string& extension_id) {
+ // First look if the manifest specifies a background page.
+ const Extension* extension =
+ profile->GetExtensionService()->GetExtensionById(extension_id, false);
+ DCHECK(!extension || extension->is_hosted_app());
+ if (extension && extension->background_url().is_valid()) {
+ LoadBackgroundContents(profile,
+ extension->background_url(),
+ ASCIIToUTF16("background"),
+ UTF8ToUTF16(extension->id()));
+ return;
+ }
+
+ // Now look in the prefs.
if (!prefs_)
return;
const DictionaryValue* contents =
@@ -342,6 +404,23 @@ void BackgroundContentsService::LoadBackgroundContentsFromDictionary(
UTF8ToUTF16(extension_id));
}
+void BackgroundContentsService::LoadBackgroundContentsFromManifests(
+ Profile* profile) {
+ const ExtensionList* extensions =
+ profile->GetExtensionService()->extensions();
+ ExtensionList::const_iterator iter = extensions->begin();
+ for (; iter != extensions->end(); ++iter) {
+ const Extension* extension = *iter;
+ if (extension->is_hosted_app() &&
+ extension->background_url().is_valid()) {
+ LoadBackgroundContents(profile,
+ extension->background_url(),
+ ASCIIToUTF16("background"),
+ UTF8ToUTF16(extension->id()));
+ }
+ }
+}
+
void BackgroundContentsService::LoadBackgroundContents(
Profile* profile,
const GURL& url,
diff --git a/chrome/browser/background_contents_service.h b/chrome/browser/background_contents_service.h
index 1b388c7..e6bf19b 100644
--- a/chrome/browser/background_contents_service.h
+++ b/chrome/browser/background_contents_service.h
@@ -78,8 +78,10 @@ class BackgroundContentsService : private NotificationObserver,
const string16& frame_name,
const string16& application_id);
- // Load the registered BackgroundContents for the specified extension. This
- // is typically used to reload a crashed background page.
+ // Load the manifest-specified background page for the specified hosted app.
+ // If the manifest doesn't specify one, then load the BackgroundContents
+ // registered in the pref. This is typically used to reload a crashed
+ // background page.
void LoadBackgroundContentsForExtension(Profile* profile,
const std::string& extension_id);
@@ -114,6 +116,10 @@ class BackgroundContentsService : private NotificationObserver,
const std::string& extension_id,
const DictionaryValue* contents);
+ // Load the manifest-specified BackgroundContents for all apps for the
+ // profile.
+ void LoadBackgroundContentsFromManifests(Profile* profile);
+
// Creates a single BackgroundContents associated with the specified |appid|,
// creates an associated RenderView with the name specified by |frame_name|,
// and navigates to the passed |url|.
diff --git a/chrome/browser/background_page_tracker.cc b/chrome/browser/background_page_tracker.cc
index 5c52ea0..5a41c8d 100644
--- a/chrome/browser/background_page_tracker.cc
+++ b/chrome/browser/background_page_tracker.cc
@@ -160,7 +160,8 @@ void BackgroundPageTracker::Observe(NotificationType type,
}
case NotificationType::EXTENSION_LOADED: {
const Extension* extension = Details<const Extension>(details).ptr();
- if (extension->background_url().is_valid())
+ if (!extension->is_hosted_app() &&
+ extension->background_url().is_valid())
OnBackgroundPageLoaded(extension->id());
break;
}
diff --git a/chrome/browser/extensions/app_background_page_apitest.cc b/chrome/browser/extensions/app_background_page_apitest.cc
index f1f98bf..b13dc77 100644
--- a/chrome/browser/extensions/app_background_page_apitest.cc
+++ b/chrome/browser/extensions/app_background_page_apitest.cc
@@ -1,10 +1,15 @@
-// Copyright (c) 2010 The Chromium Authors. All rights reserved.
+// Copyright (c) 2011 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 "base/string_util.h"
+#include "chrome/browser/background_contents_service.h"
#include "chrome/browser/extensions/extension_apitest.h"
+#include "chrome/browser/extensions/extension_service.h"
+#include "chrome/browser/profiles/profile.h"
+#include "chrome/browser/ui/browser.h"
#include "chrome/common/chrome_switches.h"
+#include "chrome/common/extensions/extension.h"
#include "chrome/test/ui_test_utils.h"
#include "net/base/mock_host_resolver.h"
@@ -13,6 +18,7 @@ class AppBackgroundPageApiTest : public ExtensionApiTest {
void SetUpCommandLine(CommandLine* command_line) {
ExtensionApiTest::SetUpCommandLine(command_line);
command_line->AppendSwitch(switches::kDisablePopupBlocking);
+ command_line->AppendSwitch(switches::kAllowHTTPBackgroundPage);
}
bool CreateApp(const std::string& app_manifest,
@@ -90,3 +96,34 @@ IN_PROC_BROWSER_TEST_F(AppBackgroundPageApiTest, DISABLED_LacksPermission) {
ASSERT_TRUE(RunExtensionTest("app_background_page/lacks_permission"))
<< message_;
}
+
+IN_PROC_BROWSER_TEST_F(AppBackgroundPageApiTest, ManifestBackgroundPage) {
+ host_resolver()->AddRule("a.com", "127.0.0.1");
+ ASSERT_TRUE(StartTestServer());
+
+ std::string app_manifest = StringPrintf(
+ "{"
+ " \"name\": \"App\","
+ " \"version\": \"0.1\","
+ " \"app\": {"
+ " \"urls\": ["
+ " \"http://a.com/\""
+ " ],"
+ " \"launch\": {"
+ " \"web_url\": \"http://a.com:%d/\""
+ " }"
+ " },"
+ " \"permissions\": [\"background\"],"
+ " \"background_page\": \"http://a.com:%d/test.html\""
+ "}",
+ 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));
+
+ const Extension* extension = GetSingleLoadedExtension();
+ ASSERT_TRUE(browser()->profile()->GetBackgroundContentsService()->
+ GetAppBackgroundContents(ASCIIToUTF16(extension->id())));
+}
diff --git a/chrome/browser/extensions/extension_process_manager.cc b/chrome/browser/extensions/extension_process_manager.cc
index b0dc81e..5eccb76 100644
--- a/chrome/browser/extensions/extension_process_manager.cc
+++ b/chrome/browser/extensions/extension_process_manager.cc
@@ -168,6 +168,11 @@ ExtensionHost* ExtensionProcessManager::CreateInfobar(const GURL& url,
void ExtensionProcessManager::CreateBackgroundHost(
const Extension* extension, const GURL& url) {
+ // Hosted apps are taken care of from BackgroundContentsService. Ignore them
+ // here.
+ if (extension->is_hosted_app())
+ return;
+
// Don't create multiple background hosts for an extension.
if (GetBackgroundHostForExtension(extension))
return;
diff --git a/chrome/browser/renderer_host/browser_render_process_host.cc b/chrome/browser/renderer_host/browser_render_process_host.cc
index 58097a2..8c2664e 100644
--- a/chrome/browser/renderer_host/browser_render_process_host.cc
+++ b/chrome/browser/renderer_host/browser_render_process_host.cc
@@ -681,6 +681,7 @@ void BrowserRenderProcessHost::PropagateBrowserCommandLineToRenderer(
// Propagate the following switches to the renderer command line (along
// with any associated values) if present in the browser command line.
static const char* const kSwitchNames[] = {
+ switches::kAllowHTTPBackgroundPage,
switches::kAllowScriptingGallery,
switches::kAlwaysAuthorizePlugins,
switches::kAppsGalleryURL,
diff --git a/chrome/browser/tab_contents/render_view_host_delegate_helper.cc b/chrome/browser/tab_contents/render_view_host_delegate_helper.cc
index 85f8ce2..c07acdd 100644
--- a/chrome/browser/tab_contents/render_view_host_delegate_helper.cc
+++ b/chrome/browser/tab_contents/render_view_host_delegate_helper.cc
@@ -58,6 +58,11 @@ RenderViewHostDelegateViewHelper::MaybeCreateBackgroundContents(
if (!extension)
return NULL;
+ // If the extension manifest specifies a background page, then don't allow one
+ // to be created here.
+ if (extension->background_url().is_valid())
+ return NULL;
+
// Only allow a single background contents per app.
if (!profile->GetBackgroundContentsService() ||
profile->GetBackgroundContentsService()->GetAppBackgroundContents(
diff --git a/chrome/browser/task_manager/task_manager_resource_providers.cc b/chrome/browser/task_manager/task_manager_resource_providers.cc
index c4dfbf8..19747c0 100644
--- a/chrome/browser/task_manager/task_manager_resource_providers.cc
+++ b/chrome/browser/task_manager/task_manager_resource_providers.cc
@@ -363,6 +363,9 @@ void TaskManagerTabContentsResourceProvider::Observe(NotificationType type,
TabContentsWrapper* tab_contents =
TabContentsWrapper::GetCurrentWrapperForContents(
Source<TabContents>(source).ptr());
+ // A background page does not have a TabContentsWrapper.
+ if (!tab_contents)
+ return;
switch (type.value) {
case NotificationType::TAB_CONTENTS_CONNECTED:
Add(tab_contents);
diff --git a/chrome/common/chrome_switches.cc b/chrome/common/chrome_switches.cc
index 82a4ab9..44aa7cd 100644
--- a/chrome/common/chrome_switches.cc
+++ b/chrome/common/chrome_switches.cc
@@ -21,6 +21,9 @@ const char kActivateOnLaunch[] = "activate-on-launch";
// directories. This switch re-enables file:// for testing.
const char kAllowFileAccess[] = "allow-file-access";
+// Allow non-https URL for background_page for hosted apps.
+const char kAllowHTTPBackgroundPage[] = "allow-http-background-page";
+
// Don't block outdated plugins.
const char kAllowOutdatedPlugins[] = "allow-outdated-plugins";
diff --git a/chrome/common/chrome_switches.h b/chrome/common/chrome_switches.h
index 582c41d..640e764 100644
--- a/chrome/common/chrome_switches.h
+++ b/chrome/common/chrome_switches.h
@@ -26,6 +26,7 @@ namespace switches {
extern const char kActivateOnLaunch[];
extern const char kAllowFileAccess[];
extern const char kAllowOutdatedPlugins[];
+extern const char kAllowHTTPBackgroundPage[];
extern const char kAllowScriptingGallery[];
extern const char kAlwaysAuthorizePlugins[];
extern const char kAlwaysEnableDevTools[];
diff --git a/chrome/common/extensions/extension.cc b/chrome/common/extensions/extension.cc
index 3fc7be1..471dd11 100644
--- a/chrome/common/extensions/extension.cc
+++ b/chrome/common/extensions/extension.cc
@@ -1284,8 +1284,10 @@ bool Extension::EnsureNotHybridApp(const DictionaryValue* manifest,
if (!IsBaseCrxKey(*key) &&
*key != keys::kApp &&
*key != keys::kPermissions &&
- *key != keys::kOptionsPage) {
- *error = errors::kHostedAppsCannotIncludeExtensionFeatures;
+ *key != keys::kOptionsPage &&
+ *key != keys::kBackground) {
+ *error = ExtensionErrorUtils::FormatErrorMessage(
+ errors::kHostedAppsCannotIncludeExtensionFeatures, *key);
return false;
}
}
@@ -1854,16 +1856,6 @@ bool Extension::InitFromValue(const DictionaryValue& source, int flags,
}
}
- // Initialize background url (optional).
- if (source.HasKey(keys::kBackground)) {
- std::string background_str;
- if (!source.GetString(keys::kBackground, &background_str)) {
- *error = errors::kInvalidBackground;
- return false;
- }
- background_url_ = GetResourceURL(background_str);
- }
-
// Initialize toolstrips. This is deprecated for public use.
// NOTE(erikkay) Although deprecated, we intend to preserve this parsing
// code indefinitely. Please contact me or Joi for details as to why.
@@ -2012,7 +2004,6 @@ bool Extension::InitFromValue(const DictionaryValue& source, int flags,
return false;
}
options_url_ = options_url;
-
} else {
GURL absolute(options_str);
if (absolute.is_valid()) {
@@ -2118,6 +2109,41 @@ bool Extension::InitFromValue(const DictionaryValue& source, int flags,
}
}
+ // Initialize background url (optional).
+ if (source.HasKey(keys::kBackground)) {
+ std::string background_str;
+ if (!source.GetString(keys::kBackground, &background_str)) {
+ *error = errors::kInvalidBackground;
+ return false;
+ }
+
+ if (is_hosted_app()) {
+ // Make sure "background" permission is set.
+ if (api_permissions_.find(kBackgroundPermission) ==
+ api_permissions_.end()) {
+ *error = errors::kBackgroundPermissionNeeded;
+ return false;
+ }
+ // Hosted apps require an absolute URL.
+ GURL bg_page(background_str);
+ if (!bg_page.is_valid()) {
+ *error = errors::kInvalidBackgroundInHostedApp;
+ return false;
+ }
+
+ if (!(bg_page.SchemeIs("https") ||
+ (CommandLine::ForCurrentProcess()->HasSwitch(
+ switches::kAllowHTTPBackgroundPage) &&
+ bg_page.SchemeIs("http")))) {
+ *error = errors::kInvalidBackgroundInHostedApp;
+ return false;
+ }
+ background_url_ = bg_page;
+ } else {
+ background_url_ = GetResourceURL(background_str);
+ }
+ }
+
if (source.HasKey(keys::kDefaultLocale)) {
if (!source.GetString(keys::kDefaultLocale, &default_locale_) ||
!l10n_util::IsValidLocaleSyntax(default_locale_)) {
diff --git a/chrome/common/extensions/extension_constants.cc b/chrome/common/extensions/extension_constants.cc
index 97243e5..27324ef 100644
--- a/chrome/common/extensions/extension_constants.cc
+++ b/chrome/common/extensions/extension_constants.cc
@@ -102,6 +102,9 @@ const char* kLaunchContainerWindow = "window";
namespace extension_manifest_errors {
const char* kAppsNotEnabled =
"Apps are not enabled.";
+const char* kBackgroundPermissionNeeded =
+ "Hosted apps that use 'background_page' must have the 'background' "
+ "permission.";
const char* kCannotAccessPage =
"Cannot access contents of url \"*\". "
"Extension manifest must request permission to access this host.";
@@ -128,11 +131,14 @@ const char *kExperimentalFeature =
"This feature requires 'experimental' permissions and"
" --enable-experimental-extension-apis command line flag.";
const char* kHostedAppsCannotIncludeExtensionFeatures =
- "Hosted apps cannot use extension features.";
+ "Hosted apps cannot use the extension feature '*'.";
const char* kInvalidAllFrames =
"Invalid value for 'content_scripts[*].all_frames'.";
const char* kInvalidBackground =
"Invalid value for 'background_page'.";
+const char* kInvalidBackgroundInHostedApp =
+ "Invalid value for 'background_page'. Hosted apps must specify an "
+ "absolute HTTPS URL for the background page.";
const char* kInvalidBrowserAction =
"Invalid value for 'browser_action'.";
const char* kInvalidChromeURLOverrides =
diff --git a/chrome/common/extensions/extension_constants.h b/chrome/common/extensions/extension_constants.h
index daa2156..f05c947 100644
--- a/chrome/common/extensions/extension_constants.h
+++ b/chrome/common/extensions/extension_constants.h
@@ -104,6 +104,7 @@ namespace extension_manifest_values {
// Error messages returned from Extension::InitFromValue().
namespace extension_manifest_errors {
extern const char* kAppsNotEnabled;
+ extern const char* kBackgroundPermissionNeeded;
extern const char* kCannotAccessPage;
extern const char* kCannotClaimAllHostsInExtent;
extern const char* kCannotClaimAllURLsInExtent;
@@ -118,6 +119,7 @@ namespace extension_manifest_errors {
extern const char* kHostedAppsCannotIncludeExtensionFeatures;
extern const char* kInvalidAllFrames;
extern const char* kInvalidBackground;
+ extern const char* kInvalidBackgroundInHostedApp;
extern const char* kInvalidBrowserAction;
extern const char* kInvalidBrowseURL;
extern const char* kInvalidBrowseURLs;
diff --git a/chrome/common/extensions/extension_file_util.cc b/chrome/common/extensions/extension_file_util.cc
index 1c4ec8c..be34cfb 100644
--- a/chrome/common/extensions/extension_file_util.cc
+++ b/chrome/common/extensions/extension_file_util.cc
@@ -245,8 +245,10 @@ bool ValidateExtension(Extension* extension, std::string* error) {
}
}
- // Validate background page location.
- if (!extension->background_url().is_empty()) {
+ // Validate background page location, except for hosted apps, which should use
+ // an external URL. Background page for hosted apps are verified when the
+ // extension is created (in Extension::InitFromValue)
+ if (!extension->background_url().is_empty() && !extension->is_hosted_app()) {
FilePath page_path = ExtensionURLToRelativeFilePath(
extension->background_url());
const FilePath path = extension->GetResource(page_path).GetFilePath();
diff --git a/chrome/common/extensions/extension_manifests_unittest.cc b/chrome/common/extensions/extension_manifests_unittest.cc
index 5deef17..e49ec0a 100644
--- a/chrome/common/extensions/extension_manifests_unittest.cc
+++ b/chrome/common/extensions/extension_manifests_unittest.cc
@@ -405,9 +405,11 @@ TEST_F(ExtensionManifestTest, Sidebar) {
TEST_F(ExtensionManifestTest, DisallowHybridApps) {
LoadAndExpectError("disallow_hybrid_1.json",
- errors::kHostedAppsCannotIncludeExtensionFeatures);
+ ExtensionErrorUtils::FormatErrorMessage(
+ errors::kHostedAppsCannotIncludeExtensionFeatures,
+ keys::kBrowserAction));
LoadAndExpectError("disallow_hybrid_2.json",
- errors::kHostedAppsCannotIncludeExtensionFeatures);
+ errors::kBackgroundPermissionNeeded);
}
TEST_F(ExtensionManifestTest, OptionsPageInApps) {