chromium/chrome/browser/ui/views/apps/app_dialog/app_uninstall_dialog_view.cc

// Copyright 2019 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/apps/app_dialog/app_uninstall_dialog_view.h"

#include <string>
#include <vector>

#include "base/barrier_callback.h"
#include "base/feature_list.h"
#include "base/functional/bind.h"
#include "base/i18n/message_formatter.h"
#include "base/memory/raw_ptr.h"
#include "base/memory/weak_ptr.h"
#include "base/strings/string_util.h"
#include "base/strings/utf_string_conversions.h"
#include "base/types/cxx23_to_underlying.h"
#include "base/values.h"
#include "build/chromeos_buildflags.h"
#include "chrome/browser/apps/app_service/app_service_proxy.h"
#include "chrome/browser/apps/app_service/app_service_proxy_factory.h"
#include "chrome/browser/extensions/extension_management.h"
#include "chrome/browser/profiles/profile.h"
#include "chrome/browser/ui/browser_element_identifiers.h"
#include "chrome/browser/ui/browser_navigator.h"
#include "chrome/browser/ui/browser_navigator_params.h"
#include "chrome/browser/ui/views/chrome_layout_provider.h"
#include "chrome/browser/ui/views/chrome_typography.h"
#include "chrome/browser/web_applications/locks/app_lock.h"
#include "chrome/browser/web_applications/web_app_command_scheduler.h"
#include "chrome/browser/web_applications/web_app_provider.h"
#include "chrome/browser/web_applications/web_app_registrar.h"
#include "chrome/common/chrome_features.h"
#include "chrome/common/extensions/manifest_handlers/app_launch_info.h"
#include "chrome/common/url_constants.h"
#include "chrome/grit/branded_strings.h"
#include "chrome/grit/generated_resources.h"
#include "components/constrained_window/constrained_window_views.h"
#include "components/google/core/common/google_util.h"
#include "components/strings/grit/components_strings.h"
#include "components/url_formatter/elide_url.h"
#include "extensions/browser/extension_registry.h"
#include "extensions/common/constants.h"
#include "extensions/common/manifest_url_handlers.h"
#include "net/base/registry_controlled_domains/registry_controlled_domain.h"
#include "ui/base/l10n/l10n_util.h"
#include "ui/base/metadata/metadata_header_macros.h"
#include "ui/base/metadata/metadata_impl_macros.h"
#include "ui/base/mojom/dialog_button.mojom.h"
#include "ui/base/mojom/ui_base_types.mojom-shared.h"
#include "ui/gfx/image/image_skia_operations.h"
#include "ui/views/accessibility/view_accessibility.h"
#include "ui/views/border.h"
#include "ui/views/controls/button/checkbox.h"
#include "ui/views/controls/label.h"
#include "ui/views/controls/scroll_view.h"
#include "ui/views/controls/separator.h"
#include "ui/views/controls/styled_label.h"
#include "ui/views/layout/table_layout.h"
#include "ui/views/view_class_properties.h"

#if defined(USE_AURA)
#include "ui/aura/window.h"
#endif

#if BUILDFLAG(IS_CHROMEOS_ASH)
#include "chrome/browser/ash/app_list/arc/arc_app_list_prefs.h"
#include "chrome/browser/ash/borealis/borealis_util.h"
#include "chrome/browser/ash/crosapi/crosapi_ash.h"
#include "chrome/browser/ash/crosapi/crosapi_manager.h"
#include "chrome/browser/ash/crosapi/web_app_service_ash.h"
#include "chromeos/crosapi/mojom/web_app_service.mojom.h"
#endif

