chromium/chrome/browser/apps/app_service/uninstall_dialog.h

// 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.

#ifndef CHROME_BROWSER_APPS_APP_SERVICE_UNINSTALL_DIALOG_H_
#define CHROME_BROWSER_APPS_APP_SERVICE_UNINSTALL_DIALOG_H_

#include <memory>
#include <string>

#include "base/functional/callback.h"
#include "base/memory/raw_ptr.h"
#include "base/memory/weak_ptr.h"
#include "components/services/app_service/public/cpp/app_types.h"
#include "components/services/app_service/public/cpp/icon_types.h"
#include "ui/gfx/native_widget_types.h"
#include "ui/views/widget/widget.h"

class Profile;

namespace gfx {
class ImageSkia;
}

namespace views {
class NativeWindowTracker;
}

namespace apps {
class IconLoader;
class UninstallDialog;
}  // namespace apps

namespace apps {

using OnUninstallForTestingCallback = base::OnceCallback<void(bool)>;
using OnDialogCreatedCallback = base::OnceCallback<void(views::Widget*)>;

// Currently, app uninstallation on Chrome OS invokes a specific dialog per app
// type, Chrome Apps / PWAs, ARC apps and Crostini. There are 3 separate views
// for app uninstalling, which are subtly different from each other.
//
// This UninstallDialog combines the above three specific dialogs, and based on
// different app type to generate different view. Once the user has confirmed,
// the App Service calls the publisher to uninstall the app directly.
//
// TODO(crbug.com/40100977):
// 1. Add an interface to the uninstall, like what is done by
// extension_uninstall_dialog_->ConfirmUninstallByExtension
class UninstallDialog {
 public:
  // The UiBase is the parent virtual class for the AppUninstallDialogView,
  // which is located in
  // chrome/browser/ui/view/apps/app_uninstall_dialog_view.h. The UiBase is also
  // used to connect the UninstallDialog and AppUninstallDialogView, to transfer
  // the icon image, and the callback function.
  class UiBase {
   public:
    explicit UiBase(UninstallDialog* uninstall_dialog)
        : uninstall_dialog_(uninstall_dialog) {}

    UiBase(const UiBase&) = delete;
    UiBase& operator=(const UiBase&) = delete;
    virtual ~UiBase() = default;

    static views::Widget* Create(Profile* profile,
                                 apps::AppType app_type,
                                 const std::string& app_id,
                                 const std::string& app_name,
                                 gfx::ImageSkia image,
                                 gfx::NativeWindow parent_window,
                                 UninstallDialog* uninstall_dialog);

    UninstallDialog* uninstall_dialog() const { return uninstall_dialog_; }

   private:
    raw_ptr<UninstallDialog, AcrossTasksDanglingUntriaged> uninstall_dialog_;
  };

  // Called when the dialog closes after the user has made a decision about
  // whether to uninstall the app. If |clear_site_data| is true, site data will
  // be removed after uninstalling the app. Only ever true for PWAs. If
  // |report_abuse| is true, report abuse after uninstalling the app. Only ever
  // true for Chrome Apps.
  using UninstallCallback =
      base::OnceCallback<void(bool uninstall,
                              bool clear_site_data,
                              bool report_rebuse,
                              UninstallDialog* uninstall_dialog)>;

  UninstallDialog(Profile* profile,
                  apps::AppType app_type,
                  const std::string& app_id,
                  const std::string& app_name,
                  gfx::NativeWindow parent_window,
                  UninstallCallback uninstall_callback);
  UninstallDialog(const UninstallDialog&) = delete;
  UninstallDialog& operator=(const UninstallDialog&) = delete;
  ~UninstallDialog();

  // Loads the app icon to show the icon in the uninstall dialog before creating
  // the dialog view.
  void PrepareToShow(IconKey icon_key,
                     apps::IconLoader* icon_loader,
                     int32_t icon_size);

  // Closes this dialog if it is open. If the dialog is not open yet because
  // icons are still loading, immediately runs `uninstall_callback_` so that
  // `this` can be deleted.
  void CloseDialog();

  views::Widget* GetWidget();

  // Called when the uninstall dialog is closing to process uninstall or cancel
  // the uninstall.
  void OnDialogClosed(bool uninstall, bool clear_site_data, bool report_abuse);

  void SetDialogCreatedCallbackForTesting(
      OnUninstallForTestingCallback callback);

 private:
  // Callback invoked when the icon is loaded.
  void OnLoadIcon(IconValuePtr icon_value);

  const raw_ptr<Profile> profile_;
  const apps::AppType app_type_;
  const std::string app_id_;
  const std::string app_name_;
  gfx::NativeWindow parent_window_;
  UninstallCallback uninstall_callback_;

  OnUninstallForTestingCallback uninstall_dialog_created_callback_;

  raw_ptr<views::Widget, AcrossTasksDanglingUntriaged> widget_ = nullptr;

  // Tracks whether |parent_window_| got destroyed.
  std::unique_ptr<views::NativeWindowTracker> parent_window_tracker_;

  base::WeakPtrFactory<UninstallDialog> weak_ptr_factory_{this};
};

}  // namespace apps

#endif  // CHROME_BROWSER_APPS_APP_SERVICE_UNINSTALL_DIALOG_H_