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/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...) Loading... | |
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 } |
OLD | NEW |