namespace {

AppUninstallDialogView* g_app_uninstall_dialog_view = nullptr;

class UninstallCheckboxView : public views::View,
                              public views::ViewTargeterDelegate {
  METADATA_HEADER(UninstallCheckboxView, views::View)

 public:
  class CheckboxTargeter : public views::ViewTargeterDelegate {
   public:
    CheckboxTargeter() = default;
    ~CheckboxTargeter() override = default;

    // views::ViewTargeterDelegate:
    bool DoesIntersectRect(const views::View* target,
                           const gfx::Rect& rect) const override {
      return true;
    }
  };

  explicit UninstallCheckboxView(std::unique_ptr<views::StyledLabel> label) {
    SetEventTargeter(std::make_unique<views::ViewTargeter>(this));

    views::TableLayout* layout =
        SetLayoutManager(std::make_unique<views::TableLayout>());
    layout
        ->AddColumn(views::LayoutAlignment::kStretch,
                    views::LayoutAlignment::kStretch,
                    views::TableLayout::kFixedSize,
                    views::TableLayout::ColumnSize::kUsePreferred, 0, 0)
        .AddPaddingColumn(views::TableLayout::kFixedSize,
                          ChromeLayoutProvider::Get()->GetDistanceMetric(
                              views::DISTANCE_RELATED_LABEL_HORIZONTAL))
        .AddColumn(views::LayoutAlignment::kStretch,
                   views::LayoutAlignment::kStretch, 1.0f,
                   views::TableLayout::ColumnSize::kFixed, 0, 0)
        .AddRows(1, views::TableLayout::kFixedSize);

    auto checkbox = std::make_unique<views::Checkbox>();
    checkbox->GetViewAccessibility().SetName(*label.get());
    checkbox->SetEventTargeter(std::make_unique<views::ViewTargeter>(
        std::make_unique<CheckboxTargeter>()));
    checkbox_ = AddChildView(std::move(checkbox));
    AddChildView(std::move(label));
  }
  ~UninstallCheckboxView() override = default;

  // views::ViewTargeterDelegate:
  View* TargetForRect(View* root, const gfx::Rect& rect) override {
    views::View* target =
        views::ViewTargeterDelegate::TargetForRect(root, rect);
    if (target->parent() == this || target->parent() == checkbox_) {
      return checkbox_;
    }
    return target;
  }

  views::Checkbox* checkbox() { return checkbox_; }

 private:
  raw_ptr<views::Checkbox> checkbox_;
};

BEGIN_METADATA(UninstallCheckboxView)
END_METADATA

#if BUILDFLAG(IS_CHROMEOS_ASH)
bool IsArcShortcutApp(Profile* profile, const std::string& app_id) {
  ArcAppListPrefs* arc_prefs = ArcAppListPrefs::Get(profile);
  DCHECK(arc_prefs);

  std::unique_ptr<ArcAppListPrefs::AppInfo> app_info =
      arc_prefs->GetApp(app_id);
  DCHECK(app_info);
  return app_info->shortcut;
}
#endif

std::u16string GetWindowTitleForApp(Profile* profile,
                                    apps::AppType app_type,
                                    const std::string& app_id,
                                    const std::string& app_name) {
#if BUILDFLAG(IS_CHROMEOS_ASH)
  // On ChromeOS, all app types exist, but Arc shortcut apps get the regular
  // extension uninstall title.
  if (app_type == apps::AppType::kArc && IsArcShortcutApp(profile, app_id)) {
    return l10n_util::GetStringUTF16(IDS_EXTENSION_UNINSTALL_PROMPT_TITLE);
  }
#else
  // On non-ChromeOS, only Chrome app and web app types meaningfully exist.
  DCHECK(app_type != apps::AppType::kChromeApp &&
         app_type != apps::AppType::kWeb);
#endif
  return l10n_util::GetStringFUTF16(IDS_PROMPT_APP_UNINSTALL_TITLE,
                                    base::UTF8ToUTF16(app_name));
}

void ResizeWidgetToContents(views::Widget* widget) {
  CHECK(widget);
  gfx::Rect bounds = widget->GetWindowBoundsInScreen();
  bounds.set_size(widget->GetRootView()->GetPreferredSize());
  widget->SetBounds(bounds);
}

}  // namespace

struct SubApp {
  explicit SubApp(std::u16string app_name, apps::IconValuePtr icon)
      : app_name(std::move(app_name)), icon(std::move(icon)) {}
  SubApp(SubApp&& sub_app) = default;
  SubApp& operator=(SubApp&& sub_app) = default;
  SubApp(const SubApp&) = delete;
  SubApp& operator=(const SubApp&) = delete;

  std::u16string app_name;
  apps::IconValuePtr icon;
};

// static
views::Widget* apps::UninstallDialog::UiBase::Create(
    Profile* profile,
    apps::AppType app_type,
    const std::string& app_id,
    const std::string& app_name,
    gfx::ImageSkia image,
    gfx::NativeWindow parent_window,
    apps::UninstallDialog* uninstall_dialog) {
  views::Widget* widget = constrained_window::CreateBrowserModalDialogViews(
      new AppUninstallDialogView(profile, app_type, app_id, app_name, image,
                                 uninstall_dialog),
      parent_window);
  widget->Show();
  return widget;
}

