OLD | NEW |
(Empty) | |
| 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 |
| 3 // found in the LICENSE file. |
| 4 |
| 5 #include "chrome/browser/extensions/crx_installer.h" |
| 6 |
| 7 #include "app/l10n_util.h" |
| 8 #include "base/file_util.h" |
| 9 #include "base/scoped_temp_dir.h" |
| 10 #include "base/string_util.h" |
| 11 #include "base/task.h" |
| 12 #include "chrome/browser/browser_process.h" |
| 13 #include "chrome/browser/extensions/extension_file_util.h" |
| 14 #include "chrome/common/extensions/extension_error_reporter.h" |
| 15 #include "grit/chromium_strings.h" |
| 16 |
| 17 #if defined(OS_WIN) |
| 18 #include "app/win_util.h" |
| 19 #elif defined(OS_MACOSX) |
| 20 #include "base/scoped_cftyperef.h" |
| 21 #include "base/sys_string_conversions.h" |
| 22 #include <CoreFoundation/CFUserNotification.h> |
| 23 #endif |
| 24 |
| 25 CrxInstaller::CrxInstaller(const FilePath& crx_path, |
| 26 const FilePath& install_directory, |
| 27 Extension::Location install_source, |
| 28 const std::string& expected_id, |
| 29 bool extensions_enabled, |
| 30 bool is_from_gallery, |
| 31 bool show_prompts, |
| 32 bool delete_crx, |
| 33 MessageLoop* file_loop, |
| 34 ExtensionsService* frontend) |
| 35 : crx_path_(crx_path), |
| 36 install_directory_(install_directory), |
| 37 install_source_(install_source), |
| 38 expected_id_(expected_id), |
| 39 extensions_enabled_(extensions_enabled), |
| 40 is_from_gallery_(is_from_gallery), |
| 41 show_prompts_(show_prompts), |
| 42 file_loop_(file_loop), |
| 43 ui_loop_(MessageLoop::current()) { |
| 44 |
| 45 // Note: this is a refptr so that we keep the frontend alive long enough to |
| 46 // get our response. |
| 47 frontend_ = frontend; |
| 48 unpacker_ = new SandboxedExtensionUnpacker( |
| 49 crx_path, g_browser_process->resource_dispatcher_host(), this); |
| 50 |
| 51 file_loop->PostTask(FROM_HERE, NewRunnableMethod(unpacker_, |
| 52 &SandboxedExtensionUnpacker::Start)); |
| 53 } |
| 54 |
| 55 void CrxInstaller::OnUnpackFailure(const std::string& error_message) { |
| 56 ReportFailureFromFileThread(error_message); |
| 57 } |
| 58 |
| 59 void CrxInstaller::OnUnpackSuccess(const FilePath& temp_dir, |
| 60 const FilePath& extension_dir, |
| 61 Extension* extension) { |
| 62 // Note: We take ownership of |extension| and |temp_dir|. |
| 63 extension_.reset(extension); |
| 64 temp_dir_ = temp_dir; |
| 65 |
| 66 // temp_dir_deleter is stack allocated instead of a member of CrxInstaller, so |
| 67 // that delete always happens on the file thread. |
| 68 ScopedTempDir temp_dir_deleter; |
| 69 temp_dir_deleter.Set(temp_dir); |
| 70 |
| 71 // The unpack dir we don't have to delete explicity since it is a child of |
| 72 // the temp dir. |
| 73 unpacked_extension_root_ = extension_dir; |
| 74 DCHECK(file_util::ContainsPath(temp_dir_, unpacked_extension_root_)); |
| 75 |
| 76 // If we were supposed to delete the source file, we can do that now. |
| 77 if (delete_crx_) |
| 78 file_util::Delete(crx_path_, false); // non-recursive |
| 79 |
| 80 // Determine whether to allow installation. We always allow themes and |
| 81 // external installs. |
| 82 if (!extensions_enabled_ && !extension->IsTheme() && |
| 83 !Extension::IsExternalLocation(install_source_)) { |
| 84 ReportFailureFromFileThread("Extensions are not enabled."); |
| 85 return; |
| 86 } |
| 87 |
| 88 // Make sure the expected id matches. |
| 89 // TODO(aa): Also support expected version? |
| 90 if (!expected_id_.empty() && expected_id_ != extension->id()) { |
| 91 ReportFailureFromFileThread( |
| 92 StringPrintf("ID in new extension manifest (%s) does not match " |
| 93 "expected id (%s)", |
| 94 extension->id().c_str(), |
| 95 expected_id_.c_str())); |
| 96 return; |
| 97 } |
| 98 |
| 99 // Show the confirm UI if necessary. |
| 100 // NOTE: We also special case themes to not have a dialog, because we show |
| 101 // a special infobar UI for them instead. |
| 102 if (show_prompts_ && !extension->IsTheme()) { |
| 103 if (!ConfirmInstall()) |
| 104 return; // error reported by ConfirmInstall() |
| 105 } |
| 106 |
| 107 CompleteInstall(); |
| 108 } |
| 109 |
| 110 bool CrxInstaller::ConfirmInstall() { |
| 111 #if defined(OS_WIN) |
| 112 if (win_util::MessageBox(GetForegroundWindow(), |
| 113 L"Are you sure you want to install this extension?\n\n" |
| 114 L"You should only install extensions from sources you trust.", |
| 115 l10n_util::GetString(IDS_PRODUCT_NAME).c_str(), |
| 116 MB_OKCANCEL) != IDOK) { |
| 117 ReportFailureFromFileThread("User did not allow extension to be " |
| 118 "installed."); |
| 119 return false; |
| 120 } |
| 121 #elif defined(OS_MACOSX) |
| 122 // Using CoreFoundation to do this dialog is unimaginably lame but will do |
| 123 // until the UI is redone. |
| 124 scoped_cftyperef<CFStringRef> product_name( |
| 125 base::SysWideToCFStringRef(l10n_util::GetString(IDS_PRODUCT_NAME))); |
| 126 CFOptionFlags response; |
| 127 if (kCFUserNotificationAlternateResponse == CFUserNotificationDisplayAlert( |
| 128 0, kCFUserNotificationCautionAlertLevel, NULL, NULL, NULL, |
| 129 product_name, |
| 130 CFSTR("Are you sure you want to install this extension?\n\n" |
| 131 "This is a temporary message and it will be removed when " |
| 132 "extensions UI is finalized."), |
| 133 NULL, CFSTR("Cancel"), NULL, &response); |
| 134 ReportFailureFromFileThread("User did not allow extension to be " |
| 135 "installed."); |
| 136 return false; |
| 137 } |
| 138 #endif // OS_* |
| 139 |
| 140 return true; |
| 141 } |
| 142 |
| 143 void CrxInstaller::CompleteInstall() { |
| 144 FilePath version_dir; |
| 145 Extension::InstallType install_type = Extension::INSTALL_ERROR; |
| 146 std::string error_msg; |
| 147 if (!extension_file_util::InstallExtension(unpacked_extension_root_, |
| 148 install_directory_, |
| 149 extension_->id(), |
| 150 extension_->VersionString(), |
| 151 &version_dir, |
| 152 &install_type, &error_msg)) { |
| 153 ReportFailureFromFileThread(error_msg); |
| 154 return; |
| 155 } |
| 156 |
| 157 if (install_type == Extension::DOWNGRADE) { |
| 158 ReportFailureFromFileThread("Attempted to downgrade extension."); |
| 159 return; |
| 160 } |
| 161 |
| 162 extension_->set_path(version_dir); |
| 163 extension_->set_location(install_source_); |
| 164 |
| 165 if (install_type == Extension::REINSTALL) { |
| 166 // We use this as a signal to switch themes. |
| 167 ReportOverinstallFromFileThread(); |
| 168 return; |
| 169 } |
| 170 |
| 171 ReportSuccessFromFileThread(); |
| 172 } |
| 173 |
| 174 void CrxInstaller::ReportFailureFromFileThread(const std::string& error) { |
| 175 ui_loop_->PostTask(FROM_HERE, NewRunnableMethod(this, |
| 176 &CrxInstaller::ReportFailureFromUIThread, error)); |
| 177 } |
| 178 |
| 179 void CrxInstaller::ReportFailureFromUIThread(const std::string& error) { |
| 180 ExtensionErrorReporter::GetInstance()->ReportError(error, show_prompts_); |
| 181 } |
| 182 |
| 183 void CrxInstaller::ReportOverinstallFromFileThread() { |
| 184 ui_loop_->PostTask(FROM_HERE, NewRunnableMethod(frontend_.get(), |
| 185 &ExtensionsService::OnExtensionOverinstallAttempted, extension_->id())); |
| 186 } |
| 187 |
| 188 void CrxInstaller::ReportSuccessFromFileThread() { |
| 189 // Tell the frontend about the installation and hand off ownership of |
| 190 // extension_ to it. |
| 191 ui_loop_->PostTask(FROM_HERE, NewRunnableMethod(frontend_.get(), |
| 192 &ExtensionsService::OnExtensionInstalled, extension_.release())); |
| 193 } |
OLD | NEW |