Chromium Code Reviews

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

Issue 160483: Ever closer. Extract a client interface out of CrxInstaller and (Closed)
Patch Set: nits 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 void CrxInstaller::Start(const FilePath& crx_path,
18 #include "app/win_util.h" 18 const FilePath& install_directory,
19 #elif defined(OS_MACOSX) 19 Extension::Location install_source,
20 #include "base/scoped_cftyperef.h" 20 const std::string& expected_id,
21 #include "base/sys_string_conversions.h" 21 bool delete_crx,
22 #include <CoreFoundation/CFUserNotification.h> 22 MessageLoop* file_loop,
23 #endif 23 ExtensionsService* frontend,
24 CrxInstallerClient* client) {
25 // Note: We don't keep a reference because this object manages its own
26 // lifetime.
27 new CrxInstaller(crx_path, install_directory, install_source, expected_id,
28 delete_crx, file_loop, frontend, client);
29 }
24 30
25 CrxInstaller::CrxInstaller(const FilePath& crx_path, 31 CrxInstaller::CrxInstaller(const FilePath& crx_path,
26 const FilePath& install_directory, 32 const FilePath& install_directory,
27 Extension::Location install_source, 33 Extension::Location install_source,
28 const std::string& expected_id, 34 const std::string& expected_id,
29 bool extensions_enabled,
30 bool is_from_gallery,
31 bool show_prompts,
32 bool delete_crx, 35 bool delete_crx,
33 MessageLoop* file_loop, 36 MessageLoop* file_loop,
34 ExtensionsService* frontend) 37 ExtensionsService* frontend,
38 CrxInstallerClient* client)
35 : crx_path_(crx_path), 39 : crx_path_(crx_path),
36 install_directory_(install_directory), 40 install_directory_(install_directory),
37 install_source_(install_source), 41 install_source_(install_source),
38 expected_id_(expected_id), 42 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), 43 delete_crx_(delete_crx),
43 file_loop_(file_loop), 44 file_loop_(file_loop),
45 frontend_(frontend),
46 client_(client),
44 ui_loop_(MessageLoop::current()) { 47 ui_loop_(MessageLoop::current()) {
45 48
46 // Note: this is a refptr so that we keep the frontend alive long enough to 49 extensions_enabled_ = frontend_->extensions_enabled();
47 // get our response. 50
48 frontend_ = frontend;
49 unpacker_ = new SandboxedExtensionUnpacker( 51 unpacker_ = new SandboxedExtensionUnpacker(
50 crx_path, g_browser_process->resource_dispatcher_host(), this); 52 crx_path, g_browser_process->resource_dispatcher_host(), this);
51 53
52 file_loop->PostTask(FROM_HERE, NewRunnableMethod(unpacker_, 54 file_loop->PostTask(FROM_HERE, NewRunnableMethod(unpacker_,
53 &SandboxedExtensionUnpacker::Start)); 55 &SandboxedExtensionUnpacker::Start));
54 } 56 }
55 57
58 CrxInstaller::~CrxInstaller() {
59 // Delete the temp directory and crx file as necessary. Note that the
60 // destructor might be called on any thread, so we post a task to the file
61 // thread to make sure the delete happens there.
62 if (!temp_dir_.value().empty()) {
63 file_loop_->PostTask(FROM_HERE, NewRunnableFunction(
64 static_cast<bool (*) (const FilePath&, bool)>(&file_util::Delete),
Matt Perry 2009/07/31 22:33:39 why is this cast necessary? How about a wrapper f
65 temp_dir_, true)); // recursive delete
66 }
67
68 if (delete_crx_) {
69 file_loop_->PostTask(FROM_HERE, NewRunnableFunction(
70 static_cast<bool (*) (const FilePath&, bool)>(&file_util::Delete),
71 crx_path_, false)); // non-recursive delete
72 }
73 }
74
56 void CrxInstaller::OnUnpackFailure(const std::string& error_message) { 75 void CrxInstaller::OnUnpackFailure(const std::string& error_message) {
76 DCHECK(MessageLoop::current() == file_loop_);
57 ReportFailureFromFileThread(error_message); 77 ReportFailureFromFileThread(error_message);
58 } 78 }
59 79
60 void CrxInstaller::OnUnpackSuccess(const FilePath& temp_dir, 80 void CrxInstaller::OnUnpackSuccess(const FilePath& temp_dir,
61 const FilePath& extension_dir, 81 const FilePath& extension_dir,
62 Extension* extension) { 82 Extension* extension) {
83 DCHECK(MessageLoop::current() == file_loop_);
84
63 // Note: We take ownership of |extension| and |temp_dir|. 85 // Note: We take ownership of |extension| and |temp_dir|.
64 extension_.reset(extension); 86 extension_.reset(extension);
65 temp_dir_ = temp_dir; 87 temp_dir_ = temp_dir;
66 88
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 89 // The unpack dir we don't have to delete explicity since it is a child of
73 // the temp dir. 90 // the temp dir.
74 unpacked_extension_root_ = extension_dir; 91 unpacked_extension_root_ = extension_dir;
75 DCHECK(file_util::ContainsPath(temp_dir_, unpacked_extension_root_)); 92 DCHECK(file_util::ContainsPath(temp_dir_, unpacked_extension_root_));
76 93
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 94 // Determine whether to allow installation. We always allow themes and
82 // external installs. 95 // external installs.
83 if (!extensions_enabled_ && !extension->IsTheme() && 96 if (!extensions_enabled_ && !extension->IsTheme() &&
84 !Extension::IsExternalLocation(install_source_)) { 97 !Extension::IsExternalLocation(install_source_)) {
85 ReportFailureFromFileThread("Extensions are not enabled."); 98 ReportFailureFromFileThread("Extensions are not enabled.");
86 return; 99 return;
87 } 100 }
88 101
89 // Make sure the expected id matches. 102 // Make sure the expected id matches.
90 // TODO(aa): Also support expected version? 103 // TODO(aa): Also support expected version?
91 if (!expected_id_.empty() && expected_id_ != extension->id()) { 104 if (!expected_id_.empty() && expected_id_ != extension->id()) {
92 ReportFailureFromFileThread( 105 ReportFailureFromFileThread(StringPrintf(
93 StringPrintf("ID in new extension manifest (%s) does not match " 106 "ID in new extension manifest (%s) does not match expected id (%s)",
94 "expected id (%s)", 107 extension->id().c_str(),
95 extension->id().c_str(), 108 expected_id_.c_str()));
96 expected_id_.c_str()));
97 return; 109 return;
98 } 110 }
99 111
100 // Show the confirm UI if necessary. 112 if (client_.get()) {
101 // NOTE: We also special case themes to not have a dialog, because we show 113 ui_loop_->PostTask(FROM_HERE, NewRunnableMethod(this,
102 // a special infobar UI for them instead. 114 &CrxInstaller::ConfirmInstall));
103 if (show_prompts_ && !extension->IsTheme()) { 115 } else {
104 if (!ConfirmInstall()) 116 CompleteInstall();
105 return; // error reported by ConfirmInstall() 117 }
118 }
119
120 void CrxInstaller::ConfirmInstall() {
121 if (!client_->ConfirmInstall(extension_.get())) {
122 // We're done. Since we don't post any more tasks to ourselves, our ref
123 // count should go to zero and we die. The destructor will clean up the temp
124 // dir.
125 return;
106 } 126 }
107 127
108 CompleteInstall(); 128 file_loop_->PostTask(FROM_HERE, NewRunnableMethod(this,
109 } 129 &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 } 130 }
143 131
144 void CrxInstaller::CompleteInstall() { 132 void CrxInstaller::CompleteInstall() {
133 DCHECK(MessageLoop::current() == file_loop_);
134
145 FilePath version_dir; 135 FilePath version_dir;
146 Extension::InstallType install_type = Extension::INSTALL_ERROR; 136 Extension::InstallType install_type = Extension::INSTALL_ERROR;
147 std::string error_msg; 137 std::string error_msg;
148 if (!extension_file_util::InstallExtension(unpacked_extension_root_, 138 if (!extension_file_util::InstallExtension(unpacked_extension_root_,
149 install_directory_, 139 install_directory_,
150 extension_->id(), 140 extension_->id(),
151 extension_->VersionString(), 141 extension_->VersionString(),
152 &version_dir, 142 &version_dir,
153 &install_type, &error_msg)) { 143 &install_type, &error_msg)) {
154 ReportFailureFromFileThread(error_msg); 144 ReportFailureFromFileThread(error_msg);
(...skipping 11 matching lines...)
166 if (install_type == Extension::REINSTALL) { 156 if (install_type == Extension::REINSTALL) {
167 // We use this as a signal to switch themes. 157 // We use this as a signal to switch themes.
168 ReportOverinstallFromFileThread(); 158 ReportOverinstallFromFileThread();
169 return; 159 return;
170 } 160 }
171 161
172 ReportSuccessFromFileThread(); 162 ReportSuccessFromFileThread();
173 } 163 }
174 164
175 void CrxInstaller::ReportFailureFromFileThread(const std::string& error) { 165 void CrxInstaller::ReportFailureFromFileThread(const std::string& error) {
166 DCHECK(MessageLoop::current() == file_loop_);
176 ui_loop_->PostTask(FROM_HERE, NewRunnableMethod(this, 167 ui_loop_->PostTask(FROM_HERE, NewRunnableMethod(this,
177 &CrxInstaller::ReportFailureFromUIThread, error)); 168 &CrxInstaller::ReportFailureFromUIThread, error));
178 } 169 }
179 170
180 void CrxInstaller::ReportFailureFromUIThread(const std::string& error) { 171 void CrxInstaller::ReportFailureFromUIThread(const std::string& error) {
181 ExtensionErrorReporter::GetInstance()->ReportError(error, show_prompts_); 172 DCHECK(MessageLoop::current() == ui_loop_);
173
174 ExtensionErrorReporter::GetInstance()->ReportError(error, false); // quiet
Matt Perry 2009/07/31 22:33:39 does quiet/noisy make sense anymore now that we ha
175
176 if (client_)
177 client_->OnInstallFailure(error);
182 } 178 }
183 179
184 void CrxInstaller::ReportOverinstallFromFileThread() { 180 void CrxInstaller::ReportOverinstallFromFileThread() {
181 DCHECK(MessageLoop::current() == file_loop_);
185 ui_loop_->PostTask(FROM_HERE, NewRunnableMethod(frontend_.get(), 182 ui_loop_->PostTask(FROM_HERE, NewRunnableMethod(frontend_.get(),
186 &ExtensionsService::OnExtensionOverinstallAttempted, extension_->id())); 183 &ExtensionsService::OnExtensionOverinstallAttempted, extension_->id()));
187 } 184 }
188 185
189 void CrxInstaller::ReportSuccessFromFileThread() { 186 void CrxInstaller::ReportSuccessFromFileThread() {
187 DCHECK(MessageLoop::current() == file_loop_);
188
189 // If there is a client, tell the client about installation.
190 if (client_.get()) {
191 ui_loop_->PostTask(FROM_HERE, NewRunnableMethod(client_.get(),
192 &CrxInstallerClient::OnInstallSuccess, extension_.get()));
193 }
194
190 // Tell the frontend about the installation and hand off ownership of 195 // Tell the frontend about the installation and hand off ownership of
191 // extension_ to it. 196 // extension_ to it.
192 ui_loop_->PostTask(FROM_HERE, NewRunnableMethod(frontend_.get(), 197 ui_loop_->PostTask(FROM_HERE, NewRunnableMethod(frontend_.get(),
Matt Perry 2009/07/31 22:33:39 why not add a ReportSuccessFromUIThread, like you
193 &ExtensionsService::OnExtensionInstalled, extension_.release())); 198 &ExtensionsService::OnExtensionInstalled, extension_.release()));
199
200 // We're done. We don't post any more tasks to ourselves so we are deleted
201 // soon.
194 } 202 }
OLDNEW

Powered by Google App Engine