AppUninstallDialogView::AppUninstallDialogView(
    Profile* profile,
    apps::AppType app_type,
    const std::string& app_id,
    const std::string& app_name,
    gfx::ImageSkia image,
    apps::UninstallDialog* uninstall_dialog)
    : apps::UninstallDialog::UiBase(uninstall_dialog),
      AppDialogView(ui::ImageModel::FromImageSkia(image)),
      profile_(profile) {
  profile_observation_.Observe(profile);

  SetModalType(ui::mojom::ModalType::kWindow);

  SetCloseCallback(base::BindOnce(&AppUninstallDialogView::OnDialogCancelled,
                                  base::Unretained(this)));
  SetCancelCallback(base::BindOnce(&AppUninstallDialogView::OnDialogCancelled,
                                   base::Unretained(this)));
  SetAcceptCallback(base::BindOnce(&AppUninstallDialogView::OnDialogAccepted,
                                   base::Unretained(this)));

  InitializeView(profile, app_type, app_id, app_name);

  g_app_uninstall_dialog_view = this;
}

AppUninstallDialogView::~AppUninstallDialogView() {
  g_app_uninstall_dialog_view = nullptr;
}

// static
AppUninstallDialogView* AppUninstallDialogView::GetActiveViewForTesting() {
  return g_app_uninstall_dialog_view;
}

void AppUninstallDialogView::OnProfileWillBeDestroyed(Profile* profile) {
  weak_ptr_factory_.InvalidateWeakPtrs();
}

void AppUninstallDialogView::InitializeView(Profile* profile,
                                            apps::AppType app_type,
                                            const std::string& app_id,
                                            const std::string& app_name) {
  SetButtonLabel(
      ui::mojom::DialogButton::kOk,
      l10n_util::GetStringUTF16(IDS_EXTENSION_PROMPT_UNINSTALL_APP_BUTTON));

  ChromeLayoutProvider* provider = ChromeLayoutProvider::Get();
  SetLayoutManager(std::make_unique<views::BoxLayout>(
      views::BoxLayout::Orientation::kVertical, gfx::Insets(),
      provider->GetDistanceMetric(views::DISTANCE_RELATED_CONTROL_VERTICAL)));

  AddTitle(GetWindowTitleForApp(profile, app_type, app_id, app_name));

  switch (app_type) {
    case apps::AppType::kUnknown:
    case apps::AppType::kBuiltIn:
    case apps::AppType::kStandaloneBrowser:
    case apps::AppType::kRemote:
    case apps::AppType::kExtension:
    case apps::AppType::kStandaloneBrowserExtension:
      NOTREACHED();
    case apps::AppType::kStandaloneBrowserChromeApp:
      // Do nothing special for kStandaloneBrowserChromeApp.
      break;
    case apps::AppType::kArc:
#if BUILDFLAG(IS_CHROMEOS_ASH)
      InitializeViewForArcApp(profile, app_id);
      break;
#else
      NOTREACHED();
#endif
    case apps::AppType::kPluginVm:
#if BUILDFLAG(IS_CHROMEOS_ASH)
      AddSubtitle(
          l10n_util::GetStringUTF16(IDS_PLUGIN_VM_UNINSTALL_PROMPT_BODY));
      break;
#else
      NOTREACHED();
#endif
    case apps::AppType::kBorealis:
#if BUILDFLAG(IS_CHROMEOS_ASH)
      if (app_id == borealis::kClientAppId) {
        AddSubtitle(l10n_util::GetStringUTF16(
            IDS_BOREALIS_CLIENT_UNINSTALL_CONFIRM_BODY));
      } else {
        AddSubtitle(l10n_util::GetStringUTF16(
            IDS_BOREALIS_APPLICATION_UNINSTALL_CONFIRM_BODY));
      }
      break;
#else
      NOTREACHED();
#endif
    case apps::AppType::kCrostini:
#if BUILDFLAG(IS_CHROMEOS_ASH)
      AddSubtitle(l10n_util::GetStringUTF16(
          IDS_CROSTINI_APPLICATION_UNINSTALL_CONFIRM_BODY));
      break;
#else
      NOTREACHED();
#endif
    case apps::AppType::kBruschetta:
#if BUILDFLAG(IS_CHROMEOS_ASH)
      // TODO(b/247636749): Implement Bruschetta uninstall.
      break;
#else
      NOTREACHED();
#endif

    case apps::AppType::kWeb:
    case apps::AppType::kSystemWeb:
      InitializeViewForWebApp(app_id);
      break;
    case apps::AppType::kChromeApp:
      InitializeViewForExtension(profile, app_id);
      break;
  }
}

