Chromium Code Reviews| Index: chrome/browser/extensions/app_host/binaries_installer_internal.cc |
| diff --git a/chrome/browser/extensions/app_host/binaries_installer_internal.cc b/chrome/browser/extensions/app_host/binaries_installer_internal.cc |
| new file mode 100644 |
| index 0000000000000000000000000000000000000000..866b8b796d99732d860d43ac89d94b1b4a43619e |
| --- /dev/null |
| +++ b/chrome/browser/extensions/app_host/binaries_installer_internal.cc |
| @@ -0,0 +1,278 @@ |
| +// Copyright (c) 2012 The Chromium Authors. All rights reserved. |
| +// Use of this source code is governed by a BSD-style license that can be |
| +// found in the LICENSE file. |
| + |
| +#include "chrome/browser/extensions/app_host/binaries_installer_internal.h" |
| + |
| +#include "base/logging.h" |
| +#include "base/win/scoped_bstr.h" |
| +#include "base/win/scoped_comptr.h" |
| +#include "google_update/google_update_idl.h" |
| + |
| +using base::win::ScopedBstr; |
| +using base::win::ScopedComPtr; |
| + |
| +namespace app_host { |
| +namespace internal { |
| + |
| +namespace { |
| +const wchar_t kBinariesAppId[] = L"{4DC8B4CA-1BDA-483e-B5FA-D3C12E15B62D}"; |
| +const wchar_t kAppHostAppId[] = L"{FDA71E6F-AC4C-4a00-8B70-9958A68906BF}"; |
| +} // namespace |
| + |
| +bool CheckIfDone(IAppBundle* app_bundle, IApp* app, HRESULT* hr) { |
| + *hr = S_OK; |
| + ScopedComPtr<ICurrentState> current_state; |
| + { |
| + ScopedComPtr<IDispatch> idispatch; |
| + *hr = app->get_currentState(idispatch.Receive()); |
| + if (FAILED(*hr)) { |
| + LOG(ERROR) << "Failed to get App Bundle state: " << *hr; |
| + return true; |
| + } |
| + *hr = current_state.QueryFrom(idispatch); |
| + if (FAILED(*hr)) { |
| + LOG(ERROR) << "get_currentState return object is not an ICurrentState."; |
| + return true; |
| + } |
| + } |
| + |
| + LONG long_state_value; |
| + CurrentState state_value; |
| + *hr = current_state->get_stateValue(&long_state_value); |
| + if (FAILED(*hr)) { |
| + LOG(ERROR) << "Failed to get App Bundle state value: " << *hr; |
| + return true; |
| + } |
| + state_value = static_cast<CurrentState>(long_state_value); |
| + switch (state_value) { |
| + case STATE_WAITING_TO_CHECK_FOR_UPDATE: |
| + case STATE_CHECKING_FOR_UPDATE: |
| + case STATE_WAITING_TO_DOWNLOAD: |
| + case STATE_RETRYING_DOWNLOAD: |
| + case STATE_DOWNLOADING: |
| + case STATE_WAITING_TO_INSTALL: |
| + case STATE_INSTALLING: |
| + case STATE_DOWNLOAD_COMPLETE: |
| + case STATE_EXTRACTING: |
| + case STATE_APPLYING_DIFFERENTIAL_PATCH: |
| + // These states will all transition on their own. |
| + break; |
| + |
| + case STATE_UPDATE_AVAILABLE: |
| + // If the app bundle is 'busy' we will just wait some more. |
| + { |
| + VARIANT_BOOL is_busy = VARIANT_TRUE; |
| + *hr = app_bundle->isBusy(&is_busy); |
| + if (FAILED(*hr)) { |
| + LOG(ERROR) << "Failed to check app_bundle->isBusy: " << *hr; |
| + return true; |
| + } |
| + |
| + if (!is_busy) { |
| + *hr = app_bundle->download(); |
| + if (FAILED(*hr)) { |
| + LOG(ERROR) << "Failed to initiate bundle download: " << *hr; |
| + return true; |
| + } |
| + } |
| + } |
| + break; |
| + |
| + case STATE_READY_TO_INSTALL: |
| + // If the app bundle is 'busy' we will just wait some more. |
| + { |
| + VARIANT_BOOL is_busy = VARIANT_TRUE; |
| + *hr = app_bundle->isBusy(&is_busy); |
| + if (FAILED(*hr)) { |
| + LOG(ERROR) << "Failed to check app_bundle->isBusy: " << *hr; |
| + return true; |
|
robertshield
2012/08/16 13:08:16
as a readability thing, could we reduce the number
erikwright (departed)
2012/08/16 21:05:15
Done.
|
| + } |
| + |
| + if (!is_busy) { |
| + *hr = app_bundle->install(); |
| + if (FAILED(*hr)) { |
| + LOG(ERROR) << "Failed to initiate bundle install: " << *hr; |
| + return true; |
| + } |
| + } |
| + } |
| + break; |
| + |
| + case STATE_NO_UPDATE: |
| + LOG(INFO) << "Google Update reports that the binaries are already " |
| + << "installed and up-to-date."; |
| + *hr = S_OK; |
| + return true; |
| + |
| + case STATE_INSTALL_COMPLETE: |
| + return S_OK; |
| + *hr = S_OK; |
|
robertshield
2012/08/16 13:08:16
this should be warning about unreachable code
erikwright (departed)
2012/08/16 21:05:15
Done.
|
| + return true; |
| + |
| + case STATE_ERROR: { |
| + LONG error_code; |
| + *hr = current_state->get_errorCode(&error_code); |
| + if (FAILED(*hr)) { |
| + LOG(ERROR) << "Failed to retrieve bundle error code: " << *hr; |
| + return true; |
| + } |
| + *hr = error_code; |
| + ScopedBstr completion_message; |
| + HRESULT completion_message_hr = |
| + current_state->get_completionMessage(completion_message.Receive()); |
| + if (FAILED(completion_message_hr)) { |
| + LOG(ERROR) << "Bundle installation failed with error " << *hr |
| + << ". Error message retrieval failed with error: " |
| + << completion_message_hr; |
| + } else { |
| + LOG(ERROR) << "Bundle installation failed with error " << *hr << ": " |
| + << completion_message; |
| + } |
| + return true; |
| + } |
| + |
| + case STATE_INIT: |
| + case STATE_PAUSED: |
| + LOG(ERROR) << "Unexpected bundle state: " << state_value << "."; |
| + *hr = E_FAIL; |
| + return true; |
| + |
| + default: |
| + LOG(ERROR) << "Unknown bundle state: " << state_value << "."; |
| + *hr = E_FAIL; |
| + return true; |
| + } |
| + |
| + DCHECK_EQ(*hr, S_OK); |
| + return false; |
| +} |
| + |
| +HRESULT CreateAppBundle(IGoogleUpdate3* update3, IAppBundle** app_bundle) { |
| + HRESULT hr = S_OK; |
| + ScopedComPtr<IAppBundle> temp_app_bundle; |
| + { |
| + ScopedComPtr<IDispatch> idispatch; |
| + hr = update3->createAppBundle(idispatch.Receive()); |
| + if (FAILED(hr)) { |
| + LOG(ERROR) << "Failed to createAppBundle: " << hr; |
| + return hr; |
| + } |
| + |
| + hr = temp_app_bundle.QueryFrom(idispatch); |
| + if (FAILED(hr)) { |
| + LOG(ERROR) << "Unexpected error: " << hr; |
| + return hr; |
| + } |
| + } |
| + |
| + hr = temp_app_bundle->initialize(); |
| + if (FAILED(hr)) { |
| + LOG(ERROR) << "Failed to initialize App Bundle: " << hr; |
| + return hr; |
| + } |
| + |
| + *app_bundle = temp_app_bundle.Detach(); |
| + |
| + return S_OK; |
| +} |
| + |
| +HRESULT GetAppHostAPValue(IGoogleUpdate3* update3, BSTR* ap_value) { |
| + ScopedComPtr<IAppBundle> app_bundle; |
| + HRESULT hr = CreateAppBundle(update3, app_bundle.Receive()); |
| + if (FAILED(hr)) { |
| + return hr; |
| + } |
| + |
| + ScopedComPtr<IApp> app; |
| + { |
| + ScopedComPtr<IDispatch> idispatch; |
| + hr = app_bundle->createInstalledApp(ScopedBstr(kAppHostAppId), |
| + idispatch.Receive()); |
| + if (FAILED(hr)) { |
| + LOG(ERROR) << "Failed to configure App Bundle: " << hr; |
| + return hr; |
|
robertshield
2012/08/16 13:08:16
I would encourage you to rewrite stuff like this w
erikwright (departed)
2012/08/16 21:05:15
Done.
|
| + } |
| + hr = app.QueryFrom(idispatch); |
| + if (FAILED(hr)) { |
| + LOG(ERROR) << "Unexpected error: " << hr; |
| + return hr; |
| + } |
| + } |
| + hr = app->get_ap(ap_value); |
| + if (FAILED(hr)) { |
| + LOG(ERROR) << "Failed to get the App Host AP value."; |
| + return hr; |
| + } |
| + return S_OK; |
| +} |
| + |
| +HRESULT CreateBinariesIApp(IAppBundle* app_bundle, BSTR ap, IApp** app) { |
|
robertshield
2012/08/16 13:08:16
app, ap, and app_bundle :) maybe ap -> "channel_va
erikwright (departed)
2012/08/16 21:05:15
What does AP stand for?
robertshield
2012/08/16 21:15:53
That knowledge is lost in the sands of time. Omaha
|
| + HRESULT hr(S_OK); |
| + ScopedComPtr<IApp> temp_app; |
| + { |
| + ScopedComPtr<IDispatch> idispatch; |
| + hr = app_bundle->createApp(ScopedBstr(kBinariesAppId), idispatch.Receive()); |
| + if (FAILED(hr)) { |
| + LOG(ERROR) << "Failed to configure App Bundle: " << hr; |
| + return hr; |
| + } |
| + hr = temp_app.QueryFrom(idispatch); |
| + if (FAILED(hr)) { |
| + LOG(ERROR) << "Unexpected error: " << hr; |
| + return hr; |
| + } |
| + } |
| + hr = temp_app->put_isEulaAccepted(VARIANT_TRUE); |
| + if (FAILED(hr)) { |
| + LOG(ERROR) << "Failed to set 'EULA Accepted': " << hr; |
| + return hr; |
| + } |
| + hr = temp_app->put_ap(ap); |
| + if (FAILED(hr)) { |
| + LOG(ERROR) << "Failed to set AP value: " << hr; |
| + return hr; |
| + } |
| + *app = temp_app.Detach(); |
| + return S_OK; |
| +} |
| + |
| +HRESULT CreateGoogleUpdate3(IGoogleUpdate3** update3) { |
| + ScopedComPtr<IGoogleUpdate3> temp_update3; |
| + HRESULT hr = temp_update3.CreateInstance(CLSID_GoogleUpdate3UserClass); |
| + if (FAILED(hr)) { |
| + // TODO(erikwright): Try in-proc to support running elevated? According |
| + // to update3_utils.cc (CreateGoogleUpdate3UserClass): |
| + // The primary reason for the LocalServer activation failing on Vista/Win7 |
| + // is that COM does not look at HKCU registration when the code is running |
| + // elevated. We fall back to an in-proc mode. The in-proc mode is limited to |
| + // one install at a time, so we use it only as a backup mechanism. |
| + LOG(ERROR) << "Failed to instantiate GoogleUpdate3: " << hr; |
| + } else { |
| + *update3 = temp_update3.Detach(); |
| + } |
| + return hr; |
| +} |
| + |
| +HRESULT SelectBinariesApValue(IGoogleUpdate3* update3, BSTR* ap_value) { |
| + HRESULT hr = GetAppHostAPValue(update3, ap_value); |
| + if (FAILED(hr)) { |
| + // TODO(erikwright): distinguish between AppHost not installed and an |
| + // error in GetAppHostAPValue. |
| + // TODO(erikwright): Use stable by default when App Host support is in |
| + // stable. |
| + ScopedBstr temp_ap_value; |
| + if (NULL == temp_ap_value.Allocate(L"2.0-dev-multi-apphost")) { |
| + LOG(ERROR) << "Unexpected error in ScopedBstr::Allocate."; |
| + hr = E_FAIL; |
| + } else { |
| + *ap_value = temp_ap_value.Release(); |
| + hr = S_OK; |
| + } |
| + } |
| + |
| + return hr; |
| +} |
| + |
| +} // namespace internal |
| +} // namespace app_host |