chromium/chrome/browser/ui/views/uninstall_view.cc

// Copyright 2012 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "chrome/browser/ui/views/uninstall_view.h"

#include "base/process/launch.h"
#include "base/run_loop.h"
#include "base/task/current_thread.h"
#include "chrome/browser/shell_integration.h"
#include "chrome/browser/ui/uninstall_browser_prompt.h"
#include "chrome/browser/ui/views/chrome_layout_provider.h"
#include "chrome/common/chrome_result_codes.h"
#include "chrome/grit/branded_strings.h"
#include "chrome/installer/util/shell_util.h"
#include "ui/base/l10n/l10n_util.h"
#include "ui/base/metadata/metadata_impl_macros.h"
#include "ui/base/mojom/dialog_button.mojom.h"
#include "ui/views/accessibility/accessibility_paint_checks.h"
#include "ui/views/controls/button/checkbox.h"
#include "ui/views/controls/combobox/combobox.h"
#include "ui/views/controls/label.h"
#include "ui/views/layout/box_layout.h"
#include "ui/views/layout/box_layout_view.h"
#include "ui/views/view_class_properties.h"
#include "ui/views/widget/widget.h"

UninstallView::UninstallView(int* user_selection,
                             const base::RepeatingClosure& quit_closure)
    : user_selection_(*user_selection), quit_closure_(quit_closure) {
  SetupControls();
}

UninstallView::~UninstallView() {
  // Exit the message loop we were started with so that uninstall can continue.
  quit_closure_.Run();
}

void UninstallView::SetupControls() {
  ChromeLayoutProvider* provider = ChromeLayoutProvider::Get();
  const int checkbox_indent =
      provider->GetDistanceMetric(DISTANCE_SUBSECTION_HORIZONTAL_INDENT);
  const int unrelated_vertical_spacing =
      provider->GetDistanceMetric(views::DISTANCE_UNRELATED_CONTROL_VERTICAL);
  const int related_vertical_spacing =
      provider->GetDistanceMetric(views::DISTANCE_RELATED_CONTROL_VERTICAL);
  const int related_horizontal_spacing =
      provider->GetDistanceMetric(views::DISTANCE_RELATED_CONTROL_HORIZONTAL);
  const int related_vertical_small =
      provider->GetDistanceMetric(DISTANCE_RELATED_CONTROL_VERTICAL_SMALL);

  auto builder =
      views::Builder<UninstallView>(this)
          .SetButtonLabel(ui::mojom::DialogButton::kOk,
                          l10n_util::GetStringUTF16(IDS_UNINSTALL_BUTTON_TEXT))
          .SetTitle(IDS_UNINSTALL_CHROME)
          .SetAcceptCallback(base::BindOnce(&UninstallView::OnDialogAccepted,
                                            base::Unretained(this)))
          .SetCancelCallback(base::BindOnce(&UninstallView::OnDialogCancelled,
                                            base::Unretained(this)))
          .SetCloseCallback(base::BindOnce(&UninstallView::OnDialogCancelled,
                                           base::Unretained(this)))
          .set_margins(provider->GetDialogInsetsForContentType(
              views::DialogContentType::kText, views::DialogContentType::kText))
          .SetLayoutManager(std::make_unique<views::BoxLayout>(
              views::BoxLayout::Orientation::kVertical))
          .AddChildren(
              // Message to confirm uninstallation.
              views::Builder<views::Label>()
                  .SetText(l10n_util::GetStringUTF16(IDS_UNINSTALL_VERIFY))
                  .SetHorizontalAlignment(gfx::ALIGN_LEFT)
                  .SetProperty(
                      views::kMarginsKey,
                      gfx::Insets::TLBR(0, 0, unrelated_vertical_spacing, 0)),
              // The "delete profile" check box.
              views::Builder<views::Checkbox>()
                  .CopyAddressTo(&delete_profile_)
                  .SetText(
                      l10n_util::GetStringUTF16(IDS_UNINSTALL_DELETE_PROFILE))
                  .SetProperty(views::kMarginsKey,
                               gfx::Insets::TLBR(0, checkbox_indent, 0, 0)));

  // Set default browser combo box. If the default should not or cannot be
  // changed, widgets are not shown. We assume here that if Chrome cannot
  // be set programatically as default, neither can any other browser (for
  // instance because the OS doesn't permit that).
  if (ShellUtil::CanMakeChromeDefaultUnattended() &&
      shell_integration::GetDefaultBrowser() == shell_integration::IS_DEFAULT) {
    browsers_ = std::make_unique<BrowsersMap>();
    ShellUtil::GetRegisteredBrowsers(browsers_.get());
    if (!browsers_->empty()) {
      builder.AddChildren(
          views::Builder<views::View>().SetProperty(
              views::kMarginsKey,
              gfx::Insets::TLBR(related_vertical_spacing, 0, 0, 0)),
          views::Builder<views::BoxLayoutView>()
              .SetOrientation(views::BoxLayout::Orientation::kHorizontal)
              .SetBetweenChildSpacing(related_horizontal_spacing)
              .AddChildren(
                  views::Builder<views::Checkbox>()
                      .CopyAddressTo(&change_default_browser_)
                      .SetText(l10n_util::GetStringUTF16(
                          IDS_UNINSTALL_SET_DEFAULT_BROWSER))
                      .SetCallback(base::BindRepeating(
                          [](UninstallView* view) {
                            view->browsers_combo_->SetEnabled(
                                view->change_default_browser_->GetChecked());
                          },
                          base::Unretained(this)))
                      .SetProperty(views::kMarginsKey,
                                   gfx::Insets::TLBR(0, checkbox_indent, 0, 0)),
                  views::Builder<views::Combobox>()
                      .CopyAddressTo(&browsers_combo_)
                      .SetModel(this)
                      .SetEnabled(false)));
    }
  }

  std::move(builder)
      .AddChild(views::Builder<views::View>().SetProperty(
          views::kMarginsKey,
          gfx::Insets::TLBR(related_vertical_small, 0, 0, 0)))
      .BuildChildren();
}