void AppUninstallDialogView::InitializeCheckbox(const GURL& app_start_url) {
  std::vector<std::u16string> replacements;
  replacements.push_back(url_formatter::FormatUrlForSecurityDisplay(
      app_start_url, url_formatter::SchemeDisplay::OMIT_CRYPTOGRAPHIC));

  const bool is_google = google_util::IsGoogleDomainUrl(
      app_start_url, google_util::ALLOW_SUBDOMAIN,
      google_util::ALLOW_NON_STANDARD_PORTS);
  if (!is_google) {
    auto domain = net::registry_controlled_domains::GetDomainAndRegistry(
        app_start_url,
        net::registry_controlled_domains::INCLUDE_PRIVATE_REGISTRIES);
    if (!domain.empty()) {
      domain[0] = base::ToUpperASCII(domain[0]);
    }

    replacements.push_back(base::ASCIIToUTF16(domain));
  }

  std::u16string learn_more_text =
      l10n_util::GetStringUTF16(IDS_APP_UNINSTALL_PROMPT_LEARN_MORE);
  replacements.push_back(learn_more_text);

  auto checkbox_label = std::make_unique<views::StyledLabel>();
  std::vector<size_t> offsets;
  checkbox_label->SetText(l10n_util::GetStringFUTF16(
      is_google ? IDS_APP_UNINSTALL_PROMPT_REMOVE_DATA_CHECKBOX_FOR_GOOGLE
                : IDS_APP_UNINSTALL_PROMPT_REMOVE_DATA_CHECKBOX_FOR_NON_GOOGLE,
      replacements, &offsets));
  DCHECK_EQ(replacements.size(), offsets.size());
  const size_t offset = offsets.back();

  checkbox_label->AddStyleRange(
      gfx::Range(offset, offset + learn_more_text.length()),
      views::StyledLabel::RangeStyleInfo::CreateForLink(base::BindRepeating(
          [](Profile* profile) {
            NavigateParams params(
                profile,
                GURL("https://support.google.com/chromebook/?p=uninstallpwa"),
                ui::PAGE_TRANSITION_LINK);
            Navigate(&params);
          },
          profile_)));
  views::StyledLabel::RangeStyleInfo checkbox_style;
  checkbox_style.text_style = views::style::STYLE_PRIMARY;
  gfx::Range before_link_range(0, offset);
  checkbox_label->AddStyleRange(before_link_range, checkbox_style);

  // Shift the text down to align with the checkbox.
  checkbox_label->SetBorder(
      views::CreateEmptyBorder(gfx::Insets::TLBR(3, 0, 0, 0)));

  // Create a view to hold the checkbox and the text.
  auto checkbox_view =
      std::make_unique<UninstallCheckboxView>(std::move(checkbox_label));
  clear_site_data_checkbox_ = checkbox_view->checkbox();
  AddChildView(std::move(checkbox_view));
}

void AppUninstallDialogView::InitializeViewForExtension(
    Profile* profile,
    const std::string& app_id) {
  const extensions::Extension* extension =
      extensions::ExtensionRegistry::Get(profile)->GetInstalledExtension(
          app_id);
  DCHECK(extension);

  extensions::ExtensionManagement* extension_management =
      extensions::ExtensionManagementFactory::GetForBrowserContext(profile);
  if (extension_management->UpdatesFromWebstore(*extension)) {
    auto report_abuse_checkbox = std::make_unique<views::Checkbox>(
        l10n_util::GetStringUTF16(IDS_EXTENSION_PROMPT_UNINSTALL_REPORT_ABUSE));
    report_abuse_checkbox->SetMultiLine(true);
    report_abuse_checkbox_ = AddChildView(std::move(report_abuse_checkbox));
  }
}

