1// Copyright (c) 2011 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 <algorithm> 8 9#include "base/base64.h" 10#include "base/basictypes.h" 11#include "base/command_line.h" 12#include "base/file_path.h" 13#include "base/file_util.h" 14#include "base/i18n/rtl.h" 15#include "base/logging.h" 16#include "base/memory/singleton.h" 17#include "base/stl_util-inl.h" 18#include "base/string16.h" 19#include "base/string_number_conversions.h" 20#include "base/utf_string_conversions.h" 21#include "base/values.h" 22#include "base/version.h" 23#include "crypto/sha2.h" 24#include "crypto/third_party/nss/blapi.h" 25#include "chrome/common/chrome_constants.h" 26#include "chrome/common/chrome_switches.h" 27#include "chrome/common/chrome_version_info.h" 28#include "chrome/common/extensions/extension_action.h" 29#include "chrome/common/extensions/extension_constants.h" 30#include "chrome/common/extensions/extension_error_utils.h" 31#include "chrome/common/extensions/extension_l10n_util.h" 32#include "chrome/common/extensions/extension_resource.h" 33#include "chrome/common/extensions/extension_sidebar_defaults.h" 34#include "chrome/common/extensions/extension_sidebar_utils.h" 35#include "chrome/common/extensions/file_browser_handler.h" 36#include "chrome/common/extensions/user_script.h" 37#include "chrome/common/url_constants.h" 38#include "googleurl/src/url_util.h" 39#include "grit/chromium_strings.h" 40#include "grit/generated_resources.h" 41#include "grit/theme_resources.h" 42#include "net/base/registry_controlled_domain.h" 43#include "third_party/skia/include/core/SkBitmap.h" 44#include "ui/base/l10n/l10n_util.h" 45#include "ui/base/resource/resource_bundle.h" 46#include "webkit/glue/image_decoder.h" 47 48namespace keys = extension_manifest_keys; 49namespace values = extension_manifest_values; 50namespace errors = extension_manifest_errors; 51 52namespace { 53 54const int kPEMOutputColumns = 65; 55 56// KEY MARKERS 57const char kKeyBeginHeaderMarker[] = "-----BEGIN"; 58const char kKeyBeginFooterMarker[] = "-----END"; 59const char kKeyInfoEndMarker[] = "KEY-----"; 60const char kPublic[] = "PUBLIC"; 61const char kPrivate[] = "PRIVATE"; 62 63const int kRSAKeySize = 1024; 64 65// Converts a normal hexadecimal string into the alphabet used by extensions. 66// We use the characters 'a'-'p' instead of '0'-'f' to avoid ever having a 67// completely numeric host, since some software interprets that as an IP 68// address. 69static void ConvertHexadecimalToIDAlphabet(std::string* id) { 70 for (size_t i = 0; i < id->size(); ++i) { 71 int val; 72 if (base::HexStringToInt(id->begin() + i, id->begin() + i + 1, &val)) 73 (*id)[i] = val + 'a'; 74 else 75 (*id)[i] = 'a'; 76 } 77} 78 79// These keys are allowed by all crx files (apps, extensions, themes, etc). 80static const char* kBaseCrxKeys[] = { 81 keys::kCurrentLocale, 82 keys::kDefaultLocale, 83 keys::kDescription, 84 keys::kIcons, 85 keys::kName, 86 keys::kPublicKey, 87 keys::kSignature, 88 keys::kVersion, 89 keys::kUpdateURL 90}; 91 92bool IsBaseCrxKey(const std::string& key) { 93 for (size_t i = 0; i < arraysize(kBaseCrxKeys); ++i) { 94 if (key == kBaseCrxKeys[i]) 95 return true; 96 } 97 98 return false; 99} 100 101// Constant used to represent an undefined l10n message id. 102const int kUndefinedMessageId = -1; 103 104// Names of API modules that do not require a permission. 105const char kBrowserActionModuleName[] = "browserAction"; 106const char kBrowserActionsModuleName[] = "browserActions"; 107const char kDevToolsModuleName[] = "devtools"; 108const char kExtensionModuleName[] = "extension"; 109const char kI18NModuleName[] = "i18n"; 110const char kOmniboxModuleName[] = "omnibox"; 111const char kPageActionModuleName[] = "pageAction"; 112const char kPageActionsModuleName[] = "pageActions"; 113const char kTestModuleName[] = "test"; 114 115// Names of modules that can be used without listing it in the permissions 116// section of the manifest. 117const char* kNonPermissionModuleNames[] = { 118 kBrowserActionModuleName, 119 kBrowserActionsModuleName, 120 kDevToolsModuleName, 121 kExtensionModuleName, 122 kI18NModuleName, 123 kOmniboxModuleName, 124 kPageActionModuleName, 125 kPageActionsModuleName, 126 kTestModuleName 127}; 128const size_t kNumNonPermissionModuleNames = 129 arraysize(kNonPermissionModuleNames); 130 131// Names of functions (within modules requiring permissions) that can be used 132// without asking for the module permission. In other words, functions you can 133// use with no permissions specified. 134const char* kNonPermissionFunctionNames[] = { 135 "tabs.create", 136 "tabs.update" 137}; 138const size_t kNumNonPermissionFunctionNames = 139 arraysize(kNonPermissionFunctionNames); 140 141// A singleton object containing global data needed by the extension objects. 142class ExtensionConfig { 143 public: 144 static ExtensionConfig* GetInstance() { 145 return Singleton<ExtensionConfig>::get(); 146 } 147 148 Extension::PermissionMessage::MessageId GetPermissionMessageId( 149 const std::string& permission) { 150 return Extension::kPermissions[permission_map_[permission]].message_id; 151 } 152 153 Extension::ScriptingWhitelist* whitelist() { return &scripting_whitelist_; } 154 155 private: 156 friend struct DefaultSingletonTraits<ExtensionConfig>; 157 158 ExtensionConfig() { 159 for (size_t i = 0; i < Extension::kNumPermissions; ++i) 160 permission_map_[Extension::kPermissions[i].name] = i; 161 }; 162 163 ~ExtensionConfig() { } 164 165 std::map<const std::string, size_t> permission_map_; 166 167 // A whitelist of extensions that can script anywhere. Do not add to this 168 // list (except in tests) without consulting the Extensions team first. 169 // Note: Component extensions have this right implicitly and do not need to be 170 // added to this list. 171 Extension::ScriptingWhitelist scripting_whitelist_; 172}; 173 174// Aliased to kTabPermission for purposes of API checks, but not allowed 175// in the permissions field of the manifest. 176static const char kWindowPermission[] = "windows"; 177 178// Rank extension locations in a way that allows 179// Extension::GetHigherPriorityLocation() to compare locations. 180// An extension installed from two locations will have the location 181// with the higher rank, as returned by this function. The actual 182// integer values may change, and should never be persisted. 183int GetLocationRank(Extension::Location location) { 184 const int kInvalidRank = -1; 185 int rank = kInvalidRank; // Will CHECK that rank is not kInvalidRank. 186 187 switch (location) { 188 // Component extensions can not be overriden by any other type. 189 case Extension::COMPONENT: 190 rank = 6; 191 break; 192 193 // Policy controlled extensions may not be overridden by any type 194 // that is not part of chrome. 195 case Extension::EXTERNAL_POLICY_DOWNLOAD: 196 rank = 5; 197 break; 198 199 // A developer-loaded extension should override any installed type 200 // that a user can disable. 201 case Extension::LOAD: 202 rank = 4; 203 break; 204 205 // The relative priority of various external sources is not important, 206 // but having some order ensures deterministic behavior. 207 case Extension::EXTERNAL_REGISTRY: 208 rank = 3; 209 break; 210 211 case Extension::EXTERNAL_PREF: 212 rank = 2; 213 break; 214 215 case Extension::EXTERNAL_PREF_DOWNLOAD: 216 rank = 1; 217 break; 218 219 // User installed extensions are overridden by any external type. 220 case Extension::INTERNAL: 221 rank = 0; 222 break; 223 224 default: 225 NOTREACHED() << "Need to add new extension locaton " << location; 226 } 227 228 CHECK(rank != kInvalidRank); 229 return rank; 230} 231 232} // namespace 233 234const FilePath::CharType Extension::kManifestFilename[] = 235 FILE_PATH_LITERAL("manifest.json"); 236const FilePath::CharType Extension::kLocaleFolder[] = 237 FILE_PATH_LITERAL("_locales"); 238const FilePath::CharType Extension::kMessagesFilename[] = 239 FILE_PATH_LITERAL("messages.json"); 240 241#if defined(OS_WIN) 242const char Extension::kExtensionRegistryPath[] = 243 "Software\\Google\\Chrome\\Extensions"; 244#endif 245 246// first 16 bytes of SHA256 hashed public key. 247const size_t Extension::kIdSize = 16; 248 249const char Extension::kMimeType[] = "application/x-chrome-extension"; 250 251const int Extension::kIconSizes[] = { 252 EXTENSION_ICON_LARGE, 253 EXTENSION_ICON_MEDIUM, 254 EXTENSION_ICON_SMALL, 255 EXTENSION_ICON_SMALLISH, 256 EXTENSION_ICON_BITTY 257}; 258 259const int Extension::kPageActionIconMaxSize = 19; 260const int Extension::kBrowserActionIconMaxSize = 19; 261const int Extension::kSidebarIconMaxSize = 16; 262 263// Explicit permissions -- permission declaration required. 264const char Extension::kBackgroundPermission[] = "background"; 265const char Extension::kBookmarkPermission[] = "bookmarks"; 266const char Extension::kContextMenusPermission[] = "contextMenus"; 267const char Extension::kContentSettingsPermission[] = "contentSettings"; 268const char Extension::kCookiePermission[] = "cookies"; 269const char Extension::kChromeosInfoPrivatePermissions[] = "chromeosInfoPrivate"; 270const char Extension::kDebuggerPermission[] = "debugger"; 271const char Extension::kExperimentalPermission[] = "experimental"; 272const char Extension::kFileBrowserHandlerPermission[] = "fileBrowserHandler"; 273const char Extension::kFileBrowserPrivatePermission[] = "fileBrowserPrivate"; 274const char Extension::kGeolocationPermission[] = "geolocation"; 275const char Extension::kHistoryPermission[] = "history"; 276const char Extension::kIdlePermission[] = "idle"; 277const char Extension::kManagementPermission[] = "management"; 278const char Extension::kNotificationPermission[] = "notifications"; 279const char Extension::kProxyPermission[] = "proxy"; 280const char Extension::kTabPermission[] = "tabs"; 281const char Extension::kUnlimitedStoragePermission[] = "unlimitedStorage"; 282const char Extension::kWebstorePrivatePermission[] = "webstorePrivate"; 283 284// In general, all permissions should have an install message. 285// See ExtensionsTest.PermissionMessages for an explanation of each 286// exception. 287const Extension::Permission Extension::kPermissions[] = { 288 { kBackgroundPermission, PermissionMessage::ID_NONE }, 289 { kBookmarkPermission, PermissionMessage::ID_BOOKMARKS }, 290 { kChromeosInfoPrivatePermissions, PermissionMessage::ID_NONE }, 291 { kContentSettingsPermission, PermissionMessage::ID_NONE }, 292 { kContextMenusPermission, PermissionMessage::ID_NONE }, 293 { kCookiePermission, PermissionMessage::ID_NONE }, 294 { kDebuggerPermission, PermissionMessage::ID_DEBUGGER }, 295 { kExperimentalPermission, PermissionMessage::ID_NONE }, 296 { kFileBrowserHandlerPermission, PermissionMessage::ID_NONE }, 297 { kFileBrowserPrivatePermission, PermissionMessage::ID_NONE }, 298 { kGeolocationPermission, PermissionMessage::ID_GEOLOCATION }, 299 { kIdlePermission, PermissionMessage::ID_NONE }, 300 { kHistoryPermission, PermissionMessage::ID_BROWSING_HISTORY }, 301 { kManagementPermission, PermissionMessage::ID_MANAGEMENT }, 302 { kNotificationPermission, PermissionMessage::ID_NONE }, 303 { kProxyPermission, PermissionMessage::ID_NONE }, 304 { kTabPermission, PermissionMessage::ID_TABS }, 305 { kUnlimitedStoragePermission, PermissionMessage::ID_NONE }, 306 { kWebstorePrivatePermission, PermissionMessage::ID_NONE } 307}; 308const size_t Extension::kNumPermissions = 309 arraysize(Extension::kPermissions); 310 311const char* const Extension::kHostedAppPermissionNames[] = { 312 Extension::kBackgroundPermission, 313 Extension::kGeolocationPermission, 314 Extension::kNotificationPermission, 315 Extension::kUnlimitedStoragePermission, 316 Extension::kWebstorePrivatePermission, 317}; 318const size_t Extension::kNumHostedAppPermissions = 319 arraysize(Extension::kHostedAppPermissionNames); 320 321const char* const Extension::kComponentPrivatePermissionNames[] = { 322 Extension::kFileBrowserPrivatePermission, 323 Extension::kWebstorePrivatePermission, 324 Extension::kChromeosInfoPrivatePermissions, 325}; 326const size_t Extension::kNumComponentPrivatePermissions = 327 arraysize(Extension::kComponentPrivatePermissionNames); 328 329// We purposefully don't put this into kPermissionNames. 330const char Extension::kOldUnlimitedStoragePermission[] = "unlimited_storage"; 331 332const int Extension::kValidWebExtentSchemes = 333 URLPattern::SCHEME_HTTP | URLPattern::SCHEME_HTTPS; 334 335const int Extension::kValidHostPermissionSchemes = 336 UserScript::kValidUserScriptSchemes | URLPattern::SCHEME_CHROMEUI; 337 338// 339// PermissionMessage 340// 341 342// static 343Extension::PermissionMessage Extension::PermissionMessage::CreateFromMessageId( 344 Extension::PermissionMessage::MessageId message_id) { 345 DCHECK_GT(PermissionMessage::ID_NONE, PermissionMessage::ID_UNKNOWN); 346 if (message_id <= ID_NONE) 347 return PermissionMessage(message_id, string16()); 348 349 string16 message = l10n_util::GetStringUTF16(kMessageIds[message_id]); 350 return PermissionMessage(message_id, message); 351} 352 353// static 354Extension::PermissionMessage Extension::PermissionMessage::CreateFromHostList( 355 const std::vector<std::string> hosts) { 356 CHECK(hosts.size() > 0); 357 358 MessageId message_id; 359 string16 message; 360 switch (hosts.size()) { 361 case 1: 362 message_id = ID_HOSTS_1; 363 message = l10n_util::GetStringFUTF16(kMessageIds[message_id], 364 UTF8ToUTF16(hosts[0])); 365 break; 366 case 2: 367 message_id = ID_HOSTS_2; 368 message = l10n_util::GetStringFUTF16(kMessageIds[message_id], 369 UTF8ToUTF16(hosts[0]), 370 UTF8ToUTF16(hosts[1])); 371 break; 372 case 3: 373 message_id = ID_HOSTS_3; 374 message = l10n_util::GetStringFUTF16(kMessageIds[message_id], 375 UTF8ToUTF16(hosts[0]), 376 UTF8ToUTF16(hosts[1]), 377 UTF8ToUTF16(hosts[2])); 378 break; 379 default: 380 message_id = ID_HOSTS_4_OR_MORE; 381 message = l10n_util::GetStringFUTF16( 382 kMessageIds[message_id], 383 UTF8ToUTF16(hosts[0]), 384 UTF8ToUTF16(hosts[1]), 385 base::IntToString16(hosts.size() - 2)); 386 break; 387 } 388 389 return PermissionMessage(message_id, message); 390} 391 392Extension::PermissionMessage::PermissionMessage( 393 Extension::PermissionMessage::MessageId message_id, string16 message) 394 : message_id_(message_id), 395 message_(message) { 396} 397 398const int Extension::PermissionMessage::kMessageIds[] = { 399 kUndefinedMessageId, // "unknown" 400 kUndefinedMessageId, // "none" 401 IDS_EXTENSION_PROMPT_WARNING_BOOKMARKS, 402 IDS_EXTENSION_PROMPT_WARNING_GEOLOCATION, 403 IDS_EXTENSION_PROMPT_WARNING_BROWSING_HISTORY, 404 IDS_EXTENSION_PROMPT_WARNING_TABS, 405 IDS_EXTENSION_PROMPT_WARNING_MANAGEMENT, 406 IDS_EXTENSION_PROMPT_WARNING_DEBUGGER, 407 IDS_EXTENSION_PROMPT_WARNING_1_HOST, 408 IDS_EXTENSION_PROMPT_WARNING_2_HOSTS, 409 IDS_EXTENSION_PROMPT_WARNING_3_HOSTS, 410 IDS_EXTENSION_PROMPT_WARNING_4_OR_MORE_HOSTS, 411 IDS_EXTENSION_PROMPT_WARNING_ALL_HOSTS, 412 IDS_EXTENSION_PROMPT_WARNING_FULL_ACCESS 413}; 414 415// 416// Extension 417// 418 419// static 420scoped_refptr<Extension> Extension::Create(const FilePath& path, 421 Location location, 422 const DictionaryValue& value, 423 int flags, 424 std::string* error) { 425 scoped_refptr<Extension> extension = new Extension(path, location); 426 427 if (!extension->InitFromValue(value, flags, error)) 428 return NULL; 429 return extension; 430} 431 432namespace { 433const char* kGalleryUpdateHttpUrl = 434 "http://clients2.google.com/service/update2/crx"; 435const char* kGalleryUpdateHttpsUrl = 436 "https://clients2.google.com/service/update2/crx"; 437} // namespace 438 439// static 440GURL Extension::GalleryUpdateUrl(bool secure) { 441 CommandLine* cmdline = CommandLine::ForCurrentProcess(); 442 if (cmdline->HasSwitch(switches::kAppsGalleryUpdateURL)) 443 return GURL(cmdline->GetSwitchValueASCII(switches::kAppsGalleryUpdateURL)); 444 else 445 return GURL(secure ? kGalleryUpdateHttpsUrl : kGalleryUpdateHttpUrl); 446} 447 448// static 449Extension::Location Extension::GetHigherPriorityLocation( 450 Extension::Location loc1, Extension::Location loc2) { 451 if (loc1 == loc2) 452 return loc1; 453 454 int loc1_rank = GetLocationRank(loc1); 455 int loc2_rank = GetLocationRank(loc2); 456 457 // If two different locations have the same rank, then we can not 458 // deterministicly choose a location. 459 CHECK(loc1_rank != loc2_rank); 460 461 // Lowest rank has highest priority. 462 return (loc1_rank > loc2_rank ? loc1 : loc2 ); 463} 464 465// static 466Extension::PermissionMessage::MessageId Extension::GetPermissionMessageId( 467 const std::string& permission) { 468 return ExtensionConfig::GetInstance()->GetPermissionMessageId(permission); 469} 470 471Extension::PermissionMessages Extension::GetPermissionMessages() const { 472 PermissionMessages messages; 473 if (!plugins().empty()) { 474 messages.push_back(PermissionMessage::CreateFromMessageId( 475 PermissionMessage::ID_FULL_ACCESS)); 476 return messages; 477 } 478 479 if (HasEffectiveAccessToAllHosts()) { 480 messages.push_back(PermissionMessage::CreateFromMessageId( 481 PermissionMessage::ID_HOSTS_ALL)); 482 } else { 483 std::vector<std::string> hosts = GetDistinctHostsForDisplay( 484 GetEffectiveHostPermissions().patterns()); 485 if (!hosts.empty()) 486 messages.push_back(PermissionMessage::CreateFromHostList(hosts)); 487 } 488 489 std::set<PermissionMessage> simple_msgs = GetSimplePermissionMessages(); 490 messages.insert(messages.end(), simple_msgs.begin(), simple_msgs.end()); 491 492 return messages; 493} 494 495std::vector<string16> Extension::GetPermissionMessageStrings() const { 496 std::vector<string16> messages; 497 PermissionMessages permissions = GetPermissionMessages(); 498 for (PermissionMessages::const_iterator i = permissions.begin(); 499 i != permissions.end(); ++i) 500 messages.push_back(i->message()); 501 return messages; 502} 503 504std::set<Extension::PermissionMessage> 505 Extension::GetSimplePermissionMessages() const { 506 std::set<PermissionMessage> messages; 507 std::set<std::string>::const_iterator i; 508 for (i = api_permissions().begin(); i != api_permissions().end(); ++i) { 509 PermissionMessage::MessageId message_id = GetPermissionMessageId(*i); 510 DCHECK_GT(PermissionMessage::ID_NONE, PermissionMessage::ID_UNKNOWN); 511 if (message_id > PermissionMessage::ID_NONE) 512 messages.insert(PermissionMessage::CreateFromMessageId(message_id)); 513 } 514 return messages; 515} 516 517// static 518std::vector<std::string> Extension::GetDistinctHostsForDisplay( 519 const URLPatternList& list) { 520 return GetDistinctHosts(list, true); 521} 522 523// static 524bool Extension::IsElevatedHostList( 525 const URLPatternList& old_list, const URLPatternList& new_list) { 526 // TODO(jstritar): This is overly conservative with respect to subdomains. 527 // For example, going from *.google.com to www.google.com will be 528 // considered an elevation, even though it is not (http://crbug.com/65337). 529 530 std::vector<std::string> new_hosts = GetDistinctHosts(new_list, false); 531 std::vector<std::string> old_hosts = GetDistinctHosts(old_list, false); 532 533 std::set<std::string> old_hosts_set(old_hosts.begin(), old_hosts.end()); 534 std::set<std::string> new_hosts_set(new_hosts.begin(), new_hosts.end()); 535 std::set<std::string> new_hosts_only; 536 537 std::set_difference(new_hosts_set.begin(), new_hosts_set.end(), 538 old_hosts_set.begin(), old_hosts_set.end(), 539 std::inserter(new_hosts_only, new_hosts_only.begin())); 540 541 return !new_hosts_only.empty(); 542} 543 544// Helper for GetDistinctHosts(): com > net > org > everything else. 545static bool RcdBetterThan(const std::string& a, const std::string& b) { 546 if (a == b) 547 return false; 548 if (a == "com") 549 return true; 550 if (a == "net") 551 return b != "com"; 552 if (a == "org") 553 return b != "com" && b != "net"; 554 return false; 555} 556 557// static 558std::vector<std::string> Extension::GetDistinctHosts( 559 const URLPatternList& host_patterns, bool include_rcd) { 560 // Use a vector to preserve order (also faster than a map on small sets). 561 // Each item is a host split into two parts: host without RCDs and 562 // current best RCD. 563 typedef std::vector<std::pair<std::string, std::string> > HostVector; 564 HostVector hosts_best_rcd; 565 for (size_t i = 0; i < host_patterns.size(); ++i) { 566 std::string host = host_patterns[i].host(); 567 568 // Add the subdomain wildcard back to the host, if necessary. 569 if (host_patterns[i].match_subdomains()) 570 host = "*." + host; 571 572 // If the host has an RCD, split it off so we can detect duplicates. 573 std::string rcd; 574 size_t reg_len = net::RegistryControlledDomainService::GetRegistryLength( 575 host, false); 576 if (reg_len && reg_len != std::string::npos) { 577 if (include_rcd) // else leave rcd empty 578 rcd = host.substr(host.size() - reg_len); 579 host = host.substr(0, host.size() - reg_len); 580 } 581 582 // Check if we've already seen this host. 583 HostVector::iterator it = hosts_best_rcd.begin(); 584 for (; it != hosts_best_rcd.end(); ++it) { 585 if (it->first == host) 586 break; 587 } 588 // If this host was found, replace the RCD if this one is better. 589 if (it != hosts_best_rcd.end()) { 590 if (include_rcd && RcdBetterThan(rcd, it->second)) 591 it->second = rcd; 592 } else { // Previously unseen host, append it. 593 hosts_best_rcd.push_back(std::make_pair(host, rcd)); 594 } 595 } 596 597 // Build up the final vector by concatenating hosts and RCDs. 598 std::vector<std::string> distinct_hosts; 599 for (HostVector::iterator it = hosts_best_rcd.begin(); 600 it != hosts_best_rcd.end(); ++it) 601 distinct_hosts.push_back(it->first + it->second); 602 return distinct_hosts; 603} 604 605FilePath Extension::MaybeNormalizePath(const FilePath& path) { 606#if defined(OS_WIN) 607 // Normalize any drive letter to upper-case. We do this for consistency with 608 // net_utils::FilePathToFileURL(), which does the same thing, to make string 609 // comparisons simpler. 610 std::wstring path_str = path.value(); 611 if (path_str.size() >= 2 && path_str[0] >= L'a' && path_str[0] <= L'z' && 612 path_str[1] == ':') 613 path_str[0] += ('A' - 'a'); 614 615 return FilePath(path_str); 616#else 617 return path; 618#endif 619} 620 621// static 622bool Extension::IsHostedAppPermission(const std::string& str) { 623 for (size_t i = 0; i < Extension::kNumHostedAppPermissions; ++i) { 624 if (str == Extension::kHostedAppPermissionNames[i]) { 625 return true; 626 } 627 } 628 return false; 629} 630 631const std::string Extension::VersionString() const { 632 return version()->GetString(); 633} 634 635// static 636bool Extension::IsExtension(const FilePath& file_name) { 637 return file_name.MatchesExtension(chrome::kExtensionFileExtension); 638} 639 640// static 641bool Extension::IdIsValid(const std::string& id) { 642 // Verify that the id is legal. 643 if (id.size() != (kIdSize * 2)) 644 return false; 645 646 // We only support lowercase IDs, because IDs can be used as URL components 647 // (where GURL will lowercase it). 648 std::string temp = StringToLowerASCII(id); 649 for (size_t i = 0; i < temp.size(); i++) 650 if (temp[i] < 'a' || temp[i] > 'p') 651 return false; 652 653 return true; 654} 655 656// static 657std::string Extension::GenerateIdForPath(const FilePath& path) { 658 FilePath new_path = Extension::MaybeNormalizePath(path); 659 std::string path_bytes = 660 std::string(reinterpret_cast<const char*>(new_path.value().data()), 661 new_path.value().size() * sizeof(FilePath::CharType)); 662 std::string id; 663 if (!GenerateId(path_bytes, &id)) 664 return ""; 665 return id; 666} 667 668Extension::Type Extension::GetType() const { 669 if (is_theme()) 670 return TYPE_THEME; 671 if (converted_from_user_script()) 672 return TYPE_USER_SCRIPT; 673 if (is_hosted_app()) 674 return TYPE_HOSTED_APP; 675 if (is_packaged_app()) 676 return TYPE_PACKAGED_APP; 677 return TYPE_EXTENSION; 678} 679 680// static 681GURL Extension::GetResourceURL(const GURL& extension_url, 682 const std::string& relative_path) { 683 DCHECK(extension_url.SchemeIs(chrome::kExtensionScheme)); 684 DCHECK_EQ("/", extension_url.path()); 685 686 GURL ret_val = GURL(extension_url.spec() + relative_path); 687 DCHECK(StartsWithASCII(ret_val.spec(), extension_url.spec(), false)); 688 689 return ret_val; 690} 691 692bool Extension::GenerateId(const std::string& input, std::string* output) { 693 CHECK(output); 694 uint8 hash[Extension::kIdSize]; 695 crypto::SHA256HashString(input, hash, sizeof(hash)); 696 *output = StringToLowerASCII(base::HexEncode(hash, sizeof(hash))); 697 ConvertHexadecimalToIDAlphabet(output); 698 699 return true; 700} 701 702// Helper method that loads a UserScript object from a dictionary in the 703// content_script list of the manifest. 704bool Extension::LoadUserScriptHelper(const DictionaryValue* content_script, 705 int definition_index, 706 int flags, 707 std::string* error, 708 UserScript* result) { 709 // When strict error checks are enabled, make URL pattern parsing strict. 710 URLPattern::ParseOption parse_strictness = 711 (flags & STRICT_ERROR_CHECKS ? URLPattern::PARSE_STRICT 712 : URLPattern::PARSE_LENIENT); 713 714 // run_at 715 if (content_script->HasKey(keys::kRunAt)) { 716 std::string run_location; 717 if (!content_script->GetString(keys::kRunAt, &run_location)) { 718 *error = ExtensionErrorUtils::FormatErrorMessage(errors::kInvalidRunAt, 719 base::IntToString(definition_index)); 720 return false; 721 } 722 723 if (run_location == values::kRunAtDocumentStart) { 724 result->set_run_location(UserScript::DOCUMENT_START); 725 } else if (run_location == values::kRunAtDocumentEnd) { 726 result->set_run_location(UserScript::DOCUMENT_END); 727 } else if (run_location == values::kRunAtDocumentIdle) { 728 result->set_run_location(UserScript::DOCUMENT_IDLE); 729 } else { 730 *error = ExtensionErrorUtils::FormatErrorMessage(errors::kInvalidRunAt, 731 base::IntToString(definition_index)); 732 return false; 733 } 734 } 735 736 // all frames 737 if (content_script->HasKey(keys::kAllFrames)) { 738 bool all_frames = false; 739 if (!content_script->GetBoolean(keys::kAllFrames, &all_frames)) { 740 *error = ExtensionErrorUtils::FormatErrorMessage( 741 errors::kInvalidAllFrames, base::IntToString(definition_index)); 742 return false; 743 } 744 result->set_match_all_frames(all_frames); 745 } 746 747 // matches 748 ListValue* matches = NULL; 749 if (!content_script->GetList(keys::kMatches, &matches)) { 750 *error = ExtensionErrorUtils::FormatErrorMessage(errors::kInvalidMatches, 751 base::IntToString(definition_index)); 752 return false; 753 } 754 755 if (matches->GetSize() == 0) { 756 *error = ExtensionErrorUtils::FormatErrorMessage(errors::kInvalidMatchCount, 757 base::IntToString(definition_index)); 758 return false; 759 } 760 for (size_t j = 0; j < matches->GetSize(); ++j) { 761 std::string match_str; 762 if (!matches->GetString(j, &match_str)) { 763 *error = ExtensionErrorUtils::FormatErrorMessage( 764 errors::kInvalidMatch, 765 base::IntToString(definition_index), 766 base::IntToString(j), 767 errors::kExpectString); 768 return false; 769 } 770 771 URLPattern pattern(UserScript::kValidUserScriptSchemes); 772 if (CanExecuteScriptEverywhere()) 773 pattern.set_valid_schemes(URLPattern::SCHEME_ALL); 774 775 URLPattern::ParseResult parse_result = pattern.Parse(match_str, 776 parse_strictness); 777 if (parse_result != URLPattern::PARSE_SUCCESS) { 778 *error = ExtensionErrorUtils::FormatErrorMessage( 779 errors::kInvalidMatch, 780 base::IntToString(definition_index), 781 base::IntToString(j), 782 URLPattern::GetParseResultString(parse_result)); 783 return false; 784 } 785 786 if (pattern.MatchesScheme(chrome::kFileScheme) && 787 !CanExecuteScriptEverywhere()) { 788 wants_file_access_ = true; 789 if (!(flags & ALLOW_FILE_ACCESS)) 790 pattern.set_valid_schemes( 791 pattern.valid_schemes() & ~URLPattern::SCHEME_FILE); 792 } 793 794 result->add_url_pattern(pattern); 795 } 796 797 // include/exclude globs (mostly for Greasemonkey compatibility) 798 if (!LoadGlobsHelper(content_script, definition_index, keys::kIncludeGlobs, 799 error, &UserScript::add_glob, result)) { 800 return false; 801 } 802 803 if (!LoadGlobsHelper(content_script, definition_index, keys::kExcludeGlobs, 804 error, &UserScript::add_exclude_glob, result)) { 805 return false; 806 } 807 808 // js and css keys 809 ListValue* js = NULL; 810 if (content_script->HasKey(keys::kJs) && 811 !content_script->GetList(keys::kJs, &js)) { 812 *error = ExtensionErrorUtils::FormatErrorMessage(errors::kInvalidJsList, 813 base::IntToString(definition_index)); 814 return false; 815 } 816 817 ListValue* css = NULL; 818 if (content_script->HasKey(keys::kCss) && 819 !content_script->GetList(keys::kCss, &css)) { 820 *error = ExtensionErrorUtils::FormatErrorMessage(errors::kInvalidCssList, 821 base::IntToString(definition_index)); 822 return false; 823 } 824 825 // The manifest needs to have at least one js or css user script definition. 826 if (((js ? js->GetSize() : 0) + (css ? css->GetSize() : 0)) == 0) { 827 *error = ExtensionErrorUtils::FormatErrorMessage(errors::kMissingFile, 828 base::IntToString(definition_index)); 829 return false; 830 } 831 832 if (js) { 833 for (size_t script_index = 0; script_index < js->GetSize(); 834 ++script_index) { 835 Value* value; 836 std::string relative; 837 if (!js->Get(script_index, &value) || !value->GetAsString(&relative)) { 838 *error = ExtensionErrorUtils::FormatErrorMessage(errors::kInvalidJs, 839 base::IntToString(definition_index), 840 base::IntToString(script_index)); 841 return false; 842 } 843 GURL url = GetResourceURL(relative); 844 ExtensionResource resource = GetResource(relative); 845 result->js_scripts().push_back(UserScript::File( 846 resource.extension_root(), resource.relative_path(), url)); 847 } 848 } 849 850 if (css) { 851 for (size_t script_index = 0; script_index < css->GetSize(); 852 ++script_index) { 853 Value* value; 854 std::string relative; 855 if (!css->Get(script_index, &value) || !value->GetAsString(&relative)) { 856 *error = ExtensionErrorUtils::FormatErrorMessage(errors::kInvalidCss, 857 base::IntToString(definition_index), 858 base::IntToString(script_index)); 859 return false; 860 } 861 GURL url = GetResourceURL(relative); 862 ExtensionResource resource = GetResource(relative); 863 result->css_scripts().push_back(UserScript::File( 864 resource.extension_root(), resource.relative_path(), url)); 865 } 866 } 867 868 return true; 869} 870 871bool Extension::LoadGlobsHelper( 872 const DictionaryValue* content_script, 873 int content_script_index, 874 const char* globs_property_name, 875 std::string* error, 876 void(UserScript::*add_method)(const std::string& glob), 877 UserScript *instance) { 878 if (!content_script->HasKey(globs_property_name)) 879 return true; // they are optional 880 881 ListValue* list = NULL; 882 if (!content_script->GetList(globs_property_name, &list)) { 883 *error = ExtensionErrorUtils::FormatErrorMessage(errors::kInvalidGlobList, 884 base::IntToString(content_script_index), 885 globs_property_name); 886 return false; 887 } 888 889 for (size_t i = 0; i < list->GetSize(); ++i) { 890 std::string glob; 891 if (!list->GetString(i, &glob)) { 892 *error = ExtensionErrorUtils::FormatErrorMessage(errors::kInvalidGlob, 893 base::IntToString(content_script_index), 894 globs_property_name, 895 base::IntToString(i)); 896 return false; 897 } 898 899 (instance->*add_method)(glob); 900 } 901 902 return true; 903} 904 905ExtensionAction* Extension::LoadExtensionActionHelper( 906 const DictionaryValue* extension_action, std::string* error) { 907 scoped_ptr<ExtensionAction> result(new ExtensionAction()); 908 result->set_extension_id(id()); 909 910 // Page actions are hidden by default, and browser actions ignore 911 // visibility. 912 result->SetIsVisible(ExtensionAction::kDefaultTabId, false); 913 914 // TODO(EXTENSIONS_DEPRECATED): icons list is obsolete. 915 ListValue* icons = NULL; 916 if (extension_action->HasKey(keys::kPageActionIcons) && 917 extension_action->GetList(keys::kPageActionIcons, &icons)) { 918 for (ListValue::const_iterator iter = icons->begin(); 919 iter != icons->end(); ++iter) { 920 std::string path; 921 if (!(*iter)->GetAsString(&path) || path.empty()) { 922 *error = errors::kInvalidPageActionIconPath; 923 return NULL; 924 } 925 926 result->icon_paths()->push_back(path); 927 } 928 } 929 930 // TODO(EXTENSIONS_DEPRECATED): Read the page action |id| (optional). 931 std::string id; 932 if (extension_action->HasKey(keys::kPageActionId)) { 933 if (!extension_action->GetString(keys::kPageActionId, &id)) { 934 *error = errors::kInvalidPageActionId; 935 return NULL; 936 } 937 result->set_id(id); 938 } 939 940 std::string default_icon; 941 // Read the page action |default_icon| (optional). 942 if (extension_action->HasKey(keys::kPageActionDefaultIcon)) { 943 if (!extension_action->GetString(keys::kPageActionDefaultIcon, 944 &default_icon) || 945 default_icon.empty()) { 946 *error = errors::kInvalidPageActionIconPath; 947 return NULL; 948 } 949 result->set_default_icon_path(default_icon); 950 } 951 952 // Read the page action title from |default_title| if present, |name| if not 953 // (both optional). 954 std::string title; 955 if (extension_action->HasKey(keys::kPageActionDefaultTitle)) { 956 if (!extension_action->GetString(keys::kPageActionDefaultTitle, &title)) { 957 *error = errors::kInvalidPageActionDefaultTitle; 958 return NULL; 959 } 960 } else if (extension_action->HasKey(keys::kName)) { 961 if (!extension_action->GetString(keys::kName, &title)) { 962 *error = errors::kInvalidPageActionName; 963 return NULL; 964 } 965 } 966 result->SetTitle(ExtensionAction::kDefaultTabId, title); 967 968 // Read the action's |popup| (optional). 969 const char* popup_key = NULL; 970 if (extension_action->HasKey(keys::kPageActionDefaultPopup)) 971 popup_key = keys::kPageActionDefaultPopup; 972 973 // For backward compatibility, alias old key "popup" to new 974 // key "default_popup". 975 if (extension_action->HasKey(keys::kPageActionPopup)) { 976 if (popup_key) { 977 *error = ExtensionErrorUtils::FormatErrorMessage( 978 errors::kInvalidPageActionOldAndNewKeys, 979 keys::kPageActionDefaultPopup, 980 keys::kPageActionPopup); 981 return NULL; 982 } 983 popup_key = keys::kPageActionPopup; 984 } 985 986 if (popup_key) { 987 DictionaryValue* popup = NULL; 988 std::string url_str; 989 990 if (extension_action->GetString(popup_key, &url_str)) { 991 // On success, |url_str| is set. Nothing else to do. 992 } else if (extension_action->GetDictionary(popup_key, &popup)) { 993 // TODO(EXTENSIONS_DEPRECATED): popup is now a string only. 994 // Support the old dictionary format for backward compatibility. 995 if (!popup->GetString(keys::kPageActionPopupPath, &url_str)) { 996 *error = ExtensionErrorUtils::FormatErrorMessage( 997 errors::kInvalidPageActionPopupPath, "<missing>"); 998 return NULL; 999 } 1000 } else { 1001 *error = errors::kInvalidPageActionPopup; 1002 return NULL; 1003 } 1004 1005 if (!url_str.empty()) { 1006 // An empty string is treated as having no popup. 1007 GURL url = GetResourceURL(url_str); 1008 if (!url.is_valid()) { 1009 *error = ExtensionErrorUtils::FormatErrorMessage( 1010 errors::kInvalidPageActionPopupPath, url_str); 1011 return NULL; 1012 } 1013 result->SetPopupUrl(ExtensionAction::kDefaultTabId, url); 1014 } else { 1015 DCHECK(!result->HasPopup(ExtensionAction::kDefaultTabId)) 1016 << "Shouldn't be posible for the popup to be set."; 1017 } 1018 } 1019 1020 return result.release(); 1021} 1022 1023Extension::FileBrowserHandlerList* Extension::LoadFileBrowserHandlers( 1024 const ListValue* extension_actions, std::string* error) { 1025 scoped_ptr<FileBrowserHandlerList> result( 1026 new FileBrowserHandlerList()); 1027 for (ListValue::const_iterator iter = extension_actions->begin(); 1028 iter != extension_actions->end(); 1029 ++iter) { 1030 if (!(*iter)->IsType(Value::TYPE_DICTIONARY)) { 1031 *error = errors::kInvalidFileBrowserHandler; 1032 return NULL; 1033 } 1034 scoped_ptr<FileBrowserHandler> action( 1035 LoadFileBrowserHandler( 1036 reinterpret_cast<DictionaryValue*>(*iter), error)); 1037 if (!action.get()) 1038 return NULL; // Failed to parse file browser action definition. 1039 result->push_back(linked_ptr<FileBrowserHandler>(action.release())); 1040 } 1041 return result.release(); 1042} 1043 1044FileBrowserHandler* Extension::LoadFileBrowserHandler( 1045 const DictionaryValue* file_browser_handler, std::string* error) { 1046 scoped_ptr<FileBrowserHandler> result( 1047 new FileBrowserHandler()); 1048 result->set_extension_id(id()); 1049 1050 std::string id; 1051 // Read the file action |id| (mandatory). 1052 if (!file_browser_handler->HasKey(keys::kPageActionId) || 1053 !file_browser_handler->GetString(keys::kPageActionId, &id)) { 1054 *error = errors::kInvalidPageActionId; 1055 return NULL; 1056 } 1057 result->set_id(id); 1058 1059 // Read the page action title from |default_title| (mandatory). 1060 std::string title; 1061 if (!file_browser_handler->HasKey(keys::kPageActionDefaultTitle) || 1062 !file_browser_handler->GetString(keys::kPageActionDefaultTitle, &title)) { 1063 *error = errors::kInvalidPageActionDefaultTitle; 1064 return NULL; 1065 } 1066 result->set_title(title); 1067 1068 // Initialize file filters (mandatory). 1069 ListValue* list_value = NULL; 1070 if (!file_browser_handler->HasKey(keys::kFileFilters) || 1071 !file_browser_handler->GetList(keys::kFileFilters, &list_value) || 1072 list_value->empty()) { 1073 *error = errors::kInvalidFileFiltersList; 1074 return NULL; 1075 } 1076 for (size_t i = 0; i < list_value->GetSize(); ++i) { 1077 std::string filter; 1078 if (!list_value->GetString(i, &filter)) { 1079 *error = ExtensionErrorUtils::FormatErrorMessage( 1080 errors::kInvalidFileFilterValue, base::IntToString(i)); 1081 return NULL; 1082 } 1083 URLPattern pattern(URLPattern::SCHEME_FILESYSTEM); 1084 if (URLPattern::PARSE_SUCCESS != pattern.Parse(filter, 1085 URLPattern::PARSE_STRICT)) { 1086 *error = ExtensionErrorUtils::FormatErrorMessage( 1087 errors::kInvalidURLPatternError, filter); 1088 return NULL; 1089 } 1090 result->AddPattern(pattern); 1091 } 1092 1093 std::string default_icon; 1094 // Read the file browser action |default_icon| (optional). 1095 if (file_browser_handler->HasKey(keys::kPageActionDefaultIcon)) { 1096 if (!file_browser_handler->GetString( 1097 keys::kPageActionDefaultIcon,&default_icon) || 1098 default_icon.empty()) { 1099 *error = errors::kInvalidPageActionIconPath; 1100 return NULL; 1101 } 1102 result->set_icon_path(default_icon); 1103 } 1104 1105 return result.release(); 1106} 1107 1108ExtensionSidebarDefaults* Extension::LoadExtensionSidebarDefaults( 1109 const DictionaryValue* extension_sidebar, std::string* error) { 1110 scoped_ptr<ExtensionSidebarDefaults> result(new ExtensionSidebarDefaults()); 1111 1112 std::string default_icon; 1113 // Read sidebar's |default_icon| (optional). 1114 if (extension_sidebar->HasKey(keys::kSidebarDefaultIcon)) { 1115 if (!extension_sidebar->GetString(keys::kSidebarDefaultIcon, 1116 &default_icon) || 1117 default_icon.empty()) { 1118 *error = errors::kInvalidSidebarDefaultIconPath; 1119 return NULL; 1120 } 1121 result->set_default_icon_path(default_icon); 1122 } 1123 1124 // Read sidebar's |default_title| (optional). 1125 string16 default_title; 1126 if (extension_sidebar->HasKey(keys::kSidebarDefaultTitle)) { 1127 if (!extension_sidebar->GetString(keys::kSidebarDefaultTitle, 1128 &default_title)) { 1129 *error = errors::kInvalidSidebarDefaultTitle; 1130 return NULL; 1131 } 1132 } 1133 result->set_default_title(default_title); 1134 1135 // Read sidebar's |default_page| (optional). 1136 std::string default_page; 1137 if (extension_sidebar->HasKey(keys::kSidebarDefaultPage)) { 1138 if (!extension_sidebar->GetString(keys::kSidebarDefaultPage, 1139 &default_page) || 1140 default_page.empty()) { 1141 *error = errors::kInvalidSidebarDefaultPage; 1142 return NULL; 1143 } 1144 GURL url = extension_sidebar_utils::ResolveRelativePath( 1145 default_page, this, error); 1146 if (!url.is_valid()) 1147 return NULL; 1148 result->set_default_page(url); 1149 } 1150 1151 return result.release(); 1152} 1153 1154bool Extension::ContainsNonThemeKeys(const DictionaryValue& source) const { 1155 for (DictionaryValue::key_iterator key = source.begin_keys(); 1156 key != source.end_keys(); ++key) { 1157 if (!IsBaseCrxKey(*key) && *key != keys::kTheme) 1158 return true; 1159 } 1160 return false; 1161} 1162 1163bool Extension::LoadIsApp(const DictionaryValue* manifest, 1164 std::string* error) { 1165 if (manifest->HasKey(keys::kApp)) 1166 is_app_ = true; 1167 1168 return true; 1169} 1170 1171bool Extension::LoadExtent(const DictionaryValue* manifest, 1172 const char* key, 1173 ExtensionExtent* extent, 1174 const char* list_error, 1175 const char* value_error, 1176 URLPattern::ParseOption parse_strictness, 1177 std::string* error) { 1178 Value* temp = NULL; 1179 if (!manifest->Get(key, &temp)) 1180 return true; 1181 1182 if (temp->GetType() != Value::TYPE_LIST) { 1183 *error = list_error; 1184 return false; 1185 } 1186 1187 ListValue* pattern_list = static_cast<ListValue*>(temp); 1188 for (size_t i = 0; i < pattern_list->GetSize(); ++i) { 1189 std::string pattern_string; 1190 if (!pattern_list->GetString(i, &pattern_string)) { 1191 *error = ExtensionErrorUtils::FormatErrorMessage(value_error, 1192 base::UintToString(i), 1193 errors::kExpectString); 1194 return false; 1195 } 1196 1197 URLPattern pattern(kValidWebExtentSchemes); 1198 URLPattern::ParseResult parse_result = pattern.Parse(pattern_string, 1199 parse_strictness); 1200 if (parse_result == URLPattern::PARSE_ERROR_EMPTY_PATH) { 1201 pattern_string += "/"; 1202 parse_result = pattern.Parse(pattern_string, parse_strictness); 1203 } 1204 1205 if (parse_result != URLPattern::PARSE_SUCCESS) { 1206 *error = ExtensionErrorUtils::FormatErrorMessage( 1207 value_error, 1208 base::UintToString(i), 1209 URLPattern::GetParseResultString(parse_result)); 1210 return false; 1211 } 1212 1213 // Do not allow authors to claim "<all_urls>". 1214 if (pattern.match_all_urls()) { 1215 *error = ExtensionErrorUtils::FormatErrorMessage( 1216 value_error, 1217 base::UintToString(i), 1218 errors::kCannotClaimAllURLsInExtent); 1219 return false; 1220 } 1221 1222 // Do not allow authors to claim "*" for host. 1223 if (pattern.host().empty()) { 1224 *error = ExtensionErrorUtils::FormatErrorMessage( 1225 value_error, 1226 base::UintToString(i), 1227 errors::kCannotClaimAllHostsInExtent); 1228 return false; 1229 } 1230 1231 // We do not allow authors to put wildcards in their paths. Instead, we 1232 // imply one at the end. 1233 if (pattern.path().find('*') != std::string::npos) { 1234 *error = ExtensionErrorUtils::FormatErrorMessage( 1235 value_error, 1236 base::UintToString(i), 1237 errors::kNoWildCardsInPaths); 1238 return false; 1239 } 1240 pattern.SetPath(pattern.path() + '*'); 1241 1242 extent->AddPattern(pattern); 1243 } 1244 1245 return true; 1246} 1247 1248bool Extension::LoadLaunchURL(const DictionaryValue* manifest, 1249 std::string* error) { 1250 Value* temp = NULL; 1251 1252 // launch URL can be either local (to chrome-extension:// root) or an absolute 1253 // web URL. 1254 if (manifest->Get(keys::kLaunchLocalPath, &temp)) { 1255 if (manifest->Get(keys::kLaunchWebURL, NULL)) { 1256 *error = errors::kLaunchPathAndURLAreExclusive; 1257 return false; 1258 } 1259 1260 std::string launch_path; 1261 if (!temp->GetAsString(&launch_path)) { 1262 *error = errors::kInvalidLaunchLocalPath; 1263 return false; 1264 } 1265 1266 // Ensure the launch path is a valid relative URL. 1267 GURL resolved = url().Resolve(launch_path); 1268 if (!resolved.is_valid() || resolved.GetOrigin() != url()) { 1269 *error = errors::kInvalidLaunchLocalPath; 1270 return false; 1271 } 1272 1273 launch_local_path_ = launch_path; 1274 } else if (manifest->Get(keys::kLaunchWebURL, &temp)) { 1275 std::string launch_url; 1276 if (!temp->GetAsString(&launch_url)) { 1277 *error = errors::kInvalidLaunchWebURL; 1278 return false; 1279 } 1280 1281 // Ensure the launch URL is a valid absolute URL and web extent scheme. 1282 GURL url(launch_url); 1283 URLPattern pattern(kValidWebExtentSchemes); 1284 if (!url.is_valid() || !pattern.SetScheme(url.scheme())) { 1285 *error = errors::kInvalidLaunchWebURL; 1286 return false; 1287 } 1288 1289 launch_web_url_ = launch_url; 1290 } else if (is_app()) { 1291 *error = errors::kLaunchURLRequired; 1292 return false; 1293 } 1294 1295 // If there is no extent, we default the extent based on the launch URL. 1296 if (web_extent().is_empty() && !launch_web_url().empty()) { 1297 GURL launch_url(launch_web_url()); 1298 URLPattern pattern(kValidWebExtentSchemes); 1299 if (!pattern.SetScheme("*")) { 1300 *error = errors::kInvalidLaunchWebURL; 1301 return false; 1302 } 1303 pattern.set_host(launch_url.host()); 1304 pattern.SetPath("/*"); 1305 extent_.AddPattern(pattern); 1306 } 1307 1308 // In order for the --apps-gallery-url switch to work with the gallery 1309 // process isolation, we must insert any provided value into the component 1310 // app's launch url and web extent. 1311 if (id() == extension_misc::kWebStoreAppId) { 1312 std::string gallery_url_str = CommandLine::ForCurrentProcess()-> 1313 GetSwitchValueASCII(switches::kAppsGalleryURL); 1314 1315 // Empty string means option was not used. 1316 if (!gallery_url_str.empty()) { 1317 GURL gallery_url(gallery_url_str); 1318 if (!gallery_url.is_valid()) { 1319 LOG(WARNING) << "Invalid url given in switch " 1320 << switches::kAppsGalleryURL; 1321 } else { 1322 if (gallery_url.has_port()) { 1323 LOG(WARNING) << "URLs passed to switch " << switches::kAppsGalleryURL 1324 << " should not contain a port. Removing it."; 1325 1326 GURL::Replacements remove_port; 1327 remove_port.ClearPort(); 1328 gallery_url = gallery_url.ReplaceComponents(remove_port); 1329 } 1330 1331 launch_web_url_ = gallery_url.spec(); 1332 1333 URLPattern pattern(kValidWebExtentSchemes); 1334 pattern.Parse(gallery_url.spec(), URLPattern::PARSE_STRICT); 1335 pattern.SetPath(pattern.path() + '*'); 1336 extent_.AddPattern(pattern); 1337 } 1338 } 1339 } 1340 1341 return true; 1342} 1343 1344bool Extension::LoadLaunchContainer(const DictionaryValue* manifest, 1345 std::string* error) { 1346 Value* temp = NULL; 1347 if (!manifest->Get(keys::kLaunchContainer, &temp)) 1348 return true; 1349 1350 std::string launch_container_string; 1351 if (!temp->GetAsString(&launch_container_string)) { 1352 *error = errors::kInvalidLaunchContainer; 1353 return false; 1354 } 1355 1356 if (launch_container_string == values::kLaunchContainerPanel) { 1357 launch_container_ = extension_misc::LAUNCH_PANEL; 1358 } else if (launch_container_string == values::kLaunchContainerTab) { 1359 launch_container_ = extension_misc::LAUNCH_TAB; 1360 } else { 1361 *error = errors::kInvalidLaunchContainer; 1362 return false; 1363 } 1364 1365 // Validate the container width if present. 1366 if (manifest->Get(keys::kLaunchWidth, &temp)) { 1367 if (launch_container() != extension_misc::LAUNCH_PANEL && 1368 launch_container() != extension_misc::LAUNCH_WINDOW) { 1369 *error = errors::kInvalidLaunchWidthContainer; 1370 return false; 1371 } 1372 if (!temp->GetAsInteger(&launch_width_) || 1373 launch_width_ < 0) { 1374 launch_width_ = 0; 1375 *error = errors::kInvalidLaunchWidth; 1376 return false; 1377 } 1378 } 1379 1380 // Validate container height if present. 1381 if (manifest->Get(keys::kLaunchHeight, &temp)) { 1382 if (launch_container() != extension_misc::LAUNCH_PANEL && 1383 launch_container() != extension_misc::LAUNCH_WINDOW) { 1384 *error = errors::kInvalidLaunchHeightContainer; 1385 return false; 1386 } 1387 if (!temp->GetAsInteger(&launch_height_) || launch_height_ < 0) { 1388 launch_height_ = 0; 1389 *error = errors::kInvalidLaunchHeight; 1390 return false; 1391 } 1392 } 1393 1394 return true; 1395} 1396 1397bool Extension::LoadAppIsolation(const DictionaryValue* manifest, 1398 std::string* error) { 1399 // Only parse app isolation features if this switch is present. 1400 if (!CommandLine::ForCurrentProcess()->HasSwitch( 1401 switches::kEnableExperimentalAppManifests)) 1402 return true; 1403 1404 Value* temp = NULL; 1405 if (!manifest->Get(keys::kIsolation, &temp)) 1406 return true; 1407 1408 if (temp->GetType() != Value::TYPE_LIST) { 1409 *error = errors::kInvalidIsolation; 1410 return false; 1411 } 1412 1413 ListValue* isolation_list = static_cast<ListValue*>(temp); 1414 for (size_t i = 0; i < isolation_list->GetSize(); ++i) { 1415 std::string isolation_string; 1416 if (!isolation_list->GetString(i, &isolation_string)) { 1417 *error = ExtensionErrorUtils::FormatErrorMessage( 1418 errors::kInvalidIsolationValue, 1419 base::UintToString(i)); 1420 return false; 1421 } 1422 1423 // Check for isolated storage. 1424 if (isolation_string == values::kIsolatedStorage) { 1425 is_storage_isolated_ = true; 1426 } else { 1427 LOG(WARNING) << "Did not recognize isolation type: " 1428 << isolation_string; 1429 } 1430 } 1431 return true; 1432} 1433 1434bool Extension::EnsureNotHybridApp(const DictionaryValue* manifest, 1435 std::string* error) { 1436 if (web_extent().is_empty()) 1437 return true; 1438 1439 for (DictionaryValue::key_iterator key = manifest->begin_keys(); 1440 key != manifest->end_keys(); ++key) { 1441 if (!IsBaseCrxKey(*key) && 1442 *key != keys::kApp && 1443 *key != keys::kPermissions && 1444 *key != keys::kOptionsPage && 1445 *key != keys::kBackground) { 1446 *error = ExtensionErrorUtils::FormatErrorMessage( 1447 errors::kHostedAppsCannotIncludeExtensionFeatures, *key); 1448 return false; 1449 } 1450 } 1451 1452 return true; 1453} 1454 1455Extension::Extension(const FilePath& path, Location location) 1456 : incognito_split_mode_(false), 1457 location_(location), 1458 converted_from_user_script_(false), 1459 is_theme_(false), 1460 is_app_(false), 1461 is_storage_isolated_(false), 1462 launch_container_(extension_misc::LAUNCH_TAB), 1463 launch_width_(0), 1464 launch_height_(0), 1465 wants_file_access_(false) { 1466 DCHECK(path.empty() || path.IsAbsolute()); 1467 path_ = MaybeNormalizePath(path); 1468} 1469 1470Extension::~Extension() { 1471} 1472 1473ExtensionResource Extension::GetResource( 1474 const std::string& relative_path) const { 1475#if defined(OS_POSIX) 1476 FilePath relative_file_path(relative_path); 1477#elif defined(OS_WIN) 1478 FilePath relative_file_path(UTF8ToWide(relative_path)); 1479#endif 1480 return ExtensionResource(id(), path(), relative_file_path); 1481} 1482 1483ExtensionResource Extension::GetResource( 1484 const FilePath& relative_file_path) const { 1485 return ExtensionResource(id(), path(), relative_file_path); 1486} 1487 1488// TODO(rafaelw): Move ParsePEMKeyBytes, ProducePEM & FormatPEMForOutput to a 1489// util class in base: 1490// http://code.google.com/p/chromium/issues/detail?id=13572 1491bool Extension::ParsePEMKeyBytes(const std::string& input, 1492 std::string* output) { 1493 DCHECK(output); 1494 if (!output) 1495 return false; 1496 if (input.length() == 0) 1497 return false; 1498 1499 std::string working = input; 1500 if (StartsWithASCII(working, kKeyBeginHeaderMarker, true)) { 1501 working = CollapseWhitespaceASCII(working, true); 1502 size_t header_pos = working.find(kKeyInfoEndMarker, 1503 sizeof(kKeyBeginHeaderMarker) - 1); 1504 if (header_pos == std::string::npos) 1505 return false; 1506 size_t start_pos = header_pos + sizeof(kKeyInfoEndMarker) - 1; 1507 size_t end_pos = working.rfind(kKeyBeginFooterMarker); 1508 if (end_pos == std::string::npos) 1509 return false; 1510 if (start_pos >= end_pos) 1511 return false; 1512 1513 working = working.substr(start_pos, end_pos - start_pos); 1514 if (working.length() == 0) 1515 return false; 1516 } 1517 1518 return base::Base64Decode(working, output); 1519} 1520 1521bool Extension::ProducePEM(const std::string& input, std::string* output) { 1522 CHECK(output); 1523 if (input.length() == 0) 1524 return false; 1525 1526 return base::Base64Encode(input, output); 1527} 1528 1529bool Extension::FormatPEMForFileOutput(const std::string& input, 1530 std::string* output, 1531 bool is_public) { 1532 CHECK(output); 1533 if (input.length() == 0) 1534 return false; 1535 *output = ""; 1536 output->append(kKeyBeginHeaderMarker); 1537 output->append(" "); 1538 output->append(is_public ? kPublic : kPrivate); 1539 output->append(" "); 1540 output->append(kKeyInfoEndMarker); 1541 output->append("\n"); 1542 for (size_t i = 0; i < input.length(); ) { 1543 int slice = std::min<int>(input.length() - i, kPEMOutputColumns); 1544 output->append(input.substr(i, slice)); 1545 output->append("\n"); 1546 i += slice; 1547 } 1548 output->append(kKeyBeginFooterMarker); 1549 output->append(" "); 1550 output->append(is_public ? kPublic : kPrivate); 1551 output->append(" "); 1552 output->append(kKeyInfoEndMarker); 1553 output->append("\n"); 1554 1555 return true; 1556} 1557 1558// static 1559bool Extension::IsPrivilegeIncrease(const bool granted_full_access, 1560 const std::set<std::string>& granted_apis, 1561 const ExtensionExtent& granted_extent, 1562 const Extension* new_extension) { 1563 // If the extension had native code access, we don't need to go any further. 1564 // Things can't get any worse. 1565 if (granted_full_access) 1566 return false; 1567 1568 // Otherwise, if the new extension has a plugin, it's a privilege increase. 1569 if (new_extension->HasFullPermissions()) 1570 return true; 1571 1572 // If the extension hadn't been granted access to all hosts in the past, then 1573 // see if the extension requires more host permissions. 1574 if (!HasEffectiveAccessToAllHosts(granted_extent, granted_apis)) { 1575 if (new_extension->HasEffectiveAccessToAllHosts()) 1576 return true; 1577 1578 const ExtensionExtent new_extent = 1579 new_extension->GetEffectiveHostPermissions(); 1580 1581 if (IsElevatedHostList(granted_extent.patterns(), new_extent.patterns())) 1582 return true; 1583 } 1584 1585 std::set<std::string> new_apis = new_extension->api_permissions(); 1586 std::set<std::string> new_apis_only; 1587 std::set_difference(new_apis.begin(), new_apis.end(), 1588 granted_apis.begin(), granted_apis.end(), 1589 std::inserter(new_apis_only, new_apis_only.begin())); 1590 1591 // Ignore API permissions that don't require user approval when deciding if 1592 // an extension has increased its privileges. 1593 size_t new_api_count = 0; 1594 for (std::set<std::string>::iterator i = new_apis_only.begin(); 1595 i != new_apis_only.end(); ++i) { 1596 DCHECK_GT(PermissionMessage::ID_NONE, PermissionMessage::ID_UNKNOWN); 1597 if (GetPermissionMessageId(*i) > PermissionMessage::ID_NONE) 1598 new_api_count++; 1599 } 1600 1601 if (new_api_count) 1602 return true; 1603 1604 return false; 1605} 1606 1607// static 1608void Extension::DecodeIcon(const Extension* extension, 1609 Icons icon_size, 1610 scoped_ptr<SkBitmap>* result) { 1611 FilePath icon_path = extension->GetIconResource( 1612 icon_size, ExtensionIconSet::MATCH_EXACTLY).GetFilePath(); 1613 DecodeIconFromPath(icon_path, icon_size, result); 1614} 1615 1616// static 1617void Extension::DecodeIconFromPath(const FilePath& icon_path, 1618 Icons icon_size, 1619 scoped_ptr<SkBitmap>* result) { 1620 if (icon_path.empty()) 1621 return; 1622 1623 std::string file_contents; 1624 if (!file_util::ReadFileToString(icon_path, &file_contents)) { 1625 LOG(ERROR) << "Could not read icon file: " << icon_path.LossyDisplayName(); 1626 return; 1627 } 1628 1629 // Decode the image using WebKit's image decoder. 1630 const unsigned char* data = 1631 reinterpret_cast<const unsigned char*>(file_contents.data()); 1632 webkit_glue::ImageDecoder decoder; 1633 scoped_ptr<SkBitmap> decoded(new SkBitmap()); 1634 *decoded = decoder.Decode(data, file_contents.length()); 1635 if (decoded->empty()) { 1636 LOG(ERROR) << "Could not decode icon file: " 1637 << icon_path.LossyDisplayName(); 1638 return; 1639 } 1640 1641 if (decoded->width() != icon_size || decoded->height() != icon_size) { 1642 LOG(ERROR) << "Icon file has unexpected size: " 1643 << base::IntToString(decoded->width()) << "x" 1644 << base::IntToString(decoded->height()); 1645 return; 1646 } 1647 1648 result->swap(decoded); 1649} 1650 1651// static 1652const SkBitmap& Extension::GetDefaultIcon(bool is_app) { 1653 if (is_app) { 1654 return *ResourceBundle::GetSharedInstance().GetBitmapNamed( 1655 IDR_APP_DEFAULT_ICON); 1656 } else { 1657 return *ResourceBundle::GetSharedInstance().GetBitmapNamed( 1658 IDR_EXTENSION_DEFAULT_ICON); 1659 } 1660} 1661 1662GURL Extension::GetBaseURLFromExtensionId(const std::string& extension_id) { 1663 return GURL(std::string(chrome::kExtensionScheme) + 1664 chrome::kStandardSchemeSeparator + extension_id + "/"); 1665} 1666 1667bool Extension::InitFromValue(const DictionaryValue& source, int flags, 1668 std::string* error) { 1669 // When strict error checks are enabled, make URL pattern parsing strict. 1670 URLPattern::ParseOption parse_strictness = 1671 (flags & STRICT_ERROR_CHECKS ? URLPattern::PARSE_STRICT 1672 : URLPattern::PARSE_LENIENT); 1673 1674 if (source.HasKey(keys::kPublicKey)) { 1675 std::string public_key_bytes; 1676 if (!source.GetString(keys::kPublicKey, 1677 &public_key_) || 1678 !ParsePEMKeyBytes(public_key_, 1679 &public_key_bytes) || 1680 !GenerateId(public_key_bytes, &id_)) { 1681 *error = errors::kInvalidKey; 1682 return false; 1683 } 1684 } else if (flags & REQUIRE_KEY) { 1685 *error = errors::kInvalidKey; 1686 return false; 1687 } else { 1688 // If there is a path, we generate the ID from it. This is useful for 1689 // development mode, because it keeps the ID stable across restarts and 1690 // reloading the extension. 1691 id_ = Extension::GenerateIdForPath(path()); 1692 if (id_.empty()) { 1693 NOTREACHED() << "Could not create ID from path."; 1694 return false; 1695 } 1696 } 1697 1698 // Make a copy of the manifest so we can store it in prefs. 1699 manifest_value_.reset(source.DeepCopy()); 1700 1701 // Initialize the URL. 1702 extension_url_ = Extension::GetBaseURLFromExtensionId(id()); 1703 1704 // Initialize version. 1705 std::string version_str; 1706 if (!source.GetString(keys::kVersion, &version_str)) { 1707 *error = errors::kInvalidVersion; 1708 return false; 1709 } 1710 version_.reset(Version::GetVersionFromString(version_str)); 1711 if (!version_.get() || 1712 version_->components().size() > 4) { 1713 *error = errors::kInvalidVersion; 1714 return false; 1715 } 1716 1717 // Initialize name. 1718 string16 localized_name; 1719 if (!source.GetString(keys::kName, &localized_name)) { 1720 *error = errors::kInvalidName; 1721 return false; 1722 } 1723 base::i18n::AdjustStringForLocaleDirection(&localized_name); 1724 name_ = UTF16ToUTF8(localized_name); 1725 1726 // Initialize description (if present). 1727 if (source.HasKey(keys::kDescription)) { 1728 if (!source.GetString(keys::kDescription, 1729 &description_)) { 1730 *error = errors::kInvalidDescription; 1731 return false; 1732 } 1733 } 1734 1735 // Initialize homepage url (if present). 1736 if (source.HasKey(keys::kHomepageURL)) { 1737 std::string tmp; 1738 if (!source.GetString(keys::kHomepageURL, &tmp)) { 1739 *error = ExtensionErrorUtils::FormatErrorMessage( 1740 errors::kInvalidHomepageURL, ""); 1741 return false; 1742 } 1743 homepage_url_ = GURL(tmp); 1744 if (!homepage_url_.is_valid()) { 1745 *error = ExtensionErrorUtils::FormatErrorMessage( 1746 errors::kInvalidHomepageURL, tmp); 1747 return false; 1748 } 1749 } 1750 1751 // Initialize update url (if present). 1752 if (source.HasKey(keys::kUpdateURL)) { 1753 std::string tmp; 1754 if (!source.GetString(keys::kUpdateURL, &tmp)) { 1755 *error = ExtensionErrorUtils::FormatErrorMessage( 1756 errors::kInvalidUpdateURL, ""); 1757 return false; 1758 } 1759 update_url_ = GURL(tmp); 1760 if (!update_url_.is_valid() || 1761 update_url_.has_ref()) { 1762 *error = ExtensionErrorUtils::FormatErrorMessage( 1763 errors::kInvalidUpdateURL, tmp); 1764 return false; 1765 } 1766 } 1767 1768 // Validate minimum Chrome version (if present). We don't need to store this, 1769 // since the extension is not valid if it is incorrect. 1770 if (source.HasKey(keys::kMinimumChromeVersion)) { 1771 std::string minimum_version_string; 1772 if (!source.GetString(keys::kMinimumChromeVersion, 1773 &minimum_version_string)) { 1774 *error = errors::kInvalidMinimumChromeVersion; 1775 return false; 1776 } 1777 1778 scoped_ptr<Version> minimum_version( 1779 Version::GetVersionFromString(minimum_version_string)); 1780 if (!minimum_version.get()) { 1781 *error = errors::kInvalidMinimumChromeVersion; 1782 return false; 1783 } 1784 1785 chrome::VersionInfo current_version_info; 1786 if (!current_version_info.is_valid()) { 1787 NOTREACHED(); 1788 return false; 1789 } 1790 1791 scoped_ptr<Version> current_version( 1792 Version::GetVersionFromString(current_version_info.Version())); 1793 if (!current_version.get()) { 1794 DCHECK(false); 1795 return false; 1796 } 1797 1798 if (current_version->CompareTo(*minimum_version) < 0) { 1799 *error = ExtensionErrorUtils::FormatErrorMessage( 1800 errors::kChromeVersionTooLow, 1801 l10n_util::GetStringUTF8(IDS_PRODUCT_NAME), 1802 minimum_version_string); 1803 return false; 1804 } 1805 } 1806 1807 // Initialize converted_from_user_script (if present) 1808 source.GetBoolean(keys::kConvertedFromUserScript, 1809 &converted_from_user_script_); 1810 1811 // Initialize icons (if present). 1812 if (source.HasKey(keys::kIcons)) { 1813 DictionaryValue* icons_value = NULL; 1814 if (!source.GetDictionary(keys::kIcons, &icons_value)) { 1815 *error = errors::kInvalidIcons; 1816 return false; 1817 } 1818 1819 for (size_t i = 0; i < arraysize(kIconSizes); ++i) { 1820 std::string key = base::IntToString(kIconSizes[i]); 1821 if (icons_value->HasKey(key)) { 1822 std::string icon_path; 1823 if (!icons_value->GetString(key, &icon_path)) { 1824 *error = ExtensionErrorUtils::FormatErrorMessage( 1825 errors::kInvalidIconPath, key); 1826 return false; 1827 } 1828 1829 if (!icon_path.empty() && icon_path[0] == '/') 1830 icon_path = icon_path.substr(1); 1831 1832 if (icon_path.empty()) { 1833 *error = ExtensionErrorUtils::FormatErrorMessage( 1834 errors::kInvalidIconPath, key); 1835 return false; 1836 } 1837 1838 icons_.Add(kIconSizes[i], icon_path); 1839 } 1840 } 1841 } 1842 1843 // Initialize themes (if present). 1844 is_theme_ = false; 1845 if (source.HasKey(keys::kTheme)) { 1846 // Themes cannot contain extension keys. 1847 if (ContainsNonThemeKeys(source)) { 1848 *error = errors::kThemesCannotContainExtensions; 1849 return false; 1850 } 1851 1852 DictionaryValue* theme_value = NULL; 1853 if (!source.GetDictionary(keys::kTheme, &theme_value)) { 1854 *error = errors::kInvalidTheme; 1855 return false; 1856 } 1857 is_theme_ = true; 1858 1859 DictionaryValue* images_value = NULL; 1860 if (theme_value->GetDictionary(keys::kThemeImages, &images_value)) { 1861 // Validate that the images are all strings 1862 for (DictionaryValue::key_iterator iter = images_value->begin_keys(); 1863 iter != images_value->end_keys(); ++iter) { 1864 std::string val; 1865 if (!images_value->GetString(*iter, &val)) { 1866 *error = errors::kInvalidThemeImages; 1867 return false; 1868 } 1869 } 1870 theme_images_.reset(images_value->DeepCopy()); 1871 } 1872 1873 DictionaryValue* colors_value = NULL; 1874 if (theme_value->GetDictionary(keys::kThemeColors, &colors_value)) { 1875 // Validate that the colors are RGB or RGBA lists 1876 for (DictionaryValue::key_iterator iter = colors_value->begin_keys(); 1877 iter != colors_value->end_keys(); ++iter) { 1878 ListValue* color_list = NULL; 1879 double alpha = 0.0; 1880 int alpha_int = 0; 1881 int color = 0; 1882 // The color must be a list 1883 if (!colors_value->GetListWithoutPathExpansion(*iter, &color_list) || 1884 // And either 3 items (RGB) or 4 (RGBA) 1885 ((color_list->GetSize() != 3) && 1886 ((color_list->GetSize() != 4) || 1887 // For RGBA, the fourth item must be a real or int alpha value 1888 (!color_list->GetDouble(3, &alpha) && 1889 !color_list->GetInteger(3, &alpha_int)))) || 1890 // For both RGB and RGBA, the first three items must be ints (R,G,B) 1891 !color_list->GetInteger(0, &color) || 1892 !color_list->GetInteger(1, &color) || 1893 !color_list->GetInteger(2, &color)) { 1894 *error = errors::kInvalidThemeColors; 1895 return false; 1896 } 1897 } 1898 theme_colors_.reset(colors_value->DeepCopy()); 1899 } 1900 1901 DictionaryValue* tints_value = NULL; 1902 if (theme_value->GetDictionary(keys::kThemeTints, &tints_value)) { 1903 // Validate that the tints are all reals. 1904 for (DictionaryValue::key_iterator iter = tints_value->begin_keys(); 1905 iter != tints_value->end_keys(); ++iter) { 1906 ListValue* tint_list = NULL; 1907 double v = 0.0; 1908 int vi = 0; 1909 if (!tints_value->GetListWithoutPathExpansion(*iter, &tint_list) || 1910 tint_list->GetSize() != 3 || 1911 !(tint_list->GetDouble(0, &v) || tint_list->GetInteger(0, &vi)) || 1912 !(tint_list->GetDouble(1, &v) || tint_list->GetInteger(1, &vi)) || 1913 !(tint_list->GetDouble(2, &v) || tint_list->GetInteger(2, &vi))) { 1914 *error = errors::kInvalidThemeTints; 1915 return false; 1916 } 1917 } 1918 theme_tints_.reset(tints_value->DeepCopy()); 1919 } 1920 1921 DictionaryValue* display_properties_value = NULL; 1922 if (theme_value->GetDictionary(keys::kThemeDisplayProperties, 1923 &display_properties_value)) { 1924 theme_display_properties_.reset( 1925 display_properties_value->DeepCopy()); 1926 } 1927 1928 return true; 1929 } 1930 1931 // Initialize plugins (optional). 1932 if (source.HasKey(keys::kPlugins)) { 1933 ListValue* list_value = NULL; 1934 if (!source.GetList(keys::kPlugins, &list_value)) { 1935 *error = errors::kInvalidPlugins; 1936 return false; 1937 } 1938 1939 for (size_t i = 0; i < list_value->GetSize(); ++i) { 1940 DictionaryValue* plugin_value = NULL; 1941 std::string path_str; 1942 bool is_public = false; 1943 1944 if (!list_value->GetDictionary(i, &plugin_value)) { 1945 *error = errors::kInvalidPlugins; 1946 return false; 1947 } 1948 1949 // Get plugins[i].path. 1950 if (!plugin_value->GetString(keys::kPluginsPath, &path_str)) { 1951 *error = ExtensionErrorUtils::FormatErrorMessage( 1952 errors::kInvalidPluginsPath, base::IntToString(i)); 1953 return false; 1954 } 1955 1956 // Get plugins[i].content (optional). 1957 if (plugin_value->HasKey(keys::kPluginsPublic)) { 1958 if (!plugin_value->GetBoolean(keys::kPluginsPublic, &is_public)) { 1959 *error = ExtensionErrorUtils::FormatErrorMessage( 1960 errors::kInvalidPluginsPublic, base::IntToString(i)); 1961 return false; 1962 } 1963 } 1964 1965 // We don't allow extension plugins to run on Chrome OS. We still 1966 // parse the manifest entry so that error messages are consistently 1967 // displayed across platforms. 1968#if !defined(OS_CHROMEOS) 1969 plugins_.push_back(PluginInfo()); 1970 plugins_.back().path = path().AppendASCII(path_str); 1971 plugins_.back().is_public = is_public; 1972#endif 1973 } 1974 } 1975 1976 if (CommandLine::ForCurrentProcess()->HasSwitch( 1977 switches::kEnableExperimentalExtensionApis) && 1978 source.HasKey(keys::kNaClModules)) { 1979 ListValue* list_value = NULL; 1980 if (!source.GetList(keys::kNaClModules, &list_value)) { 1981 *error = errors::kInvalidNaClModules; 1982 return false; 1983 } 1984 1985 for (size_t i = 0; i < list_value->GetSize(); ++i) { 1986 DictionaryValue* module_value = NULL; 1987 std::string path_str; 1988 std::string mime_type; 1989 1990 if (!list_value->GetDictionary(i, &module_value)) { 1991 *error = errors::kInvalidNaClModules; 1992 return false; 1993 } 1994 1995 // Get nacl_modules[i].path. 1996 if (!module_value->GetString(keys::kNaClModulesPath, &path_str)) { 1997 *error = ExtensionErrorUtils::FormatErrorMessage( 1998 errors::kInvalidNaClModulesPath, base::IntToString(i)); 1999 return false; 2000 } 2001 2002 // Get nacl_modules[i].mime_type. 2003 if (!module_value->GetString(keys::kNaClModulesMIMEType, &mime_type)) { 2004 *error = ExtensionErrorUtils::FormatErrorMessage( 2005 errors::kInvalidNaClModulesMIMEType, base::IntToString(i)); 2006 return false; 2007 } 2008 2009 nacl_modules_.push_back(NaClModuleInfo()); 2010 nacl_modules_.back().url = GetResourceURL(path_str); 2011 nacl_modules_.back().mime_type = mime_type; 2012 } 2013 } 2014 2015 // Initialize toolstrips. This is deprecated for public use. 2016 // NOTE(erikkay) Although deprecated, we intend to preserve this parsing 2017 // code indefinitely. Please contact me or Joi for details as to why. 2018 if (CommandLine::ForCurrentProcess()->HasSwitch( 2019 switches::kEnableExperimentalExtensionApis) && 2020 source.HasKey(keys::kToolstrips)) { 2021 ListValue* list_value = NULL; 2022 if (!source.GetList(keys::kToolstrips, &list_value)) { 2023 *error = errors::kInvalidToolstrips; 2024 return false; 2025 } 2026 2027 for (size_t i = 0; i < list_value->GetSize(); ++i) { 2028 GURL toolstrip; 2029 DictionaryValue* toolstrip_value = NULL; 2030 std::string toolstrip_path; 2031 if (list_value->GetString(i, &toolstrip_path)) { 2032 // Support a simple URL value for backwards compatibility. 2033 toolstrip = GetResourceURL(toolstrip_path); 2034 } else if (list_value->GetDictionary(i, &toolstrip_value)) { 2035 if (!toolstrip_value->GetString(keys::kToolstripPath, 2036 &toolstrip_path)) { 2037 *error = ExtensionErrorUtils::FormatErrorMessage( 2038 errors::kInvalidToolstrip, base::IntToString(i)); 2039 return false; 2040 } 2041 toolstrip = GetResourceURL(toolstrip_path); 2042 } else { 2043 *error = ExtensionErrorUtils::FormatErrorMessage( 2044 errors::kInvalidToolstrip, base::IntToString(i)); 2045 return false; 2046 } 2047 toolstrips_.push_back(toolstrip); 2048 } 2049 } 2050 2051 // Initialize content scripts (optional). 2052 if (source.HasKey(keys::kContentScripts)) { 2053 ListValue* list_value; 2054 if (!source.GetList(keys::kContentScripts, &list_value)) { 2055 *error = errors::kInvalidContentScriptsList; 2056 return false; 2057 } 2058 2059 for (size_t i = 0; i < list_value->GetSize(); ++i) { 2060 DictionaryValue* content_script = NULL; 2061 if (!list_value->GetDictionary(i, &content_script)) { 2062 *error = ExtensionErrorUtils::FormatErrorMessage( 2063 errors::kInvalidContentScript, base::IntToString(i)); 2064 return false; 2065 } 2066 2067 UserScript script; 2068 if (!LoadUserScriptHelper(content_script, i, flags, error, &script)) 2069 return false; // Failed to parse script context definition. 2070 script.set_extension_id(id()); 2071 if (converted_from_user_script_) { 2072 script.set_emulate_greasemonkey(true); 2073 script.set_match_all_frames(true); // Greasemonkey matches all frames. 2074 } 2075 content_scripts_.push_back(script); 2076 } 2077 } 2078 2079 // Initialize page action (optional). 2080 DictionaryValue* page_action_value = NULL; 2081 2082 if (source.HasKey(keys::kPageActions)) { 2083 ListValue* list_value = NULL; 2084 if (!source.GetList(keys::kPageActions, &list_value)) { 2085 *error = errors::kInvalidPageActionsList; 2086 return false; 2087 } 2088 2089 size_t list_value_length = list_value->GetSize(); 2090 2091 if (list_value_length == 0u) { 2092 // A list with zero items is allowed, and is equivalent to not having 2093 // a page_actions key in the manifest. Don't set |page_action_value|. 2094 } else if (list_value_length == 1u) { 2095 if (!list_value->GetDictionary(0, &page_action_value)) { 2096 *error = errors::kInvalidPageAction; 2097 return false; 2098 } 2099 } else { // list_value_length > 1u. 2100 *error = errors::kInvalidPageActionsListSize; 2101 return false; 2102 } 2103 } else if (source.HasKey(keys::kPageAction)) { 2104 if (!source.GetDictionary(keys::kPageAction, &page_action_value)) { 2105 *error = errors::kInvalidPageAction; 2106 return false; 2107 } 2108 } 2109 2110 // If page_action_value is not NULL, then there was a valid page action. 2111 if (page_action_value) { 2112 page_action_.reset( 2113 LoadExtensionActionHelper(page_action_value, error)); 2114 if (!page_action_.get()) 2115 return false; // Failed to parse page action definition. 2116 } 2117 2118 // Initialize browser action (optional). 2119 if (source.HasKey(keys::kBrowserAction)) { 2120 DictionaryValue* browser_action_value = NULL; 2121 if (!source.GetDictionary(keys::kBrowserAction, &browser_action_value)) { 2122 *error = errors::kInvalidBrowserAction; 2123 return false; 2124 } 2125 2126 browser_action_.reset( 2127 LoadExtensionActionHelper(browser_action_value, error)); 2128 if (!browser_action_.get()) 2129 return false; // Failed to parse browser action definition. 2130 } 2131 2132 // Initialize file browser actions (optional). 2133 if (source.HasKey(keys::kFileBrowserHandlers)) { 2134 ListValue* file_browser_handlers_value = NULL; 2135 if (!source.GetList(keys::kFileBrowserHandlers, 2136 &file_browser_handlers_value)) { 2137 *error = errors::kInvalidFileBrowserHandler; 2138 return false; 2139 } 2140 2141 file_browser_handlers_.reset( 2142 LoadFileBrowserHandlers(file_browser_handlers_value, error)); 2143 if (!file_browser_handlers_.get()) 2144 return false; // Failed to parse file browser actions definition. 2145 } 2146 2147 // Load App settings. 2148 if (!LoadIsApp(manifest_value_.get(), error) || 2149 !LoadExtent(manifest_value_.get(), keys::kWebURLs, 2150 &extent_, 2151 errors::kInvalidWebURLs, errors::kInvalidWebURL, 2152 parse_strictness, error) || 2153 !EnsureNotHybridApp(manifest_value_.get(), error) || 2154 !LoadLaunchURL(manifest_value_.get(), error) || 2155 !LoadLaunchContainer(manifest_value_.get(), error) || 2156 !LoadAppIsolation(manifest_value_.get(), error)) { 2157 return false; 2158 } 2159 2160 // Initialize options page url (optional). 2161 // Funtion LoadIsApp() set is_app_ above. 2162 if (source.HasKey(keys::kOptionsPage)) { 2163 std::string options_str; 2164 if (!source.GetString(keys::kOptionsPage, &options_str)) { 2165 *error = errors::kInvalidOptionsPage; 2166 return false; 2167 } 2168 2169 if (is_hosted_app()) { 2170 // hosted apps require an absolute URL. 2171 GURL options_url(options_str); 2172 if (!options_url.is_valid() || 2173 !(options_url.SchemeIs("http") || options_url.SchemeIs("https"))) { 2174 *error = errors::kInvalidOptionsPageInHostedApp; 2175 return false; 2176 } 2177 options_url_ = options_url; 2178 } else { 2179 GURL absolute(options_str); 2180 if (absolute.is_valid()) { 2181 *error = errors::kInvalidOptionsPageExpectUrlInPackage; 2182 return false; 2183 } 2184 options_url_ = GetResourceURL(options_str); 2185 if (!options_url_.is_valid()) { 2186 *error = errors::kInvalidOptionsPage; 2187 return false; 2188 } 2189 } 2190 } 2191 2192 // Initialize the permissions (optional). 2193 if (source.HasKey(keys::kPermissions)) { 2194 ListValue* permissions = NULL; 2195 if (!source.GetList(keys::kPermissions, &permissions)) { 2196 *error = ExtensionErrorUtils::FormatErrorMessage( 2197 errors::kInvalidPermissions, ""); 2198 return false; 2199 } 2200 2201 for (size_t i = 0; i < permissions->GetSize(); ++i) { 2202 std::string permission_str; 2203 if (!permissions->GetString(i, &permission_str)) { 2204 *error = ExtensionErrorUtils::FormatErrorMessage( 2205 errors::kInvalidPermission, base::IntToString(i)); 2206 return false; 2207 } 2208 2209 // Only COMPONENT extensions can use private APIs. 2210 // TODO(asargent) - We want a more general purpose mechanism for this, 2211 // and better error messages. (http://crbug.com/54013) 2212 if (!IsComponentOnlyPermission(permission_str) 2213#ifndef NDEBUG 2214 && !CommandLine::ForCurrentProcess()->HasSwitch( 2215 switches::kExposePrivateExtensionApi) 2216#endif 2217 ) { 2218 continue; 2219 } 2220 2221 // Remap the old unlimited storage permission name. 2222 if (permission_str == kOldUnlimitedStoragePermission) 2223 permission_str = kUnlimitedStoragePermission; 2224 2225 if (web_extent().is_empty() || location() == Extension::COMPONENT) { 2226 // Check if it's a module permission. If so, enable that permission. 2227 if (IsAPIPermission(permission_str)) { 2228 // Only allow the experimental API permission if the command line 2229 // flag is present, or if the extension is a component of Chrome. 2230 if (permission_str == Extension::kExperimentalPermission && 2231 !CommandLine::ForCurrentProcess()->HasSwitch( 2232 switches::kEnableExperimentalExtensionApis) && 2233 location() != Extension::COMPONENT) { 2234 *error = errors::kExperimentalFlagRequired; 2235 return false; 2236 } 2237 api_permissions_.insert(permission_str); 2238 continue; 2239 } 2240 } else { 2241 // Hosted apps only get access to a subset of the valid permissions. 2242 if (IsHostedAppPermission(permission_str)) { 2243 api_permissions_.insert(permission_str); 2244 continue; 2245 } 2246 } 2247 2248 // Check if it's a host pattern permission. 2249 URLPattern pattern = URLPattern(CanExecuteScriptEverywhere() ? 2250 URLPattern::SCHEME_ALL : kValidHostPermissionSchemes); 2251 2252 URLPattern::ParseResult parse_result = pattern.Parse(permission_str, 2253 parse_strictness); 2254 if (parse_result == URLPattern::PARSE_SUCCESS) { 2255 if (!CanSpecifyHostPermission(pattern)) { 2256 *error = ExtensionErrorUtils::FormatErrorMessage( 2257 errors::kInvalidPermissionScheme, base::IntToString(i)); 2258 return false; 2259 } 2260 2261 // The path component is not used for host permissions, so we force it 2262 // to match all paths. 2263 pattern.SetPath("/*"); 2264 2265 if (pattern.MatchesScheme(chrome::kFileScheme) && 2266 !CanExecuteScriptEverywhere()) { 2267 wants_file_access_ = true; 2268 if (!(flags & ALLOW_FILE_ACCESS)) 2269 pattern.set_valid_schemes( 2270 pattern.valid_schemes() & ~URLPattern::SCHEME_FILE); 2271 } 2272 2273 host_permissions_.push_back(pattern); 2274 } 2275 2276 // If it's not a host permission, then it's probably an unknown API 2277 // permission. Do not throw an error so extensions can retain 2278 // backwards compatability (http://crbug.com/42742). 2279 // TODO(jstritar): We can improve error messages by adding better 2280 // validation of API permissions here. 2281 // TODO(skerner): Consider showing the reason |permission_str| is not 2282 // a valid URL pattern if it is almost valid. For example, if it has 2283 // a valid scheme, and failed to parse because it has a port, show an 2284 // error. 2285 } 2286 } 2287 2288 // Initialize background url (optional). 2289 if (source.HasKey(keys::kBackground)) { 2290 std::string background_str; 2291 if (!source.GetString(keys::kBackground, &background_str)) { 2292 *error = errors::kInvalidBackground; 2293 return false; 2294 } 2295 2296 if (is_hosted_app()) { 2297 // Make sure "background" permission is set. 2298 if (api_permissions_.find(kBackgroundPermission) == 2299 api_permissions_.end()) { 2300 *error = errors::kBackgroundPermissionNeeded; 2301 return false; 2302 } 2303 // Hosted apps require an absolute URL. 2304 GURL bg_page(background_str); 2305 if (!bg_page.is_valid()) { 2306 *error = errors::kInvalidBackgroundInHostedApp; 2307 return false; 2308 } 2309 2310 if (!(bg_page.SchemeIs("https") || 2311 (CommandLine::ForCurrentProcess()->HasSwitch( 2312 switches::kAllowHTTPBackgroundPage) && 2313 bg_page.SchemeIs("http")))) { 2314 *error = errors::kInvalidBackgroundInHostedApp; 2315 return false; 2316 } 2317 background_url_ = bg_page; 2318 } else { 2319 background_url_ = GetResourceURL(background_str); 2320 } 2321 } 2322 2323 if (source.HasKey(keys::kDefaultLocale)) { 2324 if (!source.GetString(keys::kDefaultLocale, &default_locale_) || 2325 !l10n_util::IsValidLocaleSyntax(default_locale_)) { 2326 *error = errors::kInvalidDefaultLocale; 2327 return false; 2328 } 2329 } 2330 2331 // Chrome URL overrides (optional) 2332 if (source.HasKey(keys::kChromeURLOverrides)) { 2333 DictionaryValue* overrides = NULL; 2334 if (!source.GetDictionary(keys::kChromeURLOverrides, &overrides)) { 2335 *error = errors::kInvalidChromeURLOverrides; 2336 return false; 2337 } 2338 2339 // Validate that the overrides are all strings 2340 for (DictionaryValue::key_iterator iter = overrides->begin_keys(); 2341 iter != overrides->end_keys(); ++iter) { 2342 std::string page = *iter; 2343 std::string val; 2344 // Restrict override pages to a list of supported URLs. 2345 if ((page != chrome::kChromeUINewTabHost && 2346#if defined(TOUCH_UI) 2347 page != chrome::kChromeUIKeyboardHost && 2348#endif 2349#if defined(OS_CHROMEOS) 2350 page != chrome::kChromeUIActivationMessageHost && 2351#endif 2352 page != chrome::kChromeUIBookmarksHost && 2353 page != chrome::kChromeUIHistoryHost) || 2354 !overrides->GetStringWithoutPathExpansion(*iter, &val)) { 2355 *error = errors::kInvalidChromeURLOverrides; 2356 return false; 2357 } 2358 // Replace the entry with a fully qualified chrome-extension:// URL. 2359 chrome_url_overrides_[page] = GetResourceURL(val); 2360 } 2361 2362 // An extension may override at most one page. 2363 if (overrides->size() > 1) { 2364 *error = errors::kMultipleOverrides; 2365 return false; 2366 } 2367 } 2368 2369 if (source.HasKey(keys::kOmnibox)) { 2370 if (!source.GetString(keys::kOmniboxKeyword, &omnibox_keyword_) || 2371 omnibox_keyword_.empty()) { 2372 *error = errors::kInvalidOmniboxKeyword; 2373 return false; 2374 } 2375 } 2376 2377 // Initialize devtools page url (optional). 2378 if (source.HasKey(keys::kDevToolsPage)) { 2379 std::string devtools_str; 2380 if (!source.GetString(keys::kDevToolsPage, &devtools_str)) { 2381 *error = errors::kInvalidDevToolsPage; 2382 return false; 2383 } 2384 if (!HasApiPermission(Extension::kExperimentalPermission)) { 2385 *error = errors::kDevToolsExperimental; 2386 return false; 2387 } 2388 devtools_url_ = GetResourceURL(devtools_str); 2389 } 2390 2391 // Initialize sidebar action (optional). 2392 if (source.HasKey(keys::kSidebar)) { 2393 DictionaryValue* sidebar_value = NULL; 2394 if (!source.GetDictionary(keys::kSidebar, &sidebar_value)) { 2395 *error = errors::kInvalidSidebar; 2396 return false; 2397 } 2398 if (!HasApiPermission(Extension::kExperimentalPermission)) { 2399 *error = errors::kSidebarExperimental; 2400 return false; 2401 } 2402 sidebar_defaults_.reset(LoadExtensionSidebarDefaults(sidebar_value, error)); 2403 if (!sidebar_defaults_.get()) 2404 return false; // Failed to parse sidebar definition. 2405 } 2406 2407 // Initialize text-to-speech voices (optional). 2408 if (source.HasKey(keys::kTts)) { 2409 DictionaryValue* tts_dict = NULL; 2410 if (!source.GetDictionary(keys::kTts, &tts_dict)) { 2411 *error = errors::kInvalidTts; 2412 return false; 2413 } 2414 2415 if (tts_dict->HasKey(keys::kTtsVoices)) { 2416 ListValue* tts_voices = NULL; 2417 if (!tts_dict->GetList(keys::kTtsVoices, &tts_voices)) { 2418 *error = errors::kInvalidTtsVoices; 2419 return false; 2420 } 2421 2422 for (size_t i = 0; i < tts_voices->GetSize(); i++) { 2423 DictionaryValue* one_tts_voice = NULL; 2424 if (!tts_voices->GetDictionary(i, &one_tts_voice)) { 2425 *error = errors::kInvalidTtsVoices; 2426 return false; 2427 } 2428 2429 TtsVoice voice_data; 2430 if (one_tts_voice->HasKey(keys::kTtsVoicesVoiceName)) { 2431 if (!one_tts_voice->GetString( 2432 keys::kTtsVoicesVoiceName, &voice_data.voice_name)) { 2433 *error = errors::kInvalidTtsVoicesVoiceName; 2434 return false; 2435 } 2436 } 2437 if (one_tts_voice->HasKey(keys::kTtsVoicesLocale)) { 2438 if (!one_tts_voice->GetString( 2439 keys::kTtsVoicesLocale, &voice_data.locale) || 2440 !l10n_util::IsValidLocaleSyntax(voice_data.locale)) { 2441 *error = errors::kInvalidTtsVoicesLocale; 2442 return false; 2443 } 2444 } 2445 if (one_tts_voice->HasKey(keys::kTtsVoicesGender)) { 2446 if (!one_tts_voice->GetString( 2447 keys::kTtsVoicesGender, &voice_data.gender) || 2448 (voice_data.gender != keys::kTtsGenderMale && 2449 voice_data.gender != keys::kTtsGenderFemale)) { 2450 *error = errors::kInvalidTtsVoicesGender; 2451 return false; 2452 } 2453 } 2454 2455 tts_voices_.push_back(voice_data); 2456 } 2457 } 2458 } 2459 2460 // Initialize incognito behavior. Apps default to split mode, extensions 2461 // default to spanning. 2462 incognito_split_mode_ = is_app(); 2463 if (source.HasKey(keys::kIncognito)) { 2464 std::string value; 2465 if (!source.GetString(keys::kIncognito, &value)) { 2466 *error = errors::kInvalidIncognitoBehavior; 2467 return false; 2468 } 2469 if (value == values::kIncognitoSpanning) { 2470 incognito_split_mode_ = false; 2471 } else if (value == values::kIncognitoSplit) { 2472 incognito_split_mode_ = true; 2473 } else { 2474 *error = errors::kInvalidIncognitoBehavior; 2475 return false; 2476 } 2477 } 2478 2479 if (HasMultipleUISurfaces()) { 2480 *error = errors::kOneUISurfaceOnly; 2481 return false; 2482 } 2483 2484 InitEffectiveHostPermissions(); 2485 2486 // Although |source| is passed in as a const, it's still possible to modify 2487 // it. This is dangerous since the utility process re-uses |source| after 2488 // it calls InitFromValue, passing it up to the browser process which calls 2489 // InitFromValue again. As a result, we need to make sure that nobody 2490 // accidentally modifies it. 2491 DCHECK(source.Equals(manifest_value_.get())); 2492 2493 return true; 2494} 2495 2496// static 2497std::string Extension::ChromeStoreLaunchURL() { 2498 std::string gallery_prefix = extension_urls::kGalleryBrowsePrefix; 2499 if (CommandLine::ForCurrentProcess()->HasSwitch(switches::kAppsGalleryURL)) 2500 gallery_prefix = CommandLine::ForCurrentProcess()->GetSwitchValueASCII( 2501 switches::kAppsGalleryURL); 2502 if (EndsWith(gallery_prefix, "/", true)) 2503 gallery_prefix = gallery_prefix.substr(0, gallery_prefix.length() - 1); 2504 return gallery_prefix; 2505} 2506 2507GURL Extension::GetHomepageURL() const { 2508 if (homepage_url_.is_valid()) 2509 return homepage_url_; 2510 2511 if (!UpdatesFromGallery()) 2512 return GURL(); 2513 2514 // TODO(erikkay): This may not be entirely correct with the webstore. 2515 // I think it will have a mixture of /extensions/detail and /webstore/detail 2516 // URLs. Perhaps they'll handle this nicely with redirects? 2517 GURL url(ChromeStoreLaunchURL() + std::string("/detail/") + id()); 2518 return url; 2519} 2520 2521std::set<FilePath> Extension::GetBrowserImages() const { 2522 std::set<FilePath> image_paths; 2523 // TODO(viettrungluu): These |FilePath::FromWStringHack(UTF8ToWide())| 2524 // indicate that we're doing something wrong. 2525 2526 // Extension icons. 2527 for (ExtensionIconSet::IconMap::const_iterator iter = icons().map().begin(); 2528 iter != icons().map().end(); ++iter) { 2529 image_paths.insert(FilePath::FromWStringHack(UTF8ToWide(iter->second))); 2530 } 2531 2532 // Theme images. 2533 DictionaryValue* theme_images = GetThemeImages(); 2534 if (theme_images) { 2535 for (DictionaryValue::key_iterator it = theme_images->begin_keys(); 2536 it != theme_images->end_keys(); ++it) { 2537 std::string val; 2538 if (theme_images->GetStringWithoutPathExpansion(*it, &val)) 2539 image_paths.insert(FilePath::FromWStringHack(UTF8ToWide(val))); 2540 } 2541 } 2542 2543 // Page action icons. 2544 if (page_action()) { 2545 std::vector<std::string>* icon_paths = page_action()->icon_paths(); 2546 for (std::vector<std::string>::iterator iter = icon_paths->begin(); 2547 iter != icon_paths->end(); ++iter) { 2548 image_paths.insert(FilePath::FromWStringHack(UTF8ToWide(*iter))); 2549 } 2550 } 2551 2552 // Browser action icons. 2553 if (browser_action()) { 2554 std::vector<std::string>* icon_paths = browser_action()->icon_paths(); 2555 for (std::vector<std::string>::iterator iter = icon_paths->begin(); 2556 iter != icon_paths->end(); ++iter) { 2557 image_paths.insert(FilePath::FromWStringHack(UTF8ToWide(*iter))); 2558 } 2559 } 2560 2561 return image_paths; 2562} 2563 2564GURL Extension::GetFullLaunchURL() const { 2565 if (!launch_local_path().empty()) 2566 return url().Resolve(launch_local_path()); 2567 else 2568 return GURL(launch_web_url()); 2569} 2570 2571static std::string SizeToString(const gfx::Size& max_size) { 2572 return base::IntToString(max_size.width()) + "x" + 2573 base::IntToString(max_size.height()); 2574} 2575 2576// static 2577void Extension::SetScriptingWhitelist( 2578 const Extension::ScriptingWhitelist& whitelist) { 2579 ScriptingWhitelist* current_whitelist = 2580 ExtensionConfig::GetInstance()->whitelist(); 2581 current_whitelist->clear(); 2582 for (ScriptingWhitelist::const_iterator it = whitelist.begin(); 2583 it != whitelist.end(); ++it) { 2584 current_whitelist->push_back(*it); 2585 } 2586} 2587 2588// static 2589const Extension::ScriptingWhitelist* Extension::GetScriptingWhitelist() { 2590 return ExtensionConfig::GetInstance()->whitelist(); 2591} 2592 2593void Extension::SetCachedImage(const ExtensionResource& source, 2594 const SkBitmap& image, 2595 const gfx::Size& original_size) const { 2596 DCHECK(source.extension_root() == path()); // The resource must come from 2597 // this extension. 2598 const FilePath& path = source.relative_path(); 2599 gfx::Size actual_size(image.width(), image.height()); 2600 if (actual_size == original_size) { 2601 image_cache_[ImageCacheKey(path, std::string())] = image; 2602 } else { 2603 image_cache_[ImageCacheKey(path, SizeToString(actual_size))] = image; 2604 } 2605} 2606 2607bool Extension::HasCachedImage(const ExtensionResource& source, 2608 const gfx::Size& max_size) const { 2609 DCHECK(source.extension_root() == path()); // The resource must come from 2610 // this extension. 2611 return GetCachedImageImpl(source, max_size) != NULL; 2612} 2613 2614SkBitmap Extension::GetCachedImage(const ExtensionResource& source, 2615 const gfx::Size& max_size) const { 2616 DCHECK(source.extension_root() == path()); // The resource must come from 2617 // this extension. 2618 SkBitmap* image = GetCachedImageImpl(source, max_size); 2619 return image ? *image : SkBitmap(); 2620} 2621 2622SkBitmap* Extension::GetCachedImageImpl(const ExtensionResource& source, 2623 const gfx::Size& max_size) const { 2624 const FilePath& path = source.relative_path(); 2625 2626 // Look for exact size match. 2627 ImageCache::iterator i = image_cache_.find( 2628 ImageCacheKey(path, SizeToString(max_size))); 2629 if (i != image_cache_.end()) 2630 return &(i->second); 2631 2632 // If we have the original size version cached, return that if it's small 2633 // enough. 2634 i = image_cache_.find(ImageCacheKey(path, std::string())); 2635 if (i != image_cache_.end()) { 2636 SkBitmap& image = i->second; 2637 if (image.width() <= max_size.width() && 2638 image.height() <= max_size.height()) 2639 return &(i->second); 2640 } 2641 2642 return NULL; 2643} 2644 2645ExtensionResource Extension::GetIconResource( 2646 int size, ExtensionIconSet::MatchType match_type) const { 2647 std::string path = icons().Get(size, match_type); 2648 if (path.empty()) 2649 return ExtensionResource(); 2650 return GetResource(path); 2651} 2652 2653GURL Extension::GetIconURL(int size, 2654 ExtensionIconSet::MatchType match_type) const { 2655 std::string path = icons().Get(size, match_type); 2656 if (path.empty()) 2657 return GURL(); 2658 else 2659 return GetResourceURL(path); 2660} 2661 2662bool Extension::CanSpecifyHostPermission(const URLPattern& pattern) const { 2663 if (!pattern.match_all_urls() && 2664 pattern.MatchesScheme(chrome::kChromeUIScheme)) { 2665 // Only allow access to chrome://favicon to regular extensions. Component 2666 // extensions can have access to all of chrome://*. 2667 return (pattern.host() == chrome::kChromeUIFaviconHost || 2668 CanExecuteScriptEverywhere()); 2669 } 2670 2671 // Otherwise, the valid schemes were handled by URLPattern. 2672 return true; 2673} 2674 2675// static 2676bool Extension::HasApiPermission( 2677 const std::set<std::string>& api_permissions, 2678 const std::string& function_name) { 2679 std::string permission_name = function_name; 2680 2681 for (size_t i = 0; i < kNumNonPermissionFunctionNames; ++i) { 2682 if (permission_name == kNonPermissionFunctionNames[i]) 2683 return true; 2684 } 2685 2686 // See if this is a function or event name first and strip out the package. 2687 // Functions will be of the form package.function 2688 // Events will be of the form package/id or package.optional.stuff 2689 size_t separator = function_name.find_first_of("./"); 2690 if (separator != std::string::npos) 2691 permission_name = function_name.substr(0, separator); 2692 2693 // windows and tabs are the same permission. 2694 if (permission_name == kWindowPermission) 2695 permission_name = Extension::kTabPermission; 2696 2697 if (api_permissions.count(permission_name)) 2698 return true; 2699 2700 for (size_t i = 0; i < kNumNonPermissionModuleNames; ++i) { 2701 if (permission_name == kNonPermissionModuleNames[i]) { 2702 return true; 2703 } 2704 } 2705 2706 return false; 2707} 2708 2709bool Extension::HasHostPermission(const GURL& url) const { 2710 for (URLPatternList::const_iterator host = host_permissions().begin(); 2711 host != host_permissions().end(); ++host) { 2712 // Non-component extensions can only access chrome://favicon and no other 2713 // chrome:// scheme urls. 2714 if (url.SchemeIs(chrome::kChromeUIScheme) && 2715 url.host() != chrome::kChromeUIFaviconHost && 2716 location() != Extension::COMPONENT) 2717 return false; 2718 2719 if (host->MatchesUrl(url)) 2720 return true; 2721 } 2722 return false; 2723} 2724 2725void Extension::InitEffectiveHostPermissions() { 2726 // Some APIs effectively grant access to every site. New ones should be 2727 // added here. (I'm looking at you, network API) 2728 if (HasApiPermission(api_permissions_, kProxyPermission) || 2729 !devtools_url_.is_empty()) { 2730 URLPattern all_urls(URLPattern::SCHEME_ALL); 2731 all_urls.set_match_all_urls(true); 2732 effective_host_permissions_.AddPattern(all_urls); 2733 return; 2734 } 2735 2736 for (URLPatternList::const_iterator host = host_permissions().begin(); 2737 host != host_permissions().end(); ++host) 2738 effective_host_permissions_.AddPattern(*host); 2739 2740 for (UserScriptList::const_iterator content_script = 2741 content_scripts().begin(); 2742 content_script != content_scripts().end(); ++content_script) { 2743 UserScript::PatternList::const_iterator pattern = 2744 content_script->url_patterns().begin(); 2745 for (; pattern != content_script->url_patterns().end(); ++pattern) 2746 effective_host_permissions_.AddPattern(*pattern); 2747 } 2748} 2749 2750bool Extension::IsComponentOnlyPermission 2751 (const std::string& permission) const { 2752 if (location() == Extension::COMPONENT) 2753 return true; 2754 2755 // Non-component extensions are not allowed to access private apis. 2756 for (size_t i = 0; i < Extension::kNumComponentPrivatePermissions; ++i) { 2757 if (permission == Extension::kComponentPrivatePermissionNames[i]) 2758 return false; 2759 } 2760 return true; 2761} 2762 2763bool Extension::HasMultipleUISurfaces() const { 2764 int num_surfaces = 0; 2765 2766 if (page_action()) 2767 ++num_surfaces; 2768 2769 if (browser_action()) 2770 ++num_surfaces; 2771 2772 if (is_app()) 2773 ++num_surfaces; 2774 2775 return num_surfaces > 1; 2776} 2777 2778bool Extension::CanExecuteScriptOnPage(const GURL& page_url, 2779 const UserScript* script, 2780 std::string* error) const { 2781 // The gallery is special-cased as a restricted URL for scripting to prevent 2782 // access to special JS bindings we expose to the gallery (and avoid things 2783 // like extensions removing the "report abuse" link). 2784 // TODO(erikkay): This seems like the wrong test. Shouldn't we we testing 2785 // against the store app extent? 2786 if ((page_url.host() == GURL(Extension::ChromeStoreLaunchURL()).host()) && 2787 !CanExecuteScriptEverywhere() && 2788 !CommandLine::ForCurrentProcess()->HasSwitch( 2789 switches::kAllowScriptingGallery)) { 2790 if (error) 2791 *error = errors::kCannotScriptGallery; 2792 return false; 2793 } 2794 2795 if (page_url.SchemeIs(chrome::kChromeUIScheme) && 2796 !CanExecuteScriptEverywhere()) 2797 return false; 2798 2799 // If a script is specified, use its matches. 2800 if (script) 2801 return script->MatchesUrl(page_url); 2802 2803 // Otherwise, see if this extension has permission to execute script 2804 // programmatically on pages. 2805 for (size_t i = 0; i < host_permissions_.size(); ++i) { 2806 if (host_permissions_[i].MatchesUrl(page_url)) 2807 return true; 2808 } 2809 2810 if (error) { 2811 *error = ExtensionErrorUtils::FormatErrorMessage(errors::kCannotAccessPage, 2812 page_url.spec()); 2813 } 2814 2815 return false; 2816} 2817 2818// static 2819bool Extension::HasEffectiveAccessToAllHosts( 2820 const ExtensionExtent& effective_host_permissions, 2821 const std::set<std::string>& api_permissions) { 2822 const URLPatternList patterns = effective_host_permissions.patterns(); 2823 for (URLPatternList::const_iterator host = patterns.begin(); 2824 host != patterns.end(); ++host) { 2825 if (host->match_all_urls() || 2826 (host->match_subdomains() && host->host().empty())) 2827 return true; 2828 } 2829 2830 return false; 2831} 2832 2833bool Extension::HasEffectiveAccessToAllHosts() const { 2834 return HasEffectiveAccessToAllHosts(GetEffectiveHostPermissions(), 2835 api_permissions()); 2836} 2837 2838bool Extension::HasFullPermissions() const { 2839 return !plugins().empty(); 2840} 2841 2842bool Extension::ShowConfigureContextMenus() const { 2843 // Don't show context menu for component extensions. We might want to show 2844 // options for component extension button but now there is no component 2845 // extension with options. All other menu items like uninstall have 2846 // no sense for component extensions. 2847 return location() != Extension::COMPONENT; 2848} 2849 2850bool Extension::IsAPIPermission(const std::string& str) const { 2851 for (size_t i = 0; i < Extension::kNumPermissions; ++i) { 2852 if (str == Extension::kPermissions[i].name) { 2853 return true; 2854 } 2855 } 2856 return false; 2857} 2858 2859bool Extension::CanExecuteScriptEverywhere() const { 2860 if (location() == Extension::COMPONENT 2861#ifndef NDEBUG 2862 || CommandLine::ForCurrentProcess()->HasSwitch( 2863 switches::kExposePrivateExtensionApi) 2864#endif 2865 ) 2866 return true; 2867 2868 ScriptingWhitelist* whitelist = 2869 ExtensionConfig::GetInstance()->whitelist(); 2870 2871 for (ScriptingWhitelist::const_iterator it = whitelist->begin(); 2872 it != whitelist->end(); ++it) { 2873 if (id() == *it) { 2874 return true; 2875 } 2876 } 2877 2878 return false; 2879} 2880 2881bool Extension::CanCaptureVisiblePage(const GURL& page_url, 2882 std::string *error) const { 2883 if (HasHostPermission(page_url) || page_url.GetOrigin() == url()) 2884 return true; 2885 2886 if (error) { 2887 *error = ExtensionErrorUtils::FormatErrorMessage(errors::kCannotAccessPage, 2888 page_url.spec()); 2889 } 2890 return false; 2891} 2892 2893bool Extension::UpdatesFromGallery() const { 2894 return update_url() == GalleryUpdateUrl(false) || 2895 update_url() == GalleryUpdateUrl(true); 2896} 2897 2898bool Extension::OverlapsWithOrigin(const GURL& origin) const { 2899 if (url() == origin) 2900 return true; 2901 2902 if (web_extent().is_empty()) 2903 return false; 2904 2905 // Note: patterns and extents ignore port numbers. 2906 URLPattern origin_only_pattern(kValidWebExtentSchemes); 2907 if (!origin_only_pattern.SetScheme(origin.scheme())) 2908 return false; 2909 origin_only_pattern.set_host(origin.host()); 2910 origin_only_pattern.SetPath("/*"); 2911 2912 ExtensionExtent origin_only_pattern_list; 2913 origin_only_pattern_list.AddPattern(origin_only_pattern); 2914 2915 return web_extent().OverlapsWith(origin_only_pattern_list); 2916} 2917 2918ExtensionInfo::ExtensionInfo(const DictionaryValue* manifest, 2919 const std::string& id, 2920 const FilePath& path, 2921 Extension::Location location) 2922 : extension_id(id), 2923 extension_path(path), 2924 extension_location(location) { 2925 if (manifest) 2926 extension_manifest.reset(manifest->DeepCopy()); 2927} 2928 2929ExtensionInfo::~ExtensionInfo() {} 2930 2931UninstalledExtensionInfo::UninstalledExtensionInfo( 2932 const Extension& extension) 2933 : extension_id(extension.id()), 2934 extension_api_permissions(extension.api_permissions()), 2935 extension_type(extension.GetType()), 2936 update_url(extension.update_url()) {} 2937 2938UninstalledExtensionInfo::~UninstalledExtensionInfo() {} 2939 2940 2941UnloadedExtensionInfo::UnloadedExtensionInfo( 2942 const Extension* extension, 2943 Reason reason) 2944 : reason(reason), 2945 already_disabled(false), 2946 extension(extension) {} 2947