| Index: client/bundle_installer.cc
|
| diff --git a/client/bundle_installer.cc b/client/bundle_installer.cc
|
| deleted file mode 100644
|
| index 970e67a2e643d8cd4aaed10df753a78f68f59dfe..0000000000000000000000000000000000000000
|
| --- a/client/bundle_installer.cc
|
| +++ /dev/null
|
| @@ -1,1138 +0,0 @@
|
| -// Copyright 2009-2010 Google Inc.
|
| -//
|
| -// Licensed under the Apache License, Version 2.0 (the "License");
|
| -// you may not use this file except in compliance with the License.
|
| -// You may obtain a copy of the License at
|
| -//
|
| -// http://www.apache.org/licenses/LICENSE-2.0
|
| -//
|
| -// Unless required by applicable law or agreed to in writing, software
|
| -// distributed under the License is distributed on an "AS IS" BASIS,
|
| -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
| -// See the License for the specific language governing permissions and
|
| -// limitations under the License.
|
| -// ========================================================================
|
| -
|
| -#include "omaha/client/bundle_installer.h"
|
| -#include <atlsafe.h>
|
| -#include "base/scoped_ptr.h"
|
| -#include "omaha/base/debug.h"
|
| -#include "omaha/base/error.h"
|
| -#include "omaha/base/logging.h"
|
| -#include "omaha/base/safe_format.h"
|
| -#include "omaha/base/scoped_ptr_address.h"
|
| -#include "omaha/client/client_utils.h"
|
| -#include "omaha/client/help_url_builder.h"
|
| -#include "omaha/client/resource.h"
|
| -#include "omaha/client/shutdown_events.h"
|
| -#include "omaha/common/const_goopdate.h"
|
| -#include "omaha/common/goopdate_utils.h"
|
| -#include "omaha/common/update3_utils.h"
|
| -#include "goopdate/omaha3_idl.h"
|
| -
|
| -namespace omaha {
|
| -
|
| -namespace internal {
|
| -
|
| -CString GetAppDisplayName(IApp* app) {
|
| - ASSERT1(app);
|
| -
|
| - CComBSTR app_name;
|
| - HRESULT hr = app->get_displayName(&app_name);
|
| - if (SUCCEEDED(hr)) {
|
| - ASSERT1(app_name.Length());
|
| - if (app_name.Length()) {
|
| - return CString(app_name);
|
| - }
|
| - } else {
|
| - CORE_LOG(LW, (_T("[get_displayName failed][0x%08x]"), hr));
|
| - }
|
| - return client_utils::GetDefaultApplicationName();
|
| -}
|
| -
|
| -CString BuildAppNameList(const std::vector<CString>& app_names) {
|
| - ASSERT1(!app_names.empty());
|
| -
|
| - CString list = app_names[0];
|
| - for (size_t i = 1; i < app_names.size(); ++i) {
|
| - list.FormatMessage(IDS_APPLICATION_NAME_CONCATENATION, list, app_names[i]);
|
| - }
|
| -
|
| - return list;
|
| -}
|
| -
|
| -CompletionCodes ConvertPostInstallActionToCompletionCode(
|
| - PostInstallAction post_install_action,
|
| - bool is_browser_type_supported,
|
| - bool is_error_state) {
|
| - switch (post_install_action) {
|
| - case POST_INSTALL_ACTION_EXIT_SILENTLY_ON_LAUNCH_COMMAND:
|
| - return COMPLETION_CODE_EXIT_SILENTLY_ON_LAUNCH_COMMAND;
|
| -
|
| - case POST_INSTALL_ACTION_LAUNCH_COMMAND:
|
| - return COMPLETION_CODE_LAUNCH_COMMAND;
|
| -
|
| - case POST_INSTALL_ACTION_RESTART_BROWSER:
|
| - if (is_browser_type_supported) {
|
| - return COMPLETION_CODE_RESTART_BROWSER;
|
| - } else {
|
| - return COMPLETION_CODE_RESTART_BROWSER_NOTICE_ONLY;
|
| - }
|
| -
|
| - case POST_INSTALL_ACTION_RESTART_ALL_BROWSERS:
|
| - if (is_browser_type_supported) {
|
| - return COMPLETION_CODE_RESTART_ALL_BROWSERS;
|
| - } else {
|
| - return COMPLETION_CODE_RESTART_ALL_BROWSERS_NOTICE_ONLY;
|
| - }
|
| -
|
| - case POST_INSTALL_ACTION_REBOOT:
|
| - // We don't support reboot, always notice_only
|
| - return COMPLETION_CODE_REBOOT_NOTICE_ONLY;
|
| -
|
| - case POST_INSTALL_ACTION_EXIT_SILENTLY:
|
| - return COMPLETION_CODE_EXIT_SILENTLY;
|
| -
|
| - case POST_INSTALL_ACTION_DEFAULT:
|
| - if (is_error_state) {
|
| - return COMPLETION_CODE_ERROR;
|
| - } else {
|
| - return COMPLETION_CODE_SUCCESS;
|
| - }
|
| -
|
| - default:
|
| - ASSERT1(false);
|
| - return COMPLETION_CODE_SUCCESS;
|
| - }
|
| -}
|
| -
|
| -HRESULT GetCompletionInformation(IApp* app,
|
| - CurrentState* current_state,
|
| - AppCompletionInfo* completion_info,
|
| - bool is_browser_type_supported) {
|
| - ASSERT1(app);
|
| - ASSERT1(current_state);
|
| - ASSERT1(completion_info);
|
| -
|
| - *current_state = STATE_INIT;
|
| -
|
| - // If the COM call fails, which may be why we encountered an error, this
|
| - // could be "Google Application", which is weird, especially in the mixed
|
| - // results UI.
|
| - completion_info->display_name = internal::GetAppDisplayName(app);
|
| -
|
| - CComBSTR app_id;
|
| - HRESULT hr = app->get_appId(&app_id);
|
| - if (FAILED(hr)) {
|
| - CORE_LOG(LE, (_T("[get_appId failed][0x%08x]"), hr));
|
| - return hr;
|
| - }
|
| -
|
| - completion_info->app_id = app_id;
|
| -
|
| - CComPtr<ICurrentState> icurrent_state;
|
| - hr = update3_utils::GetAppCurrentState(app, current_state, &icurrent_state);
|
| - if (FAILED(hr)) {
|
| - CORE_LOG(LE, (_T("[GetNextVersionState failed][0x%08x]"), hr));
|
| - return hr;
|
| - }
|
| -
|
| - ASSERT1(*current_state == STATE_INSTALL_COMPLETE ||
|
| - *current_state == STATE_NO_UPDATE ||
|
| - *current_state == STATE_ERROR);
|
| -
|
| - hr = icurrent_state->get_errorCode(&completion_info->error_code);
|
| - if (FAILED(hr)) {
|
| - return hr;
|
| - }
|
| -
|
| - LONG extra_code1 = 0;
|
| - hr = icurrent_state->get_extraCode1(&extra_code1);
|
| - if (FAILED(hr)) {
|
| - return hr;
|
| - }
|
| - completion_info->extra_code1 = static_cast<int>(extra_code1);
|
| -
|
| - CComBSTR message;
|
| - hr = icurrent_state->get_completionMessage(&message);
|
| - if (FAILED(hr)) {
|
| - return hr;
|
| - }
|
| - completion_info->completion_message = message;
|
| -
|
| - LONG installer_result = 0;
|
| - hr = icurrent_state->get_installerResultCode(&installer_result);
|
| - if (FAILED(hr)) {
|
| - return hr;
|
| - }
|
| - completion_info->installer_result_code = installer_result;
|
| -
|
| - VARIANT_BOOL is_canceled = VARIANT_TRUE;
|
| - hr = icurrent_state->get_isCanceled(&is_canceled);
|
| - if (FAILED(hr)) {
|
| - return hr;
|
| - }
|
| - completion_info->is_canceled = (is_canceled == VARIANT_TRUE);
|
| -
|
| - CComBSTR post_install_launch_command_line;
|
| - hr = icurrent_state->get_postInstallLaunchCommandLine(
|
| - &post_install_launch_command_line);
|
| - if (FAILED(hr)) {
|
| - return hr;
|
| - }
|
| - completion_info->post_install_launch_command_line =
|
| - post_install_launch_command_line;
|
| -
|
| - CORE_LOG(L5, (_T("[GetCompletionInformation][%s][state: u][code: 0x%08x]")
|
| - _T("[extra code: %d][message: '%s'][installer: %u]"),
|
| - app_id, *current_state,
|
| - completion_info->error_code,
|
| - completion_info->extra_code1,
|
| - message, installer_result));
|
| -
|
| - CComBSTR post_install_url;
|
| - hr = icurrent_state->get_postInstallUrl(&post_install_url);
|
| - if (FAILED(hr)) {
|
| - return hr;
|
| - }
|
| - completion_info->post_install_url = post_install_url;
|
| -
|
| - LONG post_install_action = static_cast<LONG>(POST_INSTALL_ACTION_DEFAULT);
|
| - hr = icurrent_state->get_postInstallAction(&post_install_action);
|
| - if (FAILED(hr)) {
|
| - return hr;
|
| - }
|
| - completion_info->completion_code = ConvertPostInstallActionToCompletionCode(
|
| - static_cast<PostInstallAction>(post_install_action),
|
| - is_browser_type_supported,
|
| - *current_state == STATE_ERROR);
|
| -
|
| - ASSERT1(SUCCEEDED(completion_info->error_code) ||
|
| - completion_info->installer_result_code == 0 ||
|
| - completion_info->error_code == GOOPDATEINSTALL_E_INSTALLER_FAILED);
|
| -
|
| - return S_OK;
|
| -}
|
| -
|
| -void GetAppCompletionMessage(IApp* app,
|
| - AppCompletionInfo* app_info,
|
| - bool is_browser_type_supported) {
|
| - ASSERT1(app);
|
| - ASSERT1(app_info);
|
| -
|
| - CurrentState current_state = STATE_INIT;
|
| -
|
| - HRESULT hr = GetCompletionInformation(app,
|
| - ¤t_state,
|
| - app_info,
|
| - is_browser_type_supported);
|
| - if (FAILED(hr)) {
|
| - CORE_LOG(LW, (_T("[GetCompletionInformation failed][0x%08x]"), hr));
|
| - // Treat the failure as an app failure.
|
| - app_info->completion_message.FormatMessage(
|
| - IDS_INSTALL_FAILED_WITH_ERROR_CODE,
|
| - hr);
|
| - app_info->error_code = hr;
|
| - app_info->extra_code1 = 0;
|
| - return;
|
| - }
|
| -
|
| - ASSERT1(current_state == STATE_INSTALL_COMPLETE ||
|
| - current_state == STATE_NO_UPDATE ||
|
| - current_state == STATE_ERROR);
|
| - ASSERT1(!app_info->completion_message.IsEmpty());
|
| - ASSERT1(current_state != STATE_NO_UPDATE ||
|
| - app_info->error_code == S_OK ||
|
| - app_info->error_code == GOOPDATE_E_UPDATE_DEFERRED);
|
| -
|
| - app_info->is_noupdate = current_state == STATE_NO_UPDATE;
|
| -
|
| - // If it is not a success or noupdate, we must report a failure in error_code.
|
| - if (current_state != STATE_INSTALL_COMPLETE &&
|
| - current_state != STATE_NO_UPDATE) {
|
| - ASSERT1(FAILED(app_info->error_code));
|
| - if (SUCCEEDED(app_info->error_code)) {
|
| - app_info->error_code = E_FAIL;
|
| - app_info->extra_code1 = 0;
|
| - }
|
| - }
|
| -
|
| - if (!app_info->completion_message.IsEmpty()) {
|
| - return;
|
| - }
|
| -
|
| - ASSERT(false, (_T("There should always be a completion message.")));
|
| -
|
| - // The message is empty. Return a default message.
|
| - if (current_state == STATE_INSTALL_COMPLETE) {
|
| - app_info->completion_message.LoadString(
|
| - IDS_APPLICATION_INSTALLED_SUCCESSFULLY);
|
| - } else if (current_state == STATE_NO_UPDATE) {
|
| - VERIFY1(app_info->completion_message.LoadString(IDS_NO_UPDATE_RESPONSE));
|
| - } else {
|
| - ASSERT1(current_state == STATE_ERROR);
|
| - app_info->completion_message.FormatMessage(
|
| - IDS_INSTALL_FAILED_WITH_ERROR_CODE,
|
| - E_FAIL);
|
| - }
|
| -}
|
| -
|
| -CString BuildResultStringForApps(uint32 group_name_resource_id,
|
| - const std::vector<CString>& apps) {
|
| - CString app_result_string;
|
| - ASSERT1(!apps.empty());
|
| -
|
| - // The header (i.e. "Failed:") should be in bold.
|
| - const TCHAR* const kAppListHeaderFormatOpen = _T("<b>");
|
| - const TCHAR* const kAppListHeaderFormatClose = _T("</b>");
|
| -
|
| - CString apps_list = internal::BuildAppNameList(apps);
|
| - app_result_string.FormatMessage(group_name_resource_id,
|
| - kAppListHeaderFormatOpen,
|
| - kAppListHeaderFormatClose,
|
| - apps_list);
|
| - return app_result_string;
|
| -}
|
| -
|
| -// TODO(omaha): If we end up leaving is_noupdate in AppCompletionInfo,
|
| -// eliminate is_only_no_update parameter.
|
| -CString GetBundleCompletionMessage(
|
| - const CString& bundle_name,
|
| - const std::vector<AppCompletionInfo>& apps_info,
|
| - bool is_only_no_update,
|
| - bool is_canceled) {
|
| - ASSERT1(!apps_info.empty());
|
| - // TODO(omaha): Enable this assert if GetUpdateAllAppsBundleName() is
|
| - // changed to a different string.
|
| - // ASSERT(bundle_name != SHORT_COMPANY_NAME _T(" Application"),
|
| - // (_T("Do not pass default bundle name to this function.")));
|
| -
|
| - CString bundle_message;
|
| -
|
| - if (is_only_no_update) {
|
| - VERIFY1(bundle_message.LoadString(IDS_NO_UPDATE_RESPONSE));
|
| - return bundle_message;
|
| - }
|
| -
|
| - std::vector<CString> succeeded_apps;
|
| - std::vector<CString> failed_apps;
|
| - std::vector<CString> canceled_apps;
|
| - CString first_failure_message;
|
| - for (size_t i = 0; i < apps_info.size(); ++i) {
|
| - const AppCompletionInfo& app_info = apps_info[i];
|
| - ASSERT1(!app_info.display_name.IsEmpty());
|
| - ASSERT1(!app_info.completion_message.IsEmpty());
|
| -
|
| - if (SUCCEEDED(app_info.error_code)) {
|
| - succeeded_apps.push_back(app_info.display_name);
|
| - } else if (app_info.is_canceled) {
|
| - canceled_apps.push_back(app_info.display_name);
|
| - } else {
|
| - failed_apps.push_back(app_info.display_name);
|
| -
|
| - // For now, we only display the first error message when all apps fail.
|
| - // Remember that message.
|
| - if (first_failure_message.IsEmpty()) {
|
| - first_failure_message = app_info.completion_message;
|
| - }
|
| - }
|
| - }
|
| -
|
| - CString canceled_apps_str;
|
| - if (!canceled_apps.empty()) {
|
| - canceled_apps_str = BuildResultStringForApps(
|
| - IDS_BUNDLE_MIXED_RESULTS_CANCELED_APPS, canceled_apps);
|
| - }
|
| -
|
| - CString succeeded_apps_str;
|
| - if (!succeeded_apps.empty()) {
|
| - succeeded_apps_str = BuildResultStringForApps(
|
| - IDS_BUNDLE_MIXED_RESULTS_SUCCEEDED_APPS, succeeded_apps);
|
| - }
|
| - CString failed_apps_str;
|
| - if (!failed_apps.empty()) {
|
| - failed_apps_str = BuildResultStringForApps(
|
| - IDS_BUNDLE_MIXED_RESULTS_FAILED_APPS, failed_apps);
|
| - }
|
| -
|
| - // For mixed results, display the succeeded, failed and canceled app lists on
|
| - // their own lines below the main message with a newline between the message
|
| - // and lists.
|
| - const TCHAR* const kLayoutForTwoGroups = _T("%s\n\n%s\n%s");
|
| - const TCHAR* const kLayoutForThreeGroups = _T("%s\n\n%s\n%s\n%s");
|
| -
|
| - if (!failed_apps_str.IsEmpty()) {
|
| - // At least one app fails to install, display a failure message.
|
| - uint32 message_id =
|
| - failed_apps.size() == 1 ?
|
| - IDS_BUNDLE_MIXED_RESULTS_MESSAGE_ONE_FAILURE :
|
| - IDS_BUNDLE_MIXED_RESULTS_MESSAGE_MULTIPLE_FAILURES;
|
| - CString message;
|
| - VERIFY1(message.LoadString(message_id));
|
| -
|
| - if (!succeeded_apps.empty() && !canceled_apps.empty()) {
|
| - SafeCStringFormat(&bundle_message, kLayoutForThreeGroups,
|
| - message,
|
| - succeeded_apps_str,
|
| - failed_apps_str,
|
| - canceled_apps_str);
|
| - } else if (!succeeded_apps.empty()) {
|
| - SafeCStringFormat(&bundle_message, kLayoutForTwoGroups,
|
| - message,
|
| - succeeded_apps_str,
|
| - failed_apps_str);
|
| - } else if (!canceled_apps.empty()) {
|
| - SafeCStringFormat(&bundle_message, kLayoutForTwoGroups,
|
| - message,
|
| - failed_apps_str,
|
| - canceled_apps_str);
|
| - } else {
|
| - bundle_message = first_failure_message;
|
| - }
|
| - } else if (!canceled_apps_str.IsEmpty()) {
|
| - // No failed app, but some are canceled.
|
| - if (!succeeded_apps_str.IsEmpty()) {
|
| - CString message;
|
| - VERIFY1(message.LoadString(
|
| - IDS_BUNDLE_INSTALLED_SUCCESSFULLY_AFTER_CANCEL));
|
| - SafeCStringFormat(&bundle_message, kLayoutForTwoGroups,
|
| - message,
|
| - succeeded_apps_str,
|
| - canceled_apps_str);
|
| - } else {
|
| - // Only canceled app, no UI will be displayed.
|
| - VERIFY1(bundle_message.LoadString(IDS_CANCELED));
|
| - }
|
| - } else {
|
| - // All successes. Display a client-specific completion message that includes
|
| - // the bundle name.
|
| - // There is no special handling of apps with noupdate.
|
| - ASSERT1(succeeded_apps.size() == apps_info.size());
|
| - ASSERT1(first_failure_message.IsEmpty());
|
| - if (is_canceled) {
|
| - VERIFY1(bundle_message.LoadString(
|
| - IDS_BUNDLE_INSTALLED_SUCCESSFULLY_AFTER_CANCEL));
|
| - } else if (bundle_name.IsEmpty()) {
|
| - VERIFY1(
|
| - bundle_message.LoadString(IDS_APPLICATION_INSTALLED_SUCCESSFULLY));
|
| - } else {
|
| - bundle_message.FormatMessage(IDS_BUNDLE_INSTALLED_SUCCESSFULLY,
|
| - bundle_name);
|
| - }
|
| - }
|
| -
|
| - ASSERT1(!bundle_message.IsEmpty());
|
| - return bundle_message;
|
| -}
|
| -
|
| -} // namespace internal
|
| -
|
| -BundleInstaller::BundleInstaller(HelpUrlBuilder* help_url_builder,
|
| - bool is_update_all_apps,
|
| - bool is_update_check_only,
|
| - bool is_browser_type_supported)
|
| - : help_url_builder_(help_url_builder),
|
| - observer_(NULL),
|
| - parent_window_(NULL),
|
| - state_(kInit),
|
| - result_(E_UNEXPECTED),
|
| - is_canceled_(false),
|
| - is_update_all_apps_(is_update_all_apps),
|
| - is_update_check_only_(is_update_check_only),
|
| - is_browser_type_supported_(is_browser_type_supported) {
|
| -}
|
| -
|
| -BundleInstaller::~BundleInstaller() {
|
| - Uninitialize();
|
| -}
|
| -
|
| -LRESULT BundleInstaller::OnTimer(UINT msg,
|
| - WPARAM wparam,
|
| - LPARAM,
|
| - BOOL& handled) { // NOLINT
|
| - VERIFY1(msg == WM_TIMER);
|
| - VERIFY1(wparam == kPollingTimerId);
|
| -
|
| - // set_observer() must be called before starting the message loop.
|
| - ASSERT1(observer_);
|
| -
|
| - if (!PollServer()) {
|
| - CORE_LOG(L1, (_T("[BundleInstaller::OnTimer][Stopping polling timer]")));
|
| -
|
| - // Ignore return value. KillTimer does not remove WM_TIMER messages already
|
| - // posted to the message queue.
|
| - KillTimer(kPollingTimerId);
|
| - }
|
| -
|
| - handled = true;
|
| - return 0;
|
| -}
|
| -
|
| -HRESULT BundleInstaller::Initialize() {
|
| - CORE_LOG(L3, (_T("[BundleInstaller::Initialize]")));
|
| -
|
| - // Create a message-only window for the timer. It is not visible,
|
| - // has no z-order, cannot be enumerated, and does not receive broadcast
|
| - // messages. The window simply dispatches messages.
|
| - const TCHAR kWndName[] = _T("{139455DE-14E2-4d54-93B5-9E6ADDC04B4E}");
|
| - if (!Create(HWND_MESSAGE, NULL, kWndName)) {
|
| - return HRESULTFromLastError();
|
| - }
|
| -
|
| - if (!SetTimer(kPollingTimerId, kPollingTimerPeriodMs)) {
|
| - return HRESULTFromLastError();
|
| - }
|
| -
|
| - return S_OK;
|
| -}
|
| -
|
| -void BundleInstaller::Uninitialize() {
|
| - if (IsWindow()) {
|
| - // This may fail if it was already killed when the bundle completed.
|
| - KillTimer(kPollingTimerId);
|
| -
|
| - DestroyWindow();
|
| - }
|
| -}
|
| -
|
| -void BundleInstaller::SetBundleParentWindow(HWND parent_window) {
|
| - ASSERT1(parent_window);
|
| - parent_window_ = parent_window;
|
| -}
|
| -
|
| -HRESULT BundleInstaller::ListenToShutdownEvent(bool is_machine) {
|
| - ASSERT1(!shutdown_callback_.get());
|
| - HRESULT hr = ShutdownEvents::CreateShutdownHandler(
|
| - is_machine, this, address(shutdown_callback_));
|
| - if (FAILED(hr)) {
|
| - CORE_LOG(LE, (_T("CreateShutdownHandler failed][0x%08x]"), hr));
|
| - }
|
| -
|
| - return hr;
|
| -}
|
| -
|
| -void BundleInstaller::StopListenToShutdownEvent(bool is_machine) {
|
| - UNREFERENCED_PARAMETER(is_machine);
|
| - shutdown_callback_.reset();
|
| -}
|
| -
|
| -HRESULT BundleInstaller::InstallBundle(bool is_machine,
|
| - bool listen_to_shutdown_event,
|
| - IAppBundle* app_bundle,
|
| - InstallProgressObserver* observer) {
|
| - ASSERT1(app_bundle);
|
| - ASSERT1(!app_bundle_);
|
| - app_bundle_.Attach(app_bundle);
|
| - app_bundle_->put_parentHWND(reinterpret_cast<ULONG_PTR>(parent_window_));
|
| -
|
| - observer_ = observer;
|
| -
|
| - if (listen_to_shutdown_event) {
|
| - ListenToShutdownEvent(is_machine);
|
| - }
|
| -
|
| - _pAtlModule->Lock();
|
| -
|
| - message_loop_.Run();
|
| - CORE_LOG(L2, (_T("[message_loop_.Run() returned]")));
|
| -
|
| - if (listen_to_shutdown_event) {
|
| - StopListenToShutdownEvent(is_machine);
|
| - }
|
| -
|
| - observer_ = NULL;
|
| -
|
| - // Installer should not hold any reference to app bundle after installation.
|
| - ASSERT1(!app_bundle_);
|
| -
|
| - CORE_LOG(L1, (_T("InstallBundle returning][0x%08x]"), result()));
|
| - return result();
|
| -}
|
| -
|
| -// Shutdown() is called from a thread in the OS threadpool. The PostMessage
|
| -// marshals the call over to the UI thread, which is where DoClose needs to be
|
| -// (and is) called from.
|
| -LRESULT BundleInstaller::OnClose(UINT,
|
| - WPARAM,
|
| - LPARAM,
|
| - BOOL& handled) { // NOLINT
|
| - CORE_LOG(L3, (_T("[BundleInstaller::OnClose]")));
|
| -
|
| - DoClose();
|
| - handled = true;
|
| - return 0;
|
| -}
|
| -
|
| -// Assumes that we can call OnComplete multiple times.
|
| -void BundleInstaller::DoClose() {
|
| - CORE_LOG(L1, (_T("[BundleInstaller::DoClose]")));
|
| - if (kComplete != state_) {
|
| - CORE_LOG(L1,
|
| - (_T("[UI closed before install completed. Likely canceled.]")));
|
| - DoCancel();
|
| - }
|
| -}
|
| -
|
| -void BundleInstaller::DoExit() {
|
| - CORE_LOG(L1, (_T("[BundleInstaller::DoExit]")));
|
| - ASSERT(state_ == kComplete, (_T("[State not complete yet, cannot exit!]")));
|
| -
|
| - _pAtlModule->Unlock();
|
| -}
|
| -
|
| -void BundleInstaller::DoCancel() {
|
| - CancelBundle();
|
| - is_canceled_ = true;
|
| -}
|
| -
|
| -// Unless a catastrophic/unrecoverable error occurs, bundle processing should
|
| -// continue, resulting in NotifyBundleInstallComplete() being called and S_OK
|
| -// being returned up the callstack.
|
| -//
|
| -// The following classes of errors can be returned by DoPollServer().
|
| -// * Errors returned by methods in this class (e.g. E_FAIL).
|
| -// The state_ may already have been set to kComplete
|
| -// * (e.g. GOOPDATE_E_NO_UPDATE_RESPONSE) or not (e.g. E_FAIL).
|
| -// * Errors returned by utility methods (e.g. by goopdate_utils methods).
|
| -// * COM errors returned due to API or sever failures.
|
| -// * Errors returned by COM methods (e.g. by install()).
|
| -//
|
| -// Specifically note that app errors are not returned up the call stack. These
|
| -// are handled and reported by NotifyBundleInstallComplete().
|
| -//
|
| -// Thus, for all errors returned by DoPollServer() except those returned by COM
|
| -// methods (not property methods), the client knows the error description.
|
| -// TODO(omaha3): What should we do for the COM method errors? There is currently
|
| -// no error API for the bundle. Also, errors returned by these calls may not
|
| -// be Omaha-specific errors. They could be COM errors (e.g. server unavailable),
|
| -// in which case calling a COM method would not work.
|
| -//
|
| -// DoPollServer() handles some client-side errors
|
| -// (e.g. GOOPDATE_E_NO_UPDATE_RESPONSE) by setting state_ to kComplete. All
|
| -// other errors must be handled by calling Complete().
|
| -// In all error cases, the bundle must be canceled.
|
| -bool BundleInstaller::PollServer() {
|
| - HRESULT hr = DoPollServer();
|
| -
|
| - // Handle the error unless it has already been handled.
|
| - if (FAILED(hr)) {
|
| - CORE_LOG(LE, (_T("[DoPollServer failed][0x%08x][%d]"), hr, state_));
|
| -
|
| - CancelBundle();
|
| -
|
| - if (state_ != kComplete) {
|
| - CString message;
|
| - message.FormatMessage(IDS_INSTALL_FAILED_WITH_ERROR_CODE, hr);
|
| - BundleCompletionInfo bundle_info(COMPLETION_CODE_ERROR, hr, message);
|
| - Complete(bundle_info);
|
| - }
|
| - }
|
| -
|
| - return state_ != kComplete;
|
| -}
|
| -
|
| -HRESULT BundleInstaller::result() {
|
| - ASSERT1(kComplete == state_);
|
| - return result_;
|
| -}
|
| -
|
| -// Polls the server for the state of the job and updates the UI.
|
| -// Not thread safe. There should only be one installation per process. Do we
|
| -// need to worry about multiple WM_TIMER events at the same time or does the
|
| -// message loop ensure this doesn't happen?
|
| -HRESULT BundleInstaller::DoPollServer() {
|
| - CORE_LOG(L3, (_T("[BundleInstaller::DoPollServer][%u]"), state_));
|
| - ASSERT1(observer_);
|
| - switch (state_) {
|
| - case kInit:
|
| - return HandleInitState();
|
| - case kProcessing:
|
| - return HandleProcessingState();
|
| - case kComplete:
|
| - return S_OK;
|
| - default:
|
| - ASSERT1(false);
|
| - return E_FAIL;
|
| - }
|
| -}
|
| -
|
| -// Checks whether the update check is complete, and if so, gets the number of
|
| -// apps with updates available.
|
| -HRESULT BundleInstaller::HandleUpdateAvailable() {
|
| - CORE_LOG(L3, (_T("[BundleInstaller::HandleUpdateAvailable]")));
|
| - ASSERT1(!apps_.empty());
|
| - ASSERT1(app_bundle_);
|
| -
|
| - if (is_update_all_apps_) {
|
| - // Nothing to do. The bundle will automatically continue on to the download.
|
| - return S_OK;
|
| - }
|
| -
|
| - // The bundle will not automatically continue. Initiate the download and
|
| - // install if appropriate.
|
| -
|
| - VARIANT_BOOL is_busy = VARIANT_TRUE;
|
| - HRESULT hr = app_bundle_->isBusy(&is_busy);
|
| - if (FAILED(hr)) {
|
| - return hr;
|
| - }
|
| -
|
| - if (is_busy) {
|
| - // An update is available, but other apps may still be being processed.
|
| - // Wait until bundle is not busy to indicate all apps have been processed.
|
| - return S_OK;
|
| - }
|
| -
|
| - // The only purpose of this call now is to call NotifyUpdateAvailable().
|
| - int num_updates = 0;
|
| - hr = HandleUpdateCheckResults(&num_updates);
|
| - if (FAILED(hr)) {
|
| - return hr;
|
| - }
|
| - CORE_LOG(L2, (_T("[Update check complete][updates: %d]"), num_updates));
|
| - ASSERT1(num_updates);
|
| -
|
| - if (is_update_check_only_) {
|
| - return NotifyBundleUpdateCheckOnlyComplete();
|
| - }
|
| -
|
| - // TODO(omaha): Do we handle an unexpected number of apps correctly?
|
| - // (i.e. apps_.size() != num_updates)
|
| - // This includes one of n apps reporting no update during an install.
|
| -
|
| - return app_bundle_->install();
|
| -}
|
| -
|
| -// Populates apps_. This must be done here because updateAllApps() adds apps
|
| -// to app_bundle_.
|
| -HRESULT BundleInstaller::HandleInitState() {
|
| - CORE_LOG(L3, (_T("[BundleInstaller::HandleInitState]")));
|
| - ASSERT1(observer_);
|
| - ASSERT1(app_bundle_);
|
| -
|
| - state_ = kProcessing;
|
| - HRESULT hr = is_update_all_apps_ ?
|
| - app_bundle_->updateAllApps() :
|
| - app_bundle_->checkForUpdate();
|
| - if (FAILED(hr)) {
|
| - return hr;
|
| - }
|
| - observer_->OnCheckingForUpdate();
|
| -
|
| - long count = 0; // NOLINT
|
| - hr = app_bundle_->get_Count(&count);
|
| - if (FAILED(hr)) {
|
| - return hr;
|
| - }
|
| - ASSERT1(count > 0);
|
| -
|
| - for (long i = 0; i != count; ++i) { // NOLINT
|
| - CComPtr<IApp> app;
|
| - hr = update3_utils::GetApp(app_bundle_, i, &app);
|
| - if (FAILED(hr)) {
|
| - return hr;
|
| - }
|
| - apps_.push_back(AdaptIApp(app));
|
| - }
|
| -
|
| - ASSERT1(!apps_.empty());
|
| - return S_OK;
|
| -}
|
| -
|
| -// Iterates through the apps until it finds one in a non-terminal state.
|
| -// If all apps are in a terminal state, calls NotifyBundleInstallComplete().
|
| -// Assumes that apps are processed serially in order. If this changes, we need
|
| -// to change the algorithm to avoid a bad UI experience.
|
| -// TODO(omaha): For things like creating a log that might be visible in the UI,
|
| -// we would need to guarantee that we always report each major state for each
|
| -// app. This would also require changing this algorithm. In addition, the COM
|
| -// server would need to return information for all previous AppStates
|
| -// (i.e. download progress while in the install phase).
|
| -HRESULT BundleInstaller::HandleProcessingState() {
|
| - CORE_LOG(L3, (_T("[BundleInstaller::HandleProcessingState]")));
|
| - ASSERT1(observer_);
|
| - ASSERT1(!apps_.empty());
|
| -
|
| - const ComPtrIApp* app_to_process = NULL;
|
| -
|
| - for (size_t i = 0; i < apps_.size(); ++i) {
|
| - CurrentState current_state = STATE_INIT;
|
| - CComPtr<ICurrentState> icurrent_state;
|
| - const ComPtrIApp& app = apps_[i];
|
| - HRESULT hr = update3_utils::GetAppCurrentState(app,
|
| - ¤t_state,
|
| - &icurrent_state);
|
| - if (FAILED(hr)) {
|
| - CORE_LOG(LE, (_T("[GetNextVersionState failed][0x%08x]"), hr));
|
| - return hr;
|
| - }
|
| -
|
| - switch (current_state) {
|
| - case STATE_INSTALL_COMPLETE:
|
| - case STATE_NO_UPDATE:
|
| - case STATE_ERROR:
|
| - // Terminal state - nothing to do for this app. Check the next app.
|
| - continue;
|
| - case STATE_WAITING_TO_CHECK_FOR_UPDATE:
|
| - case STATE_CHECKING_FOR_UPDATE:
|
| - return S_OK;
|
| - case STATE_UPDATE_AVAILABLE:
|
| - return HandleUpdateAvailable();
|
| - case STATE_WAITING_TO_DOWNLOAD:
|
| - observer_->OnWaitingToDownload(internal::GetAppDisplayName(app));
|
| - return S_OK;
|
| - case STATE_RETRYING_DOWNLOAD:
|
| - ASSERT(false, (_T("Unsupported")));
|
| - return S_OK; // Keep checking in order to be forwards compatible.
|
| - case STATE_DOWNLOADING:
|
| - case STATE_DOWNLOAD_COMPLETE:
|
| - case STATE_EXTRACTING:
|
| - case STATE_APPLYING_DIFFERENTIAL_PATCH:
|
| - case STATE_READY_TO_INSTALL:
|
| - return NotifyDownloadProgress(app, icurrent_state);
|
| - case STATE_WAITING_TO_INSTALL:
|
| - return NotifyWaitingToInstall(app);
|
| - case STATE_INSTALLING:
|
| - return NotifyInstallProgress(app, icurrent_state);
|
| - case STATE_PAUSED:
|
| - ASSERT(false, (_T("Unsupported")));
|
| - return S_OK; // Keep checking in order to be forwards compatible.
|
| - case STATE_INIT:
|
| - default:
|
| - ASSERT1(false);
|
| - return S_OK; // Keep checking in order to be forwards compatible.
|
| - // Cannot support new terminal states, though.
|
| - }
|
| - }
|
| -
|
| - // No apps were in non-terminal states. The bundle may still be busy, though,
|
| - // because app states are updated separately from the bundle state.
|
| - // TODO(omaha): Should we wait for the bundle to complete? If not, we may need
|
| - // to add appropriate waits and checks for any completion UI bundle actions
|
| - // that require that the AppBundle is not busy.
|
| -
|
| - return NotifyBundleInstallComplete();
|
| -}
|
| -
|
| -HRESULT BundleInstaller::NotifyUpdateAvailable(IApp* app) {
|
| - CORE_LOG(L3, (_T("[BundleInstaller::NotifyUpdateAvailable]")));
|
| - ASSERT1(app);
|
| - ASSERT1(observer_);
|
| -
|
| - CComPtr<IAppVersion> next_version;
|
| - HRESULT hr = update3_utils::GetNextAppVersion(app, &next_version);
|
| - if (FAILED(hr)) {
|
| - return hr;
|
| - }
|
| -
|
| - CComBSTR ver;
|
| - hr = next_version->get_version(&ver);
|
| - if (FAILED(hr)) {
|
| - return hr;
|
| - }
|
| -
|
| - CORE_LOG(L3, (_T("[Next Version Update Available][%s]"), CString(ver)));
|
| -
|
| - // TODO(omaha3): Until we force app teams to provide a version, the string
|
| - // may be empty.
|
| - observer_->OnUpdateAvailable(internal::GetAppDisplayName(app), CString(ver));
|
| - return S_OK;
|
| -}
|
| -
|
| -HRESULT BundleInstaller::NotifyDownloadProgress(IApp* app,
|
| - ICurrentState* icurrent_state) {
|
| - CORE_LOG(L3, (_T("[BundleInstaller::NotifyDownloadProgress]")));
|
| - ASSERT1(icurrent_state);
|
| - ASSERT1(observer_);
|
| -
|
| - int time_remaining_ms = kCurrentStateProgressUnknown;
|
| - int percentage = 0;
|
| - time64 next_retry_time = 0;
|
| - GetAppDownloadProgress(icurrent_state,
|
| - &time_remaining_ms,
|
| - &percentage,
|
| - &next_retry_time);
|
| - if (next_retry_time != 0) {
|
| - observer_->OnWaitingRetryDownload(internal::GetAppDisplayName(app),
|
| - next_retry_time);
|
| - } else {
|
| - observer_->OnDownloading(internal::GetAppDisplayName(app),
|
| - time_remaining_ms,
|
| - percentage);
|
| - }
|
| - return S_OK;
|
| -}
|
| -
|
| -// Starts the install unless the UI prevents the install from starting, in which
|
| -// case it remains in the same state to be checked again next cycle.
|
| -HRESULT BundleInstaller::NotifyWaitingToInstall(IApp* app) {
|
| - CORE_LOG(L3, (_T("[BundleInstaller::NotifyWaitingToInstall]")));
|
| - ASSERT1(app);
|
| - ASSERT1(observer_);
|
| -
|
| - // can_start_install is ignored because download and install are no longer
|
| - // discrete phases.
|
| - bool can_start_install = false;
|
| - observer_->OnWaitingToInstall(internal::GetAppDisplayName(app),
|
| - &can_start_install);
|
| -
|
| - return S_OK;
|
| -}
|
| -
|
| -HRESULT BundleInstaller::NotifyInstallProgress(IApp* app,
|
| - ICurrentState* icurrent_state) {
|
| - CORE_LOG(L3, (_T("[BundleInstaller::NotifyInstallProgress]")));
|
| - ASSERT1(app);
|
| - ASSERT1(icurrent_state);
|
| - ASSERT1(observer_);
|
| -
|
| - // TODO(omaha3): Get the install progress and time for the current app.
|
| - // Handle kCurrentStateProgressUnknown appropriately.
|
| - UNREFERENCED_PARAMETER(icurrent_state);
|
| -
|
| - observer_->OnInstalling(internal::GetAppDisplayName(app));
|
| -
|
| - return S_OK;
|
| -}
|
| -
|
| -// Only used by legacy OnDemand.
|
| -HRESULT BundleInstaller::NotifyBundleUpdateCheckOnlyComplete() {
|
| - CORE_LOG(L3, (_T("[BundleInstaller::NotifyBundleUpdateCheckOnlyComplete]")));
|
| -
|
| - BundleCompletionInfo info(COMPLETION_CODE_SUCCESS,
|
| - S_OK,
|
| - _T("OK")); // Not used by legacy OnDemand.
|
| -
|
| - Complete(info);
|
| - return S_OK;
|
| -}
|
| -
|
| -// Assumes the AppBundle has completed and all apps are in a terminal state.
|
| -// In other words, AppBundle::Cancel does not need to be called.
|
| -// For now, append the completion message(s) from each app. If any app failed,
|
| -// display the failure UI and set this objects result to the app error.
|
| -// TODO(omaha3): Improve the UI for bundles. It does not currently handle
|
| -// restart browser, etc. Nor is the output production-ready as it just appends
|
| -// the completion strings and assumes L2R. We at least need prettier printing,
|
| -// maybe each message on its own line. Also, our error messages are inconsistent
|
| -// in whether they specify the app's name.
|
| -HRESULT BundleInstaller::NotifyBundleInstallComplete() {
|
| - CORE_LOG(L3, (_T("[BundleInstaller::NotifyBundleInstallComplete]")));
|
| - ASSERT1(!apps_.empty());
|
| - ASSERT1(app_bundle_);
|
| -
|
| - bool is_only_no_update = true;
|
| -
|
| - std::vector<AppCompletionInfo> apps_info;
|
| - HRESULT bundle_result = S_OK;
|
| -
|
| - // Get the completion info for each app and set the bundle result.
|
| - for (size_t i = 0; i < apps_.size(); ++i) {
|
| - const ComPtrIApp& app = apps_[i];
|
| - AppCompletionInfo app_info;
|
| - internal::GetAppCompletionMessage(app,
|
| - &app_info,
|
| - is_browser_type_supported_);
|
| -
|
| - CORE_LOG(L1, (_T("[App completion][%Iu][%s]"), i, app_info.ToString()));
|
| - apps_info.push_back(app_info);
|
| -
|
| - is_only_no_update &= app_info.is_noupdate;
|
| -
|
| - ASSERT1(bundle_result == S_OK || FAILED(bundle_result));
|
| - if (FAILED(app_info.error_code) && SUCCEEDED(bundle_result)) {
|
| - // This is the first app failure. Use this as the result.
|
| - bundle_result = app_info.error_code;
|
| - }
|
| - }
|
| -
|
| - ASSERT1(bundle_result == S_OK || !is_only_no_update);
|
| -
|
| - CComBSTR bundle_name;
|
| - if (FAILED(app_bundle_->get_displayName(&bundle_name))) {
|
| - bundle_name.Empty();
|
| - }
|
| -
|
| - CString current_bundle_message = internal::GetBundleCompletionMessage(
|
| - CString(bundle_name),
|
| - apps_info,
|
| - is_only_no_update,
|
| - is_canceled_);
|
| - ASSERT1(!current_bundle_message.IsEmpty());
|
| - CompletionCodes completion_code = COMPLETION_CODE_SUCCESS;
|
| - if (FAILED(bundle_result)) {
|
| - completion_code = COMPLETION_CODE_ERROR;
|
| - } else if (is_canceled_) {
|
| - // User tried to cancel but bundle is installed.
|
| - completion_code = COMPLETION_CODE_INSTALL_FINISHED_BEFORE_CANCEL;
|
| - }
|
| - BundleCompletionInfo bundle_info(completion_code,
|
| - bundle_result,
|
| - current_bundle_message);
|
| - bundle_info.apps_info = apps_info; // Copying simplifies the code above.
|
| -
|
| - // The exit code will be non-zero if any app failed to install.
|
| - // TODO(omaha3): What if apps have different settings? It seems anyone calling
|
| - // Omaha would expect to get an error code in this case. We need to make sure
|
| - // this doesn't cause undesirable behavior in the parent process(es).
|
| - Complete(bundle_info);
|
| - return S_OK;
|
| -}
|
| -
|
| -// If an update is available, this method also sets relevant information from
|
| -// the update response.
|
| -// This function, and thus, NotifyUpdateAvailable() is only called if using
|
| -// a phased install where the bundle waits after the update check (in other
|
| -// words, !is_update_all_apps_). Currently, NotifyUpdateAvailable() only does
|
| -// something in the legacy OnDemand case, so this is okay.
|
| -HRESULT BundleInstaller::HandleUpdateCheckResults(int* num_updates) {
|
| - CORE_LOG(L1, (_T("[BundleInstaller::HandleUpdateCheckResults]")));
|
| - ASSERT1(num_updates);
|
| -
|
| - *num_updates = 0;
|
| -
|
| - for (size_t i = 0; i < apps_.size(); ++i) {
|
| - CurrentState current_state = STATE_INIT;
|
| - CComPtr<ICurrentState> icurrent_state;
|
| - const ComPtrIApp& app = apps_[i];
|
| - HRESULT hr = update3_utils::GetAppCurrentState(app,
|
| - ¤t_state,
|
| - &icurrent_state);
|
| - if (FAILED(hr)) {
|
| - CORE_LOG(LE, (_T("[GetAppCurrentState failed][0x%08x]"), hr));
|
| - return hr;
|
| - }
|
| -
|
| - if (current_state == STATE_NO_UPDATE || current_state == STATE_ERROR) {
|
| - // Continue to process other apps.
|
| - // The error information, if applicable, will be reported elsewhere.
|
| - continue;
|
| - } else if (current_state != STATE_UPDATE_AVAILABLE) {
|
| - // The update check may not be complete or may be in an unexpected state.
|
| - ASSERT1(false);
|
| - return E_FAIL;
|
| - }
|
| -
|
| - ++*num_updates;
|
| - VERIFY1(SUCCEEDED(NotifyUpdateAvailable(app)));
|
| - }
|
| -
|
| - return S_OK;
|
| -}
|
| -
|
| -// Assumes icurrent_state represents an app in one of the downloading states.
|
| -// TODO(omaha3): Since this method does not check the current state, it's
|
| -// possible to be in Download Complete or later but not report 100%. The server
|
| -// should ensure it reports 100% and 0 time in these cases.
|
| -void BundleInstaller::GetAppDownloadProgress(ICurrentState* icurrent_state,
|
| - int* time_remaining_ms,
|
| - int* percentage,
|
| - time64* next_retry_time) {
|
| - ASSERT1(icurrent_state);
|
| - ASSERT1(time_remaining_ms);
|
| - ASSERT1(percentage);
|
| -
|
| - LONG local_time_remaining_ms = kCurrentStateProgressUnknown;
|
| - if (FAILED(icurrent_state->get_downloadTimeRemainingMs(
|
| - &local_time_remaining_ms))) {
|
| - local_time_remaining_ms = kCurrentStateProgressUnknown;
|
| - }
|
| -
|
| - int local_percentage = 0;
|
| - ULONG bytes = 0;
|
| - ULONG bytes_total = 0;
|
| - if (FAILED(icurrent_state->get_bytesDownloaded(&bytes)) ||
|
| - FAILED(icurrent_state->get_totalBytesToDownload(&bytes_total))) {
|
| - local_percentage = 0;
|
| - } else {
|
| - ASSERT1(bytes <= bytes_total);
|
| - local_percentage = static_cast<int>(100ULL * bytes / bytes_total);
|
| - ASSERT1(0 <= local_percentage && local_percentage <= 100);
|
| - }
|
| -
|
| -
|
| - ULONGLONG local_next_retry_time = 0;
|
| - if (FAILED(icurrent_state->get_nextRetryTime(&local_next_retry_time))) {
|
| - local_next_retry_time = 0;
|
| - }
|
| -
|
| - *time_remaining_ms = local_time_remaining_ms;
|
| - *percentage = local_percentage;
|
| - *next_retry_time = static_cast<time64>(local_next_retry_time);
|
| -
|
| - // TODO(omaha3): For now, this client treats extracting and patching as part
|
| - // of downloading. Add UI support for these phases.
|
| -
|
| - CORE_LOG(L4, (_T("[AppDownloadProgress]")
|
| - _T("[bytes %u][bytes_total %u][percentage %d][ms %d]"),
|
| - bytes, bytes_total, *percentage, *time_remaining_ms));
|
| -}
|
| -
|
| -void BundleInstaller::CancelBundle() {
|
| - CORE_LOG(L1, (_T("[BundleInstaller::CancelBundle]")));
|
| - if (app_bundle_) {
|
| - VERIFY1(SUCCEEDED(app_bundle_->stop()));
|
| - }
|
| -}
|
| -
|
| -// error_code is copied to result_, which is the return code for this object.
|
| -void BundleInstaller::Complete(const BundleCompletionInfo& bundle_info) {
|
| - CORE_LOG(L1, (_T("[BundleInstaller::Complete][%s]"), bundle_info.ToString()));
|
| - ASSERT1(observer_);
|
| - ASSERT1(!bundle_info.bundle_completion_message.IsEmpty());
|
| -
|
| - CString help_url;
|
| - if (bundle_info.completion_code == COMPLETION_CODE_ERROR) {
|
| - std::vector<HelpUrlBuilder::AppResult> app_install_results;
|
| - for (size_t i = 0; i < bundle_info.apps_info.size(); ++i) {
|
| - const AppCompletionInfo& info = bundle_info.apps_info[i];
|
| - // TODO(omaha3): Pass info.extra_code to HelpUrlBuilder as well so that
|
| - // the help URL has both extra code and installer result code.
|
| - app_install_results.push_back(
|
| - HelpUrlBuilder::AppResult(info.app_id,
|
| - info.error_code,
|
| - info.installer_result_code));
|
| - }
|
| -
|
| - if (help_url_builder_.get()) {
|
| - VERIFY1(SUCCEEDED(help_url_builder_->BuildUrl(app_install_results,
|
| - &help_url)));
|
| - ASSERT1(!help_url.IsEmpty());
|
| - }
|
| - }
|
| -
|
| - // Set result_ and state_ before calling OnComplete on the observer.
|
| - // Otherwise, we end up calling BundleInstaller::Complete() recursively from
|
| - // DoClose().
|
| - result_ = bundle_info.bundle_result;
|
| - state_ = kComplete;
|
| -
|
| - ReleaseAppBundle();
|
| -
|
| - // TODO(omaha3): We need to expose some more items to the observer, such as
|
| - // install_manifest.install_actions[].success_action. There are a lot of
|
| - // things we need to expose together: success action, restart browser,
|
| - // terminate all browsers, url, and maybe others. Let's take
|
| - // the opportunity to standardize these even if the registry and config APIs
|
| - // are not ideal (i.e. success_url should not imply an action as it does in
|
| - // the config).
|
| -
|
| - ObserverCompletionInfo observer_info(bundle_info.completion_code);
|
| - // TODO(omaha3): Consider moving the creation of the bundle completion
|
| - // message from this class to the observer.
|
| - observer_info.completion_text = bundle_info.bundle_completion_message;
|
| - observer_info.help_url = help_url;
|
| - observer_info.apps_info = bundle_info.apps_info;
|
| -
|
| - observer_->OnComplete(observer_info);
|
| -}
|
| -
|
| -// Omaha event pings are sent in AppBundle destructor. Release app_bundle_ and
|
| -// its related interfaces explicitly so that the pings can be sent sooner.
|
| -void BundleInstaller::ReleaseAppBundle() {
|
| - CORE_LOG(L3, (_T("[ReleaseAppBundle]")));
|
| - apps_.clear();
|
| - app_bundle_ = NULL;
|
| -}
|
| -
|
| -} // namespace omaha
|
|
|