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/binaries_installer_internal.h" |
| 6 |
| 7 #include "base/logging.h" |
| 8 #include "base/win/scoped_bstr.h" |
| 9 #include "base/win/scoped_comptr.h" |
| 10 #include "google_update/google_update_idl.h" |
| 11 |
| 12 using base::win::ScopedBstr; |
| 13 using base::win::ScopedComPtr; |
| 14 |
| 15 namespace app_host { |
| 16 namespace internal { |
| 17 |
| 18 namespace { |
| 19 |
| 20 const wchar_t kBinariesAppId[] = L"{4DC8B4CA-1BDA-483e-B5FA-D3C12E15B62D}"; |
| 21 const wchar_t kAppHostAppId[] = L"{FDA71E6F-AC4C-4a00-8B70-9958A68906BF}"; |
| 22 |
| 23 HRESULT CheckIsBusy(IAppBundle* app_bundle, bool* is_busy) { |
| 24 VARIANT_BOOL variant_is_busy = VARIANT_TRUE; |
| 25 HRESULT hr = app_bundle->isBusy(&variant_is_busy); |
| 26 if (FAILED(hr)) |
| 27 LOG(ERROR) << "Failed to check app_bundle->isBusy: " << hr; |
| 28 else |
| 29 *is_busy = (variant_is_busy == VARIANT_TRUE); |
| 30 return hr; |
| 31 } |
| 32 |
| 33 HRESULT OnUpdateAvailable(IAppBundle* app_bundle) { |
| 34 // If the app bundle is busy we will just wait some more. |
| 35 bool is_busy = false; |
| 36 HRESULT hr = CheckIsBusy(app_bundle, &is_busy); |
| 37 if (SUCCEEDED(hr) && !is_busy) { |
| 38 hr = app_bundle->download(); |
| 39 if (FAILED(hr)) |
| 40 LOG(ERROR) << "Failed to initiate bundle download: " << hr; |
| 41 } |
| 42 return hr; |
| 43 } |
| 44 |
| 45 HRESULT OnReadyToInstall(IAppBundle* app_bundle) { |
| 46 // If the app bundle is busy we will just wait some more. |
| 47 bool is_busy = false; |
| 48 HRESULT hr = CheckIsBusy(app_bundle, &is_busy); |
| 49 if (SUCCEEDED(hr) && !is_busy) { |
| 50 hr = app_bundle->install(); |
| 51 if (FAILED(hr)) |
| 52 LOG(ERROR) << "Failed to initiate bundle install: " << hr; |
| 53 } |
| 54 return hr; |
| 55 } |
| 56 |
| 57 HRESULT OnError(ICurrentState* current_state) { |
| 58 LONG error_code; |
| 59 HRESULT hr = current_state->get_errorCode(&error_code); |
| 60 if (FAILED(hr)) { |
| 61 LOG(ERROR) << "Failed to retrieve bundle error code: " << hr; |
| 62 } else { |
| 63 hr = error_code; |
| 64 |
| 65 ScopedBstr completion_message; |
| 66 HRESULT completion_message_hr = |
| 67 current_state->get_completionMessage(completion_message.Receive()); |
| 68 if (FAILED(completion_message_hr)) { |
| 69 LOG(ERROR) << "Bundle installation failed with error " << hr |
| 70 << ". Error message retrieval failed with error: " |
| 71 << completion_message_hr; |
| 72 } else { |
| 73 LOG(ERROR) << "Bundle installation failed with error " << hr << ": " |
| 74 << completion_message; |
| 75 } |
| 76 } |
| 77 |
| 78 return hr; |
| 79 } |
| 80 |
| 81 HRESULT GetCurrentState(IApp* app, |
| 82 ICurrentState** current_state, |
| 83 CurrentState* state_value) { |
| 84 HRESULT hr = S_OK; |
| 85 |
| 86 ScopedComPtr<ICurrentState> temp_current_state; |
| 87 { |
| 88 ScopedComPtr<IDispatch> idispatch; |
| 89 hr = app->get_currentState(idispatch.Receive()); |
| 90 if (FAILED(hr)) { |
| 91 LOG(ERROR) << "Failed to get App Bundle state: " << hr; |
| 92 } else { |
| 93 hr = temp_current_state.QueryFrom(idispatch); |
| 94 if (FAILED(hr)) { |
| 95 LOG(ERROR) << "Unexpected error querying ICurrentState from " |
| 96 << "IApp::get_currentState return value: " << hr; |
| 97 } |
| 98 } |
| 99 } |
| 100 |
| 101 if (SUCCEEDED(hr)) { |
| 102 LONG long_state_value; |
| 103 hr = temp_current_state->get_stateValue(&long_state_value); |
| 104 if (FAILED(hr)) |
| 105 LOG(ERROR) << "Failed to get App Bundle state value: " << hr; |
| 106 *state_value = static_cast<CurrentState>(long_state_value); |
| 107 *current_state = temp_current_state.Detach(); |
| 108 } |
| 109 |
| 110 return hr; |
| 111 } |
| 112 |
| 113 HRESULT CreateInstalledApp(IAppBundle* app_bundle, |
| 114 const wchar_t* app_guid, |
| 115 IApp** app) { |
| 116 ScopedComPtr<IApp> temp_app; |
| 117 ScopedComPtr<IDispatch> idispatch; |
| 118 HRESULT hr = app_bundle->createInstalledApp(ScopedBstr(app_guid), |
| 119 idispatch.Receive()); |
| 120 if (FAILED(hr)) { |
| 121 LOG(ERROR) << "Failed to configure App Bundle: " << hr; |
| 122 } else { |
| 123 hr = temp_app.QueryFrom(idispatch); |
| 124 if (FAILED(hr)) { |
| 125 LOG(ERROR) << "Unexpected error querying IApp from " |
| 126 << "IAppBundle->createInstalledApp return value: " << hr; |
| 127 } else { |
| 128 *app = temp_app.Detach(); |
| 129 } |
| 130 } |
| 131 |
| 132 return hr; |
| 133 } |
| 134 |
| 135 } // namespace |
| 136 |
| 137 bool CheckIfDone(IAppBundle* app_bundle, IApp* app, HRESULT* hr) { |
| 138 ScopedComPtr<ICurrentState> current_state; |
| 139 CurrentState state_value; |
| 140 *hr = GetCurrentState(app, current_state.Receive(), &state_value); |
| 141 |
| 142 bool complete = false; |
| 143 |
| 144 if (SUCCEEDED(hr)) { |
| 145 switch (state_value) { |
| 146 case STATE_WAITING_TO_CHECK_FOR_UPDATE: |
| 147 case STATE_CHECKING_FOR_UPDATE: |
| 148 case STATE_WAITING_TO_DOWNLOAD: |
| 149 case STATE_RETRYING_DOWNLOAD: |
| 150 case STATE_DOWNLOADING: |
| 151 case STATE_WAITING_TO_INSTALL: |
| 152 case STATE_INSTALLING: |
| 153 case STATE_DOWNLOAD_COMPLETE: |
| 154 case STATE_EXTRACTING: |
| 155 case STATE_APPLYING_DIFFERENTIAL_PATCH: |
| 156 // These states will all transition on their own. |
| 157 break; |
| 158 |
| 159 case STATE_UPDATE_AVAILABLE: |
| 160 *hr = OnUpdateAvailable(app_bundle); |
| 161 break; |
| 162 |
| 163 case STATE_READY_TO_INSTALL: |
| 164 *hr = OnReadyToInstall(app_bundle); |
| 165 break; |
| 166 |
| 167 case STATE_NO_UPDATE: |
| 168 LOG(INFO) << "Google Update reports that the binaries are already " |
| 169 << "installed and up-to-date."; |
| 170 complete = true; |
| 171 break; |
| 172 |
| 173 case STATE_INSTALL_COMPLETE: |
| 174 complete = true; |
| 175 break; |
| 176 |
| 177 case STATE_ERROR: |
| 178 *hr = OnError(current_state); |
| 179 break; |
| 180 |
| 181 case STATE_INIT: |
| 182 case STATE_PAUSED: |
| 183 default: |
| 184 LOG(ERROR) << "Unexpected bundle state: " << state_value << "."; |
| 185 *hr = E_FAIL; |
| 186 break; |
| 187 } |
| 188 } |
| 189 |
| 190 return FAILED(*hr) || complete; |
| 191 } |
| 192 |
| 193 HRESULT CreateAppBundle(IGoogleUpdate3* update3, IAppBundle** app_bundle) { |
| 194 HRESULT hr = S_OK; |
| 195 |
| 196 ScopedComPtr<IAppBundle> temp_app_bundle; |
| 197 { |
| 198 ScopedComPtr<IDispatch> idispatch; |
| 199 hr = update3->createAppBundle(idispatch.Receive()); |
| 200 if (FAILED(hr)) { |
| 201 LOG(ERROR) << "Failed to createAppBundle: " << hr; |
| 202 } else { |
| 203 hr = temp_app_bundle.QueryFrom(idispatch); |
| 204 if (FAILED(hr)) { |
| 205 LOG(ERROR) << "Unexpected error querying IAppBundle from " |
| 206 << "IGoogleUpdate3->createAppBundle return value: " << hr; |
| 207 } |
| 208 } |
| 209 } |
| 210 |
| 211 if (SUCCEEDED(hr)) { |
| 212 hr = temp_app_bundle->initialize(); |
| 213 if (FAILED(hr)) |
| 214 LOG(ERROR) << "Failed to initialize App Bundle: " << hr; |
| 215 else |
| 216 *app_bundle = temp_app_bundle.Detach(); |
| 217 } |
| 218 |
| 219 return hr; |
| 220 } |
| 221 |
| 222 HRESULT GetAppHostApValue(IGoogleUpdate3* update3, BSTR* ap_value) { |
| 223 ScopedComPtr<IAppBundle> app_bundle; |
| 224 HRESULT hr = CreateAppBundle(update3, app_bundle.Receive()); |
| 225 if (SUCCEEDED(hr)) { |
| 226 ScopedComPtr<IApp> app; |
| 227 hr = CreateInstalledApp(app_bundle, kAppHostAppId, app.Receive()); |
| 228 if (SUCCEEDED(hr)) { |
| 229 hr = app->get_ap(ap_value); |
| 230 if (FAILED(hr)) |
| 231 LOG(ERROR) << "Failed to get the App Host AP value."; |
| 232 } |
| 233 } |
| 234 |
| 235 return hr; |
| 236 } |
| 237 |
| 238 HRESULT CreateBinariesIApp(IAppBundle* app_bundle, BSTR ap, IApp** app) { |
| 239 HRESULT hr = S_OK; |
| 240 |
| 241 ScopedComPtr<IApp> temp_app; |
| 242 { |
| 243 ScopedComPtr<IDispatch> idispatch; |
| 244 hr = app_bundle->createApp(ScopedBstr(kBinariesAppId), idispatch.Receive()); |
| 245 if (FAILED(hr)) { |
| 246 LOG(ERROR) << "Failed to configure App Bundle: " << hr; |
| 247 } else { |
| 248 hr = temp_app.QueryFrom(idispatch); |
| 249 if (FAILED(hr)) { |
| 250 LOG(ERROR) << "Unexpected error querying IApp from " |
| 251 << "IAppBundle->createApp return value: " << hr; |
| 252 } |
| 253 } |
| 254 } |
| 255 |
| 256 if (SUCCEEDED(hr)) { |
| 257 hr = temp_app->put_isEulaAccepted(VARIANT_TRUE); |
| 258 if (FAILED(hr)) |
| 259 LOG(ERROR) << "Failed to set 'EULA Accepted': " << hr; |
| 260 } |
| 261 |
| 262 if (SUCCEEDED(hr)) { |
| 263 hr = temp_app->put_ap(ap); |
| 264 if (FAILED(hr)) |
| 265 LOG(ERROR) << "Failed to set AP value: " << hr; |
| 266 } |
| 267 |
| 268 if (SUCCEEDED(hr)) |
| 269 *app = temp_app.Detach(); |
| 270 |
| 271 return hr; |
| 272 } |
| 273 |
| 274 HRESULT CreateGoogleUpdate3(IGoogleUpdate3** update3) { |
| 275 ScopedComPtr<IGoogleUpdate3> temp_update3; |
| 276 HRESULT hr = temp_update3.CreateInstance(CLSID_GoogleUpdate3UserClass); |
| 277 if (SUCCEEDED(hr)) { |
| 278 *update3 = temp_update3.Detach(); |
| 279 } else { |
| 280 // TODO(erikwright): Try in-proc to support running elevated? According |
| 281 // to update3_utils.cc (CreateGoogleUpdate3UserClass): |
| 282 // The primary reason for the LocalServer activation failing on Vista/Win7 |
| 283 // is that COM does not look at HKCU registration when the code is running |
| 284 // elevated. We fall back to an in-proc mode. The in-proc mode is limited to |
| 285 // one install at a time, so we use it only as a backup mechanism. |
| 286 LOG(ERROR) << "Failed to instantiate GoogleUpdate3: " << hr; |
| 287 } |
| 288 return hr; |
| 289 } |
| 290 |
| 291 HRESULT SelectBinariesApValue(IGoogleUpdate3* update3, BSTR* ap_value) { |
| 292 HRESULT hr = GetAppHostApValue(update3, ap_value); |
| 293 if (FAILED(hr)) { |
| 294 // TODO(erikwright): distinguish between AppHost not installed and an |
| 295 // error in GetAppHostApValue. |
| 296 // TODO(erikwright): Use stable by default when App Host support is in |
| 297 // stable. |
| 298 ScopedBstr temp_ap_value; |
| 299 if (NULL == temp_ap_value.Allocate(L"2.0-dev-multi-apphost")) { |
| 300 LOG(ERROR) << "Unexpected error in ScopedBstr::Allocate."; |
| 301 hr = E_FAIL; |
| 302 } else { |
| 303 *ap_value = temp_ap_value.Release(); |
| 304 hr = S_OK; |
| 305 } |
| 306 } |
| 307 |
| 308 return hr; |
| 309 } |
| 310 |
| 311 } // namespace internal |
| 312 } // namespace app_host |
OLD | NEW |