1 // Copyright 2013 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
5 #include "extensions/common/manifest_handlers/background_info.h"
7 #include "base/command_line.h"
8 #include "base/file_util.h"
9 #include "base/lazy_instance.h"
10 #include "base/memory/scoped_ptr.h"
11 #include "base/strings/string_number_conversions.h"
12 #include "base/strings/utf_string_conversions.h"
13 #include "content/public/common/content_switches.h"
14 #include "extensions/common/constants.h"
15 #include "extensions/common/error_utils.h"
16 #include "extensions/common/file_util.h"
17 #include "extensions/common/manifest_constants.h"
18 #include "extensions/common/manifest_handlers/permissions_parser.h"
19 #include "extensions/common/permissions/api_permission_set.h"
20 #include "extensions/common/switches.h"
21 #include "grit/extensions_strings.h"
22 #include "ui/base/l10n/l10n_util.h"
24 using base::ASCIIToUTF16;
25 using base::DictionaryValue;
27 namespace extensions {
29 namespace keys = manifest_keys;
30 namespace values = manifest_values;
31 namespace errors = manifest_errors;
35 const char kBackground[] = "background";
37 static base::LazyInstance<BackgroundInfo> g_empty_background_info =
38 LAZY_INSTANCE_INITIALIZER;
40 const BackgroundInfo& GetBackgroundInfo(const Extension* extension) {
41 BackgroundInfo* info = static_cast<BackgroundInfo*>(
42 extension->GetManifestData(kBackground));
44 return g_empty_background_info.Get();
50 BackgroundInfo::BackgroundInfo()
51 : is_persistent_(true),
52 allow_js_access_(true) {
55 BackgroundInfo::~BackgroundInfo() {
59 GURL BackgroundInfo::GetBackgroundURL(const Extension* extension) {
60 const BackgroundInfo& info = GetBackgroundInfo(extension);
61 if (info.background_scripts_.empty())
62 return info.background_url_;
63 return extension->GetResourceURL(kGeneratedBackgroundPageFilename);
67 const std::vector<std::string>& BackgroundInfo::GetBackgroundScripts(
68 const Extension* extension) {
69 return GetBackgroundInfo(extension).background_scripts_;
73 const std::string& BackgroundInfo::GetServiceWorkerScript(
74 const Extension* extension) {
75 return GetBackgroundInfo(extension).service_worker_script_;
79 bool BackgroundInfo::HasBackgroundPage(const Extension* extension) {
80 return GetBackgroundInfo(extension).has_background_page();
84 bool BackgroundInfo::HasPersistentBackgroundPage(const Extension* extension) {
85 return GetBackgroundInfo(extension).has_persistent_background_page();
89 bool BackgroundInfo::HasLazyBackgroundPage(const Extension* extension) {
90 return GetBackgroundInfo(extension).has_lazy_background_page();
94 bool BackgroundInfo::HasGeneratedBackgroundPage(const Extension* extension) {
95 const BackgroundInfo& info = GetBackgroundInfo(extension);
96 return !info.background_scripts_.empty();
100 bool BackgroundInfo::HasServiceWorker(const Extension* extension) {
101 return GetBackgroundInfo(extension).has_service_worker();
105 bool BackgroundInfo::AllowJSAccess(const Extension* extension) {
106 return GetBackgroundInfo(extension).allow_js_access_;
109 bool BackgroundInfo::Parse(const Extension* extension, base::string16* error) {
110 const std::string& bg_scripts_key = extension->is_platform_app() ?
111 keys::kPlatformAppBackgroundScripts : keys::kBackgroundScripts;
112 const std::string& sw_scripts_key =
113 extension->is_platform_app()
114 ? keys::kPlatformAppServiceWorkerScript
115 : ""; // TODO(scheib): Support extensions crbug.com/346885
116 if (!LoadServiceWorkerScript(extension, sw_scripts_key, error) ||
117 !LoadBackgroundScripts(extension, bg_scripts_key, error) ||
118 !LoadBackgroundPage(extension, error) ||
119 !LoadBackgroundPersistent(extension, error) ||
120 !LoadAllowJSAccess(extension, error)) {
124 int background_solution_sum = (background_url_.is_valid() ? 1 : 0) +
125 (!background_scripts_.empty() ? 1 : 0) +
126 (has_service_worker() ? 1 : 0);
127 if (background_solution_sum > 1) {
128 *error = ASCIIToUTF16(errors::kInvalidBackgroundCombination);
135 bool BackgroundInfo::LoadServiceWorkerScript(const Extension* extension,
136 const std::string& key,
137 base::string16* error) {
138 const base::Value* service_worker_script_value = NULL;
139 if (!extension->manifest()->Get(key, &service_worker_script_value))
142 if (!CommandLine::ForCurrentProcess()->HasSwitch(
143 ::switches::kEnableExperimentalWebPlatformFeatures)) {
144 *error = ASCIIToUTF16(errors::kServiceWorkerRequiresFlag);
148 CHECK(service_worker_script_value);
149 if (!service_worker_script_value->GetAsString(&service_worker_script_)) {
150 *error = ASCIIToUTF16(errors::kInvalidServiceWorkerScript);
156 bool BackgroundInfo::LoadBackgroundScripts(const Extension* extension,
157 const std::string& key,
158 base::string16* error) {
159 const base::Value* background_scripts_value = NULL;
160 if (!extension->manifest()->Get(key, &background_scripts_value))
163 CHECK(background_scripts_value);
164 if (background_scripts_value->GetType() != base::Value::TYPE_LIST) {
165 *error = ASCIIToUTF16(errors::kInvalidBackgroundScripts);
169 const base::ListValue* background_scripts = NULL;
170 background_scripts_value->GetAsList(&background_scripts);
171 for (size_t i = 0; i < background_scripts->GetSize(); ++i) {
173 if (!background_scripts->GetString(i, &script)) {
174 *error = ErrorUtils::FormatErrorMessageUTF16(
175 errors::kInvalidBackgroundScript, base::IntToString(i));
178 background_scripts_.push_back(script);
184 bool BackgroundInfo::LoadBackgroundPage(const Extension* extension,
185 const std::string& key,
186 base::string16* error) {
187 const base::Value* background_page_value = NULL;
188 if (!extension->manifest()->Get(key, &background_page_value))
191 std::string background_str;
192 if (!background_page_value->GetAsString(&background_str)) {
193 *error = ASCIIToUTF16(errors::kInvalidBackground);
197 if (extension->is_hosted_app()) {
198 background_url_ = GURL(background_str);
200 if (!PermissionsParser::HasAPIPermission(extension,
201 APIPermission::kBackground)) {
202 *error = ASCIIToUTF16(errors::kBackgroundPermissionNeeded);
205 // Hosted apps require an absolute URL.
206 if (!background_url_.is_valid()) {
207 *error = ASCIIToUTF16(errors::kInvalidBackgroundInHostedApp);
211 if (!(background_url_.SchemeIs("https") ||
212 (CommandLine::ForCurrentProcess()->HasSwitch(
213 switches::kAllowHTTPBackgroundPage) &&
214 background_url_.SchemeIs("http")))) {
215 *error = ASCIIToUTF16(errors::kInvalidBackgroundInHostedApp);
219 background_url_ = extension->GetResourceURL(background_str);
225 bool BackgroundInfo::LoadBackgroundPage(const Extension* extension,
226 base::string16* error) {
227 if (extension->is_platform_app()) {
228 return LoadBackgroundPage(
229 extension, keys::kPlatformAppBackgroundPage, error);
232 if (!LoadBackgroundPage(extension, keys::kBackgroundPage, error))
234 if (background_url_.is_empty())
235 return LoadBackgroundPage(extension, keys::kBackgroundPageLegacy, error);
239 bool BackgroundInfo::LoadBackgroundPersistent(const Extension* extension,
240 base::string16* error) {
241 if (extension->is_platform_app()) {
242 is_persistent_ = false;
246 const base::Value* background_persistent = NULL;
247 if (!extension->manifest()->Get(keys::kBackgroundPersistent,
248 &background_persistent))
251 if (!background_persistent->GetAsBoolean(&is_persistent_)) {
252 *error = ASCIIToUTF16(errors::kInvalidBackgroundPersistent);
256 if (!has_background_page()) {
257 *error = ASCIIToUTF16(errors::kInvalidBackgroundPersistentNoPage);
264 bool BackgroundInfo::LoadAllowJSAccess(const Extension* extension,
265 base::string16* error) {
266 const base::Value* allow_js_access = NULL;
267 if (!extension->manifest()->Get(keys::kBackgroundAllowJsAccess,
271 if (!allow_js_access->IsType(base::Value::TYPE_BOOLEAN) ||
272 !allow_js_access->GetAsBoolean(&allow_js_access_)) {
273 *error = ASCIIToUTF16(errors::kInvalidBackgroundAllowJsAccess);
280 BackgroundManifestHandler::BackgroundManifestHandler() {
283 BackgroundManifestHandler::~BackgroundManifestHandler() {
286 bool BackgroundManifestHandler::Parse(Extension* extension,
287 base::string16* error) {
288 scoped_ptr<BackgroundInfo> info(new BackgroundInfo);
289 if (!info->Parse(extension, error))
292 // Platform apps must have background pages or service workers.
293 if (extension->is_platform_app() && !info->has_background_page() &&
294 !info->has_service_worker()) {
295 *error = ASCIIToUTF16(errors::kBackgroundRequiredForPlatformApps);
298 // Lazy background pages are incompatible with the webRequest API.
299 if (info->has_lazy_background_page() &&
300 PermissionsParser::HasAPIPermission(extension,
301 APIPermission::kWebRequest)) {
302 *error = ASCIIToUTF16(errors::kWebRequestConflictsWithLazyBackground);
306 extension->SetManifestData(kBackground, info.release());
310 bool BackgroundManifestHandler::Validate(
311 const Extension* extension,
313 std::vector<InstallWarning>* warnings) const {
314 // Validate that background scripts exist.
315 const std::vector<std::string>& background_scripts =
316 BackgroundInfo::GetBackgroundScripts(extension);
317 for (size_t i = 0; i < background_scripts.size(); ++i) {
318 if (!base::PathExists(
319 extension->GetResource(background_scripts[i]).GetFilePath())) {
320 *error = l10n_util::GetStringFUTF8(
321 IDS_EXTENSION_LOAD_BACKGROUND_SCRIPT_FAILED,
322 base::UTF8ToUTF16(background_scripts[i]));
327 // Validate background page location, except for hosted apps, which should use
328 // an external URL. Background page for hosted apps are verified when the
329 // extension is created (in Extension::InitFromValue)
330 if (BackgroundInfo::HasBackgroundPage(extension) &&
331 !extension->is_hosted_app() && background_scripts.empty()) {
332 base::FilePath page_path = file_util::ExtensionURLToRelativeFilePath(
333 BackgroundInfo::GetBackgroundURL(extension));
334 const base::FilePath path = extension->GetResource(page_path).GetFilePath();
335 if (path.empty() || !base::PathExists(path)) {
337 l10n_util::GetStringFUTF8(
338 IDS_EXTENSION_LOAD_BACKGROUND_PAGE_FAILED,
339 page_path.LossyDisplayName());
346 bool BackgroundManifestHandler::AlwaysParseForType(Manifest::Type type) const {
347 return type == Manifest::TYPE_PLATFORM_APP;
350 const std::vector<std::string> BackgroundManifestHandler::Keys() const {
351 static const char* keys[] = {keys::kBackgroundAllowJsAccess,
352 keys::kBackgroundPage,
353 keys::kBackgroundPageLegacy,
354 keys::kBackgroundPersistent,
355 keys::kBackgroundScripts,
356 keys::kPlatformAppBackgroundPage,
357 keys::kPlatformAppBackgroundScripts,
358 keys::kPlatformAppServiceWorkerScript};
359 return std::vector<std::string>(keys, keys + arraysize(keys));
362 } // namespace extensions