extension.cc revision ddb351dbec246cf1fab5ec20d2d5520909041de1
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