| OLD | NEW |
| 1 // Copyright (c) 2009 The Chromium Authors. All rights reserved. | 1 // Copyright (c) 2010 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_file_util.h" | 5 #include "chrome/common/extensions/extension_file_util.h" |
| 6 | 6 |
| 7 #include <map> | 7 #include <map> |
| 8 #include <vector> | 8 #include <vector> |
| 9 | 9 |
| 10 #include "app/l10n_util.h" | 10 #include "app/l10n_util.h" |
| 11 #include "base/file_util.h" | 11 #include "base/file_util.h" |
| 12 #include "base/logging.h" | 12 #include "base/logging.h" |
| 13 #include "base/scoped_temp_dir.h" | 13 #include "base/scoped_temp_dir.h" |
| 14 #include "base/utf_string_conversions.h" | 14 #include "base/utf_string_conversions.h" |
| 15 #include "chrome/common/extensions/extension.h" | 15 #include "chrome/common/extensions/extension.h" |
| 16 #include "chrome/common/extensions/extension_l10n_util.h" | 16 #include "chrome/common/extensions/extension_l10n_util.h" |
| 17 #include "chrome/common/extensions/extension_constants.h" | 17 #include "chrome/common/extensions/extension_constants.h" |
| 18 #include "chrome/common/json_value_serializer.h" | 18 #include "chrome/common/json_value_serializer.h" |
| 19 #include "grit/generated_resources.h" |
| 19 #include "net/base/escape.h" | 20 #include "net/base/escape.h" |
| 20 #include "net/base/file_stream.h" | 21 #include "net/base/file_stream.h" |
| 21 | 22 |
| 22 namespace errors = extension_manifest_errors; | 23 namespace errors = extension_manifest_errors; |
| 23 namespace keys = extension_manifest_keys; | |
| 24 | 24 |
| 25 namespace extension_file_util { | 25 namespace extension_file_util { |
| 26 | 26 |
| 27 // Validates locale info. Doesn't check if messages.json files are valid. | 27 // Validates locale info. Doesn't check if messages.json files are valid. |
| 28 static bool ValidateLocaleInfo(const Extension& extension, std::string* error); | 28 static bool ValidateLocaleInfo(const Extension& extension, std::string* error); |
| 29 | 29 |
| 30 const char kInstallDirectoryName[] = "Extensions"; | 30 const char kInstallDirectoryName[] = "Extensions"; |
| 31 // TODO(mpcomplete): obsolete. remove after migration period. | 31 // TODO(mpcomplete): obsolete. remove after migration period. |
| 32 // http://code.google.com/p/chromium/issues/detail?id=19733 | 32 // http://code.google.com/p/chromium/issues/detail?id=19733 |
| 33 const char kCurrentVersionFileName[] = "Current Version"; | 33 const char kCurrentVersionFileName[] = "Current Version"; |
| (...skipping 55 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 89 } | 89 } |
| 90 | 90 |
| 91 bool InstallExtension(const FilePath& src_dir, | 91 bool InstallExtension(const FilePath& src_dir, |
| 92 const FilePath& version_dir, | 92 const FilePath& version_dir, |
| 93 std::string* error) { | 93 std::string* error) { |
| 94 // If anything fails after this, we want to delete the extension dir. | 94 // If anything fails after this, we want to delete the extension dir. |
| 95 ScopedTempDir scoped_version_dir; | 95 ScopedTempDir scoped_version_dir; |
| 96 scoped_version_dir.Set(version_dir); | 96 scoped_version_dir.Set(version_dir); |
| 97 | 97 |
| 98 if (!MoveDirSafely(src_dir, version_dir)) { | 98 if (!MoveDirSafely(src_dir, version_dir)) { |
| 99 *error = "Could not move extension directory into profile."; | 99 *error = l10n_util::GetStringUTF8( |
| 100 IDS_EXTENSION_MOVE_DIRECTORY_TO_PROFILE_FAILED); |
| 100 return false; | 101 return false; |
| 101 } | 102 } |
| 102 | 103 |
| 103 scoped_version_dir.Take(); | 104 scoped_version_dir.Take(); |
| 104 return true; | 105 return true; |
| 105 } | 106 } |
| 106 | 107 |
| 107 Extension* LoadExtension(const FilePath& extension_path, | 108 Extension* LoadExtension(const FilePath& extension_path, |
| 108 bool require_key, | 109 bool require_key, |
| 109 std::string* error) { | 110 std::string* error) { |
| 110 FilePath manifest_path = | 111 FilePath manifest_path = |
| 111 extension_path.Append(Extension::kManifestFilename); | 112 extension_path.Append(Extension::kManifestFilename); |
| 112 if (!file_util::PathExists(manifest_path)) { | 113 if (!file_util::PathExists(manifest_path)) { |
| 113 *error = extension_manifest_errors::kManifestUnreadable; | 114 *error = |
| 115 l10n_util::GetStringUTF8(IDS_EXTENSION_MANIFEST_UNREADABLE); |
| 114 return NULL; | 116 return NULL; |
| 115 } | 117 } |
| 116 | 118 |
| 117 JSONFileValueSerializer serializer(manifest_path); | 119 JSONFileValueSerializer serializer(manifest_path); |
| 118 scoped_ptr<Value> root(serializer.Deserialize(error)); | 120 scoped_ptr<Value> root(serializer.Deserialize(error)); |
| 119 if (!root.get()) { | 121 if (!root.get()) { |
| 120 if (error->empty()) { | 122 if (error->empty()) { |
| 121 // If |error| is empty, than the file could not be read. | 123 // If |error| is empty, than the file could not be read. |
| 122 // It would be cleaner to have the JSON reader give a specific error | 124 // It would be cleaner to have the JSON reader give a specific error |
| 123 // in this case, but other code tests for a file error with | 125 // in this case, but other code tests for a file error with |
| 124 // error->empty(). For now, be consistent. | 126 // error->empty(). For now, be consistent. |
| 125 *error = extension_manifest_errors::kManifestUnreadable; | 127 *error = |
| 128 l10n_util::GetStringUTF8(IDS_EXTENSION_MANIFEST_UNREADABLE); |
| 126 } else { | 129 } else { |
| 127 *error = StringPrintf("%s %s", | 130 *error = StringPrintf("%s %s", |
| 128 extension_manifest_errors::kManifestParseError, | 131 errors::kManifestParseError, |
| 129 error->c_str()); | 132 error->c_str()); |
| 130 } | 133 } |
| 131 return NULL; | 134 return NULL; |
| 132 } | 135 } |
| 133 | 136 |
| 134 if (!root->IsType(Value::TYPE_DICTIONARY)) { | 137 if (!root->IsType(Value::TYPE_DICTIONARY)) { |
| 135 *error = extension_manifest_errors::kInvalidManifest; | 138 *error = |
| 139 l10n_util::GetStringUTF8(IDS_EXTENSION_MANIFEST_INVALID); |
| 136 return NULL; | 140 return NULL; |
| 137 } | 141 } |
| 138 | 142 |
| 139 DictionaryValue* manifest = static_cast<DictionaryValue*>(root.get()); | 143 DictionaryValue* manifest = static_cast<DictionaryValue*>(root.get()); |
| 140 | 144 |
| 141 scoped_ptr<Extension> extension(new Extension(extension_path)); | 145 scoped_ptr<Extension> extension(new Extension(extension_path)); |
| 142 | 146 |
| 143 if (!extension_l10n_util::LocalizeExtension(extension.get(), manifest, error)) | 147 if (!extension_l10n_util::LocalizeExtension(extension.get(), manifest, error)) |
| 144 return NULL; | 148 return NULL; |
| 145 | 149 |
| 146 if (!extension->InitFromValue(*manifest, require_key, error)) | 150 if (!extension->InitFromValue(*manifest, require_key, error)) |
| 147 return NULL; | 151 return NULL; |
| 148 | 152 |
| 149 if (!ValidateExtension(extension.get(), error)) | 153 if (!ValidateExtension(extension.get(), error)) |
| 150 return NULL; | 154 return NULL; |
| 151 | 155 |
| 152 return extension.release(); | 156 return extension.release(); |
| 153 } | 157 } |
| 154 | 158 |
| 155 bool ValidateExtension(Extension* extension, std::string* error) { | 159 bool ValidateExtension(Extension* extension, std::string* error) { |
| 156 // Validate icons exist. | 160 // Validate icons exist. |
| 157 for (std::map<int, std::string>::const_iterator iter = | 161 for (std::map<int, std::string>::const_iterator iter = |
| 158 extension->icons().begin(); iter != extension->icons().end(); ++iter) { | 162 extension->icons().begin(); iter != extension->icons().end(); ++iter) { |
| 159 const FilePath path = extension->GetResource(iter->second).GetFilePath(); | 163 const FilePath path = extension->GetResource(iter->second).GetFilePath(); |
| 160 if (!file_util::PathExists(path)) { | 164 if (!file_util::PathExists(path)) { |
| 161 *error = StringPrintf("Could not load extension icon '%s'.", | 165 *error = |
| 162 iter->second.c_str()); | 166 l10n_util::GetStringFUTF8(IDS_EXTENSION_LOAD_ICON_FAILED, |
| 167 UTF8ToUTF16(iter->second)); |
| 163 return false; | 168 return false; |
| 164 } | 169 } |
| 165 } | 170 } |
| 166 | 171 |
| 167 // Theme resource validation. | 172 // Theme resource validation. |
| 168 if (extension->IsTheme()) { | 173 if (extension->IsTheme()) { |
| 169 DictionaryValue* images_value = extension->GetThemeImages(); | 174 DictionaryValue* images_value = extension->GetThemeImages(); |
| 170 if (images_value) { | 175 if (images_value) { |
| 171 for (DictionaryValue::key_iterator iter = images_value->begin_keys(); | 176 for (DictionaryValue::key_iterator iter = images_value->begin_keys(); |
| 172 iter != images_value->end_keys(); ++iter) { | 177 iter != images_value->end_keys(); ++iter) { |
| 173 std::string val; | 178 std::string val; |
| 174 if (images_value->GetStringWithoutPathExpansion(*iter, &val)) { | 179 if (images_value->GetStringWithoutPathExpansion(*iter, &val)) { |
| 175 FilePath image_path = extension->path().AppendASCII(val); | 180 FilePath image_path = extension->path().AppendASCII(val); |
| 176 if (!file_util::PathExists(image_path)) { | 181 if (!file_util::PathExists(image_path)) { |
| 177 *error = StringPrintf( | 182 *error = |
| 178 "Could not load '%s' for theme.", | 183 l10n_util::GetStringFUTF8(IDS_EXTENSION_INVALID_IMAGE_PATH, |
| 179 WideToUTF8(image_path.ToWStringHack()).c_str()); | 184 WideToUTF16(image_path.ToWStringHack())); |
| 180 return false; | 185 return false; |
| 181 } | 186 } |
| 182 } | 187 } |
| 183 } | 188 } |
| 184 } | 189 } |
| 185 | 190 |
| 186 // Themes cannot contain other extension types. | 191 // Themes cannot contain other extension types. |
| 187 return true; | 192 return true; |
| 188 } | 193 } |
| 189 | 194 |
| 190 // Validate that claimed script resources actually exist. | 195 // Validate that claimed script resources actually exist. |
| 191 for (size_t i = 0; i < extension->content_scripts().size(); ++i) { | 196 for (size_t i = 0; i < extension->content_scripts().size(); ++i) { |
| 192 const UserScript& script = extension->content_scripts()[i]; | 197 const UserScript& script = extension->content_scripts()[i]; |
| 193 | 198 |
| 194 for (size_t j = 0; j < script.js_scripts().size(); j++) { | 199 for (size_t j = 0; j < script.js_scripts().size(); j++) { |
| 195 const UserScript::File& js_script = script.js_scripts()[j]; | 200 const UserScript::File& js_script = script.js_scripts()[j]; |
| 196 const FilePath& path = ExtensionResource::GetFilePath( | 201 const FilePath& path = ExtensionResource::GetFilePath( |
| 197 js_script.extension_root(), js_script.relative_path()); | 202 js_script.extension_root(), js_script.relative_path()); |
| 198 if (!file_util::PathExists(path)) { | 203 if (!file_util::PathExists(path)) { |
| 199 *error = StringPrintf( | 204 *error = |
| 200 "Could not load javascript '%s' for content script.", | 205 l10n_util::GetStringFUTF8(IDS_EXTENSION_LOAD_JAVASCRIPT_FAILED, |
| 201 WideToUTF8(js_script.relative_path().ToWStringHack()).c_str()); | 206 WideToUTF16(js_script.relative_path().ToWStringHack())); |
| 202 return false; | 207 return false; |
| 203 } | 208 } |
| 204 } | 209 } |
| 205 | 210 |
| 206 for (size_t j = 0; j < script.css_scripts().size(); j++) { | 211 for (size_t j = 0; j < script.css_scripts().size(); j++) { |
| 207 const UserScript::File& css_script = script.css_scripts()[j]; | 212 const UserScript::File& css_script = script.css_scripts()[j]; |
| 208 const FilePath& path = ExtensionResource::GetFilePath( | 213 const FilePath& path = ExtensionResource::GetFilePath( |
| 209 css_script.extension_root(), css_script.relative_path()); | 214 css_script.extension_root(), css_script.relative_path()); |
| 210 if (!file_util::PathExists(path)) { | 215 if (!file_util::PathExists(path)) { |
| 211 *error = StringPrintf( | 216 *error = |
| 212 "Could not load css '%s' for content script.", | 217 l10n_util::GetStringFUTF8(IDS_EXTENSION_LOAD_CSS_FAILED, |
| 213 WideToUTF8(css_script.relative_path().ToWStringHack()).c_str()); | 218 WideToUTF16(css_script.relative_path().ToWStringHack())); |
| 214 return false; | 219 return false; |
| 215 } | 220 } |
| 216 } | 221 } |
| 217 } | 222 } |
| 218 | 223 |
| 219 // Validate claimed plugin paths. | 224 // Validate claimed plugin paths. |
| 220 for (size_t i = 0; i < extension->plugins().size(); ++i) { | 225 for (size_t i = 0; i < extension->plugins().size(); ++i) { |
| 221 const Extension::PluginInfo& plugin = extension->plugins()[i]; | 226 const Extension::PluginInfo& plugin = extension->plugins()[i]; |
| 222 if (!file_util::PathExists(plugin.path)) { | 227 if (!file_util::PathExists(plugin.path)) { |
| 223 *error = StringPrintf("Could not load '%s' for plugin.", | 228 *error = |
| 224 WideToUTF8(plugin.path.ToWStringHack()).c_str()); | 229 l10n_util::GetStringFUTF8( |
| 230 IDS_EXTENSION_LOAD_PLUGIN_PATH_FAILED, |
| 231 WideToUTF16(plugin.path.ToWStringHack())); |
| 225 return false; | 232 return false; |
| 226 } | 233 } |
| 227 } | 234 } |
| 228 | 235 |
| 229 // Validate icon location for page actions. | 236 // Validate icon location for page actions. |
| 230 ExtensionAction* page_action = extension->page_action(); | 237 ExtensionAction* page_action = extension->page_action(); |
| 231 if (page_action) { | 238 if (page_action) { |
| 232 std::vector<std::string> icon_paths(*page_action->icon_paths()); | 239 std::vector<std::string> icon_paths(*page_action->icon_paths()); |
| 233 if (!page_action->default_icon_path().empty()) | 240 if (!page_action->default_icon_path().empty()) |
| 234 icon_paths.push_back(page_action->default_icon_path()); | 241 icon_paths.push_back(page_action->default_icon_path()); |
| 235 for (std::vector<std::string>::iterator iter = icon_paths.begin(); | 242 for (std::vector<std::string>::iterator iter = icon_paths.begin(); |
| 236 iter != icon_paths.end(); ++iter) { | 243 iter != icon_paths.end(); ++iter) { |
| 237 if (!file_util::PathExists(extension->GetResource(*iter).GetFilePath())) { | 244 if (!file_util::PathExists(extension->GetResource(*iter).GetFilePath())) { |
| 238 *error = StringPrintf("Could not load icon '%s' for page action.", | 245 *error = |
| 239 iter->c_str()); | 246 l10n_util::GetStringFUTF8( |
| 247 IDS_EXTENSION_LOAD_ICON_FOR_PAGE_ACTION_FAILED, |
| 248 UTF8ToUTF16(*iter)); |
| 240 return false; | 249 return false; |
| 241 } | 250 } |
| 242 } | 251 } |
| 243 } | 252 } |
| 244 | 253 |
| 245 // Validate icon location for browser actions. | 254 // Validate icon location for browser actions. |
| 246 // Note: browser actions don't use the icon_paths(). | 255 // Note: browser actions don't use the icon_paths(). |
| 247 ExtensionAction* browser_action = extension->browser_action(); | 256 ExtensionAction* browser_action = extension->browser_action(); |
| 248 if (browser_action) { | 257 if (browser_action) { |
| 249 std::string path = browser_action->default_icon_path(); | 258 std::string path = browser_action->default_icon_path(); |
| 250 if (!path.empty() && | 259 if (!path.empty() && |
| 251 !file_util::PathExists(extension->GetResource(path).GetFilePath())) { | 260 !file_util::PathExists(extension->GetResource(path).GetFilePath())) { |
| 252 *error = StringPrintf("Could not load icon '%s' for browser action.", | 261 *error = |
| 253 path.c_str()); | 262 l10n_util::GetStringFUTF8( |
| 263 IDS_EXTENSION_LOAD_ICON_FOR_BROWSER_ACTION_FAILED, |
| 264 UTF8ToUTF16(path)); |
| 254 return false; | 265 return false; |
| 255 } | 266 } |
| 256 } | 267 } |
| 257 | 268 |
| 258 // Validate background page location. | 269 // Validate background page location. |
| 259 if (!extension->background_url().is_empty()) { | 270 if (!extension->background_url().is_empty()) { |
| 260 FilePath page_path = ExtensionURLToRelativeFilePath( | 271 FilePath page_path = ExtensionURLToRelativeFilePath( |
| 261 extension->background_url()); | 272 extension->background_url()); |
| 262 const FilePath path = extension->GetResource(page_path).GetFilePath(); | 273 const FilePath path = extension->GetResource(page_path).GetFilePath(); |
| 263 if (path.empty() || !file_util::PathExists(path)) { | 274 if (path.empty() || !file_util::PathExists(path)) { |
| 264 *error = StringPrintf("Could not load background page '%s'.", | 275 *error = |
| 265 WideToUTF8(page_path.ToWStringHack()).c_str()); | 276 l10n_util::GetStringFUTF8( |
| 277 IDS_EXTENSION_LOAD_BACKGROUND_PAGE_FAILED, |
| 278 WideToUTF16(page_path.ToWStringHack())); |
| 266 return false; | 279 return false; |
| 267 } | 280 } |
| 268 } | 281 } |
| 269 | 282 |
| 270 // Validate locale info. | 283 // Validate locale info. |
| 271 if (!ValidateLocaleInfo(*extension, error)) | 284 if (!ValidateLocaleInfo(*extension, error)) |
| 272 return false; | 285 return false; |
| 273 | 286 |
| 274 // Check children of extension root to see if any of them start with _ and is | 287 // Check children of extension root to see if any of them start with _ and is |
| 275 // not on the reserved list. | 288 // not on the reserved list. |
| (...skipping 106 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 382 FilePath locale_path = extension_path.Append(Extension::kLocaleFolder); | 395 FilePath locale_path = extension_path.Append(Extension::kLocaleFolder); |
| 383 if (!file_util::PathExists(locale_path)) | 396 if (!file_util::PathExists(locale_path)) |
| 384 return NULL; | 397 return NULL; |
| 385 | 398 |
| 386 std::set<std::string> locales; | 399 std::set<std::string> locales; |
| 387 if (!extension_l10n_util::GetValidLocales(locale_path, &locales, error)) | 400 if (!extension_l10n_util::GetValidLocales(locale_path, &locales, error)) |
| 388 return NULL; | 401 return NULL; |
| 389 | 402 |
| 390 if (default_locale.empty() || | 403 if (default_locale.empty() || |
| 391 locales.find(default_locale) == locales.end()) { | 404 locales.find(default_locale) == locales.end()) { |
| 392 *error = extension_manifest_errors::kLocalesNoDefaultLocaleSpecified; | 405 *error = l10n_util::GetStringUTF8( |
| 406 IDS_EXTENSION_LOCALES_NO_DEFAULT_LOCALE_SPECIFIED); |
| 393 return NULL; | 407 return NULL; |
| 394 } | 408 } |
| 395 | 409 |
| 396 ExtensionMessageBundle* message_bundle = | 410 ExtensionMessageBundle* message_bundle = |
| 397 extension_l10n_util::LoadMessageCatalogs( | 411 extension_l10n_util::LoadMessageCatalogs( |
| 398 locale_path, | 412 locale_path, |
| 399 default_locale, | 413 default_locale, |
| 400 extension_l10n_util::CurrentLocaleOrDefault(), | 414 extension_l10n_util::CurrentLocaleOrDefault(), |
| 401 locales, | 415 locales, |
| 402 error); | 416 error); |
| 403 | 417 |
| 404 return message_bundle; | 418 return message_bundle; |
| 405 } | 419 } |
| 406 | 420 |
| 407 static bool ValidateLocaleInfo(const Extension& extension, std::string* error) { | 421 static bool ValidateLocaleInfo(const Extension& extension, std::string* error) { |
| 408 // default_locale and _locales have to be both present or both missing. | 422 // default_locale and _locales have to be both present or both missing. |
| 409 const FilePath path = extension.path().Append(Extension::kLocaleFolder); | 423 const FilePath path = extension.path().Append(Extension::kLocaleFolder); |
| 410 bool path_exists = file_util::PathExists(path); | 424 bool path_exists = file_util::PathExists(path); |
| 411 std::string default_locale = extension.default_locale(); | 425 std::string default_locale = extension.default_locale(); |
| 412 | 426 |
| 413 // If both default locale and _locales folder are empty, skip verification. | 427 // If both default locale and _locales folder are empty, skip verification. |
| 414 if (default_locale.empty() && !path_exists) | 428 if (default_locale.empty() && !path_exists) |
| 415 return true; | 429 return true; |
| 416 | 430 |
| 417 if (default_locale.empty() && path_exists) { | 431 if (default_locale.empty() && path_exists) { |
| 418 *error = errors::kLocalesNoDefaultLocaleSpecified; | 432 *error = l10n_util::GetStringUTF8( |
| 433 IDS_EXTENSION_LOCALES_NO_DEFAULT_LOCALE_SPECIFIED); |
| 419 return false; | 434 return false; |
| 420 } else if (!default_locale.empty() && !path_exists) { | 435 } else if (!default_locale.empty() && !path_exists) { |
| 421 *error = errors::kLocalesTreeMissing; | 436 *error = errors::kLocalesTreeMissing; |
| 422 return false; | 437 return false; |
| 423 } | 438 } |
| 424 | 439 |
| 425 // Treat all folders under _locales as valid locales. | 440 // Treat all folders under _locales as valid locales. |
| 426 file_util::FileEnumerator locales(path, | 441 file_util::FileEnumerator locales(path, |
| 427 false, | 442 false, |
| 428 file_util::FileEnumerator::DIRECTORIES); | 443 file_util::FileEnumerator::DIRECTORIES); |
| (...skipping 80 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 509 UnescapeRule::SPACES | UnescapeRule::URL_SPECIAL_CHARS); | 524 UnescapeRule::SPACES | UnescapeRule::URL_SPECIAL_CHARS); |
| 510 | 525 |
| 511 #if defined(OS_POSIX) | 526 #if defined(OS_POSIX) |
| 512 return FilePath(file_path); | 527 return FilePath(file_path); |
| 513 #elif defined(OS_WIN) | 528 #elif defined(OS_WIN) |
| 514 return FilePath(UTF8ToWide(file_path)); | 529 return FilePath(UTF8ToWide(file_path)); |
| 515 #endif | 530 #endif |
| 516 } | 531 } |
| 517 | 532 |
| 518 } // namespace extension_file_util | 533 } // namespace extension_file_util |
| OLD | NEW |