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. 4 5#include "extensions/common/manifest_handlers/background_info.h" 6 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" 23 24using base::ASCIIToUTF16; 25using base::DictionaryValue; 26 27namespace extensions { 28 29namespace keys = manifest_keys; 30namespace values = manifest_values; 31namespace errors = manifest_errors; 32 33namespace { 34 35const char kBackground[] = "background"; 36 37static base::LazyInstance<BackgroundInfo> g_empty_background_info = 38 LAZY_INSTANCE_INITIALIZER; 39 40const BackgroundInfo& GetBackgroundInfo(const Extension* extension) { 41 BackgroundInfo* info = static_cast<BackgroundInfo*>( 42 extension->GetManifestData(kBackground)); 43 if (!info) 44 return g_empty_background_info.Get(); 45 return *info; 46} 47 48} // namespace 49 50BackgroundInfo::BackgroundInfo() 51 : is_persistent_(true), 52 allow_js_access_(true) { 53} 54 55BackgroundInfo::~BackgroundInfo() { 56} 57 58// static 59GURL 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); 64} 65 66// static 67const std::vector<std::string>& BackgroundInfo::GetBackgroundScripts( 68 const Extension* extension) { 69 return GetBackgroundInfo(extension).background_scripts_; 70} 71 72// static 73const std::string& BackgroundInfo::GetServiceWorkerScript( 74 const Extension* extension) { 75 return GetBackgroundInfo(extension).service_worker_script_; 76} 77 78// static 79bool BackgroundInfo::HasBackgroundPage(const Extension* extension) { 80 return GetBackgroundInfo(extension).has_background_page(); 81} 82 83// static 84bool BackgroundInfo::HasPersistentBackgroundPage(const Extension* extension) { 85 return GetBackgroundInfo(extension).has_persistent_background_page(); 86} 87 88// static 89bool BackgroundInfo::HasLazyBackgroundPage(const Extension* extension) { 90 return GetBackgroundInfo(extension).has_lazy_background_page(); 91} 92 93// static 94bool BackgroundInfo::HasGeneratedBackgroundPage(const Extension* extension) { 95 const BackgroundInfo& info = GetBackgroundInfo(extension); 96 return !info.background_scripts_.empty(); 97} 98 99// static 100bool BackgroundInfo::HasServiceWorker(const Extension* extension) { 101 return GetBackgroundInfo(extension).has_service_worker(); 102} 103 104// static 105bool BackgroundInfo::AllowJSAccess(const Extension* extension) { 106 return GetBackgroundInfo(extension).allow_js_access_; 107} 108 109bool 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)) { 121 return false; 122 } 123 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); 129 return false; 130 } 131 132 return true; 133} 134 135bool 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)) 140 return true; 141 142 if (!CommandLine::ForCurrentProcess()->HasSwitch( 143 ::switches::kEnableServiceWorker)) { 144 *error = ASCIIToUTF16(errors::kServiceWorkerRequiresFlag); 145 return false; 146 } 147 148 CHECK(service_worker_script_value); 149 if (!service_worker_script_value->GetAsString(&service_worker_script_)) { 150 *error = ASCIIToUTF16(errors::kInvalidServiceWorkerScript); 151 return false; 152 } 153 return true; 154} 155 156bool 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)) 161 return true; 162 163 CHECK(background_scripts_value); 164 if (background_scripts_value->GetType() != base::Value::TYPE_LIST) { 165 *error = ASCIIToUTF16(errors::kInvalidBackgroundScripts); 166 return false; 167 } 168 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) { 172 std::string script; 173 if (!background_scripts->GetString(i, &script)) { 174 *error = ErrorUtils::FormatErrorMessageUTF16( 175 errors::kInvalidBackgroundScript, base::IntToString(i)); 176 return false; 177 } 178 background_scripts_.push_back(script); 179 } 180 181 return true; 182} 183 184bool 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)) 189 return true; 190 191 std::string background_str; 192 if (!background_page_value->GetAsString(&background_str)) { 193 *error = ASCIIToUTF16(errors::kInvalidBackground); 194 return false; 195 } 196 197 if (extension->is_hosted_app()) { 198 background_url_ = GURL(background_str); 199 200 if (!PermissionsParser::HasAPIPermission(extension, 201 APIPermission::kBackground)) { 202 *error = ASCIIToUTF16(errors::kBackgroundPermissionNeeded); 203 return false; 204 } 205 // Hosted apps require an absolute URL. 206 if (!background_url_.is_valid()) { 207 *error = ASCIIToUTF16(errors::kInvalidBackgroundInHostedApp); 208 return false; 209 } 210 211 if (!(background_url_.SchemeIs("https") || 212 (CommandLine::ForCurrentProcess()->HasSwitch( 213 switches::kAllowHTTPBackgroundPage) && 214 background_url_.SchemeIs("http")))) { 215 *error = ASCIIToUTF16(errors::kInvalidBackgroundInHostedApp); 216 return false; 217 } 218 } else { 219 background_url_ = extension->GetResourceURL(background_str); 220 } 221 222 return true; 223} 224 225bool BackgroundInfo::LoadBackgroundPage(const Extension* extension, 226 base::string16* error) { 227 if (extension->is_platform_app()) { 228 return LoadBackgroundPage( 229 extension, keys::kPlatformAppBackgroundPage, error); 230 } 231 232 if (!LoadBackgroundPage(extension, keys::kBackgroundPage, error)) 233 return false; 234 if (background_url_.is_empty()) 235 return LoadBackgroundPage(extension, keys::kBackgroundPageLegacy, error); 236 return true; 237} 238 239bool BackgroundInfo::LoadBackgroundPersistent(const Extension* extension, 240 base::string16* error) { 241 if (extension->is_platform_app()) { 242 is_persistent_ = false; 243 return true; 244 } 245 246 const base::Value* background_persistent = NULL; 247 if (!extension->manifest()->Get(keys::kBackgroundPersistent, 248 &background_persistent)) 249 return true; 250 251 if (!background_persistent->GetAsBoolean(&is_persistent_)) { 252 *error = ASCIIToUTF16(errors::kInvalidBackgroundPersistent); 253 return false; 254 } 255 256 if (!has_background_page()) { 257 *error = ASCIIToUTF16(errors::kInvalidBackgroundPersistentNoPage); 258 return false; 259 } 260 261 return true; 262} 263 264bool BackgroundInfo::LoadAllowJSAccess(const Extension* extension, 265 base::string16* error) { 266 const base::Value* allow_js_access = NULL; 267 if (!extension->manifest()->Get(keys::kBackgroundAllowJsAccess, 268 &allow_js_access)) 269 return true; 270 271 if (!allow_js_access->IsType(base::Value::TYPE_BOOLEAN) || 272 !allow_js_access->GetAsBoolean(&allow_js_access_)) { 273 *error = ASCIIToUTF16(errors::kInvalidBackgroundAllowJsAccess); 274 return false; 275 } 276 277 return true; 278} 279 280BackgroundManifestHandler::BackgroundManifestHandler() { 281} 282 283BackgroundManifestHandler::~BackgroundManifestHandler() { 284} 285 286bool BackgroundManifestHandler::Parse(Extension* extension, 287 base::string16* error) { 288 scoped_ptr<BackgroundInfo> info(new BackgroundInfo); 289 if (!info->Parse(extension, error)) 290 return false; 291 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); 296 return false; 297 } 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); 303 return false; 304 } 305 306 extension->SetManifestData(kBackground, info.release()); 307 return true; 308} 309 310bool BackgroundManifestHandler::Validate( 311 const Extension* extension, 312 std::string* error, 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])); 323 return false; 324 } 325 } 326 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)) { 336 *error = 337 l10n_util::GetStringFUTF8( 338 IDS_EXTENSION_LOAD_BACKGROUND_PAGE_FAILED, 339 page_path.LossyDisplayName()); 340 return false; 341 } 342 } 343 return true; 344} 345 346bool BackgroundManifestHandler::AlwaysParseForType(Manifest::Type type) const { 347 return type == Manifest::TYPE_PLATFORM_APP; 348} 349 350const 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)); 360} 361 362} // namespace extensions 363