void AppUninstallDialogView::InitializeSubAppList(
    const std::string& app_name,
    const std::vector<SubApp>& sub_apps) {
  ChromeLayoutProvider* provider = ChromeLayoutProvider::Get();
  std::u16string description =
      base::i18n::MessageFormatter::FormatWithNamedArgs(
          l10n_util::GetStringUTF16(
              IDS_APP_UNINSTALL_PROMPT_ADDITIONAL_UNINSTALLS_MESSAGE),
          /*name0=*/"NUM_SUB_APPS", static_cast<int>(sub_apps.size()),
          /*name1=*/"APP_NAME", base::UTF8ToUTF16(app_name));

  sub_apps_description_->SetText(description);
  sub_apps_description_->SetMultiLine(/*multi_line=*/true);
  sub_apps_description_->SetHorizontalAlignment(gfx::ALIGN_LEFT);

  auto sub_apps_container = std::make_unique<views::BoxLayoutView>();
  sub_apps_container->SetOrientation(views::BoxLayout::Orientation::kVertical);
  sub_apps_container->SetBetweenChildSpacing(
      provider->GetDistanceMetric(DISTANCE_CONTROL_LIST_VERTICAL));
  sub_apps_container->SetInsideBorderInsets(gfx::Insets::TLBR(
      0,
      provider->GetDistanceMetric(views::DISTANCE_UNRELATED_CONTROL_HORIZONTAL),
      0, 0));

  for (const SubApp& sub_app : sub_apps) {
    auto box = std::make_unique<views::BoxLayoutView>();
    box->SetOrientation(views::BoxLayout::Orientation::kHorizontal);
    auto* sub_app_label =
        box->AddChildView(std::make_unique<views::Label>(sub_app.app_name));

    sub_app_label->SetGroup(base::to_underlying(DialogViewID::SUB_APP_LABEL));

    sub_app_label->SetHorizontalAlignment(gfx::ALIGN_LEFT);
    sub_app_label->SetMultiLine(true);

    auto* sub_app_icon =
        box->AddChildView(std::make_unique<views::ImageView>());
    sub_app_icon->SetImage(
        ui::ImageModel::FromImageSkia(sub_app.icon->uncompressed));
    sub_app_icon->SetGroup(base::to_underlying(DialogViewID::SUB_APP_ICON));

    box->SetBetweenChildSpacing(
        provider->GetDistanceMetric(views::DISTANCE_RELATED_LABEL_HORIZONTAL));

    sub_apps_container->AddChildView(std::move(box));
  }

  sub_apps_scroll_view_->SetContents(std::move(sub_apps_container));
  sub_apps_scroll_view_->SetHorizontalScrollBarMode(
      views::ScrollView::ScrollBarMode::kDisabled);

  sub_apps_scroll_view_->ClipHeightTo(
      0, provider->GetDistanceMetric(
             views::DISTANCE_DIALOG_SCROLLABLE_AREA_MAX_HEIGHT));
  AddChildView(std::make_unique<views::Separator>());

  sub_apps_scroll_view_->SetVisible(!sub_apps.empty());
  sub_apps_description_->SetVisible(!sub_apps.empty());
  ResizeWidgetToContents(sub_apps_scroll_view_->GetWidget());
}

void AppUninstallDialogView::LoadSubAppIds(const std::string& short_app_name,
                                           const std::string& parent_app_id) {
  auto* provider = web_app::WebAppProvider::GetForWebApps(profile_);
  if (provider) {
    provider->scheduler().ScheduleCallbackWithResult(
        "AppUninstallDialogView::LoadSubAppIds",
        web_app::AppLockDescription(parent_app_id),
        base::BindOnce(
            [](const std::string& parent_app_id, web_app::AppLock& lock,
               base::Value::Dict& debug_value) {
              return lock.registrar().GetAllSubAppIds(parent_app_id);
            },
            parent_app_id),
        base::BindOnce(&AppUninstallDialogView::GetSubAppsInfo,
                       weak_ptr_factory_.GetWeakPtr(), short_app_name),
        /*arg_for_shutdown=*/std::vector<std::string>());
    return;
  }

#if BUILDFLAG(IS_CHROMEOS_ASH)
  crosapi::mojom::WebAppProviderBridge* web_app_provider_bridge =
      crosapi::CrosapiManager::Get()
          ->crosapi_ash()
          ->web_app_service_ash()
          ->GetWebAppProviderBridge();

  if (!web_app_provider_bridge) {
    LOG(ERROR) << "Could not find WebAppProviderBridge.";
    return;
  }

  web_app_provider_bridge->GetSubAppIds(
      parent_app_id,
      base::BindOnce(&AppUninstallDialogView::GetSubAppsInfo,
                     weak_ptr_factory_.GetWeakPtr(), short_app_name));
#endif
}

