| 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/crx_installer.h" | 5 #include "chrome/browser/extensions/crx_installer.h" |
| 6 | 6 |
| 7 #include "app/l10n_util.h" | 7 #include "app/l10n_util.h" |
| 8 #include "base/file_util.h" | 8 #include "base/file_util.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 "base/task.h" | 11 #include "base/task.h" |
| 12 #include "chrome/browser/browser_process.h" | 12 #include "chrome/browser/browser_process.h" |
| 13 #include "chrome/browser/chrome_thread.h" |
| 13 #include "chrome/browser/extensions/extension_file_util.h" | 14 #include "chrome/browser/extensions/extension_file_util.h" |
| 14 #include "chrome/common/extensions/extension_error_reporter.h" | 15 #include "chrome/common/extensions/extension_error_reporter.h" |
| 15 #include "chrome/common/notification_service.h" | 16 #include "chrome/common/notification_service.h" |
| 16 #include "chrome/common/notification_type.h" | 17 #include "chrome/common/notification_type.h" |
| 17 #include "grit/chromium_strings.h" | 18 #include "grit/chromium_strings.h" |
| 18 #include "third_party/skia/include/core/SkBitmap.h" | 19 #include "third_party/skia/include/core/SkBitmap.h" |
| 19 #include "webkit/glue/image_decoder.h" | 20 #include "webkit/glue/image_decoder.h" |
| 20 | 21 |
| 21 namespace { | 22 namespace { |
| 22 // Helper function to delete files. This is used to avoid ugly casts which | 23 // Helper function to delete files. This is used to avoid ugly casts which |
| 23 // would be necessary with PostMessage since file_util::Delete is overloaded. | 24 // would be necessary with PostMessage since file_util::Delete is overloaded. |
| 24 static void DeleteFileHelper(const FilePath& path, bool recursive) { | 25 static void DeleteFileHelper(const FilePath& path, bool recursive) { |
| 25 file_util::Delete(path, recursive); | 26 file_util::Delete(path, recursive); |
| 26 } | 27 } |
| 27 } | 28 } |
| 28 | 29 |
| 29 void CrxInstaller::Start(const FilePath& crx_path, | 30 void CrxInstaller::Start(const FilePath& crx_path, |
| 30 const FilePath& install_directory, | 31 const FilePath& install_directory, |
| 31 Extension::Location install_source, | 32 Extension::Location install_source, |
| 32 const std::string& expected_id, | 33 const std::string& expected_id, |
| 33 bool delete_crx, | 34 bool delete_crx, |
| 34 bool allow_privilege_increase, | 35 bool allow_privilege_increase, |
| 35 MessageLoop* file_loop, | |
| 36 ExtensionsService* frontend, | 36 ExtensionsService* frontend, |
| 37 ExtensionInstallUI* client) { | 37 ExtensionInstallUI* client) { |
| 38 // Note: We don't keep a reference because this object manages its own | 38 // Note: We don't keep a reference because this object manages its own |
| 39 // lifetime. | 39 // lifetime. |
| 40 new CrxInstaller(crx_path, install_directory, install_source, expected_id, | 40 new CrxInstaller(crx_path, install_directory, install_source, expected_id, |
| 41 delete_crx, allow_privilege_increase, file_loop, frontend, | 41 delete_crx, allow_privilege_increase, frontend, client); |
| 42 client); | |
| 43 } | 42 } |
| 44 | 43 |
| 45 CrxInstaller::CrxInstaller(const FilePath& crx_path, | 44 CrxInstaller::CrxInstaller(const FilePath& crx_path, |
| 46 const FilePath& install_directory, | 45 const FilePath& install_directory, |
| 47 Extension::Location install_source, | 46 Extension::Location install_source, |
| 48 const std::string& expected_id, | 47 const std::string& expected_id, |
| 49 bool delete_crx, | 48 bool delete_crx, |
| 50 bool allow_privilege_increase, | 49 bool allow_privilege_increase, |
| 51 MessageLoop* file_loop, | |
| 52 ExtensionsService* frontend, | 50 ExtensionsService* frontend, |
| 53 ExtensionInstallUI* client) | 51 ExtensionInstallUI* client) |
| 54 : crx_path_(crx_path), | 52 : crx_path_(crx_path), |
| 55 install_directory_(install_directory), | 53 install_directory_(install_directory), |
| 56 install_source_(install_source), | 54 install_source_(install_source), |
| 57 expected_id_(expected_id), | 55 expected_id_(expected_id), |
| 58 delete_crx_(delete_crx), | 56 delete_crx_(delete_crx), |
| 59 allow_privilege_increase_(allow_privilege_increase), | 57 allow_privilege_increase_(allow_privilege_increase), |
| 60 file_loop_(file_loop), | |
| 61 ui_loop_(MessageLoop::current()), | |
| 62 frontend_(frontend), | 58 frontend_(frontend), |
| 63 client_(client) { | 59 client_(client) { |
| 64 | 60 |
| 65 extensions_enabled_ = frontend_->extensions_enabled(); | 61 extensions_enabled_ = frontend_->extensions_enabled(); |
| 66 | 62 |
| 67 unpacker_ = new SandboxedExtensionUnpacker( | 63 unpacker_ = new SandboxedExtensionUnpacker( |
| 68 crx_path, g_browser_process->resource_dispatcher_host(), this); | 64 crx_path, g_browser_process->resource_dispatcher_host(), this); |
| 69 | 65 |
| 70 file_loop->PostTask(FROM_HERE, NewRunnableMethod(unpacker_, | 66 ChromeThread::PostTask( |
| 71 &SandboxedExtensionUnpacker::Start)); | 67 ChromeThread::FILE, FROM_HERE, |
| 68 NewRunnableMethod(unpacker_, &SandboxedExtensionUnpacker::Start)); |
| 72 } | 69 } |
| 73 | 70 |
| 74 CrxInstaller::~CrxInstaller() { | 71 CrxInstaller::~CrxInstaller() { |
| 75 // Delete the temp directory and crx file as necessary. Note that the | 72 // Delete the temp directory and crx file as necessary. Note that the |
| 76 // destructor might be called on any thread, so we post a task to the file | 73 // destructor might be called on any thread, so we post a task to the file |
| 77 // thread to make sure the delete happens there. | 74 // thread to make sure the delete happens there. |
| 78 if (!temp_dir_.value().empty()) { | 75 if (!temp_dir_.value().empty()) { |
| 79 file_loop_->PostTask(FROM_HERE, NewRunnableFunction(&DeleteFileHelper, | 76 ChromeThread::PostTask( |
| 80 temp_dir_, true)); // recursive delete | 77 ChromeThread::FILE, FROM_HERE, |
| 78 NewRunnableFunction(&DeleteFileHelper, temp_dir_, true)); |
| 81 } | 79 } |
| 82 | 80 |
| 83 if (delete_crx_) { | 81 if (delete_crx_) { |
| 84 file_loop_->PostTask(FROM_HERE, NewRunnableFunction(&DeleteFileHelper, | 82 ChromeThread::PostTask( |
| 85 crx_path_, false)); // non-recursive delete | 83 ChromeThread::FILE, FROM_HERE, |
| 84 NewRunnableFunction(&DeleteFileHelper, crx_path_, false)); |
| 86 } | 85 } |
| 87 } | 86 } |
| 88 | 87 |
| 89 void CrxInstaller::OnUnpackFailure(const std::string& error_message) { | 88 void CrxInstaller::OnUnpackFailure(const std::string& error_message) { |
| 90 DCHECK(MessageLoop::current() == file_loop_); | 89 DCHECK(ChromeThread::CurrentlyOn(ChromeThread::FILE)); |
| 91 ReportFailureFromFileThread(error_message); | 90 ReportFailureFromFileThread(error_message); |
| 92 } | 91 } |
| 93 | 92 |
| 94 void CrxInstaller::OnUnpackSuccess(const FilePath& temp_dir, | 93 void CrxInstaller::OnUnpackSuccess(const FilePath& temp_dir, |
| 95 const FilePath& extension_dir, | 94 const FilePath& extension_dir, |
| 96 Extension* extension) { | 95 Extension* extension) { |
| 97 DCHECK(MessageLoop::current() == file_loop_); | 96 DCHECK(ChromeThread::CurrentlyOn(ChromeThread::FILE)); |
| 98 | 97 |
| 99 // Note: We take ownership of |extension| and |temp_dir|. | 98 // Note: We take ownership of |extension| and |temp_dir|. |
| 100 extension_.reset(extension); | 99 extension_.reset(extension); |
| 101 temp_dir_ = temp_dir; | 100 temp_dir_ = temp_dir; |
| 102 | 101 |
| 103 // The unpack dir we don't have to delete explicity since it is a child of | 102 // The unpack dir we don't have to delete explicity since it is a child of |
| 104 // the temp dir. | 103 // the temp dir. |
| 105 unpacked_extension_root_ = extension_dir; | 104 unpacked_extension_root_ = extension_dir; |
| 106 DCHECK(file_util::ContainsPath(temp_dir_, unpacked_extension_root_)); | 105 DCHECK(file_util::ContainsPath(temp_dir_, unpacked_extension_root_)); |
| 107 | 106 |
| (...skipping 13 matching lines...) Expand all Loading... |
| 121 extension->id().c_str(), | 120 extension->id().c_str(), |
| 122 expected_id_.c_str())); | 121 expected_id_.c_str())); |
| 123 return; | 122 return; |
| 124 } | 123 } |
| 125 | 124 |
| 126 if (client_.get()) { | 125 if (client_.get()) { |
| 127 FilePath icon_path = | 126 FilePath icon_path = |
| 128 extension_->GetIconPath(Extension::EXTENSION_ICON_LARGE).GetFilePath(); | 127 extension_->GetIconPath(Extension::EXTENSION_ICON_LARGE).GetFilePath(); |
| 129 DecodeInstallIcon(icon_path, &install_icon_); | 128 DecodeInstallIcon(icon_path, &install_icon_); |
| 130 } | 129 } |
| 131 ui_loop_->PostTask(FROM_HERE, NewRunnableMethod(this, | 130 ChromeThread::PostTask( |
| 132 &CrxInstaller::ConfirmInstall)); | 131 ChromeThread::UI, FROM_HERE, |
| 132 NewRunnableMethod(this, &CrxInstaller::ConfirmInstall)); |
| 133 } | 133 } |
| 134 | 134 |
| 135 // static | 135 // static |
| 136 void CrxInstaller::DecodeInstallIcon(const FilePath& large_icon_path, | 136 void CrxInstaller::DecodeInstallIcon(const FilePath& large_icon_path, |
| 137 scoped_ptr<SkBitmap>* result) { | 137 scoped_ptr<SkBitmap>* result) { |
| 138 if (large_icon_path.empty()) | 138 if (large_icon_path.empty()) |
| 139 return; | 139 return; |
| 140 | 140 |
| 141 std::string file_contents; | 141 std::string file_contents; |
| 142 if (!file_util::ReadFileToString(large_icon_path, &file_contents)) { | 142 if (!file_util::ReadFileToString(large_icon_path, &file_contents)) { |
| (...skipping 18 matching lines...) Expand all Loading... |
| 161 LOG(ERROR) << "Icon file has unexpected size: " | 161 LOG(ERROR) << "Icon file has unexpected size: " |
| 162 << IntToString(decoded->width()) << "x" | 162 << IntToString(decoded->width()) << "x" |
| 163 << IntToString(decoded->height()); | 163 << IntToString(decoded->height()); |
| 164 return; | 164 return; |
| 165 } | 165 } |
| 166 | 166 |
| 167 result->swap(decoded); | 167 result->swap(decoded); |
| 168 } | 168 } |
| 169 | 169 |
| 170 void CrxInstaller::ConfirmInstall() { | 170 void CrxInstaller::ConfirmInstall() { |
| 171 DCHECK(MessageLoop::current() == ui_loop_); | 171 DCHECK(ChromeThread::CurrentlyOn(ChromeThread::UI)); |
| 172 if (frontend_->extension_prefs()->IsExtensionBlacklisted(extension_->id())) { | 172 if (frontend_->extension_prefs()->IsExtensionBlacklisted(extension_->id())) { |
| 173 LOG(INFO) << "This extension: " << extension_->id() | 173 LOG(INFO) << "This extension: " << extension_->id() |
| 174 << " is blacklisted. Install failed."; | 174 << " is blacklisted. Install failed."; |
| 175 if (client_.get()) { | 175 if (client_.get()) { |
| 176 client_->OnInstallFailure("This extension is blacklisted."); | 176 client_->OnInstallFailure("This extension is blacklisted."); |
| 177 } | 177 } |
| 178 return; | 178 return; |
| 179 } | 179 } |
| 180 | 180 |
| 181 current_version_ = | 181 current_version_ = |
| 182 frontend_->extension_prefs()->GetVersionString(extension_->id()); | 182 frontend_->extension_prefs()->GetVersionString(extension_->id()); |
| 183 | 183 |
| 184 if (client_.get()) { | 184 if (client_.get()) { |
| 185 AddRef(); // balanced in ContinueInstall() and AbortInstall(). | 185 AddRef(); // balanced in ContinueInstall() and AbortInstall(). |
| 186 client_->ConfirmInstall(this, extension_.get(), install_icon_.get()); | 186 client_->ConfirmInstall(this, extension_.get(), install_icon_.get()); |
| 187 } else { | 187 } else { |
| 188 file_loop_->PostTask(FROM_HERE, NewRunnableMethod(this, | 188 ChromeThread::PostTask( |
| 189 &CrxInstaller::CompleteInstall)); | 189 ChromeThread::FILE, FROM_HERE, |
| 190 NewRunnableMethod(this, &CrxInstaller::CompleteInstall)); |
| 190 } | 191 } |
| 191 return; | 192 return; |
| 192 } | 193 } |
| 193 | 194 |
| 194 void CrxInstaller::ContinueInstall() { | 195 void CrxInstaller::ContinueInstall() { |
| 195 file_loop_->PostTask(FROM_HERE, NewRunnableMethod(this, | 196 ChromeThread::PostTask( |
| 196 &CrxInstaller::CompleteInstall)); | 197 ChromeThread::FILE, FROM_HERE, |
| 198 NewRunnableMethod(this, &CrxInstaller::CompleteInstall)); |
| 197 | 199 |
| 198 Release(); // balanced in ConfirmInstall(). | 200 Release(); // balanced in ConfirmInstall(). |
| 199 } | 201 } |
| 200 | 202 |
| 201 void CrxInstaller::AbortInstall() { | 203 void CrxInstaller::AbortInstall() { |
| 202 // Kill the theme loading bubble. | 204 // Kill the theme loading bubble. |
| 203 NotificationService* service = NotificationService::current(); | 205 NotificationService* service = NotificationService::current(); |
| 204 service->Notify(NotificationType::NO_THEME_DETECTED, | 206 service->Notify(NotificationType::NO_THEME_DETECTED, |
| 205 Source<CrxInstaller>(this), | 207 Source<CrxInstaller>(this), |
| 206 NotificationService::NoDetails()); | 208 NotificationService::NoDetails()); |
| 207 Release(); // balanced in ConfirmInstall(). | 209 Release(); // balanced in ConfirmInstall(). |
| 208 | 210 |
| 209 // We're done. Since we don't post any more tasks to ourself, our ref count | 211 // We're done. Since we don't post any more tasks to ourself, our ref count |
| 210 // should go to zero and we die. The destructor will clean up the temp dir. | 212 // should go to zero and we die. The destructor will clean up the temp dir. |
| 211 } | 213 } |
| 212 | 214 |
| 213 void CrxInstaller::CompleteInstall() { | 215 void CrxInstaller::CompleteInstall() { |
| 214 DCHECK(MessageLoop::current() == file_loop_); | 216 DCHECK(ChromeThread::CurrentlyOn(ChromeThread::FILE)); |
| 215 | 217 |
| 216 FilePath version_dir; | 218 FilePath version_dir; |
| 217 Extension::InstallType install_type = | 219 Extension::InstallType install_type = |
| 218 extension_file_util::CompareToInstalledVersion( | 220 extension_file_util::CompareToInstalledVersion( |
| 219 install_directory_, extension_->id(), current_version_, | 221 install_directory_, extension_->id(), current_version_, |
| 220 extension_->VersionString(), &version_dir); | 222 extension_->VersionString(), &version_dir); |
| 221 | 223 |
| 222 if (install_type == Extension::DOWNGRADE) { | 224 if (install_type == Extension::DOWNGRADE) { |
| 223 ReportFailureFromFileThread("Attempted to downgrade extension."); | 225 ReportFailureFromFileThread("Attempted to downgrade extension."); |
| 224 return; | 226 return; |
| (...skipping 20 matching lines...) Expand all Loading... |
| 245 std::string error; | 247 std::string error; |
| 246 extension_.reset(extension_file_util::LoadExtension(version_dir, true, | 248 extension_.reset(extension_file_util::LoadExtension(version_dir, true, |
| 247 &error)); | 249 &error)); |
| 248 DCHECK(error.empty()); | 250 DCHECK(error.empty()); |
| 249 extension_->set_location(install_source_); | 251 extension_->set_location(install_source_); |
| 250 | 252 |
| 251 ReportSuccessFromFileThread(); | 253 ReportSuccessFromFileThread(); |
| 252 } | 254 } |
| 253 | 255 |
| 254 void CrxInstaller::ReportFailureFromFileThread(const std::string& error) { | 256 void CrxInstaller::ReportFailureFromFileThread(const std::string& error) { |
| 255 DCHECK(MessageLoop::current() == file_loop_); | 257 DCHECK(ChromeThread::CurrentlyOn(ChromeThread::FILE)); |
| 256 ui_loop_->PostTask(FROM_HERE, NewRunnableMethod(this, | 258 ChromeThread::PostTask( |
| 257 &CrxInstaller::ReportFailureFromUIThread, error)); | 259 ChromeThread::UI, FROM_HERE, |
| 260 NewRunnableMethod(this, &CrxInstaller::ReportFailureFromUIThread, error)); |
| 258 } | 261 } |
| 259 | 262 |
| 260 void CrxInstaller::ReportFailureFromUIThread(const std::string& error) { | 263 void CrxInstaller::ReportFailureFromUIThread(const std::string& error) { |
| 261 DCHECK(MessageLoop::current() == ui_loop_); | 264 DCHECK(ChromeThread::CurrentlyOn(ChromeThread::UI)); |
| 262 | 265 |
| 263 NotificationService* service = NotificationService::current(); | 266 NotificationService* service = NotificationService::current(); |
| 264 service->Notify(NotificationType::EXTENSION_INSTALL_ERROR, | 267 service->Notify(NotificationType::EXTENSION_INSTALL_ERROR, |
| 265 Source<CrxInstaller>(this), | 268 Source<CrxInstaller>(this), |
| 266 Details<const std::string>(&error)); | 269 Details<const std::string>(&error)); |
| 267 | 270 |
| 268 // This isn't really necessary, it is only used because unit tests expect to | 271 // This isn't really necessary, it is only used because unit tests expect to |
| 269 // see errors get reported via this interface. | 272 // see errors get reported via this interface. |
| 270 // | 273 // |
| 271 // TODO(aa): Need to go through unit tests and clean them up too, probably get | 274 // TODO(aa): Need to go through unit tests and clean them up too, probably get |
| 272 // rid of this line. | 275 // rid of this line. |
| 273 ExtensionErrorReporter::GetInstance()->ReportError(error, false); // quiet | 276 ExtensionErrorReporter::GetInstance()->ReportError(error, false); // quiet |
| 274 | 277 |
| 275 if (client_.get()) | 278 if (client_.get()) |
| 276 client_->OnInstallFailure(error); | 279 client_->OnInstallFailure(error); |
| 277 } | 280 } |
| 278 | 281 |
| 279 void CrxInstaller::ReportOverinstallFromFileThread() { | 282 void CrxInstaller::ReportOverinstallFromFileThread() { |
| 280 DCHECK(MessageLoop::current() == file_loop_); | 283 DCHECK(ChromeThread::CurrentlyOn(ChromeThread::FILE)); |
| 281 ui_loop_->PostTask(FROM_HERE, NewRunnableMethod(this, | 284 ChromeThread::PostTask( |
| 282 &CrxInstaller::ReportOverinstallFromUIThread)); | 285 ChromeThread::UI, FROM_HERE, |
| 286 NewRunnableMethod(this, &CrxInstaller::ReportOverinstallFromUIThread)); |
| 283 } | 287 } |
| 284 | 288 |
| 285 void CrxInstaller::ReportOverinstallFromUIThread() { | 289 void CrxInstaller::ReportOverinstallFromUIThread() { |
| 286 DCHECK(MessageLoop::current() == ui_loop_); | 290 DCHECK(ChromeThread::CurrentlyOn(ChromeThread::UI)); |
| 287 | 291 |
| 288 NotificationService* service = NotificationService::current(); | 292 NotificationService* service = NotificationService::current(); |
| 289 service->Notify(NotificationType::EXTENSION_OVERINSTALL_ERROR, | 293 service->Notify(NotificationType::EXTENSION_OVERINSTALL_ERROR, |
| 290 Source<CrxInstaller>(this), | 294 Source<CrxInstaller>(this), |
| 291 Details<const FilePath>(&extension_->path())); | 295 Details<const FilePath>(&extension_->path())); |
| 292 | 296 |
| 293 if (client_.get()) | 297 if (client_.get()) |
| 294 client_->OnOverinstallAttempted(extension_.get()); | 298 client_->OnOverinstallAttempted(extension_.get()); |
| 295 | 299 |
| 296 frontend_->OnExtensionOverinstallAttempted(extension_->id()); | 300 frontend_->OnExtensionOverinstallAttempted(extension_->id()); |
| 297 } | 301 } |
| 298 | 302 |
| 299 void CrxInstaller::ReportSuccessFromFileThread() { | 303 void CrxInstaller::ReportSuccessFromFileThread() { |
| 300 DCHECK(MessageLoop::current() == file_loop_); | 304 DCHECK(ChromeThread::CurrentlyOn(ChromeThread::FILE)); |
| 301 ui_loop_->PostTask(FROM_HERE, NewRunnableMethod(this, | 305 ChromeThread::PostTask( |
| 302 &CrxInstaller::ReportSuccessFromUIThread)); | 306 ChromeThread::UI, FROM_HERE, |
| 307 NewRunnableMethod(this, &CrxInstaller::ReportSuccessFromUIThread)); |
| 303 } | 308 } |
| 304 | 309 |
| 305 void CrxInstaller::ReportSuccessFromUIThread() { | 310 void CrxInstaller::ReportSuccessFromUIThread() { |
| 306 DCHECK(MessageLoop::current() == ui_loop_); | 311 DCHECK(ChromeThread::CurrentlyOn(ChromeThread::UI)); |
| 307 | 312 |
| 308 // If there is a client, tell the client about installation. | 313 // If there is a client, tell the client about installation. |
| 309 if (client_.get()) | 314 if (client_.get()) |
| 310 client_->OnInstallSuccess(extension_.get()); | 315 client_->OnInstallSuccess(extension_.get()); |
| 311 | 316 |
| 312 // Tell the frontend about the installation and hand off ownership of | 317 // Tell the frontend about the installation and hand off ownership of |
| 313 // extension_ to it. | 318 // extension_ to it. |
| 314 frontend_->OnExtensionInstalled(extension_.release(), | 319 frontend_->OnExtensionInstalled(extension_.release(), |
| 315 allow_privilege_increase_); | 320 allow_privilege_increase_); |
| 316 | 321 |
| 317 // We're done. We don't post any more tasks to ourselves so we are deleted | 322 // We're done. We don't post any more tasks to ourselves so we are deleted |
| 318 // soon. | 323 // soon. |
| 319 } | 324 } |
| OLD | NEW |