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/files/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 "extensions/common/constants.h" 14#include "extensions/common/error_utils.h" 15#include "extensions/common/file_util.h" 16#include "extensions/common/manifest_constants.h" 17#include "extensions/common/manifest_handlers/permissions_parser.h" 18#include "extensions/common/permissions/api_permission_set.h" 19#include "extensions/common/switches.h" 20#include "grit/extensions_strings.h" 21#include "ui/base/l10n/l10n_util.h" 22 23using base::ASCIIToUTF16; 24using base::DictionaryValue; 25 26namespace extensions { 27 28namespace keys = manifest_keys; 29namespace values = manifest_values; 30namespace errors = manifest_errors; 31 32namespace { 33 34const char kBackground[] = "background"; 35 36static base::LazyInstance<BackgroundInfo> g_empty_background_info = 37 LAZY_INSTANCE_INITIALIZER; 38 39const BackgroundInfo& GetBackgroundInfo(const Extension* extension) { 40 BackgroundInfo* info = static_cast<BackgroundInfo*>( 41 extension->GetManifestData(kBackground)); 42 if (!info) 43 return g_empty_background_info.Get(); 44 return *info; 45} 46 47} // namespace 48 49BackgroundInfo::BackgroundInfo() 50 : is_persistent_(true), 51 allow_js_access_(true) { 52} 53 54BackgroundInfo::~BackgroundInfo() { 55} 56 57// static 58GURL BackgroundInfo::GetBackgroundURL(const Extension* extension) { 59 const BackgroundInfo& info = GetBackgroundInfo(extension); 60 if (info.background_scripts_.empty()) 61 return info.background_url_; 62 return extension->GetResourceURL(kGeneratedBackgroundPageFilename); 63} 64 65// static 66const std::vector<std::string>& BackgroundInfo::GetBackgroundScripts( 67 const Extension* extension) { 68 return GetBackgroundInfo(extension).background_scripts_; 69} 70 71// static 72bool BackgroundInfo::HasBackgroundPage(const Extension* extension) { 73 return GetBackgroundInfo(extension).has_background_page(); 74} 75 76// static 77bool BackgroundInfo::HasPersistentBackgroundPage(const Extension* extension) { 78 return GetBackgroundInfo(extension).has_persistent_background_page(); 79} 80 81// static 82bool BackgroundInfo::HasLazyBackgroundPage(const Extension* extension) { 83 return GetBackgroundInfo(extension).has_lazy_background_page(); 84} 85 86// static 87bool BackgroundInfo::HasGeneratedBackgroundPage(const Extension* extension) { 88 const BackgroundInfo& info = GetBackgroundInfo(extension); 89 return !info.background_scripts_.empty(); 90} 91 92// static 93bool BackgroundInfo::AllowJSAccess(const Extension* extension) { 94 return GetBackgroundInfo(extension).allow_js_access_; 95} 96 97bool BackgroundInfo::Parse(const Extension* extension, base::string16* error) { 98 const std::string& bg_scripts_key = extension->is_platform_app() ? 99 keys::kPlatformAppBackgroundScripts : keys::kBackgroundScripts; 100 if (!LoadBackgroundScripts(extension, bg_scripts_key, error) || 101 !LoadBackgroundPage(extension, error) || 102 !LoadBackgroundPersistent(extension, error) || 103 !LoadAllowJSAccess(extension, error)) { 104 return false; 105 } 106 107 int background_solution_sum = (background_url_.is_valid() ? 1 : 0) + 108 (!background_scripts_.empty() ? 1 : 0); 109 if (background_solution_sum > 1) { 110 *error = ASCIIToUTF16(errors::kInvalidBackgroundCombination); 111 return false; 112 } 113 114 return true; 115} 116 117bool BackgroundInfo::LoadBackgroundScripts(const Extension* extension, 118 const std::string& key, 119 base::string16* error) { 120 const base::Value* background_scripts_value = NULL; 121 if (!extension->manifest()->Get(key, &background_scripts_value)) 122 return true; 123 124 CHECK(background_scripts_value); 125 if (background_scripts_value->GetType() != base::Value::TYPE_LIST) { 126 *error = ASCIIToUTF16(errors::kInvalidBackgroundScripts); 127 return false; 128 } 129 130 const base::ListValue* background_scripts = NULL; 131 background_scripts_value->GetAsList(&background_scripts); 132 for (size_t i = 0; i < background_scripts->GetSize(); ++i) { 133 std::string script; 134 if (!background_scripts->GetString(i, &script)) { 135 *error = ErrorUtils::FormatErrorMessageUTF16( 136 errors::kInvalidBackgroundScript, base::IntToString(i)); 137 return false; 138 } 139 background_scripts_.push_back(script); 140 } 141 142 return true; 143} 144 145bool BackgroundInfo::LoadBackgroundPage(const Extension* extension, 146 const std::string& key, 147 base::string16* error) { 148 const base::Value* background_page_value = NULL; 149 if (!extension->manifest()->Get(key, &background_page_value)) 150 return true; 151 152 std::string background_str; 153 if (!background_page_value->GetAsString(&background_str)) { 154 *error = ASCIIToUTF16(errors::kInvalidBackground); 155 return false; 156 } 157 158 if (extension->is_hosted_app()) { 159 background_url_ = GURL(background_str); 160 161 if (!PermissionsParser::HasAPIPermission(extension, 162 APIPermission::kBackground)) { 163 *error = ASCIIToUTF16(errors::kBackgroundPermissionNeeded); 164 return false; 165 } 166 // Hosted apps require an absolute URL. 167 if (!background_url_.is_valid()) { 168 *error = ASCIIToUTF16(errors::kInvalidBackgroundInHostedApp); 169 return false; 170 } 171 172 if (!(background_url_.SchemeIs("https") || 173 (CommandLine::ForCurrentProcess()->HasSwitch( 174 switches::kAllowHTTPBackgroundPage) && 175 background_url_.SchemeIs("http")))) { 176 *error = ASCIIToUTF16(errors::kInvalidBackgroundInHostedApp); 177 return false; 178 } 179 } else { 180 background_url_ = extension->GetResourceURL(background_str); 181 } 182 183 return true; 184} 185 186bool BackgroundInfo::LoadBackgroundPage(const Extension* extension, 187 base::string16* error) { 188 if (extension->is_platform_app()) { 189 return LoadBackgroundPage( 190 extension, keys::kPlatformAppBackgroundPage, error); 191 } 192 193 if (!LoadBackgroundPage(extension, keys::kBackgroundPage, error)) 194 return false; 195 if (background_url_.is_empty()) 196 return LoadBackgroundPage(extension, keys::kBackgroundPageLegacy, error); 197 return true; 198} 199 200bool BackgroundInfo::LoadBackgroundPersistent(const Extension* extension, 201 base::string16* error) { 202 if (extension->is_platform_app()) { 203 is_persistent_ = false; 204 return true; 205 } 206 207 const base::Value* background_persistent = NULL; 208 if (!extension->manifest()->Get(keys::kBackgroundPersistent, 209 &background_persistent)) 210 return true; 211 212 if (!background_persistent->GetAsBoolean(&is_persistent_)) { 213 *error = ASCIIToUTF16(errors::kInvalidBackgroundPersistent); 214 return false; 215 } 216 217 if (!has_background_page()) { 218 *error = ASCIIToUTF16(errors::kInvalidBackgroundPersistentNoPage); 219 return false; 220 } 221 222 return true; 223} 224 225bool BackgroundInfo::LoadAllowJSAccess(const Extension* extension, 226 base::string16* error) { 227 const base::Value* allow_js_access = NULL; 228 if (!extension->manifest()->Get(keys::kBackgroundAllowJsAccess, 229 &allow_js_access)) 230 return true; 231 232 if (!allow_js_access->IsType(base::Value::TYPE_BOOLEAN) || 233 !allow_js_access->GetAsBoolean(&allow_js_access_)) { 234 *error = ASCIIToUTF16(errors::kInvalidBackgroundAllowJsAccess); 235 return false; 236 } 237 238 return true; 239} 240 241BackgroundManifestHandler::BackgroundManifestHandler() { 242} 243 244BackgroundManifestHandler::~BackgroundManifestHandler() { 245} 246 247bool BackgroundManifestHandler::Parse(Extension* extension, 248 base::string16* error) { 249 scoped_ptr<BackgroundInfo> info(new BackgroundInfo); 250 if (!info->Parse(extension, error)) 251 return false; 252 253 // Platform apps must have background pages. 254 if (extension->is_platform_app() && !info->has_background_page()) { 255 *error = ASCIIToUTF16(errors::kBackgroundRequiredForPlatformApps); 256 return false; 257 } 258 // Lazy background pages are incompatible with the webRequest API. 259 if (info->has_lazy_background_page() && 260 PermissionsParser::HasAPIPermission(extension, 261 APIPermission::kWebRequest)) { 262 *error = ASCIIToUTF16(errors::kWebRequestConflictsWithLazyBackground); 263 return false; 264 } 265 266 extension->SetManifestData(kBackground, info.release()); 267 return true; 268} 269 270bool BackgroundManifestHandler::Validate( 271 const Extension* extension, 272 std::string* error, 273 std::vector<InstallWarning>* warnings) const { 274 // Validate that background scripts exist. 275 const std::vector<std::string>& background_scripts = 276 BackgroundInfo::GetBackgroundScripts(extension); 277 for (size_t i = 0; i < background_scripts.size(); ++i) { 278 if (!base::PathExists( 279 extension->GetResource(background_scripts[i]).GetFilePath())) { 280 *error = l10n_util::GetStringFUTF8( 281 IDS_EXTENSION_LOAD_BACKGROUND_SCRIPT_FAILED, 282 base::UTF8ToUTF16(background_scripts[i])); 283 return false; 284 } 285 } 286 287 // Validate background page location, except for hosted apps, which should use 288 // an external URL. Background page for hosted apps are verified when the 289 // extension is created (in Extension::InitFromValue) 290 if (BackgroundInfo::HasBackgroundPage(extension) && 291 !extension->is_hosted_app() && background_scripts.empty()) { 292 base::FilePath page_path = file_util::ExtensionURLToRelativeFilePath( 293 BackgroundInfo::GetBackgroundURL(extension)); 294 const base::FilePath path = extension->GetResource(page_path).GetFilePath(); 295 if (path.empty() || !base::PathExists(path)) { 296 *error = 297 l10n_util::GetStringFUTF8( 298 IDS_EXTENSION_LOAD_BACKGROUND_PAGE_FAILED, 299 page_path.LossyDisplayName()); 300 return false; 301 } 302 } 303 return true; 304} 305 306bool BackgroundManifestHandler::AlwaysParseForType(Manifest::Type type) const { 307 return type == Manifest::TYPE_PLATFORM_APP; 308} 309 310const std::vector<std::string> BackgroundManifestHandler::Keys() const { 311 static const char* keys[] = { 312 keys::kBackgroundAllowJsAccess, keys::kBackgroundPage, 313 keys::kBackgroundPageLegacy, keys::kBackgroundPersistent, 314 keys::kBackgroundScripts, keys::kPlatformAppBackgroundPage, 315 keys::kPlatformAppBackgroundScripts}; 316 return std::vector<std::string>(keys, keys + arraysize(keys)); 317} 318 319} // namespace extensions 320