void AppUninstallDialogView::GetSubAppsInfo(
    const std::string& short_app_name,
    const std::vector<std::string>& sub_app_ids) {
  apps::AppServiceProxy* app_service_proxy =
      apps::AppServiceProxyFactory::GetForProfile(profile_);

  const auto sub_app_info_collector = base::BarrierCallback<SubApp>(
      sub_app_ids.size(),
      base::BindOnce(&AppUninstallDialogView::InitializeSubAppList,
                     weak_ptr_factory_.GetWeakPtr(), short_app_name));

  for (const std::string& sub_app_id : sub_app_ids) {
    std::u16string sub_app_name;
    app_service_proxy->AppRegistryCache().ForOneApp(
        sub_app_id, [&sub_app_name](const apps::AppUpdate& update) {
          sub_app_name = base::UTF8ToUTF16(update.Name());
        });

    app_service_proxy->LoadIcon(
        sub_app_id, apps::IconType::kUncompressed, web_app::kWebAppIconSmall,
        /*allow_placeholder_icon=*/false,
        base::BindOnce(
            [](std::u16string sub_app_name, apps::IconValuePtr icon_value_ptr) {
              return SubApp(sub_app_name, std::move(icon_value_ptr));
            },
            sub_app_name)
            .Then(sub_app_info_collector));
  }
}

void AppUninstallDialogView::InitializeViewForWebApp(
    const std::string& app_id) {
  // For web apps, publisher id is the start url.
  GURL app_start_url;
  std::string app_name;
  apps::AppServiceProxyFactory::GetForProfile(profile_)
      ->AppRegistryCache()
      .ForOneApp(app_id,
                 [&app_start_url, &app_name](const apps::AppUpdate& update) {
                   app_start_url = GURL(update.PublisherId());
                   app_name = update.Name();
                 });
  DCHECK(app_start_url.is_valid());

  // Sub apps are currently only supported for Isolated Web Apps.
  if (app_start_url.SchemeIs(chrome::kIsolatedAppScheme)) {
    sub_apps_description_ = AddChildView(std::make_unique<views::Label>());
    sub_apps_scroll_view_ = AddChildView(std::make_unique<views::ScrollView>());
    sub_apps_description_->SetVisible(false);
    sub_apps_scroll_view_->SetVisible(false);
    LoadSubAppIds(app_name, app_id);
  } else {
    // Isolated Web Apps will always have their data cleared as part of
    // uninstallation.
    InitializeCheckbox(app_start_url);
  }
}

#if BUILDFLAG(IS_CHROMEOS_ASH)
void AppUninstallDialogView::InitializeViewForArcApp(
    Profile* profile,
    const std::string& app_id) {
  if (IsArcShortcutApp(profile, app_id)) {
    SetButtonLabel(
        ui::mojom::DialogButton::kOk,
        l10n_util::GetStringUTF16(IDS_EXTENSION_PROMPT_UNINSTALL_BUTTON));
  } else {
    AddSubtitle(l10n_util::GetStringUTF16(
        IDS_ARC_APP_UNINSTALL_PROMPT_DATA_REMOVAL_WARNING));
  }
}

#endif

void AppUninstallDialogView::OnDialogCancelled() {
  uninstall_dialog()->OnDialogClosed(false /* uninstall */,
                                     false /* clear_site_data */,
                                     false /* report_abuse */);
}

void AppUninstallDialogView::OnDialogAccepted() {
  const bool clear_site_data =
      clear_site_data_checkbox_ && clear_site_data_checkbox_->GetChecked();
  const bool report_abuse_checkbox =
      report_abuse_checkbox_ && report_abuse_checkbox_->GetChecked();
  uninstall_dialog()->OnDialogClosed(true /* uninstall */, clear_site_data,
                                     report_abuse_checkbox);
}

void AppUninstallDialogView::OnWidgetInitialized() {
  AppDialogView::OnWidgetInitialized();
  GetOkButton()->SetProperty(views::kElementIdentifierKey,
                             kAppUninstallDialogOkButtonId);
}

BEGIN_METADATA(AppUninstallDialogView)
END_METADATA