| OLD | NEW |
| 1 // Copyright (c) 2009 The Chromium Authors. All rights reserved. | 1 // Copyright (c) 2009 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/browser/extensions/extension_file_util.h" | 5 #include "chrome/browser/extensions/extension_file_util.h" |
| 6 | 6 |
| 7 #include "base/file_util.h" | 7 #include "base/file_util.h" |
| 8 #include "base/logging.h" | 8 #include "base/logging.h" |
| 9 #include "base/scoped_temp_dir.h" | 9 #include "base/scoped_temp_dir.h" |
| 10 #include "base/string_util.h" | 10 #include "base/string_util.h" |
| 11 #include "chrome/browser/extensions/extension_prefs.h" |
| 11 #include "chrome/browser/extensions/extension_l10n_util.h" | 12 #include "chrome/browser/extensions/extension_l10n_util.h" |
| 12 #include "chrome/common/extensions/extension.h" | 13 #include "chrome/common/extensions/extension.h" |
| 13 #include "chrome/common/extensions/extension_constants.h" | 14 #include "chrome/common/extensions/extension_constants.h" |
| 14 #include "chrome/common/json_value_serializer.h" | 15 #include "chrome/common/json_value_serializer.h" |
| 15 #include "net/base/file_stream.h" | 16 #include "net/base/file_stream.h" |
| 16 | 17 |
| 17 namespace extension_file_util { | 18 namespace extension_file_util { |
| 18 | 19 |
| 19 const char kInstallDirectoryName[] = "Extensions"; | 20 const char kInstallDirectoryName[] = "Extensions"; |
| 21 // TODO(mpcomplete): obsolete. remove after migration period. |
| 22 // http://code.google.com/p/chromium/issues/detail?id=19733 |
| 20 const char kCurrentVersionFileName[] = "Current Version"; | 23 const char kCurrentVersionFileName[] = "Current Version"; |
| 21 | 24 |
| 22 bool MoveDirSafely(const FilePath& source_dir, const FilePath& dest_dir) { | 25 bool MoveDirSafely(const FilePath& source_dir, const FilePath& dest_dir) { |
| 23 if (file_util::PathExists(dest_dir)) { | 26 if (file_util::PathExists(dest_dir)) { |
| 24 if (!file_util::Delete(dest_dir, true)) | 27 if (!file_util::Delete(dest_dir, true)) |
| 25 return false; | 28 return false; |
| 26 } else { | 29 } else { |
| 27 FilePath parent = dest_dir.DirName(); | 30 FilePath parent = dest_dir.DirName(); |
| 28 if (!file_util::DirectoryExists(parent)) { | 31 if (!file_util::DirectoryExists(parent)) { |
| 29 if (!file_util::CreateDirectory(parent)) | 32 if (!file_util::CreateDirectory(parent)) |
| 30 return false; | 33 return false; |
| 31 } | 34 } |
| 32 } | 35 } |
| 33 | 36 |
| 34 if (!file_util::Move(source_dir, dest_dir)) | 37 if (!file_util::Move(source_dir, dest_dir)) |
| 35 return false; | 38 return false; |
| 36 | 39 |
| 37 return true; | 40 return true; |
| 38 } | 41 } |
| 39 | 42 |
| 40 bool SetCurrentVersion(const FilePath& dest_dir, const std::string& version, | 43 Extension::InstallType CompareToInstalledVersion( |
| 41 std::string* error) { | 44 const FilePath& extensions_dir, |
| 42 // Write out the new CurrentVersion file. | 45 const std::string& extension_id, |
| 43 // <profile>/Extension/<name>/CurrentVersion | 46 const std::string& current_version_str, |
| 44 FilePath current_version = dest_dir.AppendASCII(kCurrentVersionFileName); | 47 const std::string& new_version_str, |
| 45 FilePath current_version_old = | 48 FilePath* version_dir) { |
| 46 current_version.InsertBeforeExtension(FILE_PATH_LITERAL("_old")); | 49 FilePath dest_dir = extensions_dir.AppendASCII(extension_id); |
| 47 if (file_util::PathExists(current_version_old)) { | 50 FilePath current_version_dir = dest_dir.AppendASCII(current_version_str); |
| 48 if (!file_util::Delete(current_version_old, false)) { | 51 *version_dir = dest_dir.AppendASCII(new_version_str); |
| 49 *error = "Couldn't remove CurrentVersion_old file."; | |
| 50 return false; | |
| 51 } | |
| 52 } | |
| 53 | 52 |
| 54 if (file_util::PathExists(current_version)) { | 53 if (current_version_str.empty()) |
| 55 if (!file_util::Move(current_version, current_version_old)) { | |
| 56 *error = "Couldn't move CurrentVersion file."; | |
| 57 return false; | |
| 58 } | |
| 59 } | |
| 60 net::FileStream stream; | |
| 61 int flags = base::PLATFORM_FILE_CREATE_ALWAYS | base::PLATFORM_FILE_WRITE; | |
| 62 if (stream.Open(current_version, flags) != 0) | |
| 63 return false; | |
| 64 if (stream.Write(version.c_str(), version.size(), NULL) < 0) { | |
| 65 // Restore the old CurrentVersion. | |
| 66 if (file_util::PathExists(current_version_old)) { | |
| 67 if (!file_util::Move(current_version_old, current_version)) { | |
| 68 LOG(WARNING) << "couldn't restore " << current_version_old.value() << | |
| 69 " to " << current_version.value(); | |
| 70 | |
| 71 // TODO(erikkay): This is an ugly state to be in. Try harder? | |
| 72 } | |
| 73 } | |
| 74 *error = "Couldn't create CurrentVersion file."; | |
| 75 return false; | |
| 76 } | |
| 77 return true; | |
| 78 } | |
| 79 | |
| 80 bool ReadCurrentVersion(const FilePath& dir, std::string* version_string) { | |
| 81 FilePath current_version = dir.AppendASCII(kCurrentVersionFileName); | |
| 82 if (file_util::PathExists(current_version)) { | |
| 83 if (file_util::ReadFileToString(current_version, version_string)) { | |
| 84 TrimWhitespaceASCII(*version_string, TRIM_ALL, version_string); | |
| 85 return true; | |
| 86 } | |
| 87 } | |
| 88 return false; | |
| 89 } | |
| 90 | |
| 91 Extension::InstallType CompareToInstalledVersion( | |
| 92 const FilePath& install_directory, const std::string& id, | |
| 93 const std::string& new_version_str, std::string *current_version_str) { | |
| 94 CHECK(current_version_str); | |
| 95 FilePath dir(install_directory.AppendASCII(id.c_str())); | |
| 96 if (!ReadCurrentVersion(dir, current_version_str)) | |
| 97 return Extension::NEW_INSTALL; | 54 return Extension::NEW_INSTALL; |
| 98 | 55 |
| 99 scoped_ptr<Version> current_version( | 56 scoped_ptr<Version> current_version( |
| 100 Version::GetVersionFromString(*current_version_str)); | 57 Version::GetVersionFromString(current_version_str)); |
| 101 scoped_ptr<Version> new_version( | 58 scoped_ptr<Version> new_version( |
| 102 Version::GetVersionFromString(new_version_str)); | 59 Version::GetVersionFromString(new_version_str)); |
| 103 int comp = new_version->CompareTo(*current_version); | 60 int comp = new_version->CompareTo(*current_version); |
| 104 if (comp > 0) | 61 if (comp > 0) |
| 105 return Extension::UPGRADE; | 62 return Extension::UPGRADE; |
| 106 else if (comp == 0) | 63 if (comp < 0) |
| 107 return Extension::REINSTALL; | |
| 108 else | |
| 109 return Extension::DOWNGRADE; | 64 return Extension::DOWNGRADE; |
| 65 |
| 66 // Same version. Treat corrupted existing installation as new install case. |
| 67 if (!SanityCheckExtension(current_version_dir)) |
| 68 return Extension::NEW_INSTALL; |
| 69 |
| 70 return Extension::REINSTALL; |
| 110 } | 71 } |
| 111 | 72 |
| 112 bool SanityCheckExtension(const FilePath& dir) { | 73 bool SanityCheckExtension(const FilePath& dir) { |
| 113 // Verify that the directory actually exists. | 74 // Verify that the directory actually exists. |
| 114 // TODO(erikkay): A further step would be to verify that the extension | 75 // TODO(erikkay): A further step would be to verify that the extension |
| 115 // has actually loaded successfully. | 76 // has actually loaded successfully. |
| 116 FilePath manifest_file(dir.AppendASCII(Extension::kManifestFilename)); | 77 FilePath manifest_file(dir.AppendASCII(Extension::kManifestFilename)); |
| 117 return file_util::PathExists(dir) && file_util::PathExists(manifest_file); | 78 return file_util::PathExists(dir) && file_util::PathExists(manifest_file); |
| 118 } | 79 } |
| 119 | 80 |
| 120 bool InstallExtension(const FilePath& src_dir, | 81 bool InstallExtension(const FilePath& src_dir, |
| 121 const FilePath& extensions_dir, | 82 const FilePath& version_dir, |
| 122 const std::string& extension_id, | |
| 123 const std::string& extension_version, | |
| 124 FilePath* version_dir, | |
| 125 Extension::InstallType* install_type, | |
| 126 std::string* error) { | 83 std::string* error) { |
| 127 FilePath dest_dir = extensions_dir.AppendASCII(extension_id); | |
| 128 *version_dir = dest_dir.AppendASCII(extension_version); | |
| 129 | |
| 130 std::string current_version; | |
| 131 *install_type = CompareToInstalledVersion( | |
| 132 extensions_dir, extension_id, extension_version, ¤t_version); | |
| 133 | |
| 134 // Do not allow downgrade. | |
| 135 if (*install_type == Extension::DOWNGRADE) | |
| 136 return true; | |
| 137 | |
| 138 if (*install_type == Extension::REINSTALL) { | |
| 139 if (!SanityCheckExtension(*version_dir)) { | |
| 140 // Treat corrupted existing installation as new install case. | |
| 141 *install_type = Extension::NEW_INSTALL; | |
| 142 } else { | |
| 143 return true; | |
| 144 } | |
| 145 } | |
| 146 | |
| 147 // If anything fails after this, we want to delete the extension dir. | 84 // If anything fails after this, we want to delete the extension dir. |
| 148 ScopedTempDir scoped_version_dir; | 85 ScopedTempDir scoped_version_dir; |
| 149 scoped_version_dir.Set(*version_dir); | 86 scoped_version_dir.Set(version_dir); |
| 150 | 87 |
| 151 if (!MoveDirSafely(src_dir, *version_dir)) { | 88 if (!MoveDirSafely(src_dir, version_dir)) { |
| 152 *error = "Could not move extension directory into profile."; | 89 *error = "Could not move extension directory into profile."; |
| 153 return false; | 90 return false; |
| 154 } | 91 } |
| 155 | 92 |
| 156 if (!SetCurrentVersion(dest_dir, extension_version, error)) | |
| 157 return false; | |
| 158 | |
| 159 scoped_version_dir.Take(); | 93 scoped_version_dir.Take(); |
| 160 return true; | 94 return true; |
| 161 } | 95 } |
| 162 | 96 |
| 163 Extension* LoadExtension(const FilePath& extension_path, bool require_key, | 97 Extension* LoadExtension(const FilePath& extension_path, bool require_key, |
| 164 std::string* error) { | 98 std::string* error) { |
| 165 FilePath manifest_path = | 99 FilePath manifest_path = |
| 166 extension_path.AppendASCII(Extension::kManifestFilename); | 100 extension_path.AppendASCII(Extension::kManifestFilename); |
| 167 if (!file_util::PathExists(manifest_path)) { | 101 if (!file_util::PathExists(manifest_path)) { |
| 168 *error = extension_manifest_errors::kInvalidManifest; | 102 *error = extension_manifest_errors::kInvalidManifest; |
| 169 return NULL; | 103 return NULL; |
| 170 } | 104 } |
| 171 | 105 |
| 172 JSONFileValueSerializer serializer(manifest_path); | 106 JSONFileValueSerializer serializer(manifest_path); |
| 173 scoped_ptr<Value> root(serializer.Deserialize(error)); | 107 scoped_ptr<Value> root(serializer.Deserialize(error)); |
| 174 if (!root.get()) | 108 if (!root.get()) |
| 175 return NULL; | 109 return NULL; |
| 176 | 110 |
| 177 if (!root->IsType(Value::TYPE_DICTIONARY)) { | 111 if (!root->IsType(Value::TYPE_DICTIONARY)) { |
| 178 *error = extension_manifest_errors::kInvalidManifest; | 112 *error = extension_manifest_errors::kInvalidManifest; |
| 179 return NULL; | 113 return NULL; |
| 180 } | 114 } |
| 181 | 115 |
| 182 scoped_ptr<Extension> extension(new Extension(extension_path)); | 116 scoped_ptr<Extension> extension(new Extension(extension_path)); |
| 183 if (!extension->InitFromValue(*static_cast<DictionaryValue*>(root.get()), | 117 if (!extension->InitFromValue(*static_cast<DictionaryValue*>(root.get()), |
| 184 require_key, error)) | 118 require_key, error)) |
| 185 return NULL; | 119 return NULL; |
| 186 | 120 |
| 121 if (!ValidateExtension(extension.get(), error)) |
| 122 return NULL; |
| 123 |
| 124 return extension.release(); |
| 125 } |
| 126 |
| 127 bool ValidateExtension(Extension* extension, std::string* error) { |
| 187 // Validate icons exist. | 128 // Validate icons exist. |
| 188 for (std::map<int, std::string>::const_iterator iter = | 129 for (std::map<int, std::string>::const_iterator iter = |
| 189 extension->icons().begin(); iter != extension->icons().end(); ++iter) { | 130 extension->icons().begin(); iter != extension->icons().end(); ++iter) { |
| 190 if (!file_util::PathExists(extension->GetResourcePath(iter->second))) { | 131 if (!file_util::PathExists(extension->GetResourcePath(iter->second))) { |
| 191 *error = StringPrintf("Could not load extension icon '%s'.", | 132 *error = StringPrintf("Could not load extension icon '%s'.", |
| 192 iter->second.c_str()); | 133 iter->second.c_str()); |
| 193 return NULL; | 134 return false; |
| 194 } | 135 } |
| 195 } | 136 } |
| 196 | 137 |
| 197 // Theme resource validation. | 138 // Theme resource validation. |
| 198 if (extension->IsTheme()) { | 139 if (extension->IsTheme()) { |
| 199 DictionaryValue* images_value = extension->GetThemeImages(); | 140 DictionaryValue* images_value = extension->GetThemeImages(); |
| 200 if (images_value) { | 141 if (images_value) { |
| 201 for (DictionaryValue::key_iterator iter = images_value->begin_keys(); | 142 for (DictionaryValue::key_iterator iter = images_value->begin_keys(); |
| 202 iter != images_value->end_keys(); ++iter) { | 143 iter != images_value->end_keys(); ++iter) { |
| 203 std::string val; | 144 std::string val; |
| 204 if (images_value->GetString(*iter, &val)) { | 145 if (images_value->GetString(*iter, &val)) { |
| 205 FilePath image_path = extension->path().AppendASCII(val); | 146 FilePath image_path = extension->path().AppendASCII(val); |
| 206 if (!file_util::PathExists(image_path)) { | 147 if (!file_util::PathExists(image_path)) { |
| 207 *error = StringPrintf( | 148 *error = StringPrintf( |
| 208 "Could not load '%s' for theme.", | 149 "Could not load '%s' for theme.", |
| 209 WideToUTF8(image_path.ToWStringHack()).c_str()); | 150 WideToUTF8(image_path.ToWStringHack()).c_str()); |
| 210 return NULL; | 151 return false; |
| 211 } | 152 } |
| 212 } | 153 } |
| 213 } | 154 } |
| 214 } | 155 } |
| 215 | 156 |
| 216 // Themes cannot contain other extension types. | 157 // Themes cannot contain other extension types. |
| 217 return extension.release(); | 158 return true; |
| 218 } | 159 } |
| 219 | 160 |
| 220 // Validate that claimed script resources actually exist. | 161 // Validate that claimed script resources actually exist. |
| 221 for (size_t i = 0; i < extension->content_scripts().size(); ++i) { | 162 for (size_t i = 0; i < extension->content_scripts().size(); ++i) { |
| 222 const UserScript& script = extension->content_scripts()[i]; | 163 const UserScript& script = extension->content_scripts()[i]; |
| 223 | 164 |
| 224 for (size_t j = 0; j < script.js_scripts().size(); j++) { | 165 for (size_t j = 0; j < script.js_scripts().size(); j++) { |
| 225 const FilePath& path = script.js_scripts()[j].path(); | 166 const FilePath& path = script.js_scripts()[j].path(); |
| 226 if (!file_util::PathExists(path)) { | 167 if (!file_util::PathExists(path)) { |
| 227 *error = StringPrintf("Could not load '%s' for content script.", | 168 *error = StringPrintf("Could not load '%s' for content script.", |
| 228 WideToUTF8(path.ToWStringHack()).c_str()); | 169 WideToUTF8(path.ToWStringHack()).c_str()); |
| 229 return NULL; | 170 return false; |
| 230 } | 171 } |
| 231 } | 172 } |
| 232 | 173 |
| 233 for (size_t j = 0; j < script.css_scripts().size(); j++) { | 174 for (size_t j = 0; j < script.css_scripts().size(); j++) { |
| 234 const FilePath& path = script.css_scripts()[j].path(); | 175 const FilePath& path = script.css_scripts()[j].path(); |
| 235 if (!file_util::PathExists(path)) { | 176 if (!file_util::PathExists(path)) { |
| 236 *error = StringPrintf("Could not load '%s' for content script.", | 177 *error = StringPrintf("Could not load '%s' for content script.", |
| 237 WideToUTF8(path.ToWStringHack()).c_str()); | 178 WideToUTF8(path.ToWStringHack()).c_str()); |
| 238 return NULL; | 179 return false; |
| 239 } | 180 } |
| 240 } | 181 } |
| 241 } | 182 } |
| 242 | 183 |
| 243 // Validate claimed plugin paths. | 184 // Validate claimed plugin paths. |
| 244 for (size_t i = 0; i < extension->plugins().size(); ++i) { | 185 for (size_t i = 0; i < extension->plugins().size(); ++i) { |
| 245 const Extension::PluginInfo& plugin = extension->plugins()[i]; | 186 const Extension::PluginInfo& plugin = extension->plugins()[i]; |
| 246 if (!file_util::PathExists(plugin.path)) { | 187 if (!file_util::PathExists(plugin.path)) { |
| 247 *error = StringPrintf("Could not load '%s' for plugin.", | 188 *error = StringPrintf("Could not load '%s' for plugin.", |
| 248 WideToUTF8(plugin.path.ToWStringHack()).c_str()); | 189 WideToUTF8(plugin.path.ToWStringHack()).c_str()); |
| 249 return NULL; | 190 return false; |
| 250 } | 191 } |
| 251 } | 192 } |
| 252 | 193 |
| 253 // Validate claimed privacy blacklists paths. | 194 // Validate claimed privacy blacklists paths. |
| 254 for (size_t i = 0; i < extension->privacy_blacklists().size(); ++i) { | 195 for (size_t i = 0; i < extension->privacy_blacklists().size(); ++i) { |
| 255 const Extension::PrivacyBlacklistInfo& blacklist = | 196 const Extension::PrivacyBlacklistInfo& blacklist = |
| 256 extension->privacy_blacklists()[i]; | 197 extension->privacy_blacklists()[i]; |
| 257 if (!file_util::PathExists(blacklist.path)) { | 198 if (!file_util::PathExists(blacklist.path)) { |
| 258 *error = StringPrintf("Could not load '%s' for privacy blacklist.", | 199 *error = StringPrintf("Could not load '%s' for privacy blacklist.", |
| 259 WideToUTF8(blacklist.path.ToWStringHack()).c_str()); | 200 WideToUTF8(blacklist.path.ToWStringHack()).c_str()); |
| 260 return NULL; | 201 return false; |
| 261 } | 202 } |
| 262 } | 203 } |
| 263 | 204 |
| 264 // Validate icon location for page actions. | 205 // Validate icon location for page actions. |
| 265 const PageActionMap& page_actions = extension->page_actions(); | 206 const PageActionMap& page_actions = extension->page_actions(); |
| 266 for (PageActionMap::const_iterator i(page_actions.begin()); | 207 for (PageActionMap::const_iterator i(page_actions.begin()); |
| 267 i != page_actions.end(); ++i) { | 208 i != page_actions.end(); ++i) { |
| 268 PageAction* page_action = i->second; | 209 PageAction* page_action = i->second; |
| 269 const std::vector<std::string>& icon_paths = page_action->icon_paths(); | 210 const std::vector<std::string>& icon_paths = page_action->icon_paths(); |
| 270 for (std::vector<std::string>::const_iterator iter = icon_paths.begin(); | 211 for (std::vector<std::string>::const_iterator iter = icon_paths.begin(); |
| 271 iter != icon_paths.end(); ++iter) { | 212 iter != icon_paths.end(); ++iter) { |
| 272 if (!file_util::PathExists(extension->GetResourcePath(*iter))) { | 213 if (!file_util::PathExists(extension->GetResourcePath(*iter))) { |
| 273 *error = StringPrintf("Could not load icon '%s' for page action.", | 214 *error = StringPrintf("Could not load icon '%s' for page action.", |
| 274 iter->c_str()); | 215 iter->c_str()); |
| 275 return NULL; | 216 return false; |
| 276 } | 217 } |
| 277 } | 218 } |
| 278 } | 219 } |
| 279 | 220 |
| 280 // Load locale information if available. | 221 // Load locale information if available. |
| 281 FilePath locale_path = extension_path.AppendASCII(Extension::kLocaleFolder); | 222 FilePath locale_path = |
| 223 extension->path().AppendASCII(Extension::kLocaleFolder); |
| 282 if (file_util::PathExists(locale_path)) { | 224 if (file_util::PathExists(locale_path)) { |
| 283 if (!extension_l10n_util::AddValidLocales(locale_path, | 225 if (!extension_l10n_util::AddValidLocales(locale_path, |
| 284 extension.get(), | 226 extension, |
| 285 error)) { | 227 error)) { |
| 286 return NULL; | 228 return false; |
| 287 } | 229 } |
| 288 | 230 |
| 289 if (!extension_l10n_util::ValidateDefaultLocale(extension.get())) { | 231 if (!extension_l10n_util::ValidateDefaultLocale(extension)) { |
| 290 *error = extension_manifest_errors::kLocalesNoDefaultLocaleSpecified; | 232 *error = extension_manifest_errors::kLocalesNoDefaultLocaleSpecified; |
| 291 return NULL; | 233 return false; |
| 292 } | 234 } |
| 293 } | 235 } |
| 294 | 236 |
| 295 // Check children of extension root to see if any of them start with _ and is | 237 // Check children of extension root to see if any of them start with _ and is |
| 296 // not on the reserved list. | 238 // not on the reserved list. |
| 297 if (!CheckForIllegalFilenames(extension_path, error)) { | 239 if (!CheckForIllegalFilenames(extension->path(), error)) { |
| 298 return NULL; | 240 return false; |
| 299 } | 241 } |
| 300 | 242 |
| 301 return extension.release(); | 243 return true; |
| 302 } | 244 } |
| 303 | 245 |
| 304 void UninstallExtension(const std::string& id, const FilePath& extensions_dir) { | 246 void UninstallExtension(const std::string& id, const FilePath& extensions_dir) { |
| 305 // First, delete the Current Version file. If the directory delete fails, then | 247 // First, delete the Current Version file. If the directory delete fails, then |
| 306 // at least the extension won't be loaded again. | 248 // at least the extension won't be loaded again. |
| 307 FilePath extension_root = extensions_dir.AppendASCII(id); | 249 FilePath extension_root = extensions_dir.AppendASCII(id); |
| 308 | 250 |
| 309 if (!file_util::PathExists(extension_root)) { | 251 if (!file_util::PathExists(extension_root)) { |
| 310 LOG(WARNING) << "Asked to remove a non-existent extension " << id; | 252 LOG(WARNING) << "Asked to remove a non-existent extension " << id; |
| 311 return; | 253 return; |
| 312 } | 254 } |
| 313 | 255 |
| 314 FilePath current_version_file = extension_root.AppendASCII( | 256 FilePath current_version_file = extension_root.AppendASCII( |
| 315 kCurrentVersionFileName); | 257 kCurrentVersionFileName); |
| 316 if (!file_util::PathExists(current_version_file)) { | 258 if (!file_util::PathExists(current_version_file)) { |
| 317 LOG(WARNING) << "Extension " << id | 259 // This is OK, since we're phasing out the current version file. |
| 318 << " does not have a Current Version file."; | |
| 319 } else { | 260 } else { |
| 320 if (!file_util::Delete(current_version_file, false)) { | 261 if (!file_util::Delete(current_version_file, false)) { |
| 321 LOG(WARNING) << "Could not delete Current Version file for extension " | 262 LOG(WARNING) << "Could not delete Current Version file for extension " |
| 322 << id; | 263 << id; |
| 323 return; | 264 return; |
| 324 } | 265 } |
| 325 } | 266 } |
| 326 | 267 |
| 327 // OK, now try and delete the entire rest of the directory. One major place | 268 // OK, now try and delete the entire rest of the directory. One major place |
| 328 // this can fail is if the extension contains a plugin (stupid plugins). It's | 269 // this can fail is if the extension contains a plugin (stupid plugins). It's |
| 329 // not a big deal though, because we'll notice next time we startup that the | 270 // not a big deal though, because we'll notice next time we startup that the |
| 330 // Current Version file is gone and finish the delete then. | 271 // Current Version file is gone and finish the delete then. |
| 331 if (!file_util::Delete(extension_root, true)) | 272 if (!file_util::Delete(extension_root, true)) |
| 332 LOG(WARNING) << "Could not delete directory for extension " << id; | 273 LOG(WARNING) << "Could not delete directory for extension " << id; |
| 333 } | 274 } |
| 334 | 275 |
| 335 void GarbageCollectExtensions(const FilePath& install_directory) { | 276 void GarbageCollectExtensions(const FilePath& install_directory, |
| 277 const std::set<std::string>& installed_ids) { |
| 336 // Nothing to clean up if it doesn't exist. | 278 // Nothing to clean up if it doesn't exist. |
| 337 if (!file_util::DirectoryExists(install_directory)) | 279 if (!file_util::DirectoryExists(install_directory)) |
| 338 return; | 280 return; |
| 339 | 281 |
| 340 LOG(INFO) << "Loading installed extensions..."; | 282 LOG(INFO) << "Loading installed extensions..."; |
| 341 file_util::FileEnumerator enumerator(install_directory, | 283 file_util::FileEnumerator enumerator(install_directory, |
| 342 false, // Not recursive. | 284 false, // Not recursive. |
| 343 file_util::FileEnumerator::DIRECTORIES); | 285 file_util::FileEnumerator::DIRECTORIES); |
| 344 FilePath extension_path; | 286 FilePath extension_path; |
| 345 for (extension_path = enumerator.Next(); !extension_path.value().empty(); | 287 for (extension_path = enumerator.Next(); !extension_path.value().empty(); |
| 346 extension_path = enumerator.Next()) { | 288 extension_path = enumerator.Next()) { |
| 347 std::string extension_id = WideToASCII( | 289 std::string extension_id = WideToASCII( |
| 348 extension_path.BaseName().ToWStringHack()); | 290 extension_path.BaseName().ToWStringHack()); |
| 349 | 291 |
| 350 // If there is no Current Version file, just delete the directory and move | 292 // If there is no entry in the prefs file, just delete the directory and |
| 351 // on. This can legitimately happen when an uninstall does not complete, for | 293 // move on. This can legitimately happen when an uninstall does not |
| 352 // example, when a plugin is in use at uninstall time. | 294 // complete, for example, when a plugin is in use at uninstall time. |
| 353 FilePath current_version_path = extension_path.AppendASCII( | 295 if (installed_ids.count(extension_id) == 0) { |
| 354 kCurrentVersionFileName); | 296 LOG(INFO) << "Deleting unreferenced install for directory " |
| 355 if (!file_util::PathExists(current_version_path)) { | |
| 356 LOG(INFO) << "Deleting incomplete install for directory " | |
| 357 << WideToASCII(extension_path.ToWStringHack()) << "."; | 297 << WideToASCII(extension_path.ToWStringHack()) << "."; |
| 358 file_util::Delete(extension_path, true); // Recursive. | 298 file_util::Delete(extension_path, true); // Recursive. |
| 359 continue; | 299 continue; |
| 360 } | 300 } |
| 361 | 301 |
| 362 // Ignore directories that aren't valid IDs. | 302 // Ignore directories that aren't valid IDs. |
| 363 if (!Extension::IdIsValid(extension_id)) { | 303 if (!Extension::IdIsValid(extension_id)) { |
| 364 LOG(WARNING) << "Invalid extension ID encountered in extensions " | 304 LOG(WARNING) << "Invalid extension ID encountered in extensions " |
| 365 "directory: " << extension_id; | 305 "directory: " << extension_id; |
| 366 LOG(INFO) << "Deleting invalid extension directory " | 306 LOG(INFO) << "Deleting invalid extension directory " |
| (...skipping 35 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 402 "Cannot load extension with file or directory name %s." | 342 "Cannot load extension with file or directory name %s." |
| 403 "Filenames starting with \"_\" are reserved for use by the system", | 343 "Filenames starting with \"_\" are reserved for use by the system", |
| 404 filename.c_str()); | 344 filename.c_str()); |
| 405 return false; | 345 return false; |
| 406 } | 346 } |
| 407 } | 347 } |
| 408 | 348 |
| 409 return true; | 349 return true; |
| 410 } | 350 } |
| 411 | 351 |
| 412 } // extensionfile_util | 352 } // extension_file_util |
| OLD | NEW |