Chromium Code Reviews

Side by Side Diff: chrome/browser/extensions/crx_installer.cc

Issue 160501: Try again to land: http://codereview.chromium.org/160483 (Closed)
Patch Set: Created 11 years, 4 months ago
Use n/p to move between diff chunks; N/P to move between comments.
Jump to:
View unified diff |
OLDNEW
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/extensions/extension_file_util.h" 13 #include "chrome/browser/extensions/extension_file_util.h"
14 #include "chrome/common/extensions/extension_error_reporter.h" 14 #include "chrome/common/extensions/extension_error_reporter.h"
15 #include "grit/chromium_strings.h" 15 #include "grit/chromium_strings.h"
16 16
17 #if defined(OS_WIN) 17 namespace {
18 #include "app/win_util.h" 18 // Helper function to delete files. This is used to avoid ugly casts which
19 #elif defined(OS_MACOSX) 19 // would be necessary with PostMessage since file_util::Delete is overloaded.
20 #include "base/scoped_cftyperef.h" 20 static void DeleteFileHelper(const FilePath& path, bool recursive) {
21 #include "base/sys_string_conversions.h" 21 file_util::Delete(path, recursive);
22 #include <CoreFoundation/CFUserNotification.h> 22 }
23 #endif 23 }
24
25 void CrxInstaller::Start(const FilePath& crx_path,
26 const FilePath& install_directory,
27 Extension::Location install_source,
28 const std::string& expected_id,
29 bool delete_crx,
30 MessageLoop* file_loop,
31 ExtensionsService* frontend,
32 CrxInstallerClient* client) {
33 // Note: We don't keep a reference because this object manages its own
34 // lifetime.
35 new CrxInstaller(crx_path, install_directory, install_source, expected_id,
36 delete_crx, file_loop, frontend, client);
37 }
24 38
25 CrxInstaller::CrxInstaller(const FilePath& crx_path, 39 CrxInstaller::CrxInstaller(const FilePath& crx_path,
26 const FilePath& install_directory, 40 const FilePath& install_directory,
27 Extension::Location install_source, 41 Extension::Location install_source,
28 const std::string& expected_id, 42 const std::string& expected_id,
29 bool extensions_enabled,
30 bool is_from_gallery,
31 bool show_prompts,
32 bool delete_crx, 43 bool delete_crx,
33 MessageLoop* file_loop, 44 MessageLoop* file_loop,
34 ExtensionsService* frontend) 45 ExtensionsService* frontend,
46 CrxInstallerClient* client)
35 : crx_path_(crx_path), 47 : crx_path_(crx_path),
36 install_directory_(install_directory), 48 install_directory_(install_directory),
37 install_source_(install_source), 49 install_source_(install_source),
38 expected_id_(expected_id), 50 expected_id_(expected_id),
39 extensions_enabled_(extensions_enabled),
40 is_from_gallery_(is_from_gallery),
41 show_prompts_(show_prompts),
42 delete_crx_(delete_crx), 51 delete_crx_(delete_crx),
43 file_loop_(file_loop), 52 file_loop_(file_loop),
44 ui_loop_(MessageLoop::current()) { 53 ui_loop_(MessageLoop::current()),
54 frontend_(frontend),
55 client_(client) {
45 56
46 // Note: this is a refptr so that we keep the frontend alive long enough to 57 extensions_enabled_ = frontend_->extensions_enabled();
47 // get our response. 58
48 frontend_ = frontend;
49 unpacker_ = new SandboxedExtensionUnpacker( 59 unpacker_ = new SandboxedExtensionUnpacker(
50 crx_path, g_browser_process->resource_dispatcher_host(), this); 60 crx_path, g_browser_process->resource_dispatcher_host(), this);
51 61
52 file_loop->PostTask(FROM_HERE, NewRunnableMethod(unpacker_, 62 file_loop->PostTask(FROM_HERE, NewRunnableMethod(unpacker_,
53 &SandboxedExtensionUnpacker::Start)); 63 &SandboxedExtensionUnpacker::Start));
54 } 64 }
55 65
66 CrxInstaller::~CrxInstaller() {
67 // Delete the temp directory and crx file as necessary. Note that the
68 // destructor might be called on any thread, so we post a task to the file
69 // thread to make sure the delete happens there.
70 if (!temp_dir_.value().empty()) {
71 file_loop_->PostTask(FROM_HERE, NewRunnableFunction(&DeleteFileHelper,
72 temp_dir_, true)); // recursive delete
73 }
74
75 if (delete_crx_) {
76 file_loop_->PostTask(FROM_HERE, NewRunnableFunction(&DeleteFileHelper,
77 crx_path_, false)); // non-recursive delete
78 }
79 }
80
56 void CrxInstaller::OnUnpackFailure(const std::string& error_message) { 81 void CrxInstaller::OnUnpackFailure(const std::string& error_message) {
82 DCHECK(MessageLoop::current() == file_loop_);
57 ReportFailureFromFileThread(error_message); 83 ReportFailureFromFileThread(error_message);
58 } 84 }
59 85
60 void CrxInstaller::OnUnpackSuccess(const FilePath& temp_dir, 86 void CrxInstaller::OnUnpackSuccess(const FilePath& temp_dir,
61 const FilePath& extension_dir, 87 const FilePath& extension_dir,
62 Extension* extension) { 88 Extension* extension) {
89 DCHECK(MessageLoop::current() == file_loop_);
90
63 // Note: We take ownership of |extension| and |temp_dir|. 91 // Note: We take ownership of |extension| and |temp_dir|.
64 extension_.reset(extension); 92 extension_.reset(extension);
65 temp_dir_ = temp_dir; 93 temp_dir_ = temp_dir;
66 94
67 // temp_dir_deleter is stack allocated instead of a member of CrxInstaller, so
68 // that delete always happens on the file thread.
69 ScopedTempDir temp_dir_deleter;
70 temp_dir_deleter.Set(temp_dir);
71
72 // The unpack dir we don't have to delete explicity since it is a child of 95 // The unpack dir we don't have to delete explicity since it is a child of
73 // the temp dir. 96 // the temp dir.
74 unpacked_extension_root_ = extension_dir; 97 unpacked_extension_root_ = extension_dir;
75 DCHECK(file_util::ContainsPath(temp_dir_, unpacked_extension_root_)); 98 DCHECK(file_util::ContainsPath(temp_dir_, unpacked_extension_root_));
76 99
77 // If we were supposed to delete the source file, we can do that now.
78 if (delete_crx_)
79 file_util::Delete(crx_path_, false); // non-recursive
80
81 // Determine whether to allow installation. We always allow themes and 100 // Determine whether to allow installation. We always allow themes and
82 // external installs. 101 // external installs.
83 if (!extensions_enabled_ && !extension->IsTheme() && 102 if (!extensions_enabled_ && !extension->IsTheme() &&
84 !Extension::IsExternalLocation(install_source_)) { 103 !Extension::IsExternalLocation(install_source_)) {
85 ReportFailureFromFileThread("Extensions are not enabled."); 104 ReportFailureFromFileThread("Extensions are not enabled.");
86 return; 105 return;
87 } 106 }
88 107
89 // Make sure the expected id matches. 108 // Make sure the expected id matches.
90 // TODO(aa): Also support expected version? 109 // TODO(aa): Also support expected version?
91 if (!expected_id_.empty() && expected_id_ != extension->id()) { 110 if (!expected_id_.empty() && expected_id_ != extension->id()) {
92 ReportFailureFromFileThread( 111 ReportFailureFromFileThread(StringPrintf(
93 StringPrintf("ID in new extension manifest (%s) does not match " 112 "ID in new extension manifest (%s) does not match expected id (%s)",
94 "expected id (%s)", 113 extension->id().c_str(),
95 extension->id().c_str(), 114 expected_id_.c_str()));
96 expected_id_.c_str()));
97 return; 115 return;
98 } 116 }
99 117
100 // Show the confirm UI if necessary. 118 if (client_.get()) {
101 // NOTE: We also special case themes to not have a dialog, because we show 119 ui_loop_->PostTask(FROM_HERE, NewRunnableMethod(this,
102 // a special infobar UI for them instead. 120 &CrxInstaller::ConfirmInstall));
103 if (show_prompts_ && !extension->IsTheme()) { 121 } else {
104 if (!ConfirmInstall()) 122 CompleteInstall();
105 return; // error reported by ConfirmInstall() 123 }
124 }
125
126 void CrxInstaller::ConfirmInstall() {
127 if (!client_->ConfirmInstall(extension_.get())) {
128 // We're done. Since we don't post any more tasks to ourselves, our ref
129 // count should go to zero and we die. The destructor will clean up the temp
130 // dir.
131 return;
106 } 132 }
107 133
108 CompleteInstall(); 134 file_loop_->PostTask(FROM_HERE, NewRunnableMethod(this,
109 } 135 &CrxInstaller::CompleteInstall));
110
111 bool CrxInstaller::ConfirmInstall() {
112 #if defined(OS_WIN)
113 if (win_util::MessageBox(GetForegroundWindow(),
114 L"Are you sure you want to install this extension?\n\n"
115 L"You should only install extensions from sources you trust.",
116 l10n_util::GetString(IDS_PRODUCT_NAME).c_str(),
117 MB_OKCANCEL) != IDOK) {
118 ReportFailureFromFileThread("User did not allow extension to be "
119 "installed.");
120 return false;
121 }
122 #elif defined(OS_MACOSX)
123 // Using CoreFoundation to do this dialog is unimaginably lame but will do
124 // until the UI is redone.
125 scoped_cftyperef<CFStringRef> product_name(
126 base::SysWideToCFStringRef(l10n_util::GetString(IDS_PRODUCT_NAME)));
127 CFOptionFlags response;
128 if (kCFUserNotificationAlternateResponse == CFUserNotificationDisplayAlert(
129 0, kCFUserNotificationCautionAlertLevel, NULL, NULL, NULL,
130 product_name,
131 CFSTR("Are you sure you want to install this extension?\n\n"
132 "This is a temporary message and it will be removed when "
133 "extensions UI is finalized."),
134 NULL, CFSTR("Cancel"), NULL, &response)) {
135 ReportFailureFromFileThread("User did not allow extension to be "
136 "installed.");
137 return false;
138 }
139 #endif // OS_*
140
141 return true;
142 } 136 }
143 137
144 void CrxInstaller::CompleteInstall() { 138 void CrxInstaller::CompleteInstall() {
139 DCHECK(MessageLoop::current() == file_loop_);
140
145 FilePath version_dir; 141 FilePath version_dir;
146 Extension::InstallType install_type = Extension::INSTALL_ERROR; 142 Extension::InstallType install_type = Extension::INSTALL_ERROR;
147 std::string error_msg; 143 std::string error_msg;
148 if (!extension_file_util::InstallExtension(unpacked_extension_root_, 144 if (!extension_file_util::InstallExtension(unpacked_extension_root_,
149 install_directory_, 145 install_directory_,
150 extension_->id(), 146 extension_->id(),
151 extension_->VersionString(), 147 extension_->VersionString(),
152 &version_dir, 148 &version_dir,
153 &install_type, &error_msg)) { 149 &install_type, &error_msg)) {
154 ReportFailureFromFileThread(error_msg); 150 ReportFailureFromFileThread(error_msg);
(...skipping 11 matching lines...)
166 if (install_type == Extension::REINSTALL) { 162 if (install_type == Extension::REINSTALL) {
167 // We use this as a signal to switch themes. 163 // We use this as a signal to switch themes.
168 ReportOverinstallFromFileThread(); 164 ReportOverinstallFromFileThread();
169 return; 165 return;
170 } 166 }
171 167
172 ReportSuccessFromFileThread(); 168 ReportSuccessFromFileThread();
173 } 169 }
174 170
175 void CrxInstaller::ReportFailureFromFileThread(const std::string& error) { 171 void CrxInstaller::ReportFailureFromFileThread(const std::string& error) {
172 DCHECK(MessageLoop::current() == file_loop_);
176 ui_loop_->PostTask(FROM_HERE, NewRunnableMethod(this, 173 ui_loop_->PostTask(FROM_HERE, NewRunnableMethod(this,
177 &CrxInstaller::ReportFailureFromUIThread, error)); 174 &CrxInstaller::ReportFailureFromUIThread, error));
178 } 175 }
179 176
180 void CrxInstaller::ReportFailureFromUIThread(const std::string& error) { 177 void CrxInstaller::ReportFailureFromUIThread(const std::string& error) {
181 ExtensionErrorReporter::GetInstance()->ReportError(error, show_prompts_); 178 DCHECK(MessageLoop::current() == ui_loop_);
179
180 // This isn't really necessary, it is only used because unit tests expect to
181 // see errors get reported via this interface.
182 //
183 // TODO(aa): Need to go through unit tests and clean them up too, probably get
184 // rid of this line.
185 ExtensionErrorReporter::GetInstance()->ReportError(error, false); // quiet
186
187 if (client_)
188 client_->OnInstallFailure(error);
182 } 189 }
183 190
184 void CrxInstaller::ReportOverinstallFromFileThread() { 191 void CrxInstaller::ReportOverinstallFromFileThread() {
185 ui_loop_->PostTask(FROM_HERE, NewRunnableMethod(frontend_.get(), 192 DCHECK(MessageLoop::current() == file_loop_);
186 &ExtensionsService::OnExtensionOverinstallAttempted, extension_->id())); 193 ui_loop_->PostTask(FROM_HERE, NewRunnableMethod(this,
194 &CrxInstaller::ReportOverinstallFromUIThread));
195 }
196
197 void CrxInstaller::ReportOverinstallFromUIThread() {
198 DCHECK(MessageLoop::current() == ui_loop_);
199
200 if (client_.get())
201 client_->OnOverinstallAttempted(extension_.get());
202
203 frontend_->OnExtensionOverinstallAttempted(extension_->id());
187 } 204 }
188 205
189 void CrxInstaller::ReportSuccessFromFileThread() { 206 void CrxInstaller::ReportSuccessFromFileThread() {
207 DCHECK(MessageLoop::current() == file_loop_);
208 ui_loop_->PostTask(FROM_HERE, NewRunnableMethod(this,
209 &CrxInstaller::ReportSuccessFromUIThread));
210 }
211
212 void CrxInstaller::ReportSuccessFromUIThread() {
213 DCHECK(MessageLoop::current() == ui_loop_);
214
215 // If there is a client, tell the client about installation.
216 if (client_.get())
217 client_->OnInstallSuccess(extension_.get());
218
190 // Tell the frontend about the installation and hand off ownership of 219 // Tell the frontend about the installation and hand off ownership of
191 // extension_ to it. 220 // extension_ to it.
192 ui_loop_->PostTask(FROM_HERE, NewRunnableMethod(frontend_.get(), 221 frontend_->OnExtensionInstalled(extension_.release());
193 &ExtensionsService::OnExtensionInstalled, extension_.release())); 222
223 // We're done. We don't post any more tasks to ourselves so we are deleted
224 // soon.
194 } 225 }
OLDNEW
« no previous file with comments | « chrome/browser/extensions/crx_installer.h ('k') | chrome/browser/extensions/extension_install_ui.h » ('j') | no next file with comments »

Powered by Google App Engine