• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
48 namespace keys = extension_manifest_keys;
49 namespace values = extension_manifest_values;
50 namespace errors = extension_manifest_errors;
51 
52 namespace {
53 
54 const int kPEMOutputColumns = 65;
55 
56 // KEY MARKERS
57 const char kKeyBeginHeaderMarker[] = "-----BEGIN";
58 const char kKeyBeginFooterMarker[] = "-----END";
59 const char kKeyInfoEndMarker[] = "KEY-----";
60 const char kPublic[] = "PUBLIC";
61 const char kPrivate[] = "PRIVATE";
62 
63 const 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.
ConvertHexadecimalToIDAlphabet(std::string * id)69 static 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).
80 static 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 
IsBaseCrxKey(const std::string & key)92 bool 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.
102 const int kUndefinedMessageId = -1;
103 
104 // Names of API modules that do not require a permission.
105 const char kBrowserActionModuleName[] = "browserAction";
106 const char kBrowserActionsModuleName[] = "browserActions";
107 const char kDevToolsModuleName[] = "devtools";
108 const char kExtensionModuleName[] = "extension";
109 const char kI18NModuleName[] = "i18n";
110 const char kOmniboxModuleName[] = "omnibox";
111 const char kPageActionModuleName[] = "pageAction";
112 const char kPageActionsModuleName[] = "pageActions";
113 const char kTestModuleName[] = "test";
114 
115 // Names of modules that can be used without listing it in the permissions
116 // section of the manifest.
117 const char* kNonPermissionModuleNames[] = {
118   kBrowserActionModuleName,
119   kBrowserActionsModuleName,
120   kDevToolsModuleName,
121   kExtensionModuleName,
122   kI18NModuleName,
123   kOmniboxModuleName,
124   kPageActionModuleName,
125   kPageActionsModuleName,
126   kTestModuleName
127 };
128 const 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.
134 const char* kNonPermissionFunctionNames[] = {
135   "tabs.create",
136   "tabs.update"
137 };
138 const size_t kNumNonPermissionFunctionNames =
139     arraysize(kNonPermissionFunctionNames);
140 
141 // A singleton object containing global data needed by the extension objects.
142 class ExtensionConfig {
143  public:
GetInstance()144   static ExtensionConfig* GetInstance() {
145     return Singleton<ExtensionConfig>::get();
146   }
147 
GetPermissionMessageId(const std::string & permission)148   Extension::PermissionMessage::MessageId GetPermissionMessageId(
149       const std::string& permission) {
150     return Extension::kPermissions[permission_map_[permission]].message_id;
151   }
152 
whitelist()153   Extension::ScriptingWhitelist* whitelist() { return &scripting_whitelist_; }
154 
155  private:
156   friend struct DefaultSingletonTraits<ExtensionConfig>;
157 
ExtensionConfig()158   ExtensionConfig() {
159     for (size_t i = 0; i < Extension::kNumPermissions; ++i)
160       permission_map_[Extension::kPermissions[i].name] = i;
161   };
162 
~ExtensionConfig()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.
176 static 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.
GetLocationRank(Extension::Location location)183 int 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 
234 const FilePath::CharType Extension::kManifestFilename[] =
235     FILE_PATH_LITERAL("manifest.json");
236 const FilePath::CharType Extension::kLocaleFolder[] =
237     FILE_PATH_LITERAL("_locales");
238 const FilePath::CharType Extension::kMessagesFilename[] =
239     FILE_PATH_LITERAL("messages.json");
240 
241 #if defined(OS_WIN)
242 const char Extension::kExtensionRegistryPath[] =
243     "Software\\Google\\Chrome\\Extensions";
244 #endif
245 
246 // first 16 bytes of SHA256 hashed public key.
247 const size_t Extension::kIdSize = 16;
248 
249 const char Extension::kMimeType[] = "application/x-chrome-extension";
250 
251 const 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 
259 const int Extension::kPageActionIconMaxSize = 19;
260 const int Extension::kBrowserActionIconMaxSize = 19;
261 const int Extension::kSidebarIconMaxSize = 16;
262 
263 // Explicit permissions -- permission declaration required.
264 const char Extension::kBackgroundPermission[] = "background";
265 const char Extension::kBookmarkPermission[] = "bookmarks";
266 const char Extension::kContextMenusPermission[] = "contextMenus";
267 const char Extension::kContentSettingsPermission[] = "contentSettings";
268 const char Extension::kCookiePermission[] = "cookies";
269 const char Extension::kChromeosInfoPrivatePermissions[] = "chromeosInfoPrivate";
270 const char Extension::kDebuggerPermission[] = "debugger";
271 const char Extension::kExperimentalPermission[] = "experimental";
272 const char Extension::kFileBrowserHandlerPermission[] = "fileBrowserHandler";
273 const char Extension::kFileBrowserPrivatePermission[] = "fileBrowserPrivate";
274 const char Extension::kGeolocationPermission[] = "geolocation";
275 const char Extension::kHistoryPermission[] = "history";
276 const char Extension::kIdlePermission[] = "idle";
277 const char Extension::kManagementPermission[] = "management";
278 const char Extension::kNotificationPermission[] = "notifications";
279 const char Extension::kProxyPermission[] = "proxy";
280 const char Extension::kTabPermission[] = "tabs";
281 const char Extension::kUnlimitedStoragePermission[] = "unlimitedStorage";
282 const 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.
287 const 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 };
308 const size_t Extension::kNumPermissions =
309     arraysize(Extension::kPermissions);
310 
311 const char* const Extension::kHostedAppPermissionNames[] = {
312   Extension::kBackgroundPermission,
313   Extension::kGeolocationPermission,
314   Extension::kNotificationPermission,
315   Extension::kUnlimitedStoragePermission,
316   Extension::kWebstorePrivatePermission,
317 };
318 const size_t Extension::kNumHostedAppPermissions =
319     arraysize(Extension::kHostedAppPermissionNames);
320 
321 const char* const Extension::kComponentPrivatePermissionNames[] = {
322     Extension::kFileBrowserPrivatePermission,
323     Extension::kWebstorePrivatePermission,
324     Extension::kChromeosInfoPrivatePermissions,
325 };
326 const size_t Extension::kNumComponentPrivatePermissions =
327     arraysize(Extension::kComponentPrivatePermissionNames);
328 
329 // We purposefully don't put this into kPermissionNames.
330 const char Extension::kOldUnlimitedStoragePermission[] = "unlimited_storage";
331 
332 const int Extension::kValidWebExtentSchemes =
333     URLPattern::SCHEME_HTTP | URLPattern::SCHEME_HTTPS;
334 
335 const int Extension::kValidHostPermissionSchemes =
336     UserScript::kValidUserScriptSchemes | URLPattern::SCHEME_CHROMEUI;
337 
338 //
339 // PermissionMessage
340 //
341 
342 // static
CreateFromMessageId(Extension::PermissionMessage::MessageId message_id)343 Extension::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
CreateFromHostList(const std::vector<std::string> hosts)354 Extension::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 
PermissionMessage(Extension::PermissionMessage::MessageId message_id,string16 message)392 Extension::PermissionMessage::PermissionMessage(
393     Extension::PermissionMessage::MessageId message_id, string16 message)
394     : message_id_(message_id),
395       message_(message) {
396 }
397 
398 const 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
Create(const FilePath & path,Location location,const DictionaryValue & value,int flags,std::string * error)420 scoped_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 
432 namespace {
433 const char* kGalleryUpdateHttpUrl =
434     "http://clients2.google.com/service/update2/crx";
435 const char* kGalleryUpdateHttpsUrl =
436     "https://clients2.google.com/service/update2/crx";
437 }  // namespace
438 
439 // static
GalleryUpdateUrl(bool secure)440 GURL 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
GetHigherPriorityLocation(Extension::Location loc1,Extension::Location loc2)449 Extension::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
GetPermissionMessageId(const std::string & permission)466 Extension::PermissionMessage::MessageId Extension::GetPermissionMessageId(
467     const std::string& permission) {
468   return ExtensionConfig::GetInstance()->GetPermissionMessageId(permission);
469 }
470 
GetPermissionMessages() const471 Extension::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 
GetPermissionMessageStrings() const495 std::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 
504 std::set<Extension::PermissionMessage>
GetSimplePermissionMessages() const505     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
GetDistinctHostsForDisplay(const URLPatternList & list)518 std::vector<std::string> Extension::GetDistinctHostsForDisplay(
519     const URLPatternList& list) {
520   return GetDistinctHosts(list, true);
521 }
522 
523 // static
IsElevatedHostList(const URLPatternList & old_list,const URLPatternList & new_list)524 bool 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.
RcdBetterThan(const std::string & a,const std::string & b)545 static 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
GetDistinctHosts(const URLPatternList & host_patterns,bool include_rcd)558 std::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 
MaybeNormalizePath(const FilePath & path)605 FilePath 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
IsHostedAppPermission(const std::string & str)622 bool 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 
VersionString() const631 const std::string Extension::VersionString() const {
632   return version()->GetString();
633 }
634 
635 // static
IsExtension(const FilePath & file_name)636 bool Extension::IsExtension(const FilePath& file_name) {
637   return file_name.MatchesExtension(chrome::kExtensionFileExtension);
638 }
639 
640 // static
IdIsValid(const std::string & id)641 bool 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
GenerateIdForPath(const FilePath & path)657 std::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 
GetType() const668 Extension::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
GetResourceURL(const GURL & extension_url,const std::string & relative_path)681 GURL 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 
GenerateId(const std::string & input,std::string * output)692 bool 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.
LoadUserScriptHelper(const DictionaryValue * content_script,int definition_index,int flags,std::string * error,UserScript * result)704 bool 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 
LoadGlobsHelper(const DictionaryValue * content_script,int content_script_index,const char * globs_property_name,std::string * error,void (UserScript::* add_method)(const std::string & glob),UserScript * instance)871 bool 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 
LoadExtensionActionHelper(const DictionaryValue * extension_action,std::string * error)905 ExtensionAction* 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 
LoadFileBrowserHandlers(const ListValue * extension_actions,std::string * error)1023 Extension::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 
LoadFileBrowserHandler(const DictionaryValue * file_browser_handler,std::string * error)1044 FileBrowserHandler* 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 
LoadExtensionSidebarDefaults(const DictionaryValue * extension_sidebar,std::string * error)1108 ExtensionSidebarDefaults* 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 
ContainsNonThemeKeys(const DictionaryValue & source) const1154 bool 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 
LoadIsApp(const DictionaryValue * manifest,std::string * error)1163 bool 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 
LoadExtent(const DictionaryValue * manifest,const char * key,ExtensionExtent * extent,const char * list_error,const char * value_error,URLPattern::ParseOption parse_strictness,std::string * error)1171 bool 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 
LoadLaunchURL(const DictionaryValue * manifest,std::string * error)1248 bool 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 
LoadLaunchContainer(const DictionaryValue * manifest,std::string * error)1344 bool 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 
LoadAppIsolation(const DictionaryValue * manifest,std::string * error)1397 bool 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 
EnsureNotHybridApp(const DictionaryValue * manifest,std::string * error)1434 bool 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 
Extension(const FilePath & path,Location location)1455 Extension::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 
~Extension()1470 Extension::~Extension() {
1471 }
1472 
GetResource(const std::string & relative_path) const1473 ExtensionResource 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 
GetResource(const FilePath & relative_file_path) const1483 ExtensionResource 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
ParsePEMKeyBytes(const std::string & input,std::string * output)1491 bool 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 
ProducePEM(const std::string & input,std::string * output)1521 bool 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 
FormatPEMForFileOutput(const std::string & input,std::string * output,bool is_public)1529 bool 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
IsPrivilegeIncrease(const bool granted_full_access,const std::set<std::string> & granted_apis,const ExtensionExtent & granted_extent,const Extension * new_extension)1559 bool 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
DecodeIcon(const Extension * extension,Icons icon_size,scoped_ptr<SkBitmap> * result)1608 void 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
DecodeIconFromPath(const FilePath & icon_path,Icons icon_size,scoped_ptr<SkBitmap> * result)1617 void 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
GetDefaultIcon(bool is_app)1652 const 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 
GetBaseURLFromExtensionId(const std::string & extension_id)1662 GURL Extension::GetBaseURLFromExtensionId(const std::string& extension_id) {
1663   return GURL(std::string(chrome::kExtensionScheme) +
1664               chrome::kStandardSchemeSeparator + extension_id + "/");
1665 }
1666 
InitFromValue(const DictionaryValue & source,int flags,std::string * error)1667 bool 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
ChromeStoreLaunchURL()2497 std::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 
GetHomepageURL() const2507 GURL 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 
GetBrowserImages() const2521 std::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 
GetFullLaunchURL() const2564 GURL 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 
SizeToString(const gfx::Size & max_size)2571 static 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
SetScriptingWhitelist(const Extension::ScriptingWhitelist & whitelist)2577 void 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
GetScriptingWhitelist()2589 const Extension::ScriptingWhitelist* Extension::GetScriptingWhitelist() {
2590   return ExtensionConfig::GetInstance()->whitelist();
2591 }
2592 
SetCachedImage(const ExtensionResource & source,const SkBitmap & image,const gfx::Size & original_size) const2593 void 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 
HasCachedImage(const ExtensionResource & source,const gfx::Size & max_size) const2607 bool 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 
GetCachedImage(const ExtensionResource & source,const gfx::Size & max_size) const2614 SkBitmap 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 
GetCachedImageImpl(const ExtensionResource & source,const gfx::Size & max_size) const2622 SkBitmap* 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 
GetIconResource(int size,ExtensionIconSet::MatchType match_type) const2645 ExtensionResource 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 
GetIconURL(int size,ExtensionIconSet::MatchType match_type) const2653 GURL 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 
CanSpecifyHostPermission(const URLPattern & pattern) const2662 bool 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
HasApiPermission(const std::set<std::string> & api_permissions,const std::string & function_name)2676 bool 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 
HasHostPermission(const GURL & url) const2709 bool 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 
InitEffectiveHostPermissions()2725 void 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 
IsComponentOnlyPermission(const std::string & permission) const2750 bool 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 
HasMultipleUISurfaces() const2763 bool 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 
CanExecuteScriptOnPage(const GURL & page_url,const UserScript * script,std::string * error) const2778 bool 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
HasEffectiveAccessToAllHosts(const ExtensionExtent & effective_host_permissions,const std::set<std::string> & api_permissions)2819 bool 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 
HasEffectiveAccessToAllHosts() const2833 bool Extension::HasEffectiveAccessToAllHosts() const {
2834   return HasEffectiveAccessToAllHosts(GetEffectiveHostPermissions(),
2835                                       api_permissions());
2836 }
2837 
HasFullPermissions() const2838 bool Extension::HasFullPermissions() const {
2839   return !plugins().empty();
2840 }
2841 
ShowConfigureContextMenus() const2842 bool 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 
IsAPIPermission(const std::string & str) const2850 bool 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 
CanExecuteScriptEverywhere() const2859 bool 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 
CanCaptureVisiblePage(const GURL & page_url,std::string * error) const2881 bool 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 
UpdatesFromGallery() const2893 bool Extension::UpdatesFromGallery() const {
2894   return update_url() == GalleryUpdateUrl(false) ||
2895          update_url() == GalleryUpdateUrl(true);
2896 }
2897 
OverlapsWithOrigin(const GURL & origin) const2898 bool 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 
ExtensionInfo(const DictionaryValue * manifest,const std::string & id,const FilePath & path,Extension::Location location)2918 ExtensionInfo::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 
~ExtensionInfo()2929 ExtensionInfo::~ExtensionInfo() {}
2930 
UninstalledExtensionInfo(const Extension & extension)2931 UninstalledExtensionInfo::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 
~UninstalledExtensionInfo()2938 UninstalledExtensionInfo::~UninstalledExtensionInfo() {}
2939 
2940 
UnloadedExtensionInfo(const Extension * extension,Reason reason)2941 UnloadedExtensionInfo::UnloadedExtensionInfo(
2942     const Extension* extension,
2943     Reason reason)
2944   : reason(reason),
2945     already_disabled(false),
2946     extension(extension) {}
2947