void UninstallView::OnDialogAccepted() {
  *user_selection_ = content::RESULT_CODE_NORMAL_EXIT;
  if (delete_profile_->GetChecked()) {
    *user_selection_ = chrome::RESULT_CODE_UNINSTALL_DELETE_PROFILE;
  }
  if (change_default_browser_ && change_default_browser_->GetChecked()) {
    BrowsersMap::const_iterator i = browsers_->begin();
    std::advance(i, browsers_combo_->GetSelectedIndex().value());
    base::LaunchOptions options;
    options.start_hidden = true;
    base::LaunchProcess(i->second, options);
  }
}

void UninstallView::OnDialogCancelled() {
  *user_selection_ = chrome::RESULT_CODE_UNINSTALL_USER_CANCEL;
}

size_t UninstallView::GetItemCount() const {
  DCHECK(!browsers_->empty());
  return browsers_->size();
}

std::u16string UninstallView::GetItemAt(size_t index) const {
  DCHECK_LT(index, browsers_->size());
  BrowsersMap::const_iterator i = browsers_->begin();
  std::advance(i, index);
  return base::WideToUTF16(i->first);
}

BEGIN_METADATA(UninstallView)
END_METADATA

namespace chrome {

int ShowUninstallBrowserPrompt() {
  DCHECK(base::CurrentUIThread::IsSet());
  int result = content::RESULT_CODE_NORMAL_EXIT;

  base::RunLoop run_loop;
  UninstallView* view = new UninstallView(&result, run_loop.QuitClosure());
  views::DialogDelegate::CreateDialogWidget(view, NULL, NULL)->Show();
  run_loop.Run();
  return result;
}

}  // namespace chrome