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 24 using base::ASCIIToUTF16; 25 using base::DictionaryValue; 26 27 namespace extensions { 28 29 namespace keys = manifest_keys; 30 namespace values = manifest_values; 31 namespace errors = manifest_errors; 32 33 namespace { 34 35 const char kBackground[] = "background"; 36 37 static base::LazyInstance<BackgroundInfo> g_empty_background_info = 38 LAZY_INSTANCE_INITIALIZER; 39 40 const 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 50 BackgroundInfo::BackgroundInfo() 51 : is_persistent_(true), 52 allow_js_access_(true) { 53 } 54 55 BackgroundInfo::~BackgroundInfo() { 56 } 57 58 // static 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); 64 } 65 66 // static 67 const std::vector<std::string>& BackgroundInfo::GetBackgroundScripts( 68 const Extension* extension) { 69 return GetBackgroundInfo(extension).background_scripts_; 70 } 71 72 // static 73 const std::string& BackgroundInfo::GetServiceWorkerScript( 74 const Extension* extension) { 75 return GetBackgroundInfo(extension).service_worker_script_; 76 } 77 78 // static 79 bool BackgroundInfo::HasBackgroundPage(const Extension* extension) { 80 return GetBackgroundInfo(extension).has_background_page(); 81 } 82 83 // static 84 bool BackgroundInfo::HasPersistentBackgroundPage(const Extension* extension) { 85 return GetBackgroundInfo(extension).has_persistent_background_page(); 86 } 87 88 // static 89 bool BackgroundInfo::HasLazyBackgroundPage(const Extension* extension) { 90 return GetBackgroundInfo(extension).has_lazy_background_page(); 91 } 92 93 // static 94 bool BackgroundInfo::HasGeneratedBackgroundPage(const Extension* extension) { 95 const BackgroundInfo& info = GetBackgroundInfo(extension); 96 return !info.background_scripts_.empty(); 97 } 98 99 // static 100 bool BackgroundInfo::HasServiceWorker(const Extension* extension) { 101 return GetBackgroundInfo(extension).has_service_worker(); 102 } 103 104 // static 105 bool BackgroundInfo::AllowJSAccess(const Extension* extension) { 106 return GetBackgroundInfo(extension).allow_js_access_; 107 } 108 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)) { 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 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)) 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 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)) 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 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)) 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 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); 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 239 bool 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 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, 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 280 BackgroundManifestHandler::BackgroundManifestHandler() { 281 } 282 283 BackgroundManifestHandler::~BackgroundManifestHandler() { 284 } 285 286 bool 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 310 bool 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 346 bool BackgroundManifestHandler::AlwaysParseForType(Manifest::Type type) const { 347 return type == Manifest::TYPE_PLATFORM_APP; 348 } 349 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)); 360 } 361 362 } // namespace extensions 363