| OLD | NEW |
| (Empty) |
| 1 // Copyright (c) 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 "chrome/common/extensions/extension.h" | |
| 6 | |
| 7 #include "base/base64.h" | |
| 8 #include "base/basictypes.h" | |
| 9 #include "base/command_line.h" | |
| 10 #include "base/files/file_path.h" | |
| 11 #include "base/i18n/rtl.h" | |
| 12 #include "base/logging.h" | |
| 13 #include "base/memory/singleton.h" | |
| 14 #include "base/stl_util.h" | |
| 15 #include "base/strings/string16.h" | |
| 16 #include "base/strings/string_number_conversions.h" | |
| 17 #include "base/strings/string_piece.h" | |
| 18 #include "base/strings/string_util.h" | |
| 19 #include "base/strings/stringprintf.h" | |
| 20 #include "base/strings/utf_string_conversions.h" | |
| 21 #include "base/values.h" | |
| 22 #include "base/version.h" | |
| 23 #include "chrome/common/extensions/extension_constants.h" | |
| 24 #include "chrome/common/extensions/permissions/permissions_data.h" | |
| 25 #include "content/public/common/url_constants.h" | |
| 26 #include "extensions/common/constants.h" | |
| 27 #include "extensions/common/error_utils.h" | |
| 28 #include "extensions/common/id_util.h" | |
| 29 #include "extensions/common/manifest.h" | |
| 30 #include "extensions/common/manifest_constants.h" | |
| 31 #include "extensions/common/manifest_handler.h" | |
| 32 #include "extensions/common/permissions/api_permission_set.h" | |
| 33 #include "extensions/common/permissions/permission_set.h" | |
| 34 #include "extensions/common/permissions/permissions_info.h" | |
| 35 #include "extensions/common/switches.h" | |
| 36 #include "extensions/common/url_pattern_set.h" | |
| 37 #include "grit/chromium_strings.h" | |
| 38 #include "grit/theme_resources.h" | |
| 39 #include "net/base/net_util.h" | |
| 40 #include "third_party/skia/include/core/SkBitmap.h" | |
| 41 #include "url/url_util.h" | |
| 42 | |
| 43 #if defined(OS_WIN) | |
| 44 #include "grit/generated_resources.h" | |
| 45 #endif | |
| 46 | |
| 47 namespace extensions { | |
| 48 | |
| 49 namespace keys = manifest_keys; | |
| 50 namespace values = manifest_values; | |
| 51 namespace errors = manifest_errors; | |
| 52 | |
| 53 namespace { | |
| 54 | |
| 55 const int kModernManifestVersion = 2; | |
| 56 const int kPEMOutputColumns = 65; | |
| 57 | |
| 58 // KEY MARKERS | |
| 59 const char kKeyBeginHeaderMarker[] = "-----BEGIN"; | |
| 60 const char kKeyBeginFooterMarker[] = "-----END"; | |
| 61 const char kKeyInfoEndMarker[] = "KEY-----"; | |
| 62 const char kPublic[] = "PUBLIC"; | |
| 63 const char kPrivate[] = "PRIVATE"; | |
| 64 | |
| 65 bool ContainsReservedCharacters(const base::FilePath& path) { | |
| 66 // We should disallow backslash '\\' as file path separator even on Windows, | |
| 67 // because the backslash is not regarded as file path separator on Linux/Mac. | |
| 68 // Extensions are cross-platform. | |
| 69 // Since FilePath uses backslash '\\' as file path separator on Windows, so we | |
| 70 // need to check manually. | |
| 71 if (path.value().find('\\') != path.value().npos) | |
| 72 return true; | |
| 73 return !net::IsSafePortableRelativePath(path); | |
| 74 } | |
| 75 | |
| 76 } // namespace | |
| 77 | |
| 78 const char Extension::kMimeType[] = "application/x-chrome-extension"; | |
| 79 | |
| 80 const int Extension::kValidWebExtentSchemes = | |
| 81 URLPattern::SCHEME_HTTP | URLPattern::SCHEME_HTTPS; | |
| 82 | |
| 83 const int Extension::kValidHostPermissionSchemes = URLPattern::SCHEME_CHROMEUI | | |
| 84 URLPattern::SCHEME_HTTP | | |
| 85 URLPattern::SCHEME_HTTPS | | |
| 86 URLPattern::SCHEME_FILE | | |
| 87 URLPattern::SCHEME_FTP; | |
| 88 | |
| 89 // | |
| 90 // Extension | |
| 91 // | |
| 92 | |
| 93 // static | |
| 94 scoped_refptr<Extension> Extension::Create(const base::FilePath& path, | |
| 95 Manifest::Location location, | |
| 96 const base::DictionaryValue& value, | |
| 97 int flags, | |
| 98 std::string* utf8_error) { | |
| 99 return Extension::Create(path, | |
| 100 location, | |
| 101 value, | |
| 102 flags, | |
| 103 std::string(), // ID is ignored if empty. | |
| 104 utf8_error); | |
| 105 } | |
| 106 | |
| 107 // TODO(sungguk): Continue removing std::string errors and replacing | |
| 108 // with string16. See http://crbug.com/71980. | |
| 109 scoped_refptr<Extension> Extension::Create(const base::FilePath& path, | |
| 110 Manifest::Location location, | |
| 111 const base::DictionaryValue& value, | |
| 112 int flags, | |
| 113 const std::string& explicit_id, | |
| 114 std::string* utf8_error) { | |
| 115 DCHECK(utf8_error); | |
| 116 string16 error; | |
| 117 scoped_ptr<extensions::Manifest> manifest( | |
| 118 new extensions::Manifest( | |
| 119 location, scoped_ptr<base::DictionaryValue>(value.DeepCopy()))); | |
| 120 | |
| 121 if (!InitExtensionID(manifest.get(), path, explicit_id, flags, &error)) { | |
| 122 *utf8_error = UTF16ToUTF8(error); | |
| 123 return NULL; | |
| 124 } | |
| 125 | |
| 126 std::vector<InstallWarning> install_warnings; | |
| 127 if (!manifest->ValidateManifest(utf8_error, &install_warnings)) { | |
| 128 return NULL; | |
| 129 } | |
| 130 | |
| 131 scoped_refptr<Extension> extension = new Extension(path, manifest.Pass()); | |
| 132 extension->install_warnings_.swap(install_warnings); | |
| 133 | |
| 134 if (!extension->InitFromValue(flags, &error)) { | |
| 135 *utf8_error = UTF16ToUTF8(error); | |
| 136 return NULL; | |
| 137 } | |
| 138 | |
| 139 return extension; | |
| 140 } | |
| 141 | |
| 142 // static | |
| 143 bool Extension::IdIsValid(const std::string& id) { | |
| 144 // Verify that the id is legal. | |
| 145 if (id.size() != (id_util::kIdSize * 2)) | |
| 146 return false; | |
| 147 | |
| 148 // We only support lowercase IDs, because IDs can be used as URL components | |
| 149 // (where GURL will lowercase it). | |
| 150 std::string temp = StringToLowerASCII(id); | |
| 151 for (size_t i = 0; i < temp.size(); i++) | |
| 152 if (temp[i] < 'a' || temp[i] > 'p') | |
| 153 return false; | |
| 154 | |
| 155 return true; | |
| 156 } | |
| 157 | |
| 158 Manifest::Type Extension::GetType() const { | |
| 159 return converted_from_user_script() ? | |
| 160 Manifest::TYPE_USER_SCRIPT : manifest_->type(); | |
| 161 } | |
| 162 | |
| 163 // static | |
| 164 GURL Extension::GetResourceURL(const GURL& extension_url, | |
| 165 const std::string& relative_path) { | |
| 166 DCHECK(extension_url.SchemeIs(extensions::kExtensionScheme)); | |
| 167 DCHECK_EQ("/", extension_url.path()); | |
| 168 | |
| 169 std::string path = relative_path; | |
| 170 | |
| 171 // If the relative path starts with "/", it is "absolute" relative to the | |
| 172 // extension base directory, but extension_url is already specified to refer | |
| 173 // to that base directory, so strip the leading "/" if present. | |
| 174 if (relative_path.size() > 0 && relative_path[0] == '/') | |
| 175 path = relative_path.substr(1); | |
| 176 | |
| 177 GURL ret_val = GURL(extension_url.spec() + path); | |
| 178 DCHECK(StartsWithASCII(ret_val.spec(), extension_url.spec(), false)); | |
| 179 | |
| 180 return ret_val; | |
| 181 } | |
| 182 | |
| 183 bool Extension::ResourceMatches(const URLPatternSet& pattern_set, | |
| 184 const std::string& resource) const { | |
| 185 return pattern_set.MatchesURL(extension_url_.Resolve(resource)); | |
| 186 } | |
| 187 | |
| 188 ExtensionResource Extension::GetResource( | |
| 189 const std::string& relative_path) const { | |
| 190 std::string new_path = relative_path; | |
| 191 // We have some legacy data where resources have leading slashes. | |
| 192 // See: http://crbug.com/121164 | |
| 193 if (!new_path.empty() && new_path.at(0) == '/') | |
| 194 new_path.erase(0, 1); | |
| 195 base::FilePath relative_file_path = base::FilePath::FromUTF8Unsafe(new_path); | |
| 196 if (ContainsReservedCharacters(relative_file_path)) | |
| 197 return ExtensionResource(); | |
| 198 ExtensionResource r(id(), path(), relative_file_path); | |
| 199 if ((creation_flags() & Extension::FOLLOW_SYMLINKS_ANYWHERE)) { | |
| 200 r.set_follow_symlinks_anywhere(); | |
| 201 } | |
| 202 return r; | |
| 203 } | |
| 204 | |
| 205 ExtensionResource Extension::GetResource( | |
| 206 const base::FilePath& relative_file_path) const { | |
| 207 if (ContainsReservedCharacters(relative_file_path)) | |
| 208 return ExtensionResource(); | |
| 209 ExtensionResource r(id(), path(), relative_file_path); | |
| 210 if ((creation_flags() & Extension::FOLLOW_SYMLINKS_ANYWHERE)) { | |
| 211 r.set_follow_symlinks_anywhere(); | |
| 212 } | |
| 213 return r; | |
| 214 } | |
| 215 | |
| 216 // TODO(rafaelw): Move ParsePEMKeyBytes, ProducePEM & FormatPEMForOutput to a | |
| 217 // util class in base: | |
| 218 // http://code.google.com/p/chromium/issues/detail?id=13572 | |
| 219 // static | |
| 220 bool Extension::ParsePEMKeyBytes(const std::string& input, | |
| 221 std::string* output) { | |
| 222 DCHECK(output); | |
| 223 if (!output) | |
| 224 return false; | |
| 225 if (input.length() == 0) | |
| 226 return false; | |
| 227 | |
| 228 std::string working = input; | |
| 229 if (StartsWithASCII(working, kKeyBeginHeaderMarker, true)) { | |
| 230 working = CollapseWhitespaceASCII(working, true); | |
| 231 size_t header_pos = working.find(kKeyInfoEndMarker, | |
| 232 sizeof(kKeyBeginHeaderMarker) - 1); | |
| 233 if (header_pos == std::string::npos) | |
| 234 return false; | |
| 235 size_t start_pos = header_pos + sizeof(kKeyInfoEndMarker) - 1; | |
| 236 size_t end_pos = working.rfind(kKeyBeginFooterMarker); | |
| 237 if (end_pos == std::string::npos) | |
| 238 return false; | |
| 239 if (start_pos >= end_pos) | |
| 240 return false; | |
| 241 | |
| 242 working = working.substr(start_pos, end_pos - start_pos); | |
| 243 if (working.length() == 0) | |
| 244 return false; | |
| 245 } | |
| 246 | |
| 247 return base::Base64Decode(working, output); | |
| 248 } | |
| 249 | |
| 250 // static | |
| 251 bool Extension::ProducePEM(const std::string& input, std::string* output) { | |
| 252 DCHECK(output); | |
| 253 return (input.length() == 0) ? false : base::Base64Encode(input, output); | |
| 254 } | |
| 255 | |
| 256 // static | |
| 257 bool Extension::FormatPEMForFileOutput(const std::string& input, | |
| 258 std::string* output, | |
| 259 bool is_public) { | |
| 260 DCHECK(output); | |
| 261 if (input.length() == 0) | |
| 262 return false; | |
| 263 *output = ""; | |
| 264 output->append(kKeyBeginHeaderMarker); | |
| 265 output->append(" "); | |
| 266 output->append(is_public ? kPublic : kPrivate); | |
| 267 output->append(" "); | |
| 268 output->append(kKeyInfoEndMarker); | |
| 269 output->append("\n"); | |
| 270 for (size_t i = 0; i < input.length(); ) { | |
| 271 int slice = std::min<int>(input.length() - i, kPEMOutputColumns); | |
| 272 output->append(input.substr(i, slice)); | |
| 273 output->append("\n"); | |
| 274 i += slice; | |
| 275 } | |
| 276 output->append(kKeyBeginFooterMarker); | |
| 277 output->append(" "); | |
| 278 output->append(is_public ? kPublic : kPrivate); | |
| 279 output->append(" "); | |
| 280 output->append(kKeyInfoEndMarker); | |
| 281 output->append("\n"); | |
| 282 | |
| 283 return true; | |
| 284 } | |
| 285 | |
| 286 // static | |
| 287 GURL Extension::GetBaseURLFromExtensionId(const std::string& extension_id) { | |
| 288 return GURL(std::string(extensions::kExtensionScheme) + | |
| 289 content::kStandardSchemeSeparator + extension_id + "/"); | |
| 290 } | |
| 291 | |
| 292 bool Extension::HasAPIPermission(APIPermission::ID permission) const { | |
| 293 return PermissionsData::HasAPIPermission(this, permission); | |
| 294 } | |
| 295 | |
| 296 bool Extension::HasAPIPermission(const std::string& permission_name) const { | |
| 297 return PermissionsData::HasAPIPermission(this, permission_name); | |
| 298 } | |
| 299 | |
| 300 scoped_refptr<const PermissionSet> Extension::GetActivePermissions() const { | |
| 301 return PermissionsData::GetActivePermissions(this); | |
| 302 } | |
| 303 | |
| 304 bool Extension::ShowConfigureContextMenus() const { | |
| 305 // Don't show context menu for component extensions. We might want to show | |
| 306 // options for component extension button but now there is no component | |
| 307 // extension with options. All other menu items like uninstall have | |
| 308 // no sense for component extensions. | |
| 309 return location() != Manifest::COMPONENT; | |
| 310 } | |
| 311 | |
| 312 bool Extension::OverlapsWithOrigin(const GURL& origin) const { | |
| 313 if (url() == origin) | |
| 314 return true; | |
| 315 | |
| 316 if (web_extent().is_empty()) | |
| 317 return false; | |
| 318 | |
| 319 // Note: patterns and extents ignore port numbers. | |
| 320 URLPattern origin_only_pattern(kValidWebExtentSchemes); | |
| 321 if (!origin_only_pattern.SetScheme(origin.scheme())) | |
| 322 return false; | |
| 323 origin_only_pattern.SetHost(origin.host()); | |
| 324 origin_only_pattern.SetPath("/*"); | |
| 325 | |
| 326 URLPatternSet origin_only_pattern_list; | |
| 327 origin_only_pattern_list.AddPattern(origin_only_pattern); | |
| 328 | |
| 329 return web_extent().OverlapsWith(origin_only_pattern_list); | |
| 330 } | |
| 331 | |
| 332 bool Extension::RequiresSortOrdinal() const { | |
| 333 return is_app() && (display_in_launcher_ || display_in_new_tab_page_); | |
| 334 } | |
| 335 | |
| 336 bool Extension::ShouldDisplayInAppLauncher() const { | |
| 337 // Only apps should be displayed in the launcher. | |
| 338 return is_app() && display_in_launcher_; | |
| 339 } | |
| 340 | |
| 341 bool Extension::ShouldDisplayInNewTabPage() const { | |
| 342 // Only apps should be displayed on the NTP. | |
| 343 return is_app() && display_in_new_tab_page_; | |
| 344 } | |
| 345 | |
| 346 bool Extension::ShouldDisplayInExtensionSettings() const { | |
| 347 // Don't show for themes since the settings UI isn't really useful for them. | |
| 348 if (is_theme()) | |
| 349 return false; | |
| 350 | |
| 351 // Don't show component extensions and invisible apps. | |
| 352 if (ShouldNotBeVisible()) | |
| 353 return false; | |
| 354 | |
| 355 // Always show unpacked extensions and apps. | |
| 356 if (Manifest::IsUnpackedLocation(location())) | |
| 357 return true; | |
| 358 | |
| 359 // Unless they are unpacked, never show hosted apps. Note: We intentionally | |
| 360 // show packaged apps and platform apps because there are some pieces of | |
| 361 // functionality that are only available in chrome://extensions/ but which | |
| 362 // are needed for packaged and platform apps. For example, inspecting | |
| 363 // background pages. See http://crbug.com/116134. | |
| 364 if (is_hosted_app()) | |
| 365 return false; | |
| 366 | |
| 367 return true; | |
| 368 } | |
| 369 | |
| 370 bool Extension::ShouldNotBeVisible() const { | |
| 371 // Don't show component extensions because they are only extensions as an | |
| 372 // implementation detail of Chrome. | |
| 373 if (location() == Manifest::COMPONENT && | |
| 374 !CommandLine::ForCurrentProcess()->HasSwitch( | |
| 375 switches::kShowComponentExtensionOptions)) { | |
| 376 return true; | |
| 377 } | |
| 378 | |
| 379 // Always show unpacked extensions and apps. | |
| 380 if (Manifest::IsUnpackedLocation(location())) | |
| 381 return false; | |
| 382 | |
| 383 // Don't show apps that aren't visible in either launcher or ntp. | |
| 384 if (is_app() && !ShouldDisplayInAppLauncher() && !ShouldDisplayInNewTabPage()) | |
| 385 return true; | |
| 386 | |
| 387 return false; | |
| 388 } | |
| 389 | |
| 390 Extension::ManifestData* Extension::GetManifestData(const std::string& key) | |
| 391 const { | |
| 392 DCHECK(finished_parsing_manifest_ || thread_checker_.CalledOnValidThread()); | |
| 393 ManifestDataMap::const_iterator iter = manifest_data_.find(key); | |
| 394 if (iter != manifest_data_.end()) | |
| 395 return iter->second.get(); | |
| 396 return NULL; | |
| 397 } | |
| 398 | |
| 399 void Extension::SetManifestData(const std::string& key, | |
| 400 Extension::ManifestData* data) { | |
| 401 DCHECK(!finished_parsing_manifest_ && thread_checker_.CalledOnValidThread()); | |
| 402 manifest_data_[key] = linked_ptr<ManifestData>(data); | |
| 403 } | |
| 404 | |
| 405 Manifest::Location Extension::location() const { | |
| 406 return manifest_->location(); | |
| 407 } | |
| 408 | |
| 409 const std::string& Extension::id() const { | |
| 410 return manifest_->extension_id(); | |
| 411 } | |
| 412 | |
| 413 const std::string Extension::VersionString() const { | |
| 414 return version()->GetString(); | |
| 415 } | |
| 416 | |
| 417 void Extension::AddInstallWarning(const InstallWarning& new_warning) { | |
| 418 install_warnings_.push_back(new_warning); | |
| 419 } | |
| 420 | |
| 421 void Extension::AddInstallWarnings( | |
| 422 const std::vector<InstallWarning>& new_warnings) { | |
| 423 install_warnings_.insert(install_warnings_.end(), | |
| 424 new_warnings.begin(), new_warnings.end()); | |
| 425 } | |
| 426 | |
| 427 bool Extension::is_app() const { | |
| 428 return manifest_->is_app(); | |
| 429 } | |
| 430 | |
| 431 bool Extension::is_platform_app() const { | |
| 432 return manifest_->is_platform_app(); | |
| 433 } | |
| 434 | |
| 435 bool Extension::is_hosted_app() const { | |
| 436 return manifest()->is_hosted_app(); | |
| 437 } | |
| 438 | |
| 439 bool Extension::is_legacy_packaged_app() const { | |
| 440 return manifest()->is_legacy_packaged_app(); | |
| 441 } | |
| 442 | |
| 443 bool Extension::is_extension() const { | |
| 444 return manifest()->is_extension(); | |
| 445 } | |
| 446 | |
| 447 bool Extension::can_be_incognito_enabled() const { | |
| 448 // Only component platform apps are supported in incognito. | |
| 449 return !is_platform_app() || location() == Manifest::COMPONENT; | |
| 450 } | |
| 451 | |
| 452 bool Extension::force_incognito_enabled() const { | |
| 453 return PermissionsData::HasAPIPermission(this, APIPermission::kProxy); | |
| 454 } | |
| 455 | |
| 456 void Extension::AddWebExtentPattern(const URLPattern& pattern) { | |
| 457 extent_.AddPattern(pattern); | |
| 458 } | |
| 459 | |
| 460 bool Extension::is_theme() const { | |
| 461 return manifest()->is_theme(); | |
| 462 } | |
| 463 | |
| 464 // static | |
| 465 bool Extension::InitExtensionID(extensions::Manifest* manifest, | |
| 466 const base::FilePath& path, | |
| 467 const std::string& explicit_id, | |
| 468 int creation_flags, | |
| 469 string16* error) { | |
| 470 if (!explicit_id.empty()) { | |
| 471 manifest->set_extension_id(explicit_id); | |
| 472 return true; | |
| 473 } | |
| 474 | |
| 475 if (manifest->HasKey(keys::kPublicKey)) { | |
| 476 std::string public_key; | |
| 477 std::string public_key_bytes; | |
| 478 if (!manifest->GetString(keys::kPublicKey, &public_key) || | |
| 479 !ParsePEMKeyBytes(public_key, &public_key_bytes)) { | |
| 480 *error = ASCIIToUTF16(errors::kInvalidKey); | |
| 481 return false; | |
| 482 } | |
| 483 std::string extension_id = id_util::GenerateId(public_key_bytes); | |
| 484 manifest->set_extension_id(extension_id); | |
| 485 return true; | |
| 486 } | |
| 487 | |
| 488 if (creation_flags & REQUIRE_KEY) { | |
| 489 *error = ASCIIToUTF16(errors::kInvalidKey); | |
| 490 return false; | |
| 491 } else { | |
| 492 // If there is a path, we generate the ID from it. This is useful for | |
| 493 // development mode, because it keeps the ID stable across restarts and | |
| 494 // reloading the extension. | |
| 495 std::string extension_id = id_util::GenerateIdForPath(path); | |
| 496 if (extension_id.empty()) { | |
| 497 NOTREACHED() << "Could not create ID from path."; | |
| 498 return false; | |
| 499 } | |
| 500 manifest->set_extension_id(extension_id); | |
| 501 return true; | |
| 502 } | |
| 503 } | |
| 504 | |
| 505 Extension::Extension(const base::FilePath& path, | |
| 506 scoped_ptr<extensions::Manifest> manifest) | |
| 507 : manifest_version_(0), | |
| 508 converted_from_user_script_(false), | |
| 509 manifest_(manifest.release()), | |
| 510 finished_parsing_manifest_(false), | |
| 511 display_in_launcher_(true), | |
| 512 display_in_new_tab_page_(true), | |
| 513 wants_file_access_(false), | |
| 514 creation_flags_(0) { | |
| 515 DCHECK(path.empty() || path.IsAbsolute()); | |
| 516 path_ = id_util::MaybeNormalizePath(path); | |
| 517 } | |
| 518 | |
| 519 Extension::~Extension() { | |
| 520 } | |
| 521 | |
| 522 bool Extension::InitFromValue(int flags, string16* error) { | |
| 523 DCHECK(error); | |
| 524 | |
| 525 creation_flags_ = flags; | |
| 526 | |
| 527 // Important to load manifest version first because many other features | |
| 528 // depend on its value. | |
| 529 if (!LoadManifestVersion(error)) | |
| 530 return false; | |
| 531 | |
| 532 if (!LoadRequiredFeatures(error)) | |
| 533 return false; | |
| 534 | |
| 535 // We don't need to validate because InitExtensionID already did that. | |
| 536 manifest_->GetString(keys::kPublicKey, &public_key_); | |
| 537 | |
| 538 extension_url_ = Extension::GetBaseURLFromExtensionId(id()); | |
| 539 | |
| 540 // Load App settings. LoadExtent at least has to be done before | |
| 541 // ParsePermissions(), because the valid permissions depend on what type of | |
| 542 // package this is. | |
| 543 if (is_app() && !LoadAppFeatures(error)) | |
| 544 return false; | |
| 545 | |
| 546 permissions_data_.reset(new PermissionsData); | |
| 547 if (!permissions_data_->ParsePermissions(this, error)) | |
| 548 return false; | |
| 549 | |
| 550 if (manifest_->HasKey(keys::kConvertedFromUserScript)) { | |
| 551 manifest_->GetBoolean(keys::kConvertedFromUserScript, | |
| 552 &converted_from_user_script_); | |
| 553 } | |
| 554 | |
| 555 if (!LoadSharedFeatures(error)) | |
| 556 return false; | |
| 557 | |
| 558 finished_parsing_manifest_ = true; | |
| 559 | |
| 560 permissions_data_->InitializeManifestPermissions(this); | |
| 561 permissions_data_->FinalizePermissions(this); | |
| 562 | |
| 563 return true; | |
| 564 } | |
| 565 | |
| 566 bool Extension::LoadRequiredFeatures(string16* error) { | |
| 567 if (!LoadName(error) || | |
| 568 !LoadVersion(error)) | |
| 569 return false; | |
| 570 return true; | |
| 571 } | |
| 572 | |
| 573 bool Extension::LoadName(string16* error) { | |
| 574 string16 localized_name; | |
| 575 if (!manifest_->GetString(keys::kName, &localized_name)) { | |
| 576 *error = ASCIIToUTF16(errors::kInvalidName); | |
| 577 return false; | |
| 578 } | |
| 579 non_localized_name_ = UTF16ToUTF8(localized_name); | |
| 580 base::i18n::AdjustStringForLocaleDirection(&localized_name); | |
| 581 name_ = UTF16ToUTF8(localized_name); | |
| 582 return true; | |
| 583 } | |
| 584 | |
| 585 bool Extension::LoadVersion(string16* error) { | |
| 586 std::string version_str; | |
| 587 if (!manifest_->GetString(keys::kVersion, &version_str)) { | |
| 588 *error = ASCIIToUTF16(errors::kInvalidVersion); | |
| 589 return false; | |
| 590 } | |
| 591 version_.reset(new Version(version_str)); | |
| 592 if (!version_->IsValid() || version_->components().size() > 4) { | |
| 593 *error = ASCIIToUTF16(errors::kInvalidVersion); | |
| 594 return false; | |
| 595 } | |
| 596 return true; | |
| 597 } | |
| 598 | |
| 599 bool Extension::LoadAppFeatures(string16* error) { | |
| 600 if (!LoadExtent(keys::kWebURLs, &extent_, | |
| 601 errors::kInvalidWebURLs, errors::kInvalidWebURL, error)) { | |
| 602 return false; | |
| 603 } | |
| 604 if (manifest_->HasKey(keys::kDisplayInLauncher) && | |
| 605 !manifest_->GetBoolean(keys::kDisplayInLauncher, &display_in_launcher_)) { | |
| 606 *error = ASCIIToUTF16(errors::kInvalidDisplayInLauncher); | |
| 607 return false; | |
| 608 } | |
| 609 if (manifest_->HasKey(keys::kDisplayInNewTabPage)) { | |
| 610 if (!manifest_->GetBoolean(keys::kDisplayInNewTabPage, | |
| 611 &display_in_new_tab_page_)) { | |
| 612 *error = ASCIIToUTF16(errors::kInvalidDisplayInNewTabPage); | |
| 613 return false; | |
| 614 } | |
| 615 } else { | |
| 616 // Inherit default from display_in_launcher property. | |
| 617 display_in_new_tab_page_ = display_in_launcher_; | |
| 618 } | |
| 619 return true; | |
| 620 } | |
| 621 | |
| 622 bool Extension::LoadExtent(const char* key, | |
| 623 URLPatternSet* extent, | |
| 624 const char* list_error, | |
| 625 const char* value_error, | |
| 626 string16* error) { | |
| 627 const base::Value* temp_pattern_value = NULL; | |
| 628 if (!manifest_->Get(key, &temp_pattern_value)) | |
| 629 return true; | |
| 630 | |
| 631 const base::ListValue* pattern_list = NULL; | |
| 632 if (!temp_pattern_value->GetAsList(&pattern_list)) { | |
| 633 *error = ASCIIToUTF16(list_error); | |
| 634 return false; | |
| 635 } | |
| 636 | |
| 637 for (size_t i = 0; i < pattern_list->GetSize(); ++i) { | |
| 638 std::string pattern_string; | |
| 639 if (!pattern_list->GetString(i, &pattern_string)) { | |
| 640 *error = ErrorUtils::FormatErrorMessageUTF16(value_error, | |
| 641 base::UintToString(i), | |
| 642 errors::kExpectString); | |
| 643 return false; | |
| 644 } | |
| 645 | |
| 646 URLPattern pattern(kValidWebExtentSchemes); | |
| 647 URLPattern::ParseResult parse_result = pattern.Parse(pattern_string); | |
| 648 if (parse_result == URLPattern::PARSE_ERROR_EMPTY_PATH) { | |
| 649 pattern_string += "/"; | |
| 650 parse_result = pattern.Parse(pattern_string); | |
| 651 } | |
| 652 | |
| 653 if (parse_result != URLPattern::PARSE_SUCCESS) { | |
| 654 *error = ErrorUtils::FormatErrorMessageUTF16( | |
| 655 value_error, | |
| 656 base::UintToString(i), | |
| 657 URLPattern::GetParseResultString(parse_result)); | |
| 658 return false; | |
| 659 } | |
| 660 | |
| 661 // Do not allow authors to claim "<all_urls>". | |
| 662 if (pattern.match_all_urls()) { | |
| 663 *error = ErrorUtils::FormatErrorMessageUTF16( | |
| 664 value_error, | |
| 665 base::UintToString(i), | |
| 666 errors::kCannotClaimAllURLsInExtent); | |
| 667 return false; | |
| 668 } | |
| 669 | |
| 670 // Do not allow authors to claim "*" for host. | |
| 671 if (pattern.host().empty()) { | |
| 672 *error = ErrorUtils::FormatErrorMessageUTF16( | |
| 673 value_error, | |
| 674 base::UintToString(i), | |
| 675 errors::kCannotClaimAllHostsInExtent); | |
| 676 return false; | |
| 677 } | |
| 678 | |
| 679 // We do not allow authors to put wildcards in their paths. Instead, we | |
| 680 // imply one at the end. | |
| 681 if (pattern.path().find('*') != std::string::npos) { | |
| 682 *error = ErrorUtils::FormatErrorMessageUTF16( | |
| 683 value_error, | |
| 684 base::UintToString(i), | |
| 685 errors::kNoWildCardsInPaths); | |
| 686 return false; | |
| 687 } | |
| 688 pattern.SetPath(pattern.path() + '*'); | |
| 689 | |
| 690 extent->AddPattern(pattern); | |
| 691 } | |
| 692 | |
| 693 return true; | |
| 694 } | |
| 695 | |
| 696 bool Extension::LoadSharedFeatures(string16* error) { | |
| 697 if (!LoadDescription(error) || | |
| 698 !ManifestHandler::ParseExtension(this, error) || | |
| 699 !LoadShortName(error)) | |
| 700 return false; | |
| 701 | |
| 702 return true; | |
| 703 } | |
| 704 | |
| 705 bool Extension::LoadDescription(string16* error) { | |
| 706 if (manifest_->HasKey(keys::kDescription) && | |
| 707 !manifest_->GetString(keys::kDescription, &description_)) { | |
| 708 *error = ASCIIToUTF16(errors::kInvalidDescription); | |
| 709 return false; | |
| 710 } | |
| 711 return true; | |
| 712 } | |
| 713 | |
| 714 bool Extension::LoadManifestVersion(string16* error) { | |
| 715 // Get the original value out of the dictionary so that we can validate it | |
| 716 // more strictly. | |
| 717 if (manifest_->value()->HasKey(keys::kManifestVersion)) { | |
| 718 int manifest_version = 1; | |
| 719 if (!manifest_->GetInteger(keys::kManifestVersion, &manifest_version) || | |
| 720 manifest_version < 1) { | |
| 721 *error = ASCIIToUTF16(errors::kInvalidManifestVersion); | |
| 722 return false; | |
| 723 } | |
| 724 } | |
| 725 | |
| 726 manifest_version_ = manifest_->GetManifestVersion(); | |
| 727 if (manifest_version_ < kModernManifestVersion && | |
| 728 ((creation_flags_ & REQUIRE_MODERN_MANIFEST_VERSION && | |
| 729 !CommandLine::ForCurrentProcess()->HasSwitch( | |
| 730 switches::kAllowLegacyExtensionManifests)) || | |
| 731 GetType() == Manifest::TYPE_PLATFORM_APP)) { | |
| 732 *error = ErrorUtils::FormatErrorMessageUTF16( | |
| 733 errors::kInvalidManifestVersionOld, | |
| 734 base::IntToString(kModernManifestVersion), | |
| 735 is_platform_app() ? "apps" : "extensions"); | |
| 736 return false; | |
| 737 } | |
| 738 | |
| 739 return true; | |
| 740 } | |
| 741 | |
| 742 bool Extension::LoadShortName(string16* error) { | |
| 743 if (manifest_->HasKey(keys::kShortName)) { | |
| 744 string16 localized_short_name; | |
| 745 if (!manifest_->GetString(keys::kShortName, &localized_short_name) || | |
| 746 localized_short_name.empty()) { | |
| 747 *error = ASCIIToUTF16(errors::kInvalidShortName); | |
| 748 return false; | |
| 749 } | |
| 750 | |
| 751 base::i18n::AdjustStringForLocaleDirection(&localized_short_name); | |
| 752 short_name_ = UTF16ToUTF8(localized_short_name); | |
| 753 } else { | |
| 754 short_name_ = name_; | |
| 755 } | |
| 756 return true; | |
| 757 } | |
| 758 | |
| 759 ExtensionInfo::ExtensionInfo(const base::DictionaryValue* manifest, | |
| 760 const std::string& id, | |
| 761 const base::FilePath& path, | |
| 762 Manifest::Location location) | |
| 763 : extension_id(id), | |
| 764 extension_path(path), | |
| 765 extension_location(location) { | |
| 766 if (manifest) | |
| 767 extension_manifest.reset(manifest->DeepCopy()); | |
| 768 } | |
| 769 | |
| 770 ExtensionInfo::~ExtensionInfo() {} | |
| 771 | |
| 772 InstalledExtensionInfo::InstalledExtensionInfo( | |
| 773 const Extension* extension, | |
| 774 bool is_update, | |
| 775 const std::string& old_name) | |
| 776 : extension(extension), | |
| 777 is_update(is_update), | |
| 778 old_name(old_name) {} | |
| 779 | |
| 780 UnloadedExtensionInfo::UnloadedExtensionInfo( | |
| 781 const Extension* extension, | |
| 782 UnloadedExtensionInfo::Reason reason) | |
| 783 : reason(reason), | |
| 784 extension(extension) {} | |
| 785 | |
| 786 UpdatedExtensionPermissionsInfo::UpdatedExtensionPermissionsInfo( | |
| 787 const Extension* extension, | |
| 788 const PermissionSet* permissions, | |
| 789 Reason reason) | |
| 790 : reason(reason), | |
| 791 extension(extension), | |
| 792 permissions(permissions) {} | |
| 793 | |
| 794 } // namespace extensions | |
| OLD | NEW |