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 |