OLD | NEW |
---|---|
(Empty) | |
1 // Copyright (c) 2012 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/app_host_installer.h" | |
6 | |
7 #include "base/bind.h" | |
8 #include "base/callback.h" | |
9 #include "base/compiler_specific.h" | |
10 #include "chrome/common/extensions/extension.h" | |
11 | |
12 #if defined(OS_WIN) | |
13 #include <windows.h> | |
14 #include "base/logging.h" | |
15 #include "base/process_util.h" | |
16 #include "base/string16.h" | |
17 #include "base/win/object_watcher.h" | |
18 #include "base/win/registry.h" | |
19 #include "chrome/installer/launcher_support/chrome_launcher_support.h" | |
20 #include "content/public/browser/browser_thread.h" | |
21 #endif | |
22 | |
23 namespace { | |
24 | |
25 #if defined(OS_WIN) | |
26 | |
27 // TODO(huangs) Refactor the constants: http://crbug.com/148538 | |
28 const wchar_t kGoogleRegClientsKey[] = L"Software\\Google\\Update\\Clients"; | |
29 | |
30 // Copied from chrome_appid.cc. | |
31 const wchar_t kBinariesAppGuid[] = L"{4DC8B4CA-1BDA-483e-B5FA-D3C12E15B62D}"; | |
32 | |
33 // Copied from google_update_constants.cc | |
34 const wchar_t kRegCommandLineField[] = L"CommandLine"; | |
35 const wchar_t kRegCommandsKey[] = L"Commands"; | |
36 | |
37 // Copied from util_constants.cc. | |
38 const wchar_t kCmdQuickEnableApplicationHost[] = | |
39 L"quick-enable-application-host"; | |
40 | |
41 class QuickEnableWatcher : public base::win::ObjectWatcher::Delegate { | |
42 public: | |
43 QuickEnableWatcher(const base::Callback<void(bool)>& callback) | |
44 : callback_(callback) {} | |
45 | |
46 // base::win::ObjectWatcher::Delegate implementation. | |
47 virtual void OnObjectSignaled(HANDLE object) OVERRIDE { | |
48 int exit_code = 0; | |
49 base::TerminationStatus status( | |
50 base::GetTerminationStatus(object, &exit_code)); | |
51 if (status == base::TERMINATION_STATUS_NORMAL_TERMINATION) { | |
52 callback_.Run(true); | |
53 } else { | |
54 LOG(ERROR) << "App Host install failed, status = " << status | |
55 << ", exit code = " << exit_code; | |
56 callback_.Run(false); | |
57 } | |
58 callback_.Reset(); | |
59 } | |
60 | |
61 private: | |
62 base::Callback<void(bool)> callback_; | |
63 | |
64 DISALLOW_COPY_AND_ASSIGN(QuickEnableWatcher); | |
65 }; | |
66 | |
67 // Reads the path to app_host.exe from the value "UninstallString" within the | |
68 // App Host's "ClientState" registry key. Returns an empty string if the path | |
69 // does not exist or cannot be read. | |
70 string16 GetQuickEnableAppHostCommand(bool system_level) { | |
71 HKEY root_key = system_level ? HKEY_LOCAL_MACHINE : HKEY_CURRENT_USER; | |
72 string16 subkey(kGoogleRegClientsKey); | |
73 subkey.append(1, L'\\').append(kBinariesAppGuid) | |
74 .append(1, L'\\').append(kRegCommandsKey) | |
75 .append(1, L'\\').append(kCmdQuickEnableApplicationHost); | |
76 base::win::RegKey reg_key; | |
77 string16 cmd; | |
78 if (reg_key.Open(root_key, subkey.c_str(), | |
79 KEY_QUERY_VALUE) == ERROR_SUCCESS) { | |
80 // If read is unsuccessful, |cmd| remains empty. | |
81 reg_key.ReadValue(kRegCommandLineField, &cmd); | |
82 } | |
83 return cmd; | |
84 } | |
85 | |
86 // Launches the Google Update command to quick-enable App Host. | |
87 // Returns true if the command is launched. | |
88 bool LaunchQuickEnableAppHost(base::win::ScopedHandle* process) { | |
89 DCHECK(!process->IsValid()); | |
90 bool success = false; | |
91 | |
92 string16 cmd_str(GetQuickEnableAppHostCommand(true)); | |
93 if (cmd_str.empty()) { // Try user-level if absent from system-level. | |
94 cmd_str = GetQuickEnableAppHostCommand(false); | |
95 } | |
96 if (!cmd_str.empty()) { | |
97 VLOG(1) << "Quick-enabling application host: " << cmd_str; | |
98 if (!base::LaunchProcess(cmd_str, base::LaunchOptions(), | |
99 process->Receive())) { | |
100 LOG(ERROR) << "Failed to quick-enable application host."; | |
101 } | |
102 success = process->IsValid(); | |
103 } | |
104 return success; | |
105 } | |
106 | |
107 #endif | |
108 | |
109 } // namespace | |
110 | |
111 namespace extensions { | |
112 | |
113 #if defined(OS_WIN) | |
114 | |
115 using content::BrowserThread; | |
116 | |
117 // App Host installation is necessary if | |
118 // (1) |extension| is platform app, and | |
119 // (2) app_host_exe is not installed. | |
120 // Complexity arises because (1) can be checked from the caller thread, | |
121 // but (2) must be checked in the FILE thread. | |
122 // App Host installation is also launched in the FILE thread as an | |
123 // asynchronous process. Once installation completes, QuickEnableWatcher | |
124 // is notified (on the FILE thread). This in turn notifies the caller thread | |
125 // which then proceed with the next step specified by |final_step|. | |
126 class AppHostInstallerImpl { | |
erikwright (departed)
2012/10/05 02:21:56
Move AppHostInstallerImpl to app_host_installer_im
huangs
2012/10/05 19:46:08
Done.
| |
127 public: | |
128 AppHostInstallerImpl(); | |
129 | |
130 // Implementation of AppHostInstaller::InstallAppHostIfNecessaryThenCall(). | |
131 void InstallAppHostIfNecessaryThenCall( | |
erikwright (departed)
2012/10/05 02:21:56
Rename function and parameter.
"Implementation of
huangs
2012/10/05 19:46:08
Done.
| |
132 const Extension& extension, | |
133 const base::Callback<void(bool)>& final_step); | |
134 | |
135 private: | |
136 | |
erikwright (departed)
2012/10/05 02:21:56
remove blank line
huangs
2012/10/05 19:46:08
Done.
| |
137 // Runs on the FILE thread. Continues InstallAppHostIfNecessaryThenCall(). | |
138 void InstallAppHostIfNecessaryOnFileThreadThenCall( | |
erikwright (departed)
2012/10/05 02:21:56
rename as elsewhere.
huangs
2012/10/05 19:46:08
Done.
| |
139 const base::Callback<void(bool)>& final_step); | |
140 | |
141 // Runs on the FILE thread. Installs App Host, then calls | |
142 // |wrapped_final_step|. | |
143 void InstallAppHost(const base::Callback<void(bool)>& wrapped_final_step); | |
144 | |
145 // Called from the FILE thread, to return to caller thread | |
146 // and call NotifyOnOriginalThread(), passing |final_step| and |result|. | |
147 void PostResultToCallerThread(BrowserThread::ID thread_id, | |
erikwright (departed)
2012/10/05 02:21:56
This method and the next one could actually be fre
huangs
2012/10/05 19:46:08
Yes, but now it needs to be move elsewhere, which
| |
148 const base::Callback<void(bool)>& final_step, | |
erikwright (departed)
2012/10/05 02:21:56
final_step -> original_completion_callback
huangs
2012/10/05 19:46:08
Moot in the new flow, since there is only one comp
| |
149 bool result); | |
150 | |
151 // Callback on the caller thread, to call |final_step| and pass |result|. | |
152 void NotifyOnOriginalThread(const base::Callback<void(bool)>& final_step, | |
153 bool result); | |
154 | |
155 base::win::ScopedHandle process_; | |
156 base::win::ObjectWatcher watcher_; | |
157 scoped_ptr<base::win::ObjectWatcher::Delegate> delegate_; | |
158 | |
159 DISALLOW_COPY_AND_ASSIGN(AppHostInstallerImpl); | |
160 }; | |
161 | |
162 AppHostInstallerImpl::AppHostInstallerImpl() {} | |
163 | |
164 // (1) is checked here. If true, goes to FILE thread to proceed with (2), | |
165 // else called |final_step| with true. | |
166 void AppHostInstallerImpl::InstallAppHostIfNecessaryThenCall( | |
167 const Extension& extension, | |
168 const base::Callback<void(bool)>& final_step) { | |
169 if (extension.is_platform_app()) { | |
170 BrowserThread::ID caller_thread_id; | |
171 if (!BrowserThread::GetCurrentThreadIdentifier(&caller_thread_id)) { | |
172 NOTREACHED(); | |
173 } else { | |
174 // Wrapping |final_step| with caller thread and the callback. | |
175 base::Callback<void(bool)> wrapped_final_step( | |
176 base::Bind(&AppHostInstallerImpl::PostResultToCallerThread, | |
erikwright (departed)
2012/10/05 02:21:56
Need a comment to explain why this callback can ne
huangs
2012/10/05 19:46:08
Moot, since this is unused in the new flow.
| |
177 base::Unretained(this), caller_thread_id, final_step)); | |
178 // Continue check (2) in FILE thread. | |
179 BrowserThread::PostTask(BrowserThread::FILE, FROM_HERE, base::Bind( | |
180 &AppHostInstallerImpl::InstallAppHostIfNecessaryOnFileThreadThenCall, | |
181 base::Unretained(this), wrapped_final_step)); | |
182 } | |
183 } else { | |
184 final_step.Run(true); | |
185 } | |
186 } | |
187 | |
188 // (2) is checked here. If false, calls |wrapped_final_step| and finish, | |
189 // else install App Host. | |
190 void AppHostInstallerImpl::InstallAppHostIfNecessaryOnFileThreadThenCall( | |
191 const base::Callback<void(bool)>& wrapped_final_step) { | |
192 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE)); | |
193 if (chrome_launcher_support::IsAppHostPresent()) | |
194 wrapped_final_step.Run(true); | |
195 else | |
196 InstallAppHost(wrapped_final_step); | |
197 } | |
198 | |
199 // Main installation step, calls |wrapped_final_step| after. | |
200 void AppHostInstallerImpl::InstallAppHost( | |
201 const base::Callback<void(bool)>& wrapped_final_step) { | |
202 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE)); | |
203 DCHECK(!process_.IsValid()); | |
204 if (!LaunchQuickEnableAppHost(&process_)) { | |
205 wrapped_final_step.Run(false); | |
206 } else { | |
207 DCHECK(process_.IsValid()); | |
208 DCHECK(!delegate_.get()); | |
209 watcher_.StopWatching(); | |
210 delegate_.reset(new QuickEnableWatcher(wrapped_final_step)); | |
211 watcher_.StartWatching(process_, delegate_.get()); | |
212 } | |
213 } | |
214 | |
215 // Destination of |wrapped_final_step|, invokes NotifyOnOriginalThread() | |
216 // in the caller thread, passing |final_step| and |result|. | |
217 void AppHostInstallerImpl::PostResultToCallerThread( | |
218 BrowserThread::ID thread_id, | |
219 const base::Callback<void(bool)>& final_step, | |
220 bool result) { | |
221 BrowserThread::PostTask(thread_id, FROM_HERE, | |
222 base::Bind(&AppHostInstallerImpl::NotifyOnOriginalThread, | |
223 base::Unretained(this), final_step, result)); | |
224 } | |
225 | |
226 // Calls |final_step|, and passes |result| to complete flow. | |
227 void AppHostInstallerImpl::NotifyOnOriginalThread( | |
228 const base::Callback<void(bool)>& final_step, | |
229 bool result) { | |
230 final_step.Run(result); | |
231 } | |
232 | |
233 #else | |
234 | |
235 // Stub implementation for OS without App Host. | |
236 class AppHostInstallerImpl { | |
erikwright (departed)
2012/10/05 02:21:56
No impl required in the else case - see below...
huangs
2012/10/05 19:46:08
Done.
| |
237 public: | |
238 AppHostInstallerImpl::AppHostInstallerImpl(); | |
239 | |
240 void AppHostInstallerImpl::InstallAppHostIfNecessaryThenCall( | |
241 const Extension& extension, | |
242 const base::Callback<void(bool)>& final_step); | |
243 }; | |
244 | |
245 AppHostInstallerImpl::AppHostInstallerImpl() {} | |
246 | |
247 void AppHostInstallerImpl::InstallAppHostIfNecessaryThenCall( | |
248 const Extension& extension, | |
249 const base::Callback<void(bool)>& final_step) { | |
250 final_step.Run(true); | |
251 } | |
252 | |
253 #endif | |
254 | |
255 AppHostInstaller::AppHostInstaller() : impl_(new AppHostInstallerImpl()) {} | |
erikwright (departed)
2012/10/05 02:21:56
#if win, new AppHostInstallerImpl
#else 0
huangs
2012/10/05 19:46:08
Done.
| |
256 | |
257 AppHostInstaller::~AppHostInstaller() { | |
258 delete impl_; | |
erikwright (departed)
2012/10/05 02:21:56
if impl_
delete impl_
huangs
2012/10/05 19:46:08
Done.
| |
259 } | |
260 | |
261 void AppHostInstaller::InstallAppHostIfNecessaryThenCall( | |
262 const Extension& extension, | |
263 const base::Callback<void(bool)>& final_step) { | |
264 impl_->InstallAppHostIfNecessaryThenCall(extension, final_step); | |
erikwright (departed)
2012/10/05 02:21:56
if impl_
impl_->...
else
final_step.Run(true)
huangs
2012/10/05 19:46:08
Done, but need to use #if define(OS_WIN), since we
| |
265 } | |
266 | |
267 } // namespace extensions | |
OLD | NEW |