Chromium Code Reviews| OLD | NEW |
|---|---|
| 1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. | 1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. |
| 2 // Use of this source code is governed by a BSD-style license that can be | 2 // Use of this source code is governed by a BSD-style license that can be |
| 3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
| 4 | 4 |
| 5 #include "chrome/common/extensions/extension.h" | 5 #include "chrome/common/extensions/extension.h" |
| 6 | 6 |
| 7 #include <algorithm> | 7 #include <algorithm> |
| 8 | 8 |
| 9 #include "base/base64.h" | 9 #include "base/base64.h" |
| 10 #include "base/basictypes.h" | 10 #include "base/basictypes.h" |
| (...skipping 315 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 326 utf8_error); | 326 utf8_error); |
| 327 } | 327 } |
| 328 | 328 |
| 329 scoped_refptr<Extension> Extension::Create(const FilePath& path, | 329 scoped_refptr<Extension> Extension::Create(const FilePath& path, |
| 330 Location location, | 330 Location location, |
| 331 const DictionaryValue& value, | 331 const DictionaryValue& value, |
| 332 int flags, | 332 int flags, |
| 333 const std::string& explicit_id, | 333 const std::string& explicit_id, |
| 334 std::string* utf8_error) { | 334 std::string* utf8_error) { |
| 335 DCHECK(utf8_error); | 335 DCHECK(utf8_error); |
| 336 | |
| 337 string16 error; | 336 string16 error; |
| 338 scoped_ptr<extensions::Manifest> manifest( | 337 scoped_ptr<extensions::Manifest> manifest( |
| 339 new extensions::Manifest( | 338 new extensions::Manifest( |
| 340 location, | 339 location, |
| 341 scoped_ptr<DictionaryValue>(value.DeepCopy()))); | 340 scoped_ptr<DictionaryValue>(value.DeepCopy()))); |
| 342 | 341 |
| 343 if (!InitExtensionID(manifest.get(), path, explicit_id, flags, &error) || | 342 if (!InitExtensionID(manifest.get(), path, explicit_id, flags, &error) || |
| 344 !manifest->ValidateManifest(&error)) { | 343 !manifest->ValidateManifest(&error)) { |
| 345 *utf8_error = UTF16ToUTF8(error); | 344 *utf8_error = UTF16ToUTF8(error); |
| 346 return NULL; | 345 return NULL; |
| (...skipping 126 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 473 DCHECK(StartsWithASCII(ret_val.spec(), extension_url.spec(), false)); | 472 DCHECK(StartsWithASCII(ret_val.spec(), extension_url.spec(), false)); |
| 474 | 473 |
| 475 return ret_val; | 474 return ret_val; |
| 476 } | 475 } |
| 477 | 476 |
| 478 bool Extension::is_platform_app() const { | 477 bool Extension::is_platform_app() const { |
| 479 return manifest_->IsPlatformApp(); | 478 return manifest_->IsPlatformApp(); |
| 480 } | 479 } |
| 481 | 480 |
| 482 bool Extension::is_hosted_app() const { | 481 bool Extension::is_hosted_app() const { |
| 483 return manifest()->IsHostedApp(); | 482 return manifest()->IsHostedApp(); |
| 484 } | 483 } |
| 485 | 484 |
| 486 bool Extension::is_packaged_app() const { | 485 bool Extension::is_packaged_app() const { |
| 487 return manifest()->IsPackagedApp(); | 486 return manifest()->IsPackagedApp(); |
| 488 } | 487 } |
| 489 | 488 |
| 490 bool Extension::is_theme() const { | 489 bool Extension::is_theme() const { |
| 491 return manifest()->IsTheme(); | 490 return manifest()->IsTheme(); |
| 492 } | 491 } |
| 493 | 492 |
| 494 GURL Extension::GetBackgroundURL() const { | 493 GURL Extension::GetBackgroundURL() const { |
| 495 if (!background_scripts_.empty()) { | 494 if (!background_scripts_.empty()) { |
| 496 return GetResourceURL( | 495 return GetResourceURL( |
| 497 extension_filenames::kGeneratedBackgroundPageFilename); | 496 extension_filenames::kGeneratedBackgroundPageFilename); |
| (...skipping 387 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 885 result->SetPopupUrl(ExtensionAction::kDefaultTabId, url); | 884 result->SetPopupUrl(ExtensionAction::kDefaultTabId, url); |
| 886 } else { | 885 } else { |
| 887 DCHECK(!result->HasPopup(ExtensionAction::kDefaultTabId)) | 886 DCHECK(!result->HasPopup(ExtensionAction::kDefaultTabId)) |
| 888 << "Shouldn't be possible for the popup to be set."; | 887 << "Shouldn't be possible for the popup to be set."; |
| 889 } | 888 } |
| 890 } | 889 } |
| 891 | 890 |
| 892 return result.release(); | 891 return result.release(); |
| 893 } | 892 } |
| 894 | 893 |
| 895 Extension::FileBrowserHandlerList* Extension::LoadFileBrowserHandlers( | 894 bool Extension::LoadSharedFeatures(const int& flags, |
| 895 string16* error) { | |
| 896 if (!LoadName(error)) | |
| 897 return false; | |
| 898 | |
| 899 if (manifest_->HasKey(keys::kDescription) && !LoadDescription(error)) | |
|
Aaron Boodman
2012/02/29 20:38:25
If you put the check for the presence of the key i
Devlin
2012/03/02 17:31:04
Done.
| |
| 900 return false; | |
| 901 | |
| 902 if (!LoadVersion(error)) | |
| 903 return false; | |
| 904 | |
| 905 // Validate minimum Chrome version. We don't need to store this, since the | |
| 906 // extension is not valid if it is incorrect | |
| 907 if (manifest_->HasKey(keys::kMinimumChromeVersion) && | |
| 908 !LoadMinimumChromeVersion(error)) | |
|
Yoyo Zhou
2012/03/01 01:17:25
Maybe call this CheckMinimumChromeVersion instead,
Devlin
2012/03/02 17:31:04
Done.
| |
| 909 return false; | |
| 910 | |
| 911 extension_url_ = Extension::GetBaseURLFromExtensionId(id()); | |
| 912 | |
| 913 if (manifest_->HasKey(keys::kHomepageURL) && !LoadHomepageURL(error)) | |
| 914 return false; | |
| 915 | |
| 916 if (manifest_->HasKey(keys::kUpdateURL) && !LoadUpdateURL(error)) | |
| 917 return false; | |
| 918 | |
| 919 return true; | |
| 920 } | |
| 921 | |
| 922 bool Extension::LoadName(string16* error) { | |
| 923 string16 localized_name; | |
| 924 if (!manifest_->GetString(keys::kName, &localized_name)) { | |
| 925 *error = ASCIIToUTF16(errors::kInvalidName); | |
| 926 return false; | |
| 927 } | |
| 928 base::i18n::AdjustStringForLocaleDirection(&localized_name); | |
| 929 name_ = UTF16ToUTF8(localized_name); | |
| 930 return true; | |
| 931 } | |
| 932 | |
| 933 bool Extension::LoadDescription(string16* error) { | |
| 934 if (!manifest_->GetString(keys::kDescription, | |
| 935 &description_)) { | |
| 936 *error = ASCIIToUTF16(errors::kInvalidDescription); | |
| 937 return false; | |
| 938 } | |
| 939 return true; | |
| 940 } | |
| 941 | |
| 942 bool Extension::LoadVersion(string16* error) { | |
| 943 std::string version_str; | |
| 944 if (!manifest_->GetString(keys::kVersion, &version_str)) { | |
| 945 *error = ASCIIToUTF16(errors::kInvalidVersion); | |
| 946 return false; | |
| 947 } | |
| 948 version_.reset(Version::GetVersionFromString(version_str)); | |
| 949 if (!version_.get() || | |
| 950 version_->components().size() > 4) { | |
| 951 *error = ASCIIToUTF16(errors::kInvalidVersion); | |
| 952 return false; | |
| 953 } | |
| 954 return true; | |
| 955 } | |
| 956 | |
| 957 bool Extension::LoadMinimumChromeVersion(string16* error) { | |
| 958 std::string minimum_version_string; | |
| 959 if (!manifest_->GetString(keys::kMinimumChromeVersion, | |
| 960 &minimum_version_string)) { | |
|
Aaron Boodman
2012/02/29 20:38:25
+1 indent
Devlin
2012/03/02 17:31:04
Done.
| |
| 961 *error = ASCIIToUTF16(errors::kInvalidMinimumChromeVersion); | |
| 962 return false; | |
| 963 } | |
| 964 | |
| 965 scoped_ptr<Version> minimum_version( | |
| 966 Version::GetVersionFromString(minimum_version_string)); | |
| 967 if (!minimum_version.get()) { | |
| 968 *error = ASCIIToUTF16(errors::kInvalidMinimumChromeVersion); | |
| 969 return false; | |
| 970 } | |
| 971 | |
| 972 chrome::VersionInfo current_version_info; | |
| 973 if (!current_version_info.is_valid()) { | |
| 974 NOTREACHED(); | |
| 975 return false; | |
| 976 } | |
| 977 | |
| 978 scoped_ptr<Version> current_version( | |
| 979 Version::GetVersionFromString(current_version_info.Version())); | |
| 980 if (!current_version.get()) { | |
| 981 DCHECK(false); | |
| 982 return false; | |
| 983 } | |
| 984 | |
| 985 if (current_version->CompareTo(*minimum_version) < 0) { | |
| 986 *error = ExtensionErrorUtils::FormatErrorMessageUTF16( | |
| 987 errors::kChromeVersionTooLow, | |
| 988 l10n_util::GetStringUTF8(IDS_PRODUCT_NAME), | |
| 989 minimum_version_string); | |
| 990 return false; | |
| 991 } | |
| 992 return true; | |
| 993 } | |
| 994 | |
| 995 bool Extension::LoadManifestVersion(string16* error) { | |
| 996 // Get the original value out of the dictionary so that we can validate it | |
| 997 // more strictly. | |
| 998 if (manifest_->value()->HasKey(keys::kManifestVersion)) { | |
| 999 int manifest_version = 1; | |
| 1000 if (!manifest_->GetInteger(keys::kManifestVersion, &manifest_version) || | |
| 1001 manifest_version < 1) { | |
| 1002 *error = ASCIIToUTF16(errors::kInvalidManifestVersion); | |
| 1003 return false; | |
| 1004 } | |
| 1005 } | |
| 1006 | |
| 1007 manifest_version_ = manifest_->GetManifestVersion(); | |
| 1008 if (creation_flags_ & REQUIRE_MODERN_MANIFEST_VERSION && | |
| 1009 manifest_version_ < kModernManifestVersion && | |
| 1010 !CommandLine::ForCurrentProcess()->HasSwitch( | |
| 1011 switches::kAllowLegacyExtensionManifests)) { | |
| 1012 *error = ASCIIToUTF16(errors::kInvalidManifestVersion); | |
| 1013 return false; | |
| 1014 } | |
| 1015 | |
| 1016 return true; | |
| 1017 } | |
| 1018 | |
| 1019 // static | |
| 1020 bool Extension::InitExtensionID(extensions::Manifest* manifest, | |
| 1021 const FilePath& path, | |
| 1022 const std::string& explicit_id, | |
| 1023 int creation_flags, | |
| 1024 string16* error) { | |
| 1025 if (!explicit_id.empty()) { | |
| 1026 manifest->set_extension_id(explicit_id); | |
| 1027 return true; | |
| 1028 } | |
| 1029 | |
| 1030 if (manifest->HasKey(keys::kPublicKey)) { | |
| 1031 std::string public_key; | |
| 1032 std::string public_key_bytes; | |
| 1033 std::string extension_id; | |
| 1034 if (!manifest->GetString(keys::kPublicKey, &public_key) || | |
| 1035 !ParsePEMKeyBytes(public_key, &public_key_bytes) || | |
| 1036 !GenerateId(public_key_bytes, &extension_id)) { | |
| 1037 *error = ASCIIToUTF16(errors::kInvalidKey); | |
| 1038 return false; | |
| 1039 } | |
| 1040 manifest->set_extension_id(extension_id); | |
| 1041 return true; | |
| 1042 } | |
| 1043 | |
| 1044 if (creation_flags & REQUIRE_KEY) { | |
| 1045 *error = ASCIIToUTF16(errors::kInvalidKey); | |
| 1046 return false; | |
| 1047 } else { | |
| 1048 // If there is a path, we generate the ID from it. This is useful for | |
| 1049 // development mode, because it keeps the ID stable across restarts and | |
| 1050 // reloading the extension. | |
| 1051 std::string extension_id = GenerateIdForPath(path); | |
| 1052 if (extension_id.empty()) { | |
| 1053 NOTREACHED() << "Could not create ID from path."; | |
| 1054 return false; | |
| 1055 } | |
| 1056 manifest->set_extension_id(extension_id); | |
| 1057 return true; | |
| 1058 } | |
| 1059 } | |
| 1060 | |
| 1061 bool Extension::LoadHomepageURL(string16* error) { | |
| 1062 std::string tmp; | |
| 1063 if (!manifest_->GetString(keys::kHomepageURL, &tmp)) { | |
| 1064 *error = ExtensionErrorUtils::FormatErrorMessageUTF16( | |
| 1065 errors::kInvalidHomepageURL, ""); | |
| 1066 return false; | |
| 1067 } | |
| 1068 homepage_url_ = GURL(tmp); | |
| 1069 if (!homepage_url_.is_valid() || | |
| 1070 (!homepage_url_.SchemeIs("http") && | |
| 1071 !homepage_url_.SchemeIs("https"))) { | |
| 1072 *error = ExtensionErrorUtils::FormatErrorMessageUTF16( | |
| 1073 errors::kInvalidHomepageURL, tmp); | |
| 1074 return false; | |
| 1075 } | |
| 1076 return true; | |
| 1077 } | |
| 1078 | |
| 1079 bool Extension::LoadUpdateURL(string16* error) { | |
| 1080 std::string tmp; | |
| 1081 if (!manifest_->GetString(keys::kUpdateURL, &tmp)) { | |
| 1082 *error = ExtensionErrorUtils::FormatErrorMessageUTF16( | |
| 1083 errors::kInvalidUpdateURL, ""); | |
| 1084 return false; | |
| 1085 } | |
| 1086 update_url_ = GURL(tmp); | |
| 1087 if (!update_url_.is_valid() || | |
| 1088 update_url_.has_ref()) { | |
| 1089 *error = ExtensionErrorUtils::FormatErrorMessageUTF16( | |
| 1090 errors::kInvalidUpdateURL, tmp); | |
| 1091 return false; | |
| 1092 } | |
| 1093 return true; | |
| 1094 } | |
| 1095 | |
| 1096 bool Extension::LoadExtensionFeatures( | |
| 1097 const int& flags, | |
| 1098 const ExtensionAPIPermissionSet& api_permissions, | |
| 1099 string16* error) { | |
| 1100 if (manifest_->HasKey(keys::kOptionsPage) && | |
|
Yoyo Zhou
2012/03/01 01:17:25
As Aaron wrote, you can check for the presence of
Devlin
2012/03/02 17:31:04
Done.
| |
| 1101 !LoadOptionsPage(error)) | |
|
Yoyo Zhou
2012/03/01 01:17:25
unfortunately these aren't all exclusively extensi
Devlin
2012/03/02 17:31:04
Thanks! A while back, Aaron listed the ones he th
| |
| 1102 return false; | |
| 1103 | |
| 1104 if (manifest_->HasKey(keys::kDevToolsPage) && | |
| 1105 !LoadDevToolsPage(error)) | |
| 1106 return false; | |
| 1107 | |
| 1108 if (manifest_->HasKey(keys::kInputComponents) && | |
| 1109 !LoadInputComponents(api_permissions, error)) | |
| 1110 return false; | |
| 1111 | |
| 1112 if (manifest_->HasKey(keys::kConvertedFromUserScript)) | |
| 1113 manifest_->GetBoolean(keys::kConvertedFromUserScript, | |
| 1114 &converted_from_user_script_); | |
|
Yoyo Zhou
2012/03/01 01:17:25
indent +1
Devlin
2012/03/02 17:31:04
Done.
| |
| 1115 | |
| 1116 if (manifest_->HasKey(keys::kIcons) && !LoadIcons(error)) | |
| 1117 return false; | |
| 1118 | |
| 1119 if (manifest_->HasKey(keys::kCommands) && !LoadCommands(error)) | |
| 1120 return false; | |
| 1121 | |
| 1122 if (manifest_->HasKey(keys::kPlugins) && !LoadPlugins(error)) | |
| 1123 return false; | |
| 1124 | |
| 1125 if (manifest_->HasKey(keys::kNaClModules) && !LoadNaClModules(error)) | |
| 1126 return false; | |
| 1127 | |
| 1128 if (manifest_->HasKey(keys::kContentScripts) && | |
| 1129 !LoadContentScripts(flags, error)) | |
| 1130 return false; | |
| 1131 | |
| 1132 if (manifest_->HasKey(keys::kWebAccessibleResources) && | |
| 1133 !LoadWebAccessibleResources(error)) | |
| 1134 return false; | |
| 1135 | |
| 1136 if (!LoadPageAction(error)) | |
| 1137 return false; | |
| 1138 | |
| 1139 if (manifest_->HasKey(keys::kBrowserAction) && | |
| 1140 !LoadBrowserAction(error)) | |
| 1141 return false; | |
| 1142 | |
| 1143 if (manifest_->HasKey(keys::kFileBrowserHandlers) && | |
| 1144 !LoadFileBrowserHandlers(error)) | |
| 1145 return false; | |
| 1146 | |
| 1147 if (manifest_->HasKey(keys::kChromeURLOverrides) && | |
| 1148 !LoadChromeURLOverrides(error)) | |
| 1149 return false; | |
| 1150 | |
| 1151 if (manifest_->HasKey(keys::kOmnibox) && !LoadOmnibox(error)) | |
| 1152 return false; | |
| 1153 | |
| 1154 if (manifest_->HasKey(keys::kTtsEngine) && | |
| 1155 !LoadTextToSpeechVoices(error)) | |
| 1156 return false; | |
| 1157 | |
| 1158 if (!LoadWebIntentServices(error)) | |
| 1159 return false; | |
| 1160 | |
| 1161 // These are not actually persisted (they're only used by the store), but | |
| 1162 // still validated. | |
| 1163 if (manifest_->HasKey(keys::kRequirements) && | |
| 1164 !LoadRequirements(error)) | |
| 1165 return false; | |
| 1166 | |
| 1167 if (manifest_->HasKey(keys::kDefaultLocale) && | |
| 1168 !LoadDefaultLocale(error)) | |
| 1169 return false; | |
| 1170 | |
| 1171 // Defaults to false. | |
|
Yoyo Zhou
2012/03/01 01:17:25
Comments like this seem like they should be in the
Devlin
2012/03/02 17:31:04
Done.
| |
| 1172 if (manifest_->HasKey(keys::kOfflineEnabled) && | |
| 1173 !LoadOfflineEnabled(error)) | |
| 1174 return false; | |
| 1175 | |
| 1176 // Apps default to split mode, extensions default to spanning. | |
| 1177 incognito_split_mode_ = is_app(); | |
|
Yoyo Zhou
2012/03/01 01:17:25
Should be inside LoadIncognito, which I would rena
Devlin
2012/03/02 17:31:04
Done. I was debating making it LoadIncognitoMode
| |
| 1178 if (manifest_->HasKey(keys::kIncognito) && !LoadIncognito(error)) | |
| 1179 return false; | |
| 1180 | |
| 1181 if (!LoadBackgroundScripts(error)) | |
| 1182 return false; | |
| 1183 | |
| 1184 if (!LoadBackgroundPage(api_permissions, error)) | |
| 1185 return false; | |
| 1186 | |
| 1187 if (!LoadBackgroundPersistent(api_permissions, error)) | |
| 1188 return false; | |
| 1189 | |
| 1190 if (!LoadContentSecurityPolicy(error)) | |
| 1191 return false; | |
| 1192 | |
| 1193 return true; | |
| 1194 } | |
| 1195 | |
| 1196 bool Extension::LoadOptionsPage(string16* error) { | |
| 1197 std::string options_str; | |
| 1198 if (!manifest_->GetString(keys::kOptionsPage, &options_str)) { | |
| 1199 *error = ASCIIToUTF16(errors::kInvalidOptionsPage); | |
| 1200 return false; | |
| 1201 } | |
| 1202 | |
| 1203 if (is_hosted_app()) { | |
| 1204 // hosted apps require an absolute URL. | |
| 1205 GURL options_url(options_str); | |
| 1206 if (!options_url.is_valid() || | |
| 1207 !(options_url.SchemeIs("http") || options_url.SchemeIs("https"))) { | |
| 1208 *error = ASCIIToUTF16(errors::kInvalidOptionsPageInHostedApp); | |
| 1209 return false; | |
| 1210 } | |
| 1211 options_url_ = options_url; | |
| 1212 } else { | |
| 1213 GURL absolute(options_str); | |
| 1214 if (absolute.is_valid()) { | |
| 1215 *error = ASCIIToUTF16(errors::kInvalidOptionsPageExpectUrlInPackage); | |
| 1216 return false; | |
| 1217 } | |
| 1218 options_url_ = GetResourceURL(options_str); | |
| 1219 if (!options_url_.is_valid()) { | |
| 1220 *error = ASCIIToUTF16(errors::kInvalidOptionsPage); | |
| 1221 return false; | |
| 1222 } | |
| 1223 } | |
| 1224 | |
| 1225 return true; | |
| 1226 } | |
| 1227 | |
| 1228 bool Extension::LoadDevToolsPage(string16* error) { | |
| 1229 std::string devtools_str; | |
| 1230 if (!manifest_->GetString(keys::kDevToolsPage, &devtools_str)) { | |
| 1231 *error = ASCIIToUTF16(errors::kInvalidDevToolsPage); | |
| 1232 return false; | |
| 1233 } | |
| 1234 devtools_url_ = GetResourceURL(devtools_str); | |
| 1235 return true; | |
| 1236 } | |
| 1237 | |
| 1238 bool Extension::LoadInputComponents( | |
| 1239 const ExtensionAPIPermissionSet& api_permissions, | |
| 1240 string16* error) { | |
| 1241 ListValue* list_value = NULL; | |
| 1242 if (!manifest_->GetList(keys::kInputComponents, &list_value)) { | |
| 1243 *error = ASCIIToUTF16(errors::kInvalidInputComponents); | |
| 1244 return false; | |
| 1245 } | |
| 1246 | |
| 1247 for (size_t i = 0; i < list_value->GetSize(); ++i) { | |
| 1248 DictionaryValue* module_value = NULL; | |
| 1249 std::string name_str; | |
| 1250 InputComponentType type; | |
| 1251 std::string id_str; | |
| 1252 std::string description_str; | |
| 1253 std::string language_str; | |
| 1254 std::set<std::string> layouts; | |
| 1255 std::string shortcut_keycode_str; | |
| 1256 bool shortcut_alt = false; | |
| 1257 bool shortcut_ctrl = false; | |
| 1258 bool shortcut_shift = false; | |
| 1259 | |
| 1260 if (!list_value->GetDictionary(i, &module_value)) { | |
| 1261 *error = ASCIIToUTF16(errors::kInvalidInputComponents); | |
| 1262 return false; | |
| 1263 } | |
| 1264 | |
| 1265 // Get input_components[i].name. | |
| 1266 if (!module_value->GetString(keys::kName, &name_str)) { | |
| 1267 *error = ExtensionErrorUtils::FormatErrorMessageUTF16( | |
| 1268 errors::kInvalidInputComponentName, base::IntToString(i)); | |
| 1269 return false; | |
| 1270 } | |
| 1271 | |
| 1272 // Get input_components[i].type. | |
| 1273 std::string type_str; | |
| 1274 if (module_value->GetString(keys::kType, &type_str)) { | |
| 1275 if (type_str == "ime") { | |
| 1276 type = INPUT_COMPONENT_TYPE_IME; | |
| 1277 } else if (type_str == "virtual_keyboard") { | |
| 1278 if (!api_permissions.count(ExtensionAPIPermission::kExperimental)) { | |
| 1279 // Virtual Keyboards require the experimental flag. | |
| 1280 *error = ExtensionErrorUtils::FormatErrorMessageUTF16( | |
| 1281 errors::kInvalidInputComponentType, base::IntToString(i)); | |
| 1282 return false; | |
| 1283 } | |
| 1284 type = INPUT_COMPONENT_TYPE_VIRTUAL_KEYBOARD; | |
| 1285 } else { | |
| 1286 *error = ExtensionErrorUtils::FormatErrorMessageUTF16( | |
| 1287 errors::kInvalidInputComponentType, base::IntToString(i)); | |
| 1288 return false; | |
| 1289 } | |
| 1290 } else { | |
| 1291 *error = ExtensionErrorUtils::FormatErrorMessageUTF16( | |
| 1292 errors::kInvalidInputComponentType, base::IntToString(i)); | |
| 1293 return false; | |
| 1294 } | |
| 1295 | |
| 1296 // Get input_components[i].id. | |
| 1297 if (!module_value->GetString(keys::kId, &id_str)) { | |
| 1298 id_str = ""; | |
| 1299 } | |
| 1300 | |
| 1301 // Get input_components[i].description. | |
| 1302 if (!module_value->GetString(keys::kDescription, &description_str)) { | |
| 1303 *error = ExtensionErrorUtils::FormatErrorMessageUTF16( | |
| 1304 errors::kInvalidInputComponentDescription, base::IntToString(i)); | |
| 1305 return false; | |
| 1306 } | |
| 1307 // Get input_components[i].language. | |
| 1308 if (!module_value->GetString(keys::kLanguage, &language_str)) { | |
| 1309 language_str = ""; | |
| 1310 } | |
| 1311 | |
| 1312 // Get input_components[i].layouts. | |
| 1313 ListValue* layouts_value = NULL; | |
| 1314 if (!module_value->GetList(keys::kLayouts, &layouts_value)) { | |
| 1315 *error = ASCIIToUTF16(errors::kInvalidInputComponentLayouts); | |
| 1316 return false; | |
| 1317 } | |
| 1318 | |
| 1319 for (size_t j = 0; j < layouts_value->GetSize(); ++j) { | |
| 1320 std::string layout_name_str; | |
| 1321 if (!layouts_value->GetString(j, &layout_name_str)) { | |
| 1322 *error = ExtensionErrorUtils::FormatErrorMessageUTF16( | |
| 1323 errors::kInvalidInputComponentLayoutName, base::IntToString(i), | |
| 1324 base::IntToString(j)); | |
| 1325 return false; | |
| 1326 } | |
| 1327 layouts.insert(layout_name_str); | |
| 1328 } | |
| 1329 | |
| 1330 if (module_value->HasKey(keys::kShortcutKey)) { | |
| 1331 DictionaryValue* shortcut_value = NULL; | |
| 1332 if (!module_value->GetDictionary(keys::kShortcutKey, &shortcut_value)) { | |
| 1333 *error = ExtensionErrorUtils::FormatErrorMessageUTF16( | |
| 1334 errors::kInvalidInputComponentShortcutKey, base::IntToString(i)); | |
| 1335 return false; | |
| 1336 } | |
| 1337 | |
| 1338 // Get input_components[i].shortcut_keycode. | |
| 1339 if (!shortcut_value->GetString(keys::kKeycode, &shortcut_keycode_str)) { | |
| 1340 *error = ExtensionErrorUtils::FormatErrorMessageUTF16( | |
| 1341 errors::kInvalidInputComponentShortcutKeycode, | |
| 1342 base::IntToString(i)); | |
| 1343 return false; | |
| 1344 } | |
| 1345 | |
| 1346 // Get input_components[i].shortcut_alt. | |
| 1347 if (!shortcut_value->GetBoolean(keys::kAltKey, &shortcut_alt)) { | |
| 1348 shortcut_alt = false; | |
| 1349 } | |
| 1350 | |
| 1351 // Get input_components[i].shortcut_ctrl. | |
| 1352 if (!shortcut_value->GetBoolean(keys::kCtrlKey, &shortcut_ctrl)) { | |
| 1353 shortcut_ctrl = false; | |
| 1354 } | |
| 1355 | |
| 1356 // Get input_components[i].shortcut_shift. | |
| 1357 if (!shortcut_value->GetBoolean(keys::kShiftKey, &shortcut_shift)) { | |
| 1358 shortcut_shift = false; | |
| 1359 } | |
| 1360 } | |
| 1361 | |
| 1362 input_components_.push_back(InputComponentInfo()); | |
| 1363 input_components_.back().name = name_str; | |
| 1364 input_components_.back().type = type; | |
| 1365 input_components_.back().id = id_str; | |
| 1366 input_components_.back().description = description_str; | |
| 1367 input_components_.back().language = language_str; | |
| 1368 input_components_.back().layouts.insert(layouts.begin(), layouts.end()); | |
| 1369 input_components_.back().shortcut_keycode = shortcut_keycode_str; | |
| 1370 input_components_.back().shortcut_alt = shortcut_alt; | |
| 1371 input_components_.back().shortcut_ctrl = shortcut_ctrl; | |
| 1372 input_components_.back().shortcut_shift = shortcut_shift; | |
| 1373 } | |
| 1374 | |
| 1375 return true; | |
| 1376 } | |
| 1377 bool Extension::LoadIcons(string16* error) { | |
| 1378 DictionaryValue* icons_value = NULL; | |
| 1379 if (!manifest_->GetDictionary(keys::kIcons, &icons_value)) { | |
| 1380 *error = ASCIIToUTF16(errors::kInvalidIcons); | |
| 1381 return false; | |
| 1382 } | |
| 1383 | |
| 1384 for (size_t i = 0; i < ExtensionIconSet::kNumIconSizes; ++i) { | |
| 1385 std::string key = base::IntToString(ExtensionIconSet::kIconSizes[i]); | |
| 1386 if (icons_value->HasKey(key)) { | |
| 1387 std::string icon_path; | |
| 1388 if (!icons_value->GetString(key, &icon_path)) { | |
| 1389 *error = ExtensionErrorUtils::FormatErrorMessageUTF16( | |
| 1390 errors::kInvalidIconPath, key); | |
| 1391 return false; | |
| 1392 } | |
| 1393 | |
| 1394 if (!icon_path.empty() && icon_path[0] == '/') | |
| 1395 icon_path = icon_path.substr(1); | |
| 1396 | |
| 1397 if (icon_path.empty()) { | |
| 1398 *error = ExtensionErrorUtils::FormatErrorMessageUTF16( | |
| 1399 errors::kInvalidIconPath, key); | |
| 1400 return false; | |
| 1401 } | |
| 1402 icons_.Add(ExtensionIconSet::kIconSizes[i], icon_path); | |
| 1403 } | |
| 1404 } | |
| 1405 return true; | |
| 1406 } | |
| 1407 | |
| 1408 bool Extension::LoadCommands(string16* error) { | |
| 1409 if (manifest_->HasKey(keys::kCommands)) { | |
| 1410 DictionaryValue* commands = NULL; | |
| 1411 if (!manifest_->GetDictionary(keys::kCommands, &commands)) { | |
| 1412 *error = ASCIIToUTF16(errors::kInvalidCommandsKey); | |
| 1413 return false; | |
| 1414 } | |
| 1415 | |
| 1416 int command_index = 0; | |
| 1417 for (DictionaryValue::key_iterator iter = commands->begin_keys(); | |
| 1418 iter != commands->end_keys(); ++iter) { | |
| 1419 ++command_index; | |
| 1420 | |
| 1421 DictionaryValue* command = NULL; | |
| 1422 if (!commands->GetDictionary(*iter, &command)) { | |
| 1423 *error = ExtensionErrorUtils::FormatErrorMessageUTF16( | |
| 1424 errors::kInvalidKeyBindingDictionary, | |
| 1425 base::IntToString(command_index)); | |
| 1426 return false; | |
| 1427 } | |
| 1428 | |
| 1429 ExtensionKeybinding binding; | |
| 1430 if (!binding.Parse(command, *iter, command_index, error)) | |
| 1431 return false; // |error| already set. | |
| 1432 | |
| 1433 commands_.push_back(binding); | |
| 1434 } | |
| 1435 } | |
| 1436 return true; | |
| 1437 } | |
| 1438 | |
| 1439 bool Extension::LoadPlugins(string16* error) { | |
| 1440 ListValue* list_value = NULL; | |
| 1441 if (!manifest_->GetList(keys::kPlugins, &list_value)) { | |
| 1442 *error = ASCIIToUTF16(errors::kInvalidPlugins); | |
| 1443 return false; | |
| 1444 } | |
| 1445 | |
| 1446 for (size_t i = 0; i < list_value->GetSize(); ++i) { | |
| 1447 DictionaryValue* plugin_value = NULL; | |
| 1448 std::string path_str; | |
| 1449 bool is_public = false; | |
| 1450 if (!list_value->GetDictionary(i, &plugin_value)) { | |
| 1451 *error = ASCIIToUTF16(errors::kInvalidPlugins); | |
| 1452 return false; | |
| 1453 } | |
| 1454 // Get plugins[i].path. | |
| 1455 if (!plugin_value->GetString(keys::kPluginsPath, &path_str)) { | |
| 1456 *error = ExtensionErrorUtils::FormatErrorMessageUTF16( | |
| 1457 errors::kInvalidPluginsPath, base::IntToString(i)); | |
| 1458 return false; | |
| 1459 } | |
| 1460 | |
| 1461 // Get plugins[i].content (optional). | |
| 1462 if (plugin_value->HasKey(keys::kPluginsPublic)) { | |
| 1463 if (!plugin_value->GetBoolean(keys::kPluginsPublic, &is_public)) { | |
| 1464 *error = ExtensionErrorUtils::FormatErrorMessageUTF16( | |
| 1465 errors::kInvalidPluginsPublic, base::IntToString(i)); | |
| 1466 return false; | |
| 1467 } | |
| 1468 } | |
| 1469 | |
| 1470 // We don't allow extension plugins to run on Chrome OS. We still | |
| 1471 // parse the manifest entry so that error messages are consistently | |
| 1472 // displayed across platforms. | |
| 1473 #if !defined(OS_CHROMEOS) | |
| 1474 plugins_.push_back(PluginInfo()); | |
| 1475 plugins_.back().path = path().Append(FilePath::FromUTF8Unsafe(path_str)); | |
| 1476 plugins_.back().is_public = is_public; | |
| 1477 #endif | |
| 1478 } | |
| 1479 return true; | |
| 1480 } | |
| 1481 | |
| 1482 bool Extension::LoadNaClModules(string16* error) { | |
| 1483 ListValue* list_value = NULL; | |
| 1484 if (!manifest_->GetList(keys::kNaClModules, &list_value)) { | |
| 1485 *error = ASCIIToUTF16(errors::kInvalidNaClModules); | |
| 1486 return false; | |
| 1487 } | |
| 1488 | |
| 1489 for (size_t i = 0; i < list_value->GetSize(); ++i) { | |
| 1490 DictionaryValue* module_value = NULL; | |
| 1491 std::string path_str; | |
| 1492 std::string mime_type; | |
| 1493 | |
| 1494 if (!list_value->GetDictionary(i, &module_value)) { | |
| 1495 *error = ASCIIToUTF16(errors::kInvalidNaClModules); | |
| 1496 return false; | |
| 1497 } | |
| 1498 | |
| 1499 // Get nacl_modules[i].path. | |
| 1500 if (!module_value->GetString(keys::kNaClModulesPath, &path_str)) { | |
| 1501 *error = ExtensionErrorUtils::FormatErrorMessageUTF16( | |
| 1502 errors::kInvalidNaClModulesPath, base::IntToString(i)); | |
| 1503 return false; | |
| 1504 } | |
| 1505 | |
| 1506 // Get nacl_modules[i].mime_type. | |
| 1507 if (!module_value->GetString(keys::kNaClModulesMIMEType, &mime_type)) { | |
| 1508 *error = ExtensionErrorUtils::FormatErrorMessageUTF16( | |
| 1509 errors::kInvalidNaClModulesMIMEType, base::IntToString(i)); | |
| 1510 return false; | |
| 1511 } | |
| 1512 | |
| 1513 nacl_modules_.push_back(NaClModuleInfo()); | |
| 1514 nacl_modules_.back().url = GetResourceURL(path_str); | |
| 1515 nacl_modules_.back().mime_type = mime_type; | |
| 1516 } | |
| 1517 | |
| 1518 return true; | |
| 1519 } | |
| 1520 | |
| 1521 bool Extension::LoadContentScripts(const int& flags, | |
| 1522 string16* error) { | |
| 1523 ListValue* list_value; | |
| 1524 if (!manifest_->GetList(keys::kContentScripts, &list_value)) { | |
| 1525 *error = ASCIIToUTF16(errors::kInvalidContentScriptsList); | |
| 1526 return false; | |
| 1527 } | |
| 1528 | |
| 1529 for (size_t i = 0; i < list_value->GetSize(); ++i) { | |
| 1530 DictionaryValue* content_script = NULL; | |
| 1531 if (!list_value->GetDictionary(i, &content_script)) { | |
| 1532 *error = ExtensionErrorUtils::FormatErrorMessageUTF16( | |
| 1533 errors::kInvalidContentScript, base::IntToString(i)); | |
| 1534 return false; | |
| 1535 } | |
| 1536 | |
| 1537 UserScript script; | |
| 1538 if (!LoadUserScriptHelper(content_script, i, flags, error, &script)) | |
| 1539 return false; // Failed to parse script context definition. | |
| 1540 script.set_extension_id(id()); | |
| 1541 if (converted_from_user_script_) { | |
| 1542 script.set_emulate_greasemonkey(true); | |
| 1543 script.set_match_all_frames(true); // Greasemonkey matches all frames. | |
| 1544 } | |
| 1545 content_scripts_.push_back(script); | |
| 1546 } | |
| 1547 return true; | |
| 1548 } | |
| 1549 | |
| 1550 bool Extension::LoadWebAccessibleResources(string16* error) { | |
| 1551 ListValue* list_value; | |
| 1552 if (!manifest_->GetList(keys::kWebAccessibleResources, &list_value)) { | |
| 1553 *error = ASCIIToUTF16(errors::kInvalidWebAccessibleResourcesList); | |
| 1554 return false; | |
| 1555 } | |
| 1556 for (size_t i = 0; i < list_value->GetSize(); ++i) { | |
| 1557 std::string relative_path; | |
| 1558 if (!list_value->GetString(i, &relative_path)) { | |
| 1559 *error = ExtensionErrorUtils::FormatErrorMessageUTF16( | |
| 1560 errors::kInvalidWebAccessibleResource, base::IntToString(i)); | |
| 1561 return false; | |
| 1562 } | |
| 1563 if (relative_path[0] != '/') | |
| 1564 relative_path = '/' + relative_path; | |
| 1565 web_accessible_resources_.insert(relative_path); | |
| 1566 } | |
| 1567 | |
| 1568 return true; | |
| 1569 } | |
| 1570 | |
| 1571 bool Extension::LoadPageAction(string16* error) { | |
| 1572 DictionaryValue* page_action_value = NULL; | |
| 1573 | |
| 1574 if (manifest_->HasKey(keys::kPageActions)) { | |
| 1575 ListValue* list_value = NULL; | |
| 1576 if (!manifest_->GetList(keys::kPageActions, &list_value)) { | |
| 1577 *error = ASCIIToUTF16(errors::kInvalidPageActionsList); | |
| 1578 return false; | |
| 1579 } | |
| 1580 | |
| 1581 size_t list_value_length = list_value->GetSize(); | |
| 1582 | |
| 1583 if (list_value_length == 0u) { | |
| 1584 // A list with zero items is allowed, and is equivalent to not having | |
| 1585 // a page_actions key in the manifest. Don't set |page_action_value|. | |
| 1586 } else if (list_value_length == 1u) { | |
| 1587 if (!list_value->GetDictionary(0, &page_action_value)) { | |
| 1588 *error = ASCIIToUTF16(errors::kInvalidPageAction); | |
| 1589 return false; | |
| 1590 } | |
| 1591 } else { // list_value_length > 1u. | |
| 1592 *error = ASCIIToUTF16(errors::kInvalidPageActionsListSize); | |
| 1593 return false; | |
| 1594 } | |
| 1595 } else if (manifest_->HasKey(keys::kPageAction)) { | |
| 1596 if (!manifest_->GetDictionary(keys::kPageAction, &page_action_value)) { | |
| 1597 *error = ASCIIToUTF16(errors::kInvalidPageAction); | |
| 1598 return false; | |
| 1599 } | |
| 1600 } | |
| 1601 | |
| 1602 // If page_action_value is not NULL, then there was a valid page action. | |
| 1603 if (page_action_value) { | |
| 1604 page_action_.reset( | |
| 1605 LoadExtensionActionHelper(page_action_value, error)); | |
| 1606 if (!page_action_.get()) | |
| 1607 return false; // Failed to parse page action definition. | |
| 1608 } | |
| 1609 | |
| 1610 return true; | |
| 1611 } | |
| 1612 | |
| 1613 bool Extension::LoadBrowserAction(string16* error) { | |
| 1614 DictionaryValue* browser_action_value = NULL; | |
| 1615 if (!manifest_->GetDictionary(keys::kBrowserAction, &browser_action_value)) { | |
| 1616 *error = ASCIIToUTF16(errors::kInvalidBrowserAction); | |
| 1617 return false; | |
| 1618 } | |
| 1619 | |
| 1620 browser_action_.reset( | |
| 1621 LoadExtensionActionHelper(browser_action_value, error)); | |
| 1622 if (!browser_action_.get()) | |
| 1623 return false; // Failed to parse browser action definition. | |
| 1624 return true; | |
| 1625 } | |
| 1626 | |
| 1627 bool Extension::LoadFileBrowserHandlers(string16* error) { | |
| 1628 ListValue* file_browser_handlers_value = NULL; | |
| 1629 if (!manifest_->GetList(keys::kFileBrowserHandlers, | |
| 1630 &file_browser_handlers_value)) { | |
| 1631 *error = ASCIIToUTF16(errors::kInvalidFileBrowserHandler); | |
| 1632 return false; | |
| 1633 } | |
| 1634 file_browser_handlers_.reset( | |
| 1635 LoadFileBrowserHandlersHelper(file_browser_handlers_value, error)); | |
| 1636 if (!file_browser_handlers_.get()) | |
| 1637 return false; // Failed to parse file browser actions definition. | |
| 1638 return true; | |
| 1639 } | |
| 1640 | |
| 1641 Extension::FileBrowserHandlerList* Extension::LoadFileBrowserHandlersHelper( | |
| 896 const ListValue* extension_actions, string16* error) { | 1642 const ListValue* extension_actions, string16* error) { |
| 897 scoped_ptr<FileBrowserHandlerList> result( | 1643 scoped_ptr<FileBrowserHandlerList> result( |
| 898 new FileBrowserHandlerList()); | 1644 new FileBrowserHandlerList()); |
| 899 for (ListValue::const_iterator iter = extension_actions->begin(); | 1645 for (ListValue::const_iterator iter = extension_actions->begin(); |
| 900 iter != extension_actions->end(); | 1646 iter != extension_actions->end(); |
| 901 ++iter) { | 1647 ++iter) { |
| 902 if (!(*iter)->IsType(Value::TYPE_DICTIONARY)) { | 1648 if (!(*iter)->IsType(Value::TYPE_DICTIONARY)) { |
| 903 *error = ASCIIToUTF16(errors::kInvalidFileBrowserHandler); | 1649 *error = ASCIIToUTF16(errors::kInvalidFileBrowserHandler); |
| 904 return NULL; | 1650 return NULL; |
| 905 } | 1651 } |
| (...skipping 73 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 979 default_icon.empty()) { | 1725 default_icon.empty()) { |
| 980 *error = ASCIIToUTF16(errors::kInvalidPageActionIconPath); | 1726 *error = ASCIIToUTF16(errors::kInvalidPageActionIconPath); |
| 981 return NULL; | 1727 return NULL; |
| 982 } | 1728 } |
| 983 result->set_icon_path(default_icon); | 1729 result->set_icon_path(default_icon); |
| 984 } | 1730 } |
| 985 | 1731 |
| 986 return result.release(); | 1732 return result.release(); |
| 987 } | 1733 } |
| 988 | 1734 |
| 989 bool Extension::LoadExtent(const char* key, | 1735 bool Extension::LoadChromeURLOverrides(string16* error) { |
| 990 URLPatternSet* extent, | 1736 DictionaryValue* overrides = NULL; |
| 991 const char* list_error, | 1737 if (!manifest_->GetDictionary(keys::kChromeURLOverrides, &overrides)) { |
| 992 const char* value_error, | 1738 *error = ASCIIToUTF16(errors::kInvalidChromeURLOverrides); |
| 993 string16* error) { | 1739 return false; |
| 994 Value* temp = NULL; | 1740 } |
| 995 if (!manifest_->Get(key, &temp)) | 1741 |
| 996 return true; | 1742 // Validate that the overrides are all strings |
| 997 | 1743 for (DictionaryValue::key_iterator iter = overrides->begin_keys(); |
| 998 if (temp->GetType() != Value::TYPE_LIST) { | 1744 iter != overrides->end_keys(); ++iter) { |
| 999 *error = ASCIIToUTF16(list_error); | 1745 std::string page = *iter; |
| 1000 return false; | 1746 std::string val; |
| 1001 } | 1747 // Restrict override pages to a list of supported URLs. |
| 1002 | 1748 if ((page != chrome::kChromeUINewTabHost && |
| 1003 ListValue* pattern_list = static_cast<ListValue*>(temp); | 1749 #if defined(USE_VIRTUAL_KEYBOARD) |
| 1004 for (size_t i = 0; i < pattern_list->GetSize(); ++i) { | 1750 page != chrome::kChromeUIKeyboardHost && |
| 1005 std::string pattern_string; | 1751 #endif |
| 1006 if (!pattern_list->GetString(i, &pattern_string)) { | 1752 #if defined(OS_CHROMEOS) |
| 1007 *error = ExtensionErrorUtils::FormatErrorMessageUTF16(value_error, | 1753 page != chrome::kChromeUIActivationMessageHost && |
| 1008 base::UintToString(i), | 1754 #endif |
| 1009 errors::kExpectString); | 1755 page != chrome::kChromeUIBookmarksHost && |
| 1010 return false; | 1756 page != chrome::kChromeUIHistoryHost |
| 1011 } | 1757 #if defined(FILE_MANAGER_EXTENSION) |
| 1012 | 1758 && |
| 1013 URLPattern pattern(kValidWebExtentSchemes); | 1759 !(location() == COMPONENT && |
| 1014 URLPattern::ParseResult parse_result = pattern.Parse(pattern_string); | 1760 page == chrome::kChromeUIFileManagerHost) |
| 1015 if (parse_result == URLPattern::PARSE_ERROR_EMPTY_PATH) { | 1761 #endif |
| 1016 pattern_string += "/"; | 1762 ) || |
| 1017 parse_result = pattern.Parse(pattern_string); | 1763 !overrides->GetStringWithoutPathExpansion(*iter, &val)) { |
| 1018 } | 1764 *error = ASCIIToUTF16(errors::kInvalidChromeURLOverrides); |
| 1019 | 1765 return false; |
| 1020 if (parse_result != URLPattern::PARSE_SUCCESS) { | 1766 } |
| 1021 *error = ExtensionErrorUtils::FormatErrorMessageUTF16( | 1767 // Replace the entry with a fully qualified chrome-extension:// URL. |
| 1022 value_error, | 1768 chrome_url_overrides_[page] = GetResourceURL(val); |
| 1023 base::UintToString(i), | 1769 } |
| 1024 URLPattern::GetParseResultString(parse_result)); | 1770 |
| 1025 return false; | 1771 // An extension may override at most one page. |
| 1026 } | 1772 if (overrides->size() > 1) { |
| 1027 | 1773 *error = ASCIIToUTF16(errors::kMultipleOverrides); |
| 1028 // Do not allow authors to claim "<all_urls>". | 1774 return false; |
| 1029 if (pattern.match_all_urls()) { | 1775 } |
| 1030 *error = ExtensionErrorUtils::FormatErrorMessageUTF16( | 1776 |
| 1031 value_error, | 1777 return true; |
| 1032 base::UintToString(i), | 1778 } |
| 1033 errors::kCannotClaimAllURLsInExtent); | 1779 |
| 1034 return false; | 1780 bool Extension::LoadOmnibox(string16* error) { |
| 1035 } | 1781 if (!manifest_->GetString(keys::kOmniboxKeyword, &omnibox_keyword_) || |
| 1036 | 1782 omnibox_keyword_.empty()) { |
| 1037 // Do not allow authors to claim "*" for host. | 1783 *error = ASCIIToUTF16(errors::kInvalidOmniboxKeyword); |
| 1038 if (pattern.host().empty()) { | 1784 return false; |
| 1039 *error = ExtensionErrorUtils::FormatErrorMessageUTF16( | 1785 } |
| 1040 value_error, | 1786 return true; |
| 1041 base::UintToString(i), | 1787 } |
| 1042 errors::kCannotClaimAllHostsInExtent); | 1788 |
| 1043 return false; | 1789 bool Extension::LoadTextToSpeechVoices(string16* error) { |
| 1044 } | 1790 DictionaryValue* tts_dict = NULL; |
| 1045 | 1791 if (!manifest_->GetDictionary(keys::kTtsEngine, &tts_dict)) { |
| 1046 // We do not allow authors to put wildcards in their paths. Instead, we | 1792 *error = ASCIIToUTF16(errors::kInvalidTts); |
| 1047 // imply one at the end. | 1793 return false; |
| 1048 if (pattern.path().find('*') != std::string::npos) { | 1794 } |
| 1049 *error = ExtensionErrorUtils::FormatErrorMessageUTF16( | 1795 |
| 1050 value_error, | 1796 if (tts_dict->HasKey(keys::kTtsVoices)) { |
| 1051 base::UintToString(i), | 1797 ListValue* tts_voices = NULL; |
| 1052 errors::kNoWildCardsInPaths); | 1798 if (!tts_dict->GetList(keys::kTtsVoices, &tts_voices)) { |
| 1053 return false; | 1799 *error = ASCIIToUTF16(errors::kInvalidTtsVoices); |
| 1054 } | 1800 return false; |
| 1055 pattern.SetPath(pattern.path() + '*'); | 1801 } |
| 1056 | 1802 |
| 1057 extent->AddPattern(pattern); | 1803 for (size_t i = 0; i < tts_voices->GetSize(); i++) { |
| 1058 } | 1804 DictionaryValue* one_tts_voice = NULL; |
| 1059 | 1805 if (!tts_voices->GetDictionary(i, &one_tts_voice)) { |
| 1060 return true; | 1806 *error = ASCIIToUTF16(errors::kInvalidTtsVoices); |
| 1061 } | 1807 return false; |
| 1062 | 1808 } |
| 1063 bool Extension::LoadLaunchURL(string16* error) { | 1809 |
| 1064 Value* temp = NULL; | 1810 TtsVoice voice_data; |
| 1065 | 1811 if (one_tts_voice->HasKey(keys::kTtsVoicesVoiceName)) { |
| 1066 // launch URL can be either local (to chrome-extension:// root) or an absolute | 1812 if (!one_tts_voice->GetString( |
| 1067 // web URL. | 1813 keys::kTtsVoicesVoiceName, &voice_data.voice_name)) { |
| 1068 if (manifest_->Get(keys::kLaunchLocalPath, &temp)) { | 1814 *error = ASCIIToUTF16(errors::kInvalidTtsVoicesVoiceName); |
| 1069 if (manifest_->Get(keys::kLaunchWebURL, NULL)) { | 1815 return false; |
| 1070 *error = ASCIIToUTF16(errors::kLaunchPathAndURLAreExclusive); | 1816 } |
| 1071 return false; | 1817 } |
| 1072 } | 1818 if (one_tts_voice->HasKey(keys::kTtsVoicesLang)) { |
| 1073 | 1819 if (!one_tts_voice->GetString( |
| 1074 if (manifest_->Get(keys::kWebURLs, NULL)) { | 1820 keys::kTtsVoicesLang, &voice_data.lang) || |
| 1075 *error = ASCIIToUTF16(errors::kLaunchPathAndExtentAreExclusive); | 1821 !l10n_util::IsValidLocaleSyntax(voice_data.lang)) { |
| 1076 return false; | 1822 *error = ASCIIToUTF16(errors::kInvalidTtsVoicesLang); |
| 1077 } | 1823 return false; |
| 1078 | 1824 } |
| 1079 std::string launch_path; | 1825 } |
| 1080 if (!temp->GetAsString(&launch_path)) { | 1826 if (one_tts_voice->HasKey(keys::kTtsVoicesGender)) { |
| 1081 *error = ExtensionErrorUtils::FormatErrorMessageUTF16( | 1827 if (!one_tts_voice->GetString( |
| 1082 errors::kInvalidLaunchValue, | 1828 keys::kTtsVoicesGender, &voice_data.gender) || |
| 1083 keys::kLaunchLocalPath); | 1829 (voice_data.gender != keys::kTtsGenderMale && |
| 1084 return false; | 1830 voice_data.gender != keys::kTtsGenderFemale)) { |
| 1085 } | 1831 *error = ASCIIToUTF16(errors::kInvalidTtsVoicesGender); |
| 1086 | 1832 return false; |
| 1087 // Ensure the launch path is a valid relative URL. | 1833 } |
| 1088 GURL resolved = url().Resolve(launch_path); | 1834 } |
| 1089 if (!resolved.is_valid() || resolved.GetOrigin() != url()) { | 1835 if (one_tts_voice->HasKey(keys::kTtsVoicesEventTypes)) { |
| 1090 *error = ExtensionErrorUtils::FormatErrorMessageUTF16( | 1836 ListValue* event_types_list; |
| 1091 errors::kInvalidLaunchValue, | 1837 if (!one_tts_voice->GetList( |
| 1092 keys::kLaunchLocalPath); | 1838 keys::kTtsVoicesEventTypes, &event_types_list)) { |
| 1093 return false; | 1839 *error = ASCIIToUTF16(errors::kInvalidTtsVoicesEventTypes); |
| 1094 } | 1840 return false; |
| 1095 | 1841 } |
| 1096 launch_local_path_ = launch_path; | 1842 for (size_t i = 0; i < event_types_list->GetSize(); i++) { |
| 1097 } else if (manifest_->Get(keys::kLaunchWebURL, &temp)) { | 1843 std::string event_type; |
| 1098 std::string launch_url; | 1844 if (!event_types_list->GetString(i, &event_type)) { |
| 1099 if (!temp->GetAsString(&launch_url)) { | 1845 *error = ASCIIToUTF16(errors::kInvalidTtsVoicesEventTypes); |
| 1100 *error = ExtensionErrorUtils::FormatErrorMessageUTF16( | 1846 return false; |
| 1101 errors::kInvalidLaunchValue, | 1847 } |
| 1102 keys::kLaunchWebURL); | 1848 if (event_type != keys::kTtsVoicesEventTypeEnd && |
| 1103 return false; | 1849 event_type != keys::kTtsVoicesEventTypeError && |
| 1104 } | 1850 event_type != keys::kTtsVoicesEventTypeMarker && |
| 1105 | 1851 event_type != keys::kTtsVoicesEventTypeSentence && |
| 1106 // Ensure the launch URL is a valid absolute URL and web extent scheme. | 1852 event_type != keys::kTtsVoicesEventTypeStart && |
| 1107 GURL url(launch_url); | 1853 event_type != keys::kTtsVoicesEventTypeWord) { |
| 1108 URLPattern pattern(kValidWebExtentSchemes); | 1854 *error = ASCIIToUTF16(errors::kInvalidTtsVoicesEventTypes); |
| 1109 if (!url.is_valid() || !pattern.SetScheme(url.scheme())) { | 1855 return false; |
| 1110 *error = ExtensionErrorUtils::FormatErrorMessageUTF16( | 1856 } |
| 1111 errors::kInvalidLaunchValue, | 1857 if (voice_data.event_types.find(event_type) != |
| 1112 keys::kLaunchWebURL); | 1858 voice_data.event_types.end()) { |
| 1113 return false; | 1859 *error = ASCIIToUTF16(errors::kInvalidTtsVoicesEventTypes); |
| 1114 } | 1860 return false; |
| 1115 | 1861 } |
| 1116 launch_web_url_ = launch_url; | 1862 voice_data.event_types.insert(event_type); |
| 1117 } else if (is_app()) { | 1863 } |
| 1118 *error = ASCIIToUTF16(errors::kLaunchURLRequired); | 1864 } |
| 1119 return false; | 1865 |
| 1120 } | 1866 tts_voices_.push_back(voice_data); |
| 1121 | |
| 1122 // If there is no extent, we default the extent based on the launch URL. | |
| 1123 if (web_extent().is_empty() && !launch_web_url().empty()) { | |
| 1124 GURL launch_url(launch_web_url()); | |
| 1125 URLPattern pattern(kValidWebExtentSchemes); | |
| 1126 if (!pattern.SetScheme("*")) { | |
| 1127 *error = ExtensionErrorUtils::FormatErrorMessageUTF16( | |
| 1128 errors::kInvalidLaunchValue, | |
| 1129 keys::kLaunchWebURL); | |
| 1130 return false; | |
| 1131 } | |
| 1132 pattern.SetHost(launch_url.host()); | |
| 1133 pattern.SetPath("/*"); | |
| 1134 extent_.AddPattern(pattern); | |
| 1135 } | |
| 1136 | |
| 1137 // In order for the --apps-gallery-url switch to work with the gallery | |
| 1138 // process isolation, we must insert any provided value into the component | |
| 1139 // app's launch url and web extent. | |
| 1140 if (id() == extension_misc::kWebStoreAppId) { | |
| 1141 std::string gallery_url_str = CommandLine::ForCurrentProcess()-> | |
| 1142 GetSwitchValueASCII(switches::kAppsGalleryURL); | |
| 1143 | |
| 1144 // Empty string means option was not used. | |
| 1145 if (!gallery_url_str.empty()) { | |
| 1146 GURL gallery_url(gallery_url_str); | |
| 1147 OverrideLaunchUrl(gallery_url); | |
| 1148 } | |
| 1149 } else if (id() == extension_misc::kCloudPrintAppId) { | |
| 1150 // In order for the --cloud-print-service switch to work, we must update | |
| 1151 // the launch URL and web extent. | |
| 1152 // TODO(sanjeevr): Ideally we want to use CloudPrintURL here but that is | |
| 1153 // currently under chrome/browser. | |
| 1154 const CommandLine& command_line = *CommandLine::ForCurrentProcess(); | |
| 1155 GURL cloud_print_service_url = GURL(command_line.GetSwitchValueASCII( | |
| 1156 switches::kCloudPrintServiceURL)); | |
| 1157 if (!cloud_print_service_url.is_empty()) { | |
| 1158 std::string path( | |
| 1159 cloud_print_service_url.path() + "/enable_chrome_connector"); | |
| 1160 GURL::Replacements replacements; | |
| 1161 replacements.SetPathStr(path); | |
| 1162 GURL cloud_print_enable_connector_url = | |
| 1163 cloud_print_service_url.ReplaceComponents(replacements); | |
| 1164 OverrideLaunchUrl(cloud_print_enable_connector_url); | |
| 1165 } | |
| 1166 } | |
| 1167 return true; | |
| 1168 } | |
| 1169 | |
| 1170 bool ReadLaunchDimension(const extensions::Manifest* manifest, | |
| 1171 const char* key, | |
| 1172 int* target, | |
| 1173 bool is_valid_container, | |
| 1174 string16* error) { | |
| 1175 Value* temp = NULL; | |
| 1176 if (manifest->Get(key, &temp)) { | |
| 1177 if (!is_valid_container) { | |
| 1178 *error = ExtensionErrorUtils::FormatErrorMessageUTF16( | |
| 1179 errors::kInvalidLaunchValueContainer, | |
| 1180 key); | |
| 1181 return false; | |
| 1182 } | |
| 1183 if (!temp->GetAsInteger(target) || *target < 0) { | |
| 1184 *target = 0; | |
| 1185 *error = ExtensionErrorUtils::FormatErrorMessageUTF16( | |
| 1186 errors::kInvalidLaunchValue, | |
| 1187 key); | |
| 1188 return false; | |
| 1189 } | |
| 1190 } | |
| 1191 return true; | |
| 1192 } | |
| 1193 | |
| 1194 bool Extension::LoadLaunchContainer(string16* error) { | |
| 1195 Value* temp = NULL; | |
| 1196 if (!manifest_->Get(keys::kLaunchContainer, &temp)) | |
| 1197 return true; | |
| 1198 | |
| 1199 std::string launch_container_string; | |
| 1200 if (!temp->GetAsString(&launch_container_string)) { | |
| 1201 *error = ASCIIToUTF16(errors::kInvalidLaunchContainer); | |
| 1202 return false; | |
| 1203 } | |
| 1204 | |
| 1205 if (launch_container_string == values::kLaunchContainerShell) { | |
| 1206 launch_container_ = extension_misc::LAUNCH_SHELL; | |
| 1207 } else if (launch_container_string == values::kLaunchContainerPanel) { | |
| 1208 launch_container_ = extension_misc::LAUNCH_PANEL; | |
| 1209 } else if (launch_container_string == values::kLaunchContainerTab) { | |
| 1210 launch_container_ = extension_misc::LAUNCH_TAB; | |
| 1211 } else { | |
| 1212 *error = ASCIIToUTF16(errors::kInvalidLaunchContainer); | |
| 1213 return false; | |
| 1214 } | |
| 1215 | |
| 1216 bool can_specify_initial_size = | |
| 1217 launch_container() == extension_misc::LAUNCH_PANEL || | |
| 1218 launch_container() == extension_misc::LAUNCH_WINDOW || | |
| 1219 launch_container() == extension_misc::LAUNCH_SHELL; | |
| 1220 | |
| 1221 // Validate the container width if present. | |
| 1222 if (!ReadLaunchDimension(manifest_, | |
| 1223 keys::kLaunchWidth, | |
| 1224 &launch_width_, | |
| 1225 can_specify_initial_size, | |
| 1226 error)) | |
| 1227 return false; | |
| 1228 | |
| 1229 // Validate container height if present. | |
| 1230 if (!ReadLaunchDimension(manifest_, | |
| 1231 keys::kLaunchHeight, | |
| 1232 &launch_height_, | |
| 1233 can_specify_initial_size, | |
| 1234 error)) | |
| 1235 return false; | |
| 1236 | |
| 1237 bool can_specify_size_range = | |
| 1238 launch_container() == extension_misc::LAUNCH_SHELL; | |
| 1239 | |
| 1240 // Validate min size if present. | |
| 1241 if (!ReadLaunchDimension(manifest_, | |
| 1242 keys::kLaunchMinWidth, | |
| 1243 &launch_min_width_, | |
| 1244 can_specify_size_range, | |
| 1245 error)) | |
| 1246 return false; | |
| 1247 if (!ReadLaunchDimension(manifest_, | |
| 1248 keys::kLaunchMinHeight, | |
| 1249 &launch_min_height_, | |
| 1250 can_specify_size_range, | |
| 1251 error)) | |
| 1252 return false; | |
| 1253 | |
| 1254 if (launch_container() == extension_misc::LAUNCH_SHELL) { | |
| 1255 if (!manifest_->Get(keys::kLaunchWidth, &temp)) { | |
| 1256 *error = ExtensionErrorUtils::FormatErrorMessageUTF16( | |
| 1257 errors::kInvalidLaunchValue, | |
| 1258 keys::kLaunchWidth); | |
| 1259 return false; | |
| 1260 } | |
| 1261 if (!manifest_->Get(keys::kLaunchHeight, &temp)) { | |
| 1262 *error = ExtensionErrorUtils::FormatErrorMessageUTF16( | |
| 1263 errors::kInvalidLaunchValue, | |
| 1264 keys::kLaunchHeight); | |
| 1265 return false; | |
| 1266 } | |
| 1267 } | |
| 1268 | |
| 1269 return true; | |
| 1270 } | |
| 1271 | |
| 1272 bool Extension::LoadAppIsolation(string16* error) { | |
| 1273 Value* temp = NULL; | |
| 1274 if (!manifest_->Get(keys::kIsolation, &temp)) | |
| 1275 return true; | |
| 1276 | |
| 1277 if (temp->GetType() != Value::TYPE_LIST) { | |
| 1278 *error = ASCIIToUTF16(errors::kInvalidIsolation); | |
| 1279 return false; | |
| 1280 } | |
| 1281 | |
| 1282 ListValue* isolation_list = static_cast<ListValue*>(temp); | |
| 1283 for (size_t i = 0; i < isolation_list->GetSize(); ++i) { | |
| 1284 std::string isolation_string; | |
| 1285 if (!isolation_list->GetString(i, &isolation_string)) { | |
| 1286 *error = ExtensionErrorUtils::FormatErrorMessageUTF16( | |
| 1287 errors::kInvalidIsolationValue, | |
| 1288 base::UintToString(i)); | |
| 1289 return false; | |
| 1290 } | |
| 1291 | |
| 1292 // Check for isolated storage. | |
| 1293 if (isolation_string == values::kIsolatedStorage) { | |
| 1294 is_storage_isolated_ = true; | |
| 1295 } else { | |
| 1296 DLOG(WARNING) << "Did not recognize isolation type: " | |
| 1297 << isolation_string; | |
| 1298 } | 1867 } |
| 1299 } | 1868 } |
| 1300 return true; | 1869 return true; |
| 1301 } | 1870 } |
| 1302 | 1871 |
| 1303 bool Extension::LoadWebIntentServices(string16* error) { | 1872 bool Extension::LoadWebIntentServices(string16* error) { |
| 1304 DCHECK(error); | 1873 DCHECK(error); |
| 1305 | 1874 |
| 1306 if (!manifest_->HasKey(keys::kIntents)) | 1875 if (!manifest_->HasKey(keys::kIntents)) |
| 1307 return true; | 1876 return true; |
| (...skipping 79 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 1387 errors::kInvalidIntentTypeElement, *iter, | 1956 errors::kInvalidIntentTypeElement, *iter, |
| 1388 std::string(base::IntToString(i))); | 1957 std::string(base::IntToString(i))); |
| 1389 return false; | 1958 return false; |
| 1390 } | 1959 } |
| 1391 intents_services_.push_back(service); | 1960 intents_services_.push_back(service); |
| 1392 } | 1961 } |
| 1393 } | 1962 } |
| 1394 return true; | 1963 return true; |
| 1395 } | 1964 } |
| 1396 | 1965 |
| 1966 bool Extension::LoadRequirements(string16* error) { | |
| 1967 DictionaryValue* requirements_value = NULL; | |
| 1968 if (!manifest_->GetDictionary(keys::kRequirements, &requirements_value)) { | |
| 1969 *error = ASCIIToUTF16(errors::kInvalidRequirements); | |
| 1970 return false; | |
| 1971 } | |
| 1972 | |
| 1973 for (DictionaryValue::key_iterator it = requirements_value->begin_keys(); | |
| 1974 it != requirements_value->end_keys(); ++it) { | |
| 1975 DictionaryValue* requirement_value; | |
| 1976 if (!requirements_value->GetDictionaryWithoutPathExpansion( | |
| 1977 *it, &requirement_value)) { | |
| 1978 *error = ExtensionErrorUtils::FormatErrorMessageUTF16( | |
| 1979 errors::kInvalidRequirement, *it); | |
| 1980 return false; | |
| 1981 } | |
| 1982 } | |
| 1983 return true; | |
| 1984 } | |
| 1985 | |
| 1986 bool Extension::LoadDefaultLocale(string16* error) { | |
| 1987 if (!manifest_->GetString(keys::kDefaultLocale, &default_locale_) || | |
| 1988 !l10n_util::IsValidLocaleSyntax(default_locale_)) { | |
| 1989 *error = ASCIIToUTF16(errors::kInvalidDefaultLocale); | |
| 1990 return false; | |
| 1991 } | |
| 1992 return true; | |
| 1993 } | |
| 1994 | |
| 1995 bool Extension::LoadOfflineEnabled(string16* error) { | |
| 1996 if (!manifest_->GetBoolean(keys::kOfflineEnabled, &offline_enabled_)) { | |
| 1997 *error = ASCIIToUTF16(errors::kInvalidOfflineEnabled); | |
| 1998 return false; | |
| 1999 } | |
| 2000 return true; | |
| 2001 } | |
| 2002 | |
| 2003 bool Extension::LoadIncognito(string16* error) { | |
| 2004 std::string value; | |
| 2005 if (!manifest_->GetString(keys::kIncognito, &value)) { | |
| 2006 *error = ASCIIToUTF16(errors::kInvalidIncognitoBehavior); | |
| 2007 return false; | |
| 2008 } | |
| 2009 if (value == values::kIncognitoSpanning) { | |
| 2010 incognito_split_mode_ = false; | |
| 2011 } else if (value == values::kIncognitoSplit) { | |
| 2012 incognito_split_mode_ = true; | |
| 2013 } else { | |
| 2014 *error = ASCIIToUTF16(errors::kInvalidIncognitoBehavior); | |
| 2015 return false; | |
| 2016 } | |
| 2017 return true; | |
| 2018 } | |
| 2019 | |
| 1397 bool Extension::LoadBackgroundScripts(string16* error) { | 2020 bool Extension::LoadBackgroundScripts(string16* error) { |
| 1398 Value* background_scripts_value = NULL; | 2021 Value* background_scripts_value = NULL; |
| 1399 if (!manifest_->Get(keys::kBackgroundScripts, &background_scripts_value)) | 2022 if (!manifest_->Get(keys::kBackgroundScripts, &background_scripts_value)) |
| 1400 return true; | 2023 return true; |
| 1401 | 2024 |
| 1402 CHECK(background_scripts_value); | 2025 CHECK(background_scripts_value); |
| 1403 if (background_scripts_value->GetType() != Value::TYPE_LIST) { | 2026 if (background_scripts_value->GetType() != Value::TYPE_LIST) { |
| 1404 *error = ASCIIToUTF16(errors::kInvalidBackgroundScripts); | 2027 *error = ASCIIToUTF16(errors::kInvalidBackgroundScripts); |
| 1405 return false; | 2028 return false; |
| 1406 } | 2029 } |
| (...skipping 11 matching lines...) Expand all Loading... | |
| 1418 } | 2041 } |
| 1419 | 2042 |
| 1420 return true; | 2043 return true; |
| 1421 } | 2044 } |
| 1422 | 2045 |
| 1423 bool Extension::LoadBackgroundPage( | 2046 bool Extension::LoadBackgroundPage( |
| 1424 const ExtensionAPIPermissionSet& api_permissions, | 2047 const ExtensionAPIPermissionSet& api_permissions, |
| 1425 string16* error) { | 2048 string16* error) { |
| 1426 base::Value* background_page_value = NULL; | 2049 base::Value* background_page_value = NULL; |
| 1427 if (!manifest_->Get(keys::kBackgroundPage, &background_page_value)) | 2050 if (!manifest_->Get(keys::kBackgroundPage, &background_page_value)) |
| 1428 manifest_->Get(keys::kBackgroundPageLegacy, &background_page_value); | 2051 manifest_->Get(keys::kBackgroundPageLegacy, &background_page_value); |
|
Yoyo Zhou
2012/03/01 01:17:25
leave the original indent
Devlin
2012/03/02 17:31:04
Whoops, didn't mean to do that. Done.
| |
| 1429 | 2052 |
| 1430 if (!background_page_value) | 2053 if (!background_page_value) |
| 1431 return true; | 2054 return true; |
| 1432 | 2055 |
| 1433 std::string background_str; | 2056 std::string background_str; |
| 1434 if (!background_page_value->GetAsString(&background_str)) { | 2057 if (!background_page_value->GetAsString(&background_str)) { |
| 1435 *error = ASCIIToUTF16(errors::kInvalidBackground); | 2058 *error = ASCIIToUTF16(errors::kInvalidBackground); |
| 1436 return false; | 2059 return false; |
| 1437 } | 2060 } |
| 1438 | 2061 |
| (...skipping 45 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 1484 } | 2107 } |
| 1485 | 2108 |
| 1486 if (!has_background_page()) { | 2109 if (!has_background_page()) { |
| 1487 *error = ASCIIToUTF16(errors::kInvalidBackgroundPersistentNoPage); | 2110 *error = ASCIIToUTF16(errors::kInvalidBackgroundPersistentNoPage); |
| 1488 return false; | 2111 return false; |
| 1489 } | 2112 } |
| 1490 | 2113 |
| 1491 return true; | 2114 return true; |
| 1492 } | 2115 } |
| 1493 | 2116 |
| 1494 // static | 2117 bool Extension::LoadContentSecurityPolicy(string16* error) { |
| 1495 bool Extension::IsTrustedId(const std::string& id) { | 2118 if (manifest_->HasKey(keys::kContentSecurityPolicy)) { |
| 1496 // See http://b/4946060 for more details. | 2119 std::string content_security_policy; |
| 1497 return id == std::string("nckgahadagoaajjgafhacjanaoiihapd"); | 2120 if (!manifest_->GetString(keys::kContentSecurityPolicy, |
| 1498 } | 2121 &content_security_policy)) { |
| 1499 | 2122 *error = ASCIIToUTF16(errors::kInvalidContentSecurityPolicy); |
| 1500 Extension::Extension(const FilePath& path, | 2123 return false; |
| 1501 scoped_ptr<extensions::Manifest> manifest) | 2124 } |
| 1502 : manifest_version_(0), | 2125 if (!ContentSecurityPolicyIsLegal(content_security_policy)) { |
| 1503 incognito_split_mode_(false), | 2126 *error = ASCIIToUTF16(errors::kInvalidContentSecurityPolicy); |
| 1504 offline_enabled_(false), | 2127 return false; |
| 1505 converted_from_user_script_(false), | 2128 } |
| 1506 background_page_persists_(true), | 2129 if (manifest_version_ >= 2 && |
| 1507 manifest_(manifest.release()), | 2130 !ContentSecurityPolicyIsSecure(content_security_policy)) { |
| 1508 is_storage_isolated_(false), | 2131 *error = ASCIIToUTF16(errors::kInvalidContentSecurityPolicy); |
| 1509 launch_container_(extension_misc::LAUNCH_TAB), | 2132 return false; |
| 1510 launch_width_(0), | 2133 } |
| 1511 launch_height_(0), | 2134 |
| 1512 launch_min_width_(0), | 2135 content_security_policy_ = content_security_policy; |
| 1513 launch_min_height_(0), | 2136 } else if (manifest_version_ >= 2) { |
| 1514 wants_file_access_(false), | 2137 // Manifest version 2 introduced a default Content-Security-Policy. |
| 1515 creation_flags_(0) { | 2138 // TODO(abarth): Should we continue to let extensions override the |
| 1516 DCHECK(path.empty() || path.IsAbsolute()); | 2139 // default Content-Security-Policy? |
| 1517 path_ = MaybeNormalizePath(path); | 2140 content_security_policy_ = kDefaultContentSecurityPolicy; |
| 1518 } | 2141 CHECK(ContentSecurityPolicyIsSecure(content_security_policy_)); |
| 1519 | 2142 } |
| 1520 Extension::~Extension() { | 2143 return true; |
| 1521 if (manifest_) | 2144 } |
| 1522 delete manifest_; | 2145 |
| 1523 } | 2146 // Load App settings. LoadExtent at least has to be done before |
| 1524 | 2147 // ParsePermissions(), because the valid permissions depend on what type of |
| 1525 ExtensionResource Extension::GetResource( | 2148 // package this is. |
|
Yoyo Zhou
2012/03/01 01:17:25
This comment belongs in InitValue (at this functio
Devlin
2012/03/02 17:31:04
Done.
| |
| 1526 const std::string& relative_path) const { | 2149 bool Extension::LoadAppFeatures(string16* error) { |
| 1527 #if defined(OS_POSIX) | 2150 if (!LoadExtent(keys::kWebURLs, &extent_, |
| 1528 FilePath relative_file_path(relative_path); | 2151 errors::kInvalidWebURLs, errors::kInvalidWebURL, error) || |
| 1529 #elif defined(OS_WIN) | 2152 !LoadLaunchURL(error) || |
| 1530 FilePath relative_file_path(UTF8ToWide(relative_path)); | 2153 !LoadLaunchContainer(error)) |
| 1531 #endif | 2154 return false; |
| 1532 return ExtensionResource(id(), path(), relative_file_path); | |
| 1533 } | |
| 1534 | |
| 1535 ExtensionResource Extension::GetResource( | |
| 1536 const FilePath& relative_file_path) const { | |
| 1537 return ExtensionResource(id(), path(), relative_file_path); | |
| 1538 } | |
| 1539 | |
| 1540 // TODO(rafaelw): Move ParsePEMKeyBytes, ProducePEM & FormatPEMForOutput to a | |
| 1541 // util class in base: | |
| 1542 // http://code.google.com/p/chromium/issues/detail?id=13572 | |
| 1543 bool Extension::ParsePEMKeyBytes(const std::string& input, | |
| 1544 std::string* output) { | |
| 1545 DCHECK(output); | |
| 1546 if (!output) | |
| 1547 return false; | |
| 1548 if (input.length() == 0) | |
| 1549 return false; | |
| 1550 | |
| 1551 std::string working = input; | |
| 1552 if (StartsWithASCII(working, kKeyBeginHeaderMarker, true)) { | |
| 1553 working = CollapseWhitespaceASCII(working, true); | |
| 1554 size_t header_pos = working.find(kKeyInfoEndMarker, | |
| 1555 sizeof(kKeyBeginHeaderMarker) - 1); | |
| 1556 if (header_pos == std::string::npos) | |
| 1557 return false; | |
| 1558 size_t start_pos = header_pos + sizeof(kKeyInfoEndMarker) - 1; | |
| 1559 size_t end_pos = working.rfind(kKeyBeginFooterMarker); | |
| 1560 if (end_pos == std::string::npos) | |
| 1561 return false; | |
| 1562 if (start_pos >= end_pos) | |
| 1563 return false; | |
| 1564 | |
| 1565 working = working.substr(start_pos, end_pos - start_pos); | |
| 1566 if (working.length() == 0) | |
| 1567 return false; | |
| 1568 } | |
| 1569 | |
| 1570 return base::Base64Decode(working, output); | |
| 1571 } | |
| 1572 | |
| 1573 bool Extension::ProducePEM(const std::string& input, std::string* output) { | |
| 1574 DCHECK(output); | |
| 1575 if (input.length() == 0) | |
| 1576 return false; | |
| 1577 | |
| 1578 return base::Base64Encode(input, output); | |
| 1579 } | |
| 1580 | |
| 1581 bool Extension::FormatPEMForFileOutput(const std::string& input, | |
| 1582 std::string* output, | |
| 1583 bool is_public) { | |
| 1584 DCHECK(output); | |
| 1585 if (input.length() == 0) | |
| 1586 return false; | |
| 1587 *output = ""; | |
| 1588 output->append(kKeyBeginHeaderMarker); | |
| 1589 output->append(" "); | |
| 1590 output->append(is_public ? kPublic : kPrivate); | |
| 1591 output->append(" "); | |
| 1592 output->append(kKeyInfoEndMarker); | |
| 1593 output->append("\n"); | |
| 1594 for (size_t i = 0; i < input.length(); ) { | |
| 1595 int slice = std::min<int>(input.length() - i, kPEMOutputColumns); | |
| 1596 output->append(input.substr(i, slice)); | |
| 1597 output->append("\n"); | |
| 1598 i += slice; | |
| 1599 } | |
| 1600 output->append(kKeyBeginFooterMarker); | |
| 1601 output->append(" "); | |
| 1602 output->append(is_public ? kPublic : kPrivate); | |
| 1603 output->append(" "); | |
| 1604 output->append(kKeyInfoEndMarker); | |
| 1605 output->append("\n"); | |
| 1606 | |
| 1607 return true; | |
| 1608 } | |
| 1609 | |
| 1610 // static | |
| 1611 void Extension::DecodeIcon(const Extension* extension, | |
| 1612 ExtensionIconSet::Icons preferred_icon_size, | |
| 1613 ExtensionIconSet::MatchType match_type, | |
| 1614 scoped_ptr<SkBitmap>* result) { | |
| 1615 std::string path = extension->icons().Get(preferred_icon_size, match_type); | |
| 1616 ExtensionIconSet::Icons size = extension->icons().GetIconSizeFromPath(path); | |
| 1617 ExtensionResource icon_resource = extension->GetResource(path); | |
| 1618 DecodeIconFromPath(icon_resource.GetFilePath(), size, result); | |
| 1619 } | |
| 1620 | |
| 1621 // static | |
| 1622 void Extension::DecodeIcon(const Extension* extension, | |
| 1623 ExtensionIconSet::Icons icon_size, | |
| 1624 scoped_ptr<SkBitmap>* result) { | |
| 1625 DecodeIcon(extension, icon_size, ExtensionIconSet::MATCH_EXACTLY, result); | |
| 1626 } | |
| 1627 | |
| 1628 // static | |
| 1629 void Extension::DecodeIconFromPath(const FilePath& icon_path, | |
| 1630 ExtensionIconSet::Icons icon_size, | |
| 1631 scoped_ptr<SkBitmap>* result) { | |
| 1632 if (icon_path.empty()) | |
| 1633 return; | |
| 1634 | |
| 1635 std::string file_contents; | |
| 1636 if (!file_util::ReadFileToString(icon_path, &file_contents)) { | |
| 1637 DLOG(ERROR) << "Could not read icon file: " << icon_path.LossyDisplayName(); | |
| 1638 return; | |
| 1639 } | |
| 1640 | |
| 1641 // Decode the image using WebKit's image decoder. | |
| 1642 const unsigned char* data = | |
| 1643 reinterpret_cast<const unsigned char*>(file_contents.data()); | |
| 1644 webkit_glue::ImageDecoder decoder; | |
| 1645 scoped_ptr<SkBitmap> decoded(new SkBitmap()); | |
| 1646 *decoded = decoder.Decode(data, file_contents.length()); | |
| 1647 if (decoded->empty()) { | |
| 1648 DLOG(ERROR) << "Could not decode icon file: " | |
| 1649 << icon_path.LossyDisplayName(); | |
| 1650 return; | |
| 1651 } | |
| 1652 | |
| 1653 if (decoded->width() != icon_size || decoded->height() != icon_size) { | |
| 1654 DLOG(ERROR) << "Icon file has unexpected size: " | |
| 1655 << base::IntToString(decoded->width()) << "x" | |
| 1656 << base::IntToString(decoded->height()); | |
| 1657 return; | |
| 1658 } | |
| 1659 | |
| 1660 result->swap(decoded); | |
| 1661 } | |
| 1662 | |
| 1663 // static | |
| 1664 const SkBitmap& Extension::GetDefaultIcon(bool is_app) { | |
| 1665 if (is_app) { | |
| 1666 return *ResourceBundle::GetSharedInstance().GetBitmapNamed( | |
| 1667 IDR_APP_DEFAULT_ICON); | |
| 1668 } else { | |
| 1669 return *ResourceBundle::GetSharedInstance().GetBitmapNamed( | |
| 1670 IDR_EXTENSION_DEFAULT_ICON); | |
| 1671 } | |
| 1672 } | |
| 1673 | |
| 1674 GURL Extension::GetBaseURLFromExtensionId(const std::string& extension_id) { | |
| 1675 return GURL(std::string(chrome::kExtensionScheme) + | |
| 1676 chrome::kStandardSchemeSeparator + extension_id + "/"); | |
| 1677 } | |
| 1678 | |
| 1679 bool Extension::LoadManifestVersion(string16* error) { | |
| 1680 // Get the original value out of the dictionary so that we can validate it | |
| 1681 // more strictly. | |
| 1682 if (manifest_->value()->HasKey(keys::kManifestVersion)) { | |
| 1683 int manifest_version = 1; | |
| 1684 if (!manifest_->GetInteger(keys::kManifestVersion, &manifest_version) || | |
| 1685 manifest_version < 1) { | |
| 1686 *error = ASCIIToUTF16(errors::kInvalidManifestVersion); | |
| 1687 return false; | |
| 1688 } | |
| 1689 } | |
| 1690 | |
| 1691 manifest_version_ = manifest_->GetManifestVersion(); | |
| 1692 if (creation_flags_ & REQUIRE_MODERN_MANIFEST_VERSION && | |
| 1693 manifest_version_ < kModernManifestVersion && | |
| 1694 !CommandLine::ForCurrentProcess()->HasSwitch( | |
| 1695 switches::kAllowLegacyExtensionManifests)) { | |
| 1696 *error = ASCIIToUTF16(errors::kInvalidManifestVersion); | |
| 1697 return false; | |
| 1698 } | |
| 1699 | |
| 1700 return true; | |
| 1701 } | |
| 1702 | |
| 1703 // static | |
| 1704 bool Extension::InitExtensionID(extensions::Manifest* manifest, | |
| 1705 const FilePath& path, | |
| 1706 const std::string& explicit_id, | |
| 1707 int creation_flags, | |
| 1708 string16* error) { | |
| 1709 if (!explicit_id.empty()) { | |
| 1710 manifest->set_extension_id(explicit_id); | |
| 1711 return true; | |
| 1712 } | |
| 1713 | |
| 1714 if (manifest->HasKey(keys::kPublicKey)) { | |
| 1715 std::string public_key; | |
| 1716 std::string public_key_bytes; | |
| 1717 std::string extension_id; | |
| 1718 if (!manifest->GetString(keys::kPublicKey, &public_key) || | |
| 1719 !ParsePEMKeyBytes(public_key, &public_key_bytes) || | |
| 1720 !GenerateId(public_key_bytes, &extension_id)) { | |
| 1721 *error = ASCIIToUTF16(errors::kInvalidKey); | |
| 1722 return false; | |
| 1723 } | |
| 1724 manifest->set_extension_id(extension_id); | |
| 1725 return true; | |
| 1726 } | |
| 1727 | |
| 1728 if (creation_flags & REQUIRE_KEY) { | |
| 1729 *error = ASCIIToUTF16(errors::kInvalidKey); | |
| 1730 return false; | |
| 1731 } else { | |
| 1732 // If there is a path, we generate the ID from it. This is useful for | |
| 1733 // development mode, because it keeps the ID stable across restarts and | |
| 1734 // reloading the extension. | |
| 1735 std::string extension_id = GenerateIdForPath(path); | |
| 1736 if (extension_id.empty()) { | |
| 1737 NOTREACHED() << "Could not create ID from path."; | |
| 1738 return false; | |
| 1739 } | |
| 1740 manifest->set_extension_id(extension_id); | |
| 1741 return true; | |
| 1742 } | |
| 1743 } | |
| 1744 | |
| 1745 bool Extension::InitFromValue(int flags, string16* error) { | |
| 1746 DCHECK(error); | |
| 1747 | |
| 1748 base::AutoLock auto_lock(runtime_data_lock_); | |
| 1749 | |
| 1750 // Initialize permissions with an empty, default permission set. | |
| 1751 runtime_data_.SetActivePermissions(new ExtensionPermissionSet()); | |
| 1752 optional_permission_set_ = new ExtensionPermissionSet(); | |
| 1753 required_permission_set_ = new ExtensionPermissionSet(); | |
| 1754 | |
| 1755 creation_flags_ = flags; | |
| 1756 | |
| 1757 if (!LoadManifestVersion(error)) | |
| 1758 return false; | |
| 1759 | |
| 1760 // We don't ned to validate because InitExtensionID already did that. | |
| 1761 manifest_->GetString(keys::kPublicKey, &public_key_); | |
| 1762 | |
| 1763 // Initialize the URL. | |
| 1764 extension_url_ = Extension::GetBaseURLFromExtensionId(id()); | |
| 1765 | |
| 1766 // Initialize version. | |
| 1767 std::string version_str; | |
| 1768 if (!manifest_->GetString(keys::kVersion, &version_str)) { | |
| 1769 *error = ASCIIToUTF16(errors::kInvalidVersion); | |
| 1770 return false; | |
| 1771 } | |
| 1772 version_.reset(Version::GetVersionFromString(version_str)); | |
| 1773 if (!version_.get() || | |
| 1774 version_->components().size() > 4) { | |
| 1775 *error = ASCIIToUTF16(errors::kInvalidVersion); | |
| 1776 return false; | |
| 1777 } | |
| 1778 | |
| 1779 // Initialize name. | |
| 1780 string16 localized_name; | |
| 1781 if (!manifest_->GetString(keys::kName, &localized_name)) { | |
| 1782 *error = ASCIIToUTF16(errors::kInvalidName); | |
| 1783 return false; | |
| 1784 } | |
| 1785 base::i18n::AdjustStringForLocaleDirection(&localized_name); | |
| 1786 name_ = UTF16ToUTF8(localized_name); | |
| 1787 | |
| 1788 // Load App settings. LoadExtent at least has to be done before | |
| 1789 // ParsePermissions(), because the valid permissions depend on what type of | |
| 1790 // package this is. | |
| 1791 if (is_app() && | |
| 1792 (!LoadExtent(keys::kWebURLs, &extent_,errors::kInvalidWebURLs, | |
| 1793 errors::kInvalidWebURL, error) || | |
| 1794 !LoadLaunchURL(error) || | |
| 1795 !LoadLaunchContainer(error))) { | |
| 1796 return false; | |
| 1797 } | |
| 1798 | 2155 |
| 1799 if (is_platform_app()) { | 2156 if (is_platform_app()) { |
| 1800 if (launch_container() != extension_misc::LAUNCH_SHELL) { | 2157 if (launch_container() != extension_misc::LAUNCH_SHELL) { |
| 1801 *error = ASCIIToUTF16(errors::kInvalidLaunchContainerForPlatform); | 2158 *error = ASCIIToUTF16(errors::kInvalidLaunchContainerForPlatform); |
| 1802 return false; | 2159 return false; |
| 1803 } | 2160 } |
| 1804 } else if (launch_container() == extension_misc::LAUNCH_SHELL) { | 2161 } else if (launch_container() == extension_misc::LAUNCH_SHELL) { |
| 1805 *error = ASCIIToUTF16(errors::kInvalidLaunchContainerForNonPlatform); | 2162 *error = ASCIIToUTF16(errors::kInvalidLaunchContainerForNonPlatform); |
| 1806 return false; | 2163 return false; |
| 1807 } | 2164 } |
| 1808 | 2165 |
| 1809 // Initialize the permissions (optional). | 2166 return true; |
| 2167 } | |
| 2168 | |
| 2169 bool Extension::LoadExtent(const char* key, | |
| 2170 URLPatternSet* extent, | |
| 2171 const char* list_error, | |
| 2172 const char* value_error, | |
| 2173 string16* error) { | |
| 2174 Value* temp = NULL; | |
| 2175 if (!manifest_->Get(key, &temp)) | |
| 2176 return true; | |
| 2177 | |
| 2178 if (temp->GetType() != Value::TYPE_LIST) { | |
| 2179 *error = ASCIIToUTF16(list_error); | |
| 2180 return false; | |
| 2181 } | |
| 2182 | |
| 2183 ListValue* pattern_list = static_cast<ListValue*>(temp); | |
| 2184 for (size_t i = 0; i < pattern_list->GetSize(); ++i) { | |
| 2185 std::string pattern_string; | |
| 2186 if (!pattern_list->GetString(i, &pattern_string)) { | |
| 2187 *error = ExtensionErrorUtils::FormatErrorMessageUTF16(value_error, | |
| 2188 base::UintToString(i), | |
| 2189 errors::kExpectString); | |
| 2190 return false; | |
| 2191 } | |
| 2192 | |
| 2193 URLPattern pattern(kValidWebExtentSchemes); | |
| 2194 URLPattern::ParseResult parse_result = pattern.Parse(pattern_string); | |
| 2195 if (parse_result == URLPattern::PARSE_ERROR_EMPTY_PATH) { | |
| 2196 pattern_string += "/"; | |
| 2197 parse_result = pattern.Parse(pattern_string); | |
| 2198 } | |
| 2199 | |
| 2200 if (parse_result != URLPattern::PARSE_SUCCESS) { | |
| 2201 *error = ExtensionErrorUtils::FormatErrorMessageUTF16( | |
| 2202 value_error, | |
| 2203 base::UintToString(i), | |
| 2204 URLPattern::GetParseResultString(parse_result)); | |
| 2205 return false; | |
| 2206 } | |
| 2207 | |
| 2208 // Do not allow authors to claim "<all_urls>". | |
| 2209 if (pattern.match_all_urls()) { | |
| 2210 *error = ExtensionErrorUtils::FormatErrorMessageUTF16( | |
| 2211 value_error, | |
| 2212 base::UintToString(i), | |
| 2213 errors::kCannotClaimAllURLsInExtent); | |
| 2214 return false; | |
| 2215 } | |
| 2216 | |
| 2217 // Do not allow authors to claim "*" for host. | |
| 2218 if (pattern.host().empty()) { | |
| 2219 *error = ExtensionErrorUtils::FormatErrorMessageUTF16( | |
| 2220 value_error, | |
| 2221 base::UintToString(i), | |
| 2222 errors::kCannotClaimAllHostsInExtent); | |
| 2223 return false; | |
| 2224 } | |
| 2225 | |
| 2226 // We do not allow authors to put wildcards in their paths. Instead, we | |
| 2227 // imply one at the end. | |
| 2228 if (pattern.path().find('*') != std::string::npos) { | |
| 2229 *error = ExtensionErrorUtils::FormatErrorMessageUTF16( | |
| 2230 value_error, | |
| 2231 base::UintToString(i), | |
| 2232 errors::kNoWildCardsInPaths); | |
| 2233 return false; | |
| 2234 } | |
| 2235 pattern.SetPath(pattern.path() + '*'); | |
| 2236 | |
| 2237 extent->AddPattern(pattern); | |
| 2238 } | |
| 2239 | |
| 2240 return true; | |
| 2241 } | |
| 2242 | |
| 2243 bool Extension::LoadLaunchURL(string16* error) { | |
| 2244 Value* temp = NULL; | |
| 2245 | |
| 2246 // launch URL can be either local (to chrome-extension:// root) or an absolute | |
| 2247 // web URL. | |
| 2248 if (manifest_->Get(keys::kLaunchLocalPath, &temp)) { | |
| 2249 if (manifest_->Get(keys::kLaunchWebURL, NULL)) { | |
| 2250 *error = ASCIIToUTF16(errors::kLaunchPathAndURLAreExclusive); | |
| 2251 return false; | |
| 2252 } | |
| 2253 | |
| 2254 if (manifest_->Get(keys::kWebURLs, NULL)) { | |
| 2255 *error = ASCIIToUTF16(errors::kLaunchPathAndExtentAreExclusive); | |
| 2256 return false; | |
| 2257 } | |
| 2258 | |
| 2259 std::string launch_path; | |
| 2260 if (!temp->GetAsString(&launch_path)) { | |
| 2261 *error = ExtensionErrorUtils::FormatErrorMessageUTF16( | |
| 2262 errors::kInvalidLaunchValue, | |
| 2263 keys::kLaunchLocalPath); | |
| 2264 return false; | |
| 2265 } | |
| 2266 | |
| 2267 // Ensure the launch path is a valid relative URL. | |
| 2268 GURL resolved = url().Resolve(launch_path); | |
| 2269 if (!resolved.is_valid() || resolved.GetOrigin() != url()) { | |
| 2270 *error = ExtensionErrorUtils::FormatErrorMessageUTF16( | |
| 2271 errors::kInvalidLaunchValue, | |
| 2272 keys::kLaunchLocalPath); | |
| 2273 return false; | |
| 2274 } | |
| 2275 | |
| 2276 launch_local_path_ = launch_path; | |
| 2277 } else if (manifest_->Get(keys::kLaunchWebURL, &temp)) { | |
| 2278 std::string launch_url; | |
| 2279 if (!temp->GetAsString(&launch_url)) { | |
| 2280 *error = ExtensionErrorUtils::FormatErrorMessageUTF16( | |
| 2281 errors::kInvalidLaunchValue, | |
| 2282 keys::kLaunchWebURL); | |
| 2283 return false; | |
| 2284 } | |
| 2285 | |
| 2286 // Ensure the launch URL is a valid absolute URL and web extent scheme. | |
| 2287 GURL url(launch_url); | |
| 2288 URLPattern pattern(kValidWebExtentSchemes); | |
| 2289 if (!url.is_valid() || !pattern.SetScheme(url.scheme())) { | |
| 2290 *error = ExtensionErrorUtils::FormatErrorMessageUTF16( | |
| 2291 errors::kInvalidLaunchValue, | |
| 2292 keys::kLaunchWebURL); | |
| 2293 return false; | |
| 2294 } | |
| 2295 | |
| 2296 launch_web_url_ = launch_url; | |
| 2297 } else if (is_app()) { | |
| 2298 *error = ASCIIToUTF16(errors::kLaunchURLRequired); | |
| 2299 return false; | |
| 2300 } | |
| 2301 | |
| 2302 // If there is no extent, we default the extent based on the launch URL. | |
| 2303 if (web_extent().is_empty() && !launch_web_url().empty()) { | |
| 2304 GURL launch_url(launch_web_url()); | |
| 2305 URLPattern pattern(kValidWebExtentSchemes); | |
| 2306 if (!pattern.SetScheme("*")) { | |
| 2307 *error = ExtensionErrorUtils::FormatErrorMessageUTF16( | |
| 2308 errors::kInvalidLaunchValue, | |
| 2309 keys::kLaunchWebURL); | |
| 2310 return false; | |
| 2311 } | |
| 2312 pattern.SetHost(launch_url.host()); | |
| 2313 pattern.SetPath("/*"); | |
| 2314 extent_.AddPattern(pattern); | |
| 2315 } | |
| 2316 | |
| 2317 // In order for the --apps-gallery-url switch to work with the gallery | |
| 2318 // process isolation, we must insert any provided value into the component | |
| 2319 // app's launch url and web extent. | |
| 2320 if (id() == extension_misc::kWebStoreAppId) { | |
| 2321 std::string gallery_url_str = CommandLine::ForCurrentProcess()-> | |
| 2322 GetSwitchValueASCII(switches::kAppsGalleryURL); | |
| 2323 | |
| 2324 // Empty string means option was not used. | |
| 2325 if (!gallery_url_str.empty()) { | |
| 2326 GURL gallery_url(gallery_url_str); | |
| 2327 OverrideLaunchUrl(gallery_url); | |
| 2328 } | |
| 2329 } else if (id() == extension_misc::kCloudPrintAppId) { | |
| 2330 // In order for the --cloud-print-service switch to work, we must update | |
| 2331 // the launch URL and web extent. | |
| 2332 // TODO(sanjeevr): Ideally we want to use CloudPrintURL here but that is | |
| 2333 // currently under chrome/browser. | |
| 2334 const CommandLine& command_line = *CommandLine::ForCurrentProcess(); | |
| 2335 GURL cloud_print_service_url = GURL(command_line.GetSwitchValueASCII( | |
| 2336 switches::kCloudPrintServiceURL)); | |
| 2337 if (!cloud_print_service_url.is_empty()) { | |
| 2338 std::string path( | |
| 2339 cloud_print_service_url.path() + "/enable_chrome_connector"); | |
| 2340 GURL::Replacements replacements; | |
| 2341 replacements.SetPathStr(path); | |
| 2342 GURL cloud_print_enable_connector_url = | |
| 2343 cloud_print_service_url.ReplaceComponents(replacements); | |
| 2344 OverrideLaunchUrl(cloud_print_enable_connector_url); | |
| 2345 } | |
| 2346 } | |
| 2347 return true; | |
| 2348 } | |
| 2349 | |
| 2350 bool ReadLaunchDimension(const extensions::Manifest* manifest, | |
| 2351 const char* key, | |
| 2352 int* target, | |
| 2353 bool is_valid_container, | |
| 2354 string16* error) { | |
| 2355 Value* temp = NULL; | |
| 2356 if (manifest->Get(key, &temp)) { | |
| 2357 if (!is_valid_container) { | |
| 2358 *error = ExtensionErrorUtils::FormatErrorMessageUTF16( | |
| 2359 errors::kInvalidLaunchValueContainer, | |
| 2360 key); | |
| 2361 return false; | |
| 2362 } | |
| 2363 if (!temp->GetAsInteger(target) || *target < 0) { | |
| 2364 *target = 0; | |
| 2365 *error = ExtensionErrorUtils::FormatErrorMessageUTF16( | |
| 2366 errors::kInvalidLaunchValue, | |
| 2367 key); | |
| 2368 return false; | |
| 2369 } | |
| 2370 } | |
| 2371 return true; | |
| 2372 } | |
| 2373 | |
| 2374 bool Extension::LoadLaunchContainer(string16* error) { | |
| 2375 Value* temp = NULL; | |
| 2376 if (!manifest_->Get(keys::kLaunchContainer, &temp)) | |
| 2377 return true; | |
| 2378 | |
| 2379 std::string launch_container_string; | |
| 2380 if (!temp->GetAsString(&launch_container_string)) { | |
| 2381 *error = ASCIIToUTF16(errors::kInvalidLaunchContainer); | |
| 2382 return false; | |
| 2383 } | |
| 2384 | |
| 2385 if (launch_container_string == values::kLaunchContainerShell) { | |
| 2386 launch_container_ = extension_misc::LAUNCH_SHELL; | |
| 2387 } else if (launch_container_string == values::kLaunchContainerPanel) { | |
| 2388 launch_container_ = extension_misc::LAUNCH_PANEL; | |
| 2389 } else if (launch_container_string == values::kLaunchContainerTab) { | |
| 2390 launch_container_ = extension_misc::LAUNCH_TAB; | |
| 2391 } else { | |
| 2392 *error = ASCIIToUTF16(errors::kInvalidLaunchContainer); | |
| 2393 return false; | |
| 2394 } | |
| 2395 | |
| 2396 bool can_specify_initial_size = | |
| 2397 launch_container() == extension_misc::LAUNCH_PANEL || | |
| 2398 launch_container() == extension_misc::LAUNCH_WINDOW || | |
| 2399 launch_container() == extension_misc::LAUNCH_SHELL; | |
| 2400 | |
| 2401 // Validate the container width if present. | |
| 2402 if (!ReadLaunchDimension(manifest_, | |
| 2403 keys::kLaunchWidth, | |
| 2404 &launch_width_, | |
| 2405 can_specify_initial_size, | |
| 2406 error)) | |
| 2407 return false; | |
| 2408 | |
| 2409 // Validate container height if present. | |
| 2410 if (!ReadLaunchDimension(manifest_, | |
| 2411 keys::kLaunchHeight, | |
| 2412 &launch_height_, | |
| 2413 can_specify_initial_size, | |
| 2414 error)) | |
| 2415 return false; | |
| 2416 | |
| 2417 bool can_specify_size_range = | |
| 2418 launch_container() == extension_misc::LAUNCH_SHELL; | |
| 2419 | |
| 2420 // Validate min size if present. | |
| 2421 if (!ReadLaunchDimension(manifest_, | |
| 2422 keys::kLaunchMinWidth, | |
| 2423 &launch_min_width_, | |
| 2424 can_specify_size_range, | |
| 2425 error)) | |
| 2426 return false; | |
| 2427 if (!ReadLaunchDimension(manifest_, | |
| 2428 keys::kLaunchMinHeight, | |
| 2429 &launch_min_height_, | |
| 2430 can_specify_size_range, | |
| 2431 error)) | |
| 2432 return false; | |
| 2433 | |
| 2434 if (launch_container() == extension_misc::LAUNCH_SHELL) { | |
| 2435 if (!manifest_->Get(keys::kLaunchWidth, &temp)) { | |
| 2436 *error = ExtensionErrorUtils::FormatErrorMessageUTF16( | |
| 2437 errors::kInvalidLaunchValue, | |
| 2438 keys::kLaunchWidth); | |
| 2439 return false; | |
| 2440 } | |
| 2441 if (!manifest_->Get(keys::kLaunchHeight, &temp)) { | |
| 2442 *error = ExtensionErrorUtils::FormatErrorMessageUTF16( | |
| 2443 errors::kInvalidLaunchValue, | |
| 2444 keys::kLaunchHeight); | |
| 2445 return false; | |
| 2446 } | |
| 2447 } | |
| 2448 | |
| 2449 return true; | |
| 2450 } | |
| 2451 | |
| 2452 | |
| 2453 bool Extension::LoadAppIsolation(string16* error) { | |
| 2454 Value* temp = NULL; | |
| 2455 if (!manifest_->Get(keys::kIsolation, &temp)) | |
| 2456 return true; | |
| 2457 | |
| 2458 if (temp->GetType() != Value::TYPE_LIST) { | |
| 2459 *error = ASCIIToUTF16(errors::kInvalidIsolation); | |
| 2460 return false; | |
| 2461 } | |
| 2462 | |
| 2463 ListValue* isolation_list = static_cast<ListValue*>(temp); | |
| 2464 for (size_t i = 0; i < isolation_list->GetSize(); ++i) { | |
| 2465 std::string isolation_string; | |
| 2466 if (!isolation_list->GetString(i, &isolation_string)) { | |
| 2467 *error = ExtensionErrorUtils::FormatErrorMessageUTF16( | |
| 2468 errors::kInvalidIsolationValue, | |
| 2469 base::UintToString(i)); | |
| 2470 return false; | |
| 2471 } | |
| 2472 | |
| 2473 // Check for isolated storage. | |
| 2474 if (isolation_string == values::kIsolatedStorage) { | |
| 2475 is_storage_isolated_ = true; | |
| 2476 } else { | |
| 2477 DLOG(WARNING) << "Did not recognize isolation type: " | |
| 2478 << isolation_string; | |
| 2479 } | |
| 2480 } | |
| 2481 return true; | |
| 2482 } | |
| 2483 | |
| 2484 bool Extension::LoadThemeFeatures(string16* error) { | |
| 2485 DictionaryValue* theme_value = NULL; | |
| 2486 if (!manifest_->GetDictionary(keys::kTheme, &theme_value)) { | |
| 2487 *error = ASCIIToUTF16(errors::kInvalidTheme); | |
| 2488 return false; | |
| 2489 } | |
| 2490 if (!LoadThemeImages(theme_value, error)) | |
| 2491 return false; | |
| 2492 if (!LoadThemeColors(theme_value, error)) | |
| 2493 return false; | |
| 2494 if (!LoadThemeTints(theme_value, error)) | |
| 2495 return false; | |
| 2496 if (!LoadThemeDisplayProperties(theme_value, error)) | |
| 2497 return false; | |
| 2498 | |
| 2499 return true; | |
| 2500 } | |
| 2501 | |
| 2502 bool Extension::LoadThemeImages(const DictionaryValue* theme_value, | |
| 2503 string16* error) { | |
| 2504 DictionaryValue* images_value = NULL; | |
| 2505 if (theme_value->GetDictionary(keys::kThemeImages, &images_value)) { | |
| 2506 // Validate that the images are all strings | |
| 2507 for (DictionaryValue::key_iterator iter = images_value->begin_keys(); | |
| 2508 iter != images_value->end_keys(); ++iter) { | |
| 2509 std::string val; | |
| 2510 if (!images_value->GetString(*iter, &val)) { | |
| 2511 *error = ASCIIToUTF16(errors::kInvalidThemeImages); | |
| 2512 return false; | |
| 2513 } | |
| 2514 } | |
| 2515 theme_images_.reset(images_value->DeepCopy()); | |
| 2516 } | |
| 2517 return true; | |
| 2518 } | |
| 2519 | |
| 2520 bool Extension::LoadThemeColors(const DictionaryValue* theme_value, | |
| 2521 string16* error) { | |
| 2522 DictionaryValue* colors_value = NULL; | |
| 2523 if (theme_value->GetDictionary(keys::kThemeColors, &colors_value)) { | |
| 2524 // Validate that the colors are RGB or RGBA lists | |
| 2525 for (DictionaryValue::key_iterator iter = colors_value->begin_keys(); | |
| 2526 iter != colors_value->end_keys(); ++iter) { | |
| 2527 ListValue* color_list = NULL; | |
| 2528 double alpha = 0.0; | |
| 2529 int color = 0; | |
| 2530 // The color must be a list | |
| 2531 if (!colors_value->GetListWithoutPathExpansion(*iter, &color_list) || | |
| 2532 // And either 3 items (RGB) or 4 (RGBA) | |
| 2533 ((color_list->GetSize() != 3) && | |
| 2534 ((color_list->GetSize() != 4) || | |
| 2535 // For RGBA, the fourth item must be a real or int alpha value. | |
| 2536 // Note that GetDouble() can get an integer value. | |
| 2537 !color_list->GetDouble(3, &alpha))) || | |
| 2538 // For both RGB and RGBA, the first three items must be ints (R,G,B) | |
| 2539 !color_list->GetInteger(0, &color) || | |
| 2540 !color_list->GetInteger(1, &color) || | |
| 2541 !color_list->GetInteger(2, &color)) { | |
| 2542 *error = ASCIIToUTF16(errors::kInvalidThemeColors); | |
| 2543 return false; | |
| 2544 } | |
| 2545 } | |
| 2546 theme_colors_.reset(colors_value->DeepCopy()); | |
| 2547 } | |
| 2548 return true; | |
| 2549 } | |
| 2550 | |
| 2551 bool Extension::LoadThemeTints(const DictionaryValue* theme_value, | |
| 2552 string16* error) { | |
| 2553 DictionaryValue* tints_value = NULL; | |
| 2554 if (theme_value->GetDictionary(keys::kThemeTints, &tints_value)) { | |
| 2555 // Validate that the tints are all reals. | |
| 2556 for (DictionaryValue::key_iterator iter = tints_value->begin_keys(); | |
| 2557 iter != tints_value->end_keys(); ++iter) { | |
| 2558 ListValue* tint_list = NULL; | |
| 2559 double v = 0.0; | |
| 2560 if (!tints_value->GetListWithoutPathExpansion(*iter, &tint_list) || | |
| 2561 tint_list->GetSize() != 3 || | |
| 2562 !tint_list->GetDouble(0, &v) || | |
| 2563 !tint_list->GetDouble(1, &v) || | |
| 2564 !tint_list->GetDouble(2, &v)) { | |
| 2565 *error = ASCIIToUTF16(errors::kInvalidThemeTints); | |
| 2566 return false; | |
| 2567 } | |
| 2568 } | |
| 2569 theme_tints_.reset(tints_value->DeepCopy()); | |
| 2570 } | |
| 2571 return true; | |
| 2572 } | |
| 2573 | |
| 2574 bool Extension::LoadThemeDisplayProperties(const DictionaryValue* theme_value, | |
| 2575 string16* error) { | |
| 2576 DictionaryValue* display_properties_value = NULL; | |
| 2577 if (theme_value->GetDictionary(keys::kThemeDisplayProperties, | |
| 2578 &display_properties_value)) { | |
| 2579 theme_display_properties_.reset( | |
| 2580 display_properties_value->DeepCopy()); | |
| 2581 } | |
| 2582 return true; | |
| 2583 } | |
| 2584 | |
| 2585 // static | |
| 2586 bool Extension::IsTrustedId(const std::string& id) { | |
| 2587 // See http://b/4946060 for more details. | |
| 2588 return id == std::string("nckgahadagoaajjgafhacjanaoiihapd"); | |
| 2589 } | |
| 2590 | |
| 2591 Extension::Extension(const FilePath& path, | |
| 2592 scoped_ptr<extensions::Manifest> manifest) | |
| 2593 : manifest_version_(0), | |
| 2594 incognito_split_mode_(false), | |
| 2595 offline_enabled_(false), | |
| 2596 converted_from_user_script_(false), | |
| 2597 background_page_persists_(true), | |
| 2598 manifest_(manifest.release()), | |
| 2599 is_storage_isolated_(false), | |
| 2600 launch_container_(extension_misc::LAUNCH_TAB), | |
| 2601 launch_width_(0), | |
| 2602 launch_height_(0), | |
| 2603 launch_min_width_(0), | |
| 2604 launch_min_height_(0), | |
| 2605 wants_file_access_(false), | |
| 2606 creation_flags_(0) { | |
| 2607 DCHECK(path.empty() || path.IsAbsolute()); | |
| 2608 path_ = MaybeNormalizePath(path); | |
| 2609 } | |
| 2610 | |
| 2611 Extension::~Extension() { | |
| 2612 if (manifest_) | |
| 2613 delete manifest_; | |
| 2614 } | |
| 2615 | |
| 2616 ExtensionResource Extension::GetResource( | |
| 2617 const std::string& relative_path) const { | |
| 2618 #if defined(OS_POSIX) | |
| 2619 FilePath relative_file_path(relative_path); | |
| 2620 #elif defined(OS_WIN) | |
| 2621 FilePath relative_file_path(UTF8ToWide(relative_path)); | |
| 2622 #endif | |
| 2623 return ExtensionResource(id(), path(), relative_file_path); | |
| 2624 } | |
| 2625 | |
| 2626 ExtensionResource Extension::GetResource( | |
| 2627 const FilePath& relative_file_path) const { | |
| 2628 return ExtensionResource(id(), path(), relative_file_path); | |
| 2629 } | |
| 2630 | |
| 2631 // TODO(rafaelw): Move ParsePEMKeyBytes, ProducePEM & FormatPEMForOutput to a | |
| 2632 // util class in base: | |
| 2633 // http://code.google.com/p/chromium/issues/detail?id=13572 | |
| 2634 bool Extension::ParsePEMKeyBytes(const std::string& input, | |
| 2635 std::string* output) { | |
| 2636 DCHECK(output); | |
| 2637 if (!output) | |
| 2638 return false; | |
| 2639 if (input.length() == 0) | |
| 2640 return false; | |
| 2641 | |
| 2642 std::string working = input; | |
| 2643 if (StartsWithASCII(working, kKeyBeginHeaderMarker, true)) { | |
| 2644 working = CollapseWhitespaceASCII(working, true); | |
| 2645 size_t header_pos = working.find(kKeyInfoEndMarker, | |
| 2646 sizeof(kKeyBeginHeaderMarker) - 1); | |
| 2647 if (header_pos == std::string::npos) | |
| 2648 return false; | |
| 2649 size_t start_pos = header_pos + sizeof(kKeyInfoEndMarker) - 1; | |
| 2650 size_t end_pos = working.rfind(kKeyBeginFooterMarker); | |
| 2651 if (end_pos == std::string::npos) | |
| 2652 return false; | |
| 2653 if (start_pos >= end_pos) | |
| 2654 return false; | |
| 2655 | |
| 2656 working = working.substr(start_pos, end_pos - start_pos); | |
| 2657 if (working.length() == 0) | |
| 2658 return false; | |
| 2659 } | |
| 2660 | |
| 2661 return base::Base64Decode(working, output); | |
| 2662 } | |
| 2663 | |
| 2664 bool Extension::ProducePEM(const std::string& input, std::string* output) { | |
| 2665 DCHECK(output); | |
| 2666 if (input.length() == 0) | |
| 2667 return false; | |
| 2668 | |
| 2669 return base::Base64Encode(input, output); | |
| 2670 } | |
| 2671 | |
| 2672 bool Extension::FormatPEMForFileOutput(const std::string& input, | |
| 2673 std::string* output, | |
| 2674 bool is_public) { | |
| 2675 DCHECK(output); | |
| 2676 if (input.length() == 0) | |
| 2677 return false; | |
| 2678 *output = ""; | |
| 2679 output->append(kKeyBeginHeaderMarker); | |
| 2680 output->append(" "); | |
| 2681 output->append(is_public ? kPublic : kPrivate); | |
| 2682 output->append(" "); | |
| 2683 output->append(kKeyInfoEndMarker); | |
| 2684 output->append("\n"); | |
| 2685 for (size_t i = 0; i < input.length(); ) { | |
| 2686 int slice = std::min<int>(input.length() - i, kPEMOutputColumns); | |
| 2687 output->append(input.substr(i, slice)); | |
| 2688 output->append("\n"); | |
| 2689 i += slice; | |
| 2690 } | |
| 2691 output->append(kKeyBeginFooterMarker); | |
| 2692 output->append(" "); | |
| 2693 output->append(is_public ? kPublic : kPrivate); | |
| 2694 output->append(" "); | |
| 2695 output->append(kKeyInfoEndMarker); | |
| 2696 output->append("\n"); | |
| 2697 | |
| 2698 return true; | |
| 2699 } | |
| 2700 | |
| 2701 // static | |
| 2702 void Extension::DecodeIcon(const Extension* extension, | |
| 2703 ExtensionIconSet::Icons preferred_icon_size, | |
| 2704 ExtensionIconSet::MatchType match_type, | |
| 2705 scoped_ptr<SkBitmap>* result) { | |
| 2706 std::string path = extension->icons().Get(preferred_icon_size, match_type); | |
| 2707 ExtensionIconSet::Icons size = extension->icons().GetIconSizeFromPath(path); | |
| 2708 ExtensionResource icon_resource = extension->GetResource(path); | |
| 2709 DecodeIconFromPath(icon_resource.GetFilePath(), size, result); | |
| 2710 } | |
| 2711 | |
| 2712 // static | |
| 2713 void Extension::DecodeIcon(const Extension* extension, | |
| 2714 ExtensionIconSet::Icons icon_size, | |
| 2715 scoped_ptr<SkBitmap>* result) { | |
| 2716 DecodeIcon(extension, icon_size, ExtensionIconSet::MATCH_EXACTLY, result); | |
| 2717 } | |
| 2718 | |
| 2719 // static | |
| 2720 void Extension::DecodeIconFromPath(const FilePath& icon_path, | |
| 2721 ExtensionIconSet::Icons icon_size, | |
| 2722 scoped_ptr<SkBitmap>* result) { | |
| 2723 if (icon_path.empty()) | |
| 2724 return; | |
| 2725 | |
| 2726 std::string file_contents; | |
| 2727 if (!file_util::ReadFileToString(icon_path, &file_contents)) { | |
| 2728 DLOG(ERROR) << "Could not read icon file: " << icon_path.LossyDisplayName(); | |
| 2729 return; | |
| 2730 } | |
| 2731 | |
| 2732 // Decode the image using WebKit's image decoder. | |
| 2733 const unsigned char* data = | |
| 2734 reinterpret_cast<const unsigned char*>(file_contents.data()); | |
| 2735 webkit_glue::ImageDecoder decoder; | |
| 2736 scoped_ptr<SkBitmap> decoded(new SkBitmap()); | |
| 2737 *decoded = decoder.Decode(data, file_contents.length()); | |
| 2738 if (decoded->empty()) { | |
| 2739 DLOG(ERROR) << "Could not decode icon file: " | |
| 2740 << icon_path.LossyDisplayName(); | |
| 2741 return; | |
| 2742 } | |
| 2743 | |
| 2744 if (decoded->width() != icon_size || decoded->height() != icon_size) { | |
| 2745 DLOG(ERROR) << "Icon file has unexpected size: " | |
| 2746 << base::IntToString(decoded->width()) << "x" | |
| 2747 << base::IntToString(decoded->height()); | |
| 2748 return; | |
| 2749 } | |
| 2750 | |
| 2751 result->swap(decoded); | |
| 2752 } | |
| 2753 | |
| 2754 // static | |
| 2755 const SkBitmap& Extension::GetDefaultIcon(bool is_app) { | |
| 2756 if (is_app) { | |
| 2757 return *ResourceBundle::GetSharedInstance().GetBitmapNamed( | |
| 2758 IDR_APP_DEFAULT_ICON); | |
| 2759 } else { | |
| 2760 return *ResourceBundle::GetSharedInstance().GetBitmapNamed( | |
| 2761 IDR_EXTENSION_DEFAULT_ICON); | |
| 2762 } | |
| 2763 } | |
| 2764 | |
| 2765 GURL Extension::GetBaseURLFromExtensionId(const std::string& extension_id) { | |
|
Yoyo Zhou
2012/03/01 01:17:25
Put a
// static
before this.
| |
| 2766 return GURL(std::string(chrome::kExtensionScheme) + | |
| 2767 chrome::kStandardSchemeSeparator + extension_id + "/"); | |
| 2768 } | |
| 2769 | |
| 2770 bool Extension::InitFromValue(int flags, string16* error) { | |
| 2771 DCHECK(error); | |
| 2772 | |
| 2773 base::AutoLock auto_lock(runtime_data_lock_); | |
| 2774 | |
| 2775 // Initialize permissions with an empty, default permission set. | |
| 2776 runtime_data_.SetActivePermissions(new ExtensionPermissionSet()); | |
| 2777 optional_permission_set_ = new ExtensionPermissionSet(); | |
| 2778 required_permission_set_ = new ExtensionPermissionSet(); | |
| 2779 | |
| 2780 creation_flags_ = flags; | |
| 2781 | |
| 2782 if (!LoadManifestVersion(error)) | |
| 2783 return false; | |
| 2784 | |
| 2785 // We don't ned to validate because InitExtensionID already did that. | |
| 2786 manifest_->GetString(keys::kPublicKey, &public_key_); | |
| 2787 | |
| 2788 // Initialize permissions with an empty, default permission set. | |
| 2789 runtime_data_.SetActivePermissions(new ExtensionPermissionSet()); | |
| 2790 optional_permission_set_ = new ExtensionPermissionSet(); | |
| 2791 required_permission_set_ = new ExtensionPermissionSet(); | |
| 2792 | |
| 2793 if (!LoadSharedFeatures(flags, error)) | |
|
Yoyo Zhou
2012/03/01 01:17:25
In this and in LoadExtensionFeatures and whatever
| |
| 2794 return false; | |
| 2795 | |
| 2796 if (is_app() && !LoadAppFeatures(error)) | |
| 2797 return false; | |
| 2798 | |
| 1810 ExtensionAPIPermissionSet api_permissions; | 2799 ExtensionAPIPermissionSet api_permissions; |
| 1811 URLPatternSet host_permissions; | 2800 URLPatternSet host_permissions; |
| 1812 if (!ParsePermissions(keys::kPermissions, | 2801 if (!ParsePermissions(keys::kPermissions, |
| 1813 flags, | 2802 flags, |
| 1814 error, | 2803 error, |
| 1815 &api_permissions, | 2804 &api_permissions, |
| 1816 &host_permissions)) { | 2805 &host_permissions)) { |
| 1817 return false; | 2806 return false; |
| 1818 } | 2807 } |
| 1819 | 2808 |
| 1820 // Initialize the optional permissions (optional). | |
| 1821 ExtensionAPIPermissionSet optional_api_permissions; | 2809 ExtensionAPIPermissionSet optional_api_permissions; |
| 1822 URLPatternSet optional_host_permissions; | 2810 URLPatternSet optional_host_permissions; |
| 1823 if (!ParsePermissions(keys::kOptionalPermissions, | 2811 if (!ParsePermissions(keys::kOptionalPermissions, |
| 1824 flags, | 2812 flags, |
| 1825 error, | 2813 error, |
| 1826 &optional_api_permissions, | 2814 &optional_api_permissions, |
| 1827 &optional_host_permissions)) { | 2815 &optional_host_permissions)) { |
| 1828 return false; | 2816 return false; |
| 1829 } | 2817 } |
| 1830 | 2818 |
| 1831 // Initialize description (if present). | |
| 1832 if (manifest_->HasKey(keys::kDescription)) { | |
| 1833 if (!manifest_->GetString(keys::kDescription, &description_)) { | |
| 1834 *error = ASCIIToUTF16(errors::kInvalidDescription); | |
| 1835 return false; | |
| 1836 } | |
| 1837 } | |
| 1838 | |
| 1839 // Initialize homepage url (if present). | |
| 1840 if (manifest_->HasKey(keys::kHomepageURL)) { | |
| 1841 std::string tmp; | |
| 1842 if (!manifest_->GetString(keys::kHomepageURL, &tmp)) { | |
| 1843 *error = ExtensionErrorUtils::FormatErrorMessageUTF16( | |
| 1844 errors::kInvalidHomepageURL, ""); | |
| 1845 return false; | |
| 1846 } | |
| 1847 homepage_url_ = GURL(tmp); | |
| 1848 if (!homepage_url_.is_valid() || | |
| 1849 (!homepage_url_.SchemeIs("http") && | |
| 1850 !homepage_url_.SchemeIs("https"))) { | |
| 1851 *error = ExtensionErrorUtils::FormatErrorMessageUTF16( | |
| 1852 errors::kInvalidHomepageURL, tmp); | |
| 1853 return false; | |
| 1854 } | |
| 1855 } | |
| 1856 | |
| 1857 // Initialize update url (if present). | |
| 1858 if (manifest_->HasKey(keys::kUpdateURL)) { | |
| 1859 std::string tmp; | |
| 1860 if (!manifest_->GetString(keys::kUpdateURL, &tmp)) { | |
| 1861 *error = ExtensionErrorUtils::FormatErrorMessageUTF16( | |
| 1862 errors::kInvalidUpdateURL, ""); | |
| 1863 return false; | |
| 1864 } | |
| 1865 update_url_ = GURL(tmp); | |
| 1866 if (!update_url_.is_valid() || | |
| 1867 update_url_.has_ref()) { | |
| 1868 *error = ExtensionErrorUtils::FormatErrorMessageUTF16( | |
| 1869 errors::kInvalidUpdateURL, tmp); | |
| 1870 return false; | |
| 1871 } | |
| 1872 } | |
| 1873 | |
| 1874 // Validate minimum Chrome version (if present). We don't need to store this, | |
| 1875 // since the extension is not valid if it is incorrect. | |
| 1876 if (manifest_->HasKey(keys::kMinimumChromeVersion)) { | |
| 1877 std::string minimum_version_string; | |
| 1878 if (!manifest_->GetString(keys::kMinimumChromeVersion, | |
| 1879 &minimum_version_string)) { | |
| 1880 *error = ASCIIToUTF16(errors::kInvalidMinimumChromeVersion); | |
| 1881 return false; | |
| 1882 } | |
| 1883 | |
| 1884 scoped_ptr<Version> minimum_version( | |
| 1885 Version::GetVersionFromString(minimum_version_string)); | |
| 1886 if (!minimum_version.get()) { | |
| 1887 *error = ASCIIToUTF16(errors::kInvalidMinimumChromeVersion); | |
| 1888 return false; | |
| 1889 } | |
| 1890 | |
| 1891 chrome::VersionInfo current_version_info; | |
| 1892 if (!current_version_info.is_valid()) { | |
| 1893 NOTREACHED(); | |
| 1894 return false; | |
| 1895 } | |
| 1896 | |
| 1897 scoped_ptr<Version> current_version( | |
| 1898 Version::GetVersionFromString(current_version_info.Version())); | |
| 1899 if (!current_version.get()) { | |
| 1900 DCHECK(false); | |
| 1901 return false; | |
| 1902 } | |
| 1903 | |
| 1904 if (current_version->CompareTo(*minimum_version) < 0) { | |
| 1905 *error = ExtensionErrorUtils::FormatErrorMessageUTF16( | |
| 1906 errors::kChromeVersionTooLow, | |
| 1907 l10n_util::GetStringUTF8(IDS_PRODUCT_NAME), | |
| 1908 minimum_version_string); | |
| 1909 return false; | |
| 1910 } | |
| 1911 } | |
| 1912 | |
| 1913 // Initialize converted_from_user_script (if present) | |
| 1914 if (manifest_->HasKey(keys::kConvertedFromUserScript)) | |
| 1915 manifest_->GetBoolean(keys::kConvertedFromUserScript, | |
| 1916 &converted_from_user_script_); | |
| 1917 | |
| 1918 // Initialize commands (if present). | |
| 1919 if (manifest_->HasKey(keys::kCommands)) { | |
| 1920 DictionaryValue* commands = NULL; | |
| 1921 if (!manifest_->GetDictionary(keys::kCommands, &commands)) { | |
| 1922 *error = ASCIIToUTF16(errors::kInvalidCommandsKey); | |
| 1923 return false; | |
| 1924 } | |
| 1925 | |
| 1926 int command_index = 0; | |
| 1927 for (DictionaryValue::key_iterator iter = commands->begin_keys(); | |
| 1928 iter != commands->end_keys(); ++iter) { | |
| 1929 ++command_index; | |
| 1930 | |
| 1931 DictionaryValue* command = NULL; | |
| 1932 if (!commands->GetDictionary(*iter, &command)) { | |
| 1933 *error = ExtensionErrorUtils::FormatErrorMessageUTF16( | |
| 1934 errors::kInvalidKeyBindingDictionary, | |
| 1935 base::IntToString(command_index)); | |
| 1936 return false; | |
| 1937 } | |
| 1938 | |
| 1939 ExtensionKeybinding binding; | |
| 1940 if (!binding.Parse(command, *iter, command_index, error)) | |
| 1941 return false; // |error| already set. | |
| 1942 | |
| 1943 commands_.push_back(binding); | |
| 1944 } | |
| 1945 } | |
| 1946 | |
| 1947 // Initialize icons (if present). | |
| 1948 if (manifest_->HasKey(keys::kIcons)) { | |
| 1949 DictionaryValue* icons_value = NULL; | |
| 1950 if (!manifest_->GetDictionary(keys::kIcons, &icons_value)) { | |
| 1951 *error = ASCIIToUTF16(errors::kInvalidIcons); | |
| 1952 return false; | |
| 1953 } | |
| 1954 | |
| 1955 for (size_t i = 0; i < ExtensionIconSet::kNumIconSizes; ++i) { | |
| 1956 std::string key = base::IntToString(ExtensionIconSet::kIconSizes[i]); | |
| 1957 if (icons_value->HasKey(key)) { | |
| 1958 std::string icon_path; | |
| 1959 if (!icons_value->GetString(key, &icon_path)) { | |
| 1960 *error = ExtensionErrorUtils::FormatErrorMessageUTF16( | |
| 1961 errors::kInvalidIconPath, key); | |
| 1962 return false; | |
| 1963 } | |
| 1964 | |
| 1965 if (!icon_path.empty() && icon_path[0] == '/') | |
| 1966 icon_path = icon_path.substr(1); | |
| 1967 | |
| 1968 if (icon_path.empty()) { | |
| 1969 *error = ExtensionErrorUtils::FormatErrorMessageUTF16( | |
| 1970 errors::kInvalidIconPath, key); | |
| 1971 return false; | |
| 1972 } | |
| 1973 | |
| 1974 icons_.Add(ExtensionIconSet::kIconSizes[i], icon_path); | |
| 1975 } | |
| 1976 } | |
| 1977 } | |
| 1978 | |
| 1979 // Initialize themes (if present). | |
| 1980 if (manifest_->HasKey(keys::kTheme)) { | |
| 1981 DictionaryValue* theme_value = NULL; | |
| 1982 if (!manifest_->GetDictionary(keys::kTheme, &theme_value)) { | |
| 1983 *error = ASCIIToUTF16(errors::kInvalidTheme); | |
| 1984 return false; | |
| 1985 } | |
| 1986 | |
| 1987 DictionaryValue* images_value = NULL; | |
| 1988 if (theme_value->GetDictionary(keys::kThemeImages, &images_value)) { | |
| 1989 // Validate that the images are all strings | |
| 1990 for (DictionaryValue::key_iterator iter = images_value->begin_keys(); | |
| 1991 iter != images_value->end_keys(); ++iter) { | |
| 1992 std::string val; | |
| 1993 if (!images_value->GetString(*iter, &val)) { | |
| 1994 *error = ASCIIToUTF16(errors::kInvalidThemeImages); | |
| 1995 return false; | |
| 1996 } | |
| 1997 } | |
| 1998 theme_images_.reset(images_value->DeepCopy()); | |
| 1999 } | |
| 2000 | |
| 2001 DictionaryValue* colors_value = NULL; | |
| 2002 if (theme_value->GetDictionary(keys::kThemeColors, &colors_value)) { | |
| 2003 // Validate that the colors are RGB or RGBA lists | |
| 2004 for (DictionaryValue::key_iterator iter = colors_value->begin_keys(); | |
| 2005 iter != colors_value->end_keys(); ++iter) { | |
| 2006 ListValue* color_list = NULL; | |
| 2007 double alpha = 0.0; | |
| 2008 int color = 0; | |
| 2009 // The color must be a list | |
| 2010 if (!colors_value->GetListWithoutPathExpansion(*iter, &color_list) || | |
| 2011 // And either 3 items (RGB) or 4 (RGBA) | |
| 2012 ((color_list->GetSize() != 3) && | |
| 2013 ((color_list->GetSize() != 4) || | |
| 2014 // For RGBA, the fourth item must be a real or int alpha value. | |
| 2015 // Note that GetDouble() can get an integer value. | |
| 2016 !color_list->GetDouble(3, &alpha))) || | |
| 2017 // For both RGB and RGBA, the first three items must be ints (R,G,B) | |
| 2018 !color_list->GetInteger(0, &color) || | |
| 2019 !color_list->GetInteger(1, &color) || | |
| 2020 !color_list->GetInteger(2, &color)) { | |
| 2021 *error = ASCIIToUTF16(errors::kInvalidThemeColors); | |
| 2022 return false; | |
| 2023 } | |
| 2024 } | |
| 2025 theme_colors_.reset(colors_value->DeepCopy()); | |
| 2026 } | |
| 2027 | |
| 2028 DictionaryValue* tints_value = NULL; | |
| 2029 if (theme_value->GetDictionary(keys::kThemeTints, &tints_value)) { | |
| 2030 // Validate that the tints are all reals. | |
| 2031 for (DictionaryValue::key_iterator iter = tints_value->begin_keys(); | |
| 2032 iter != tints_value->end_keys(); ++iter) { | |
| 2033 ListValue* tint_list = NULL; | |
| 2034 double v = 0.0; | |
| 2035 if (!tints_value->GetListWithoutPathExpansion(*iter, &tint_list) || | |
| 2036 tint_list->GetSize() != 3 || | |
| 2037 !tint_list->GetDouble(0, &v) || | |
| 2038 !tint_list->GetDouble(1, &v) || | |
| 2039 !tint_list->GetDouble(2, &v)) { | |
| 2040 *error = ASCIIToUTF16(errors::kInvalidThemeTints); | |
| 2041 return false; | |
| 2042 } | |
| 2043 } | |
| 2044 theme_tints_.reset(tints_value->DeepCopy()); | |
| 2045 } | |
| 2046 | |
| 2047 DictionaryValue* display_properties_value = NULL; | |
| 2048 if (theme_value->GetDictionary(keys::kThemeDisplayProperties, | |
| 2049 &display_properties_value)) { | |
| 2050 theme_display_properties_.reset( | |
| 2051 display_properties_value->DeepCopy()); | |
| 2052 } | |
| 2053 | |
| 2054 return true; | |
| 2055 } | |
| 2056 | |
| 2057 // Initialize plugins (optional). | |
| 2058 if (manifest_->HasKey(keys::kPlugins)) { | |
| 2059 ListValue* list_value = NULL; | |
| 2060 if (!manifest_->GetList(keys::kPlugins, &list_value)) { | |
| 2061 *error = ASCIIToUTF16(errors::kInvalidPlugins); | |
| 2062 return false; | |
| 2063 } | |
| 2064 | |
| 2065 for (size_t i = 0; i < list_value->GetSize(); ++i) { | |
| 2066 DictionaryValue* plugin_value = NULL; | |
| 2067 std::string path_str; | |
| 2068 bool is_public = false; | |
| 2069 | |
| 2070 if (!list_value->GetDictionary(i, &plugin_value)) { | |
| 2071 *error = ASCIIToUTF16(errors::kInvalidPlugins); | |
| 2072 return false; | |
| 2073 } | |
| 2074 | |
| 2075 // Get plugins[i].path. | |
| 2076 if (!plugin_value->GetString(keys::kPluginsPath, &path_str)) { | |
| 2077 *error = ExtensionErrorUtils::FormatErrorMessageUTF16( | |
| 2078 errors::kInvalidPluginsPath, base::IntToString(i)); | |
| 2079 return false; | |
| 2080 } | |
| 2081 | |
| 2082 // Get plugins[i].content (optional). | |
| 2083 if (plugin_value->HasKey(keys::kPluginsPublic)) { | |
| 2084 if (!plugin_value->GetBoolean(keys::kPluginsPublic, &is_public)) { | |
| 2085 *error = ExtensionErrorUtils::FormatErrorMessageUTF16( | |
| 2086 errors::kInvalidPluginsPublic, base::IntToString(i)); | |
| 2087 return false; | |
| 2088 } | |
| 2089 } | |
| 2090 | |
| 2091 // We don't allow extension plugins to run on Chrome OS. We still | |
| 2092 // parse the manifest entry so that error messages are consistently | |
| 2093 // displayed across platforms. | |
| 2094 #if !defined(OS_CHROMEOS) | |
| 2095 plugins_.push_back(PluginInfo()); | |
| 2096 plugins_.back().path = path().Append(FilePath::FromUTF8Unsafe(path_str)); | |
| 2097 plugins_.back().is_public = is_public; | |
| 2098 #endif | |
| 2099 } | |
| 2100 } | |
| 2101 | |
| 2102 if (manifest_->HasKey(keys::kNaClModules)) { | |
| 2103 ListValue* list_value = NULL; | |
| 2104 if (!manifest_->GetList(keys::kNaClModules, &list_value)) { | |
| 2105 *error = ASCIIToUTF16(errors::kInvalidNaClModules); | |
| 2106 return false; | |
| 2107 } | |
| 2108 | |
| 2109 for (size_t i = 0; i < list_value->GetSize(); ++i) { | |
| 2110 DictionaryValue* module_value = NULL; | |
| 2111 std::string path_str; | |
| 2112 std::string mime_type; | |
| 2113 | |
| 2114 if (!list_value->GetDictionary(i, &module_value)) { | |
| 2115 *error = ASCIIToUTF16(errors::kInvalidNaClModules); | |
| 2116 return false; | |
| 2117 } | |
| 2118 | |
| 2119 // Get nacl_modules[i].path. | |
| 2120 if (!module_value->GetString(keys::kNaClModulesPath, &path_str)) { | |
| 2121 *error = ExtensionErrorUtils::FormatErrorMessageUTF16( | |
| 2122 errors::kInvalidNaClModulesPath, base::IntToString(i)); | |
| 2123 return false; | |
| 2124 } | |
| 2125 | |
| 2126 // Get nacl_modules[i].mime_type. | |
| 2127 if (!module_value->GetString(keys::kNaClModulesMIMEType, &mime_type)) { | |
| 2128 *error = ExtensionErrorUtils::FormatErrorMessageUTF16( | |
| 2129 errors::kInvalidNaClModulesMIMEType, base::IntToString(i)); | |
| 2130 return false; | |
| 2131 } | |
| 2132 | |
| 2133 nacl_modules_.push_back(NaClModuleInfo()); | |
| 2134 nacl_modules_.back().url = GetResourceURL(path_str); | |
| 2135 nacl_modules_.back().mime_type = mime_type; | |
| 2136 } | |
| 2137 } | |
| 2138 | |
| 2139 // Initialize content scripts (optional). | |
| 2140 if (manifest_->HasKey(keys::kContentScripts)) { | |
| 2141 ListValue* list_value; | |
| 2142 if (!manifest_->GetList(keys::kContentScripts, &list_value)) { | |
| 2143 *error = ASCIIToUTF16(errors::kInvalidContentScriptsList); | |
| 2144 return false; | |
| 2145 } | |
| 2146 | |
| 2147 for (size_t i = 0; i < list_value->GetSize(); ++i) { | |
| 2148 DictionaryValue* content_script = NULL; | |
| 2149 if (!list_value->GetDictionary(i, &content_script)) { | |
| 2150 *error = ExtensionErrorUtils::FormatErrorMessageUTF16( | |
| 2151 errors::kInvalidContentScript, base::IntToString(i)); | |
| 2152 return false; | |
| 2153 } | |
| 2154 | |
| 2155 UserScript script; | |
| 2156 if (!LoadUserScriptHelper(content_script, i, flags, error, &script)) | |
| 2157 return false; // Failed to parse script context definition. | |
| 2158 script.set_extension_id(id()); | |
| 2159 if (converted_from_user_script_) { | |
| 2160 script.set_emulate_greasemonkey(true); | |
| 2161 script.set_match_all_frames(true); // Greasemonkey matches all frames. | |
| 2162 } | |
| 2163 content_scripts_.push_back(script); | |
| 2164 } | |
| 2165 } | |
| 2166 | |
| 2167 // Initialize web accessible resources (optional). | |
| 2168 if (manifest_->HasKey(keys::kWebAccessibleResources)) { | |
| 2169 ListValue* list_value; | |
| 2170 if (!manifest_->GetList(keys::kWebAccessibleResources, &list_value)) { | |
| 2171 *error = ASCIIToUTF16(errors::kInvalidWebAccessibleResourcesList); | |
| 2172 return false; | |
| 2173 } | |
| 2174 for (size_t i = 0; i < list_value->GetSize(); ++i) { | |
| 2175 std::string relative_path; | |
| 2176 if (!list_value->GetString(i, &relative_path)) { | |
| 2177 *error = ExtensionErrorUtils::FormatErrorMessageUTF16( | |
| 2178 errors::kInvalidWebAccessibleResource, base::IntToString(i)); | |
| 2179 return false; | |
| 2180 } | |
| 2181 if (relative_path[0] != '/') | |
| 2182 relative_path = '/' + relative_path; | |
| 2183 web_accessible_resources_.insert(relative_path); | |
| 2184 } | |
| 2185 } | |
| 2186 | |
| 2187 // Initialize page action (optional). | |
| 2188 DictionaryValue* page_action_value = NULL; | |
| 2189 | |
| 2190 if (manifest_->HasKey(keys::kPageActions)) { | |
| 2191 ListValue* list_value = NULL; | |
| 2192 if (!manifest_->GetList(keys::kPageActions, &list_value)) { | |
| 2193 *error = ASCIIToUTF16(errors::kInvalidPageActionsList); | |
| 2194 return false; | |
| 2195 } | |
| 2196 | |
| 2197 size_t list_value_length = list_value->GetSize(); | |
| 2198 | |
| 2199 if (list_value_length == 0u) { | |
| 2200 // A list with zero items is allowed, and is equivalent to not having | |
| 2201 // a page_actions key in the manifest. Don't set |page_action_value|. | |
| 2202 } else if (list_value_length == 1u) { | |
| 2203 if (!list_value->GetDictionary(0, &page_action_value)) { | |
| 2204 *error = ASCIIToUTF16(errors::kInvalidPageAction); | |
| 2205 return false; | |
| 2206 } | |
| 2207 } else { // list_value_length > 1u. | |
| 2208 *error = ASCIIToUTF16(errors::kInvalidPageActionsListSize); | |
| 2209 return false; | |
| 2210 } | |
| 2211 } else if (manifest_->HasKey(keys::kPageAction)) { | |
| 2212 if (!manifest_->GetDictionary(keys::kPageAction, &page_action_value)) { | |
| 2213 *error = ASCIIToUTF16(errors::kInvalidPageAction); | |
| 2214 return false; | |
| 2215 } | |
| 2216 } | |
| 2217 | |
| 2218 // If page_action_value is not NULL, then there was a valid page action. | |
| 2219 if (page_action_value) { | |
| 2220 page_action_.reset( | |
| 2221 LoadExtensionActionHelper(page_action_value, error)); | |
| 2222 if (!page_action_.get()) | |
| 2223 return false; // Failed to parse page action definition. | |
| 2224 } | |
| 2225 | |
| 2226 // Initialize browser action (optional). | |
| 2227 if (manifest_->HasKey(keys::kBrowserAction)) { | |
| 2228 DictionaryValue* browser_action_value = NULL; | |
| 2229 if (!manifest_->GetDictionary(keys::kBrowserAction, | |
| 2230 &browser_action_value)) { | |
| 2231 *error = ASCIIToUTF16(errors::kInvalidBrowserAction); | |
| 2232 return false; | |
| 2233 } | |
| 2234 | |
| 2235 browser_action_.reset( | |
| 2236 LoadExtensionActionHelper(browser_action_value, error)); | |
| 2237 if (!browser_action_.get()) | |
| 2238 return false; // Failed to parse browser action definition. | |
| 2239 } | |
| 2240 | |
| 2241 // Initialize file browser actions (optional). | |
| 2242 if (manifest_->HasKey(keys::kFileBrowserHandlers)) { | |
| 2243 ListValue* file_browser_handlers_value = NULL; | |
| 2244 if (!manifest_->GetList(keys::kFileBrowserHandlers, | |
| 2245 &file_browser_handlers_value)) { | |
| 2246 *error = ASCIIToUTF16(errors::kInvalidFileBrowserHandler); | |
| 2247 return false; | |
| 2248 } | |
| 2249 | |
| 2250 file_browser_handlers_.reset( | |
| 2251 LoadFileBrowserHandlers(file_browser_handlers_value, error)); | |
| 2252 if (!file_browser_handlers_.get()) | |
| 2253 return false; // Failed to parse file browser actions definition. | |
| 2254 } | |
| 2255 | |
| 2256 // App isolation. | 2819 // App isolation. |
| 2257 if (api_permissions.count(ExtensionAPIPermission::kExperimental)) { | 2820 if (api_permissions.count(ExtensionAPIPermission::kExperimental) && |
| 2258 if (is_app() && !LoadAppIsolation(error)) | 2821 !LoadAppIsolation(error)) |
|
Yoyo Zhou
2012/03/01 01:17:25
This used to have an is_app check.
Ideally this w
Yoyo Zhou
2012/03/02 20:20:07
Can you put the is_app check back here?
| |
| 2259 return false; | 2822 return false; |
| 2260 } | 2823 |
| 2261 | 2824 if (!LoadExtensionFeatures(flags, api_permissions, error)) |
| 2262 // Initialize options page url (optional). | 2825 return false; |
| 2263 if (manifest_->HasKey(keys::kOptionsPage)) { | 2826 |
| 2264 std::string options_str; | 2827 if (manifest_->HasKey(keys::kTheme) && !LoadThemeFeatures(error)) |
| 2265 if (!manifest_->GetString(keys::kOptionsPage, &options_str)) { | 2828 return false; |
| 2266 *error = ASCIIToUTF16(errors::kInvalidOptionsPage); | |
| 2267 return false; | |
| 2268 } | |
| 2269 | |
| 2270 if (is_hosted_app()) { | |
| 2271 // hosted apps require an absolute URL. | |
| 2272 GURL options_url(options_str); | |
| 2273 if (!options_url.is_valid() || | |
| 2274 !(options_url.SchemeIs("http") || options_url.SchemeIs("https"))) { | |
| 2275 *error = ASCIIToUTF16(errors::kInvalidOptionsPageInHostedApp); | |
| 2276 return false; | |
| 2277 } | |
| 2278 options_url_ = options_url; | |
| 2279 } else { | |
| 2280 GURL absolute(options_str); | |
| 2281 if (absolute.is_valid()) { | |
| 2282 *error = ASCIIToUTF16(errors::kInvalidOptionsPageExpectUrlInPackage); | |
| 2283 return false; | |
| 2284 } | |
| 2285 options_url_ = GetResourceURL(options_str); | |
| 2286 if (!options_url_.is_valid()) { | |
| 2287 *error = ASCIIToUTF16(errors::kInvalidOptionsPage); | |
| 2288 return false; | |
| 2289 } | |
| 2290 } | |
| 2291 } | |
| 2292 | |
| 2293 if (!LoadBackgroundScripts(error)) | |
| 2294 return false; | |
| 2295 | |
| 2296 if (!LoadBackgroundPage(api_permissions, error)) | |
| 2297 return false; | |
| 2298 | |
| 2299 if (!LoadBackgroundPersistent(api_permissions, error)) | |
| 2300 return false; | |
| 2301 | |
| 2302 if (manifest_->HasKey(keys::kDefaultLocale)) { | |
| 2303 if (!manifest_->GetString(keys::kDefaultLocale, &default_locale_) || | |
| 2304 !l10n_util::IsValidLocaleSyntax(default_locale_)) { | |
| 2305 *error = ASCIIToUTF16(errors::kInvalidDefaultLocale); | |
| 2306 return false; | |
| 2307 } | |
| 2308 } | |
| 2309 | |
| 2310 // Chrome URL overrides (optional) | |
| 2311 if (manifest_->HasKey(keys::kChromeURLOverrides)) { | |
| 2312 DictionaryValue* overrides = NULL; | |
| 2313 if (!manifest_->GetDictionary(keys::kChromeURLOverrides, &overrides)) { | |
| 2314 *error = ASCIIToUTF16(errors::kInvalidChromeURLOverrides); | |
| 2315 return false; | |
| 2316 } | |
| 2317 | |
| 2318 // Validate that the overrides are all strings | |
| 2319 for (DictionaryValue::key_iterator iter = overrides->begin_keys(); | |
| 2320 iter != overrides->end_keys(); ++iter) { | |
| 2321 std::string page = *iter; | |
| 2322 std::string val; | |
| 2323 // Restrict override pages to a list of supported URLs. | |
| 2324 if ((page != chrome::kChromeUINewTabHost && | |
| 2325 #if defined(USE_VIRTUAL_KEYBOARD) | |
| 2326 page != chrome::kChromeUIKeyboardHost && | |
| 2327 #endif | |
| 2328 #if defined(OS_CHROMEOS) | |
| 2329 page != chrome::kChromeUIActivationMessageHost && | |
| 2330 #endif | |
| 2331 page != chrome::kChromeUIBookmarksHost && | |
| 2332 page != chrome::kChromeUIHistoryHost | |
| 2333 #if defined(FILE_MANAGER_EXTENSION) | |
| 2334 && | |
| 2335 !(location() == COMPONENT && | |
| 2336 page == chrome::kChromeUIFileManagerHost) | |
| 2337 #endif | |
| 2338 ) || | |
| 2339 !overrides->GetStringWithoutPathExpansion(*iter, &val)) { | |
| 2340 *error = ASCIIToUTF16(errors::kInvalidChromeURLOverrides); | |
| 2341 return false; | |
| 2342 } | |
| 2343 // Replace the entry with a fully qualified chrome-extension:// URL. | |
| 2344 chrome_url_overrides_[page] = GetResourceURL(val); | |
| 2345 } | |
| 2346 | |
| 2347 // An extension may override at most one page. | |
| 2348 if (overrides->size() > 1) { | |
| 2349 *error = ASCIIToUTF16(errors::kMultipleOverrides); | |
| 2350 return false; | |
| 2351 } | |
| 2352 } | |
| 2353 | |
| 2354 if (manifest_->HasKey(keys::kInputComponents)) { | |
| 2355 ListValue* list_value = NULL; | |
| 2356 if (!manifest_->GetList(keys::kInputComponents, &list_value)) { | |
| 2357 *error = ASCIIToUTF16(errors::kInvalidInputComponents); | |
| 2358 return false; | |
| 2359 } | |
| 2360 | |
| 2361 for (size_t i = 0; i < list_value->GetSize(); ++i) { | |
| 2362 DictionaryValue* module_value = NULL; | |
| 2363 std::string name_str; | |
| 2364 InputComponentType type; | |
| 2365 std::string id_str; | |
| 2366 std::string description_str; | |
| 2367 std::string language_str; | |
| 2368 std::set<std::string> layouts; | |
| 2369 std::string shortcut_keycode_str; | |
| 2370 bool shortcut_alt = false; | |
| 2371 bool shortcut_ctrl = false; | |
| 2372 bool shortcut_shift = false; | |
| 2373 | |
| 2374 if (!list_value->GetDictionary(i, &module_value)) { | |
| 2375 *error = ASCIIToUTF16(errors::kInvalidInputComponents); | |
| 2376 return false; | |
| 2377 } | |
| 2378 | |
| 2379 // Get input_components[i].name. | |
| 2380 if (!module_value->GetString(keys::kName, &name_str)) { | |
| 2381 *error = ExtensionErrorUtils::FormatErrorMessageUTF16( | |
| 2382 errors::kInvalidInputComponentName, base::IntToString(i)); | |
| 2383 return false; | |
| 2384 } | |
| 2385 | |
| 2386 // Get input_components[i].type. | |
| 2387 std::string type_str; | |
| 2388 if (module_value->GetString(keys::kType, &type_str)) { | |
| 2389 if (type_str == "ime") { | |
| 2390 type = INPUT_COMPONENT_TYPE_IME; | |
| 2391 } else if (type_str == "virtual_keyboard") { | |
| 2392 if (!api_permissions.count(ExtensionAPIPermission::kExperimental)) { | |
| 2393 // Virtual Keyboards require the experimental flag. | |
| 2394 *error = ExtensionErrorUtils::FormatErrorMessageUTF16( | |
| 2395 errors::kInvalidInputComponentType, base::IntToString(i)); | |
| 2396 return false; | |
| 2397 } | |
| 2398 type = INPUT_COMPONENT_TYPE_VIRTUAL_KEYBOARD; | |
| 2399 } else { | |
| 2400 *error = ExtensionErrorUtils::FormatErrorMessageUTF16( | |
| 2401 errors::kInvalidInputComponentType, base::IntToString(i)); | |
| 2402 return false; | |
| 2403 } | |
| 2404 } else { | |
| 2405 *error = ExtensionErrorUtils::FormatErrorMessageUTF16( | |
| 2406 errors::kInvalidInputComponentType, base::IntToString(i)); | |
| 2407 return false; | |
| 2408 } | |
| 2409 | |
| 2410 // Get input_components[i].id. | |
| 2411 if (!module_value->GetString(keys::kId, &id_str)) { | |
| 2412 id_str = ""; | |
| 2413 } | |
| 2414 | |
| 2415 // Get input_components[i].description. | |
| 2416 if (!module_value->GetString(keys::kDescription, &description_str)) { | |
| 2417 *error = ExtensionErrorUtils::FormatErrorMessageUTF16( | |
| 2418 errors::kInvalidInputComponentDescription, base::IntToString(i)); | |
| 2419 return false; | |
| 2420 } | |
| 2421 | |
| 2422 // Get input_components[i].language. | |
| 2423 if (!module_value->GetString(keys::kLanguage, &language_str)) { | |
| 2424 language_str = ""; | |
| 2425 } | |
| 2426 | |
| 2427 // Get input_components[i].layouts. | |
| 2428 ListValue* layouts_value = NULL; | |
| 2429 if (!module_value->GetList(keys::kLayouts, &layouts_value)) { | |
| 2430 *error = ASCIIToUTF16(errors::kInvalidInputComponentLayouts); | |
| 2431 return false; | |
| 2432 } | |
| 2433 | |
| 2434 for (size_t j = 0; j < layouts_value->GetSize(); ++j) { | |
| 2435 std::string layout_name_str; | |
| 2436 if (!layouts_value->GetString(j, &layout_name_str)) { | |
| 2437 *error = ExtensionErrorUtils::FormatErrorMessageUTF16( | |
| 2438 errors::kInvalidInputComponentLayoutName, base::IntToString(i), | |
| 2439 base::IntToString(j)); | |
| 2440 return false; | |
| 2441 } | |
| 2442 layouts.insert(layout_name_str); | |
| 2443 } | |
| 2444 | |
| 2445 if (module_value->HasKey(keys::kShortcutKey)) { | |
| 2446 DictionaryValue* shortcut_value = NULL; | |
| 2447 if (!module_value->GetDictionary(keys::kShortcutKey, &shortcut_value)) { | |
| 2448 *error = ExtensionErrorUtils::FormatErrorMessageUTF16( | |
| 2449 errors::kInvalidInputComponentShortcutKey, base::IntToString(i)); | |
| 2450 return false; | |
| 2451 } | |
| 2452 | |
| 2453 // Get input_components[i].shortcut_keycode. | |
| 2454 if (!shortcut_value->GetString(keys::kKeycode, &shortcut_keycode_str)) { | |
| 2455 *error = ExtensionErrorUtils::FormatErrorMessageUTF16( | |
| 2456 errors::kInvalidInputComponentShortcutKeycode, | |
| 2457 base::IntToString(i)); | |
| 2458 return false; | |
| 2459 } | |
| 2460 | |
| 2461 // Get input_components[i].shortcut_alt. | |
| 2462 if (!shortcut_value->GetBoolean(keys::kAltKey, &shortcut_alt)) { | |
| 2463 shortcut_alt = false; | |
| 2464 } | |
| 2465 | |
| 2466 // Get input_components[i].shortcut_ctrl. | |
| 2467 if (!shortcut_value->GetBoolean(keys::kCtrlKey, &shortcut_ctrl)) { | |
| 2468 shortcut_ctrl = false; | |
| 2469 } | |
| 2470 | |
| 2471 // Get input_components[i].shortcut_shift. | |
| 2472 if (!shortcut_value->GetBoolean(keys::kShiftKey, &shortcut_shift)) { | |
| 2473 shortcut_shift = false; | |
| 2474 } | |
| 2475 } | |
| 2476 | |
| 2477 input_components_.push_back(InputComponentInfo()); | |
| 2478 input_components_.back().name = name_str; | |
| 2479 input_components_.back().type = type; | |
| 2480 input_components_.back().id = id_str; | |
| 2481 input_components_.back().description = description_str; | |
| 2482 input_components_.back().language = language_str; | |
| 2483 input_components_.back().layouts.insert(layouts.begin(), layouts.end()); | |
| 2484 input_components_.back().shortcut_keycode = shortcut_keycode_str; | |
| 2485 input_components_.back().shortcut_alt = shortcut_alt; | |
| 2486 input_components_.back().shortcut_ctrl = shortcut_ctrl; | |
| 2487 input_components_.back().shortcut_shift = shortcut_shift; | |
| 2488 } | |
| 2489 } | |
| 2490 | |
| 2491 if (manifest_->HasKey(keys::kOmnibox)) { | |
| 2492 if (!manifest_->GetString(keys::kOmniboxKeyword, &omnibox_keyword_) || | |
| 2493 omnibox_keyword_.empty()) { | |
| 2494 *error = ASCIIToUTF16(errors::kInvalidOmniboxKeyword); | |
| 2495 return false; | |
| 2496 } | |
| 2497 } | |
| 2498 | |
| 2499 if (manifest_->HasKey(keys::kContentSecurityPolicy)) { | |
| 2500 std::string content_security_policy; | |
| 2501 if (!manifest_->GetString(keys::kContentSecurityPolicy, | |
| 2502 &content_security_policy)) { | |
| 2503 *error = ASCIIToUTF16(errors::kInvalidContentSecurityPolicy); | |
| 2504 return false; | |
| 2505 } | |
| 2506 if (!ContentSecurityPolicyIsLegal(content_security_policy)) { | |
| 2507 *error = ASCIIToUTF16(errors::kInvalidContentSecurityPolicy); | |
| 2508 return false; | |
| 2509 } | |
| 2510 if (manifest_version_ >= 2 && | |
| 2511 !ContentSecurityPolicyIsSecure(content_security_policy)) { | |
| 2512 *error = ASCIIToUTF16(errors::kInvalidContentSecurityPolicy); | |
| 2513 return false; | |
| 2514 } | |
| 2515 | |
| 2516 content_security_policy_ = content_security_policy; | |
| 2517 } else if (manifest_version_ >= 2) { | |
| 2518 // Manifest version 2 introduced a default Content-Security-Policy. | |
| 2519 // TODO(abarth): Should we continue to let extensions override the | |
| 2520 // default Content-Security-Policy? | |
| 2521 content_security_policy_ = kDefaultContentSecurityPolicy; | |
| 2522 CHECK(ContentSecurityPolicyIsSecure(content_security_policy_)); | |
| 2523 } | |
| 2524 | |
| 2525 // Initialize devtools page url (optional). | |
| 2526 if (manifest_->HasKey(keys::kDevToolsPage)) { | |
| 2527 std::string devtools_str; | |
| 2528 if (!manifest_->GetString(keys::kDevToolsPage, &devtools_str)) { | |
| 2529 *error = ASCIIToUTF16(errors::kInvalidDevToolsPage); | |
| 2530 return false; | |
| 2531 } | |
| 2532 devtools_url_ = GetResourceURL(devtools_str); | |
| 2533 } | |
| 2534 | |
| 2535 // Initialize text-to-speech voices (optional). | |
| 2536 if (manifest_->HasKey(keys::kTtsEngine)) { | |
| 2537 DictionaryValue* tts_dict = NULL; | |
| 2538 if (!manifest_->GetDictionary(keys::kTtsEngine, &tts_dict)) { | |
| 2539 *error = ASCIIToUTF16(errors::kInvalidTts); | |
| 2540 return false; | |
| 2541 } | |
| 2542 | |
| 2543 if (tts_dict->HasKey(keys::kTtsVoices)) { | |
| 2544 ListValue* tts_voices = NULL; | |
| 2545 if (!tts_dict->GetList(keys::kTtsVoices, &tts_voices)) { | |
| 2546 *error = ASCIIToUTF16(errors::kInvalidTtsVoices); | |
| 2547 return false; | |
| 2548 } | |
| 2549 | |
| 2550 for (size_t i = 0; i < tts_voices->GetSize(); i++) { | |
| 2551 DictionaryValue* one_tts_voice = NULL; | |
| 2552 if (!tts_voices->GetDictionary(i, &one_tts_voice)) { | |
| 2553 *error = ASCIIToUTF16(errors::kInvalidTtsVoices); | |
| 2554 return false; | |
| 2555 } | |
| 2556 | |
| 2557 TtsVoice voice_data; | |
| 2558 if (one_tts_voice->HasKey(keys::kTtsVoicesVoiceName)) { | |
| 2559 if (!one_tts_voice->GetString( | |
| 2560 keys::kTtsVoicesVoiceName, &voice_data.voice_name)) { | |
| 2561 *error = ASCIIToUTF16(errors::kInvalidTtsVoicesVoiceName); | |
| 2562 return false; | |
| 2563 } | |
| 2564 } | |
| 2565 if (one_tts_voice->HasKey(keys::kTtsVoicesLang)) { | |
| 2566 if (!one_tts_voice->GetString( | |
| 2567 keys::kTtsVoicesLang, &voice_data.lang) || | |
| 2568 !l10n_util::IsValidLocaleSyntax(voice_data.lang)) { | |
| 2569 *error = ASCIIToUTF16(errors::kInvalidTtsVoicesLang); | |
| 2570 return false; | |
| 2571 } | |
| 2572 } | |
| 2573 if (one_tts_voice->HasKey(keys::kTtsVoicesGender)) { | |
| 2574 if (!one_tts_voice->GetString( | |
| 2575 keys::kTtsVoicesGender, &voice_data.gender) || | |
| 2576 (voice_data.gender != keys::kTtsGenderMale && | |
| 2577 voice_data.gender != keys::kTtsGenderFemale)) { | |
| 2578 *error = ASCIIToUTF16(errors::kInvalidTtsVoicesGender); | |
| 2579 return false; | |
| 2580 } | |
| 2581 } | |
| 2582 if (one_tts_voice->HasKey(keys::kTtsVoicesEventTypes)) { | |
| 2583 ListValue* event_types_list; | |
| 2584 if (!one_tts_voice->GetList( | |
| 2585 keys::kTtsVoicesEventTypes, &event_types_list)) { | |
| 2586 *error = ASCIIToUTF16(errors::kInvalidTtsVoicesEventTypes); | |
| 2587 return false; | |
| 2588 } | |
| 2589 for (size_t i = 0; i < event_types_list->GetSize(); i++) { | |
| 2590 std::string event_type; | |
| 2591 if (!event_types_list->GetString(i, &event_type)) { | |
| 2592 *error = ASCIIToUTF16(errors::kInvalidTtsVoicesEventTypes); | |
| 2593 return false; | |
| 2594 } | |
| 2595 if (event_type != keys::kTtsVoicesEventTypeEnd && | |
| 2596 event_type != keys::kTtsVoicesEventTypeError && | |
| 2597 event_type != keys::kTtsVoicesEventTypeMarker && | |
| 2598 event_type != keys::kTtsVoicesEventTypeSentence && | |
| 2599 event_type != keys::kTtsVoicesEventTypeStart && | |
| 2600 event_type != keys::kTtsVoicesEventTypeWord) { | |
| 2601 *error = ASCIIToUTF16(errors::kInvalidTtsVoicesEventTypes); | |
| 2602 return false; | |
| 2603 } | |
| 2604 if (voice_data.event_types.find(event_type) != | |
| 2605 voice_data.event_types.end()) { | |
| 2606 *error = ASCIIToUTF16(errors::kInvalidTtsVoicesEventTypes); | |
| 2607 return false; | |
| 2608 } | |
| 2609 voice_data.event_types.insert(event_type); | |
| 2610 } | |
| 2611 } | |
| 2612 | |
| 2613 tts_voices_.push_back(voice_data); | |
| 2614 } | |
| 2615 } | |
| 2616 } | |
| 2617 | |
| 2618 // Initialize web intents (optional). | |
| 2619 if (!LoadWebIntentServices(error)) | |
| 2620 return false; | |
| 2621 | |
| 2622 // Initialize incognito behavior. Apps default to split mode, extensions | |
| 2623 // default to spanning. | |
| 2624 incognito_split_mode_ = is_app(); | |
| 2625 if (manifest_->HasKey(keys::kIncognito)) { | |
| 2626 std::string value; | |
| 2627 if (!manifest_->GetString(keys::kIncognito, &value)) { | |
| 2628 *error = ASCIIToUTF16(errors::kInvalidIncognitoBehavior); | |
| 2629 return false; | |
| 2630 } | |
| 2631 if (value == values::kIncognitoSpanning) { | |
| 2632 incognito_split_mode_ = false; | |
| 2633 } else if (value == values::kIncognitoSplit) { | |
| 2634 incognito_split_mode_ = true; | |
| 2635 } else { | |
| 2636 *error = ASCIIToUTF16(errors::kInvalidIncognitoBehavior); | |
| 2637 return false; | |
| 2638 } | |
| 2639 } | |
| 2640 | |
| 2641 // Initialize offline-enabled status. Defaults to false. | |
| 2642 if (manifest_->HasKey(keys::kOfflineEnabled)) { | |
| 2643 if (!manifest_->GetBoolean(keys::kOfflineEnabled, &offline_enabled_)) { | |
| 2644 *error = ASCIIToUTF16(errors::kInvalidOfflineEnabled); | |
| 2645 return false; | |
| 2646 } | |
| 2647 } | |
| 2648 | |
| 2649 // Initialize requirements (optional). Not actually persisted (they're only | |
| 2650 // used by the store), but still validated. | |
| 2651 if (manifest_->HasKey(keys::kRequirements)) { | |
| 2652 DictionaryValue* requirements_value = NULL; | |
| 2653 if (!manifest_->GetDictionary(keys::kRequirements, &requirements_value)) { | |
| 2654 *error = ASCIIToUTF16(errors::kInvalidRequirements); | |
| 2655 return false; | |
| 2656 } | |
| 2657 | |
| 2658 for (DictionaryValue::key_iterator it = requirements_value->begin_keys(); | |
| 2659 it != requirements_value->end_keys(); ++it) { | |
| 2660 DictionaryValue* requirement_value; | |
| 2661 if (!requirements_value->GetDictionaryWithoutPathExpansion( | |
| 2662 *it, &requirement_value)) { | |
| 2663 *error = ExtensionErrorUtils::FormatErrorMessageUTF16( | |
| 2664 errors::kInvalidRequirement, *it); | |
| 2665 return false; | |
| 2666 } | |
| 2667 } | |
| 2668 } | |
| 2669 | 2829 |
| 2670 if (HasMultipleUISurfaces()) { | 2830 if (HasMultipleUISurfaces()) { |
| 2671 *error = ASCIIToUTF16(errors::kOneUISurfaceOnly); | 2831 *error = ASCIIToUTF16(errors::kOneUISurfaceOnly); |
| 2672 return false; | 2832 return false; |
| 2673 } | 2833 } |
| 2674 | 2834 |
| 2675 runtime_data_.SetActivePermissions(new ExtensionPermissionSet( | 2835 runtime_data_.SetActivePermissions(new ExtensionPermissionSet( |
| 2676 this, api_permissions, host_permissions)); | 2836 this, api_permissions, host_permissions)); |
| 2677 required_permission_set_ = new ExtensionPermissionSet( | 2837 required_permission_set_ = new ExtensionPermissionSet( |
| 2678 this, api_permissions, host_permissions); | 2838 this, api_permissions, host_permissions); |
| (...skipping 389 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 3068 // extension with options. All other menu items like uninstall have | 3228 // extension with options. All other menu items like uninstall have |
| 3069 // no sense for component extensions. | 3229 // no sense for component extensions. |
| 3070 return location() != Extension::COMPONENT; | 3230 return location() != Extension::COMPONENT; |
| 3071 } | 3231 } |
| 3072 | 3232 |
| 3073 bool Extension::CanSpecifyAPIPermission( | 3233 bool Extension::CanSpecifyAPIPermission( |
| 3074 const ExtensionAPIPermission* permission, | 3234 const ExtensionAPIPermission* permission, |
| 3075 string16* error) const { | 3235 string16* error) const { |
| 3076 if (location() == Extension::COMPONENT) | 3236 if (location() == Extension::COMPONENT) |
| 3077 return true; | 3237 return true; |
| 3078 | |
| 3079 bool access_denied = false; | 3238 bool access_denied = false; |
| 3080 if (permission->HasWhitelist()) { | 3239 if (permission->HasWhitelist()) { |
| 3081 if (permission->IsWhitelisted(id())) | 3240 if (permission->IsWhitelisted(id())) |
| 3082 return true; | 3241 return true; |
| 3083 else | 3242 else |
| 3084 access_denied = true; | 3243 access_denied = true; |
| 3085 } else if (permission->is_component_only()) { | 3244 } else if (permission->is_component_only()) { |
| 3086 access_denied = true; | 3245 access_denied = true; |
| 3087 } | 3246 } |
| 3088 | 3247 |
| (...skipping 157 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 3246 return SYNC_TYPE_APP; | 3405 return SYNC_TYPE_APP; |
| 3247 | 3406 |
| 3248 default: | 3407 default: |
| 3249 return SYNC_TYPE_NONE; | 3408 return SYNC_TYPE_NONE; |
| 3250 } | 3409 } |
| 3251 } | 3410 } |
| 3252 | 3411 |
| 3253 bool Extension::IsSyncable() const { | 3412 bool Extension::IsSyncable() const { |
| 3254 // TODO(akalin): Figure out if we need to allow some other types. | 3413 // TODO(akalin): Figure out if we need to allow some other types. |
| 3255 | 3414 |
| 3256 // We want to sync any extensions that are shown in the luancher because | 3415 // We want to sync any extensions that are shown in the launcher because |
| 3257 // their positions should sync. | 3416 // their positions should sync. |
| 3258 return location() == Extension::INTERNAL || | 3417 return location() == Extension::INTERNAL || |
| 3259 ShouldDisplayInLauncher(); | 3418 ShouldDisplayInLauncher(); |
| 3260 } | 3419 } |
| 3261 | 3420 |
| 3262 bool Extension::ShouldDisplayInLauncher() const { | 3421 bool Extension::ShouldDisplayInLauncher() const { |
| 3263 // All apps should be displayed on the NTP except for the Cloud Print App. | 3422 // All apps should be displayed on the NTP except for the Cloud Print App. |
| 3264 return is_app() && id() != extension_misc::kCloudPrintAppId; | 3423 return is_app() && id() != extension_misc::kCloudPrintAppId; |
| 3265 } | 3424 } |
| 3266 | 3425 |
| (...skipping 32 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 3299 already_disabled(false), | 3458 already_disabled(false), |
| 3300 extension(extension) {} | 3459 extension(extension) {} |
| 3301 | 3460 |
| 3302 UpdatedExtensionPermissionsInfo::UpdatedExtensionPermissionsInfo( | 3461 UpdatedExtensionPermissionsInfo::UpdatedExtensionPermissionsInfo( |
| 3303 const Extension* extension, | 3462 const Extension* extension, |
| 3304 const ExtensionPermissionSet* permissions, | 3463 const ExtensionPermissionSet* permissions, |
| 3305 Reason reason) | 3464 Reason reason) |
| 3306 : reason(reason), | 3465 : reason(reason), |
| 3307 extension(extension), | 3466 extension(extension), |
| 3308 permissions(permissions) {} | 3467 permissions(permissions) {} |
| OLD | NEW |