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