chromium/chrome/browser/ui/web_applications/web_app_ui_manager_impl.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/web_applications/web_app_ui_manager_impl.h"

#include <map>
#include <memory>
#include <type_traits>
#include <utility>

#include "base/check.h"
#include "base/check_op.h"
#include "base/files/file_path.h"
#include "base/functional/bind.h"
#include "base/functional/callback.h"
#include "base/location.h"
#include "base/memory/scoped_refptr.h"
#include "base/memory/weak_ptr.h"
#include "base/metrics/user_metrics.h"
#include "base/metrics/user_metrics_action.h"
#include "base/one_shot_event.h"
#include "base/task/sequenced_task_runner.h"
#include "build/build_config.h"
#include "build/chromeos_buildflags.h"
#include "chrome/browser/apps/app_service/app_launch_params.h"
#include "chrome/browser/profiles/profile.h"
#include "chrome/browser/ui/browser.h"
#include "chrome/browser/ui/browser_element_identifiers.h"
#include "chrome/browser/ui/browser_finder.h"
#include "chrome/browser/ui/browser_list.h"
#include "chrome/browser/ui/browser_navigator.h"
#include "chrome/browser/ui/browser_navigator_params.h"
#include "chrome/browser/ui/browser_window.h"
#include "chrome/browser/ui/chrome_pages.h"
#include "chrome/browser/ui/web_applications/app_browser_controller.h"
#include "chrome/browser/ui/web_applications/web_app_dialog_utils.h"
#include "chrome/browser/ui/web_applications/web_app_dialogs.h"
#include "chrome/browser/ui/web_applications/web_app_launch_utils.h"
#include "chrome/browser/ui/web_applications/web_app_metrics.h"
#include "chrome/browser/ui/web_applications/web_app_run_on_os_login_notification.h"
#include "chrome/browser/web_applications/web_app_callback_app_identity.h"
#include "chrome/browser/web_applications/web_app_command_scheduler.h"
#include "chrome/browser/web_applications/web_app_icon_manager.h"
#include "chrome/browser/web_applications/web_app_pref_guardrails.h"
#include "chrome/browser/web_applications/web_app_provider.h"
#include "chrome/browser/web_applications/web_app_registrar.h"
#include "chrome/browser/web_applications/web_app_ui_manager.h"
#include "chrome/browser/web_applications/web_app_uninstall_dialog_user_options.h"
#include "components/feature_engagement/public/feature_constants.h"
#include "components/user_education/common/feature_promo_controller.h"
#include "components/user_education/common/feature_promo_data.h"
#include "components/webapps/browser/installable/installable_metrics.h"
#include "components/webapps/browser/uninstall_result_code.h"
#include "components/webapps/common/web_app_id.h"
#include "content/public/browser/clear_site_data_utils.h"
#include "content/public/browser/navigation_handle.h"
#include "content/public/browser/web_contents.h"
#include "extensions/browser/app_sorting.h"
#include "extensions/browser/extension_system.h"
#include "third_party/blink/public/mojom/manifest/manifest.mojom-shared.h"
#include "third_party/skia/include/core/SkBitmap.h"
#include "ui/base/page_transition_types.h"
#include "ui/views/native_window_tracker.h"
#include "url/gurl.h"
#include "url/origin.h"
#include "url/url_constants.h"

#if !BUILDFLAG(IS_CHROMEOS)
#include "chrome/browser/apps/link_capturing/enable_link_capturing_infobar_delegate.h"
#include "chrome/browser/infobars/confirm_infobar_creator.h"
#include "components/infobars/content/content_infobar_manager.h"
#include "components/infobars/core/infobar.h"
#else
#include "chrome/browser/ui/web_applications/web_app_relaunch_notification.h"
#endif  // !BUILDFLAG(IS_CHROMEOS)

#if !BUILDFLAG(IS_MAC)
#include "ui/aura/window.h"
#endif  // !BUILDFLAG(IS_MAC)

#if BUILDFLAG(IS_CHROMEOS_ASH)
#include "ash/public/cpp/shelf_model.h"
#include "chrome/browser/ash/app_list/app_list_syncable_service.h"
#include "chrome/browser/ash/app_list/app_list_syncable_service_factory.h"
#include "chrome/browser/ui/ash/shelf/chrome_shelf_controller.h"
#include "chrome/browser/ui/ash/shelf/chrome_shelf_controller_util.h"
#include "chromeos/ash/components/nonclosable_app_ui/nonclosable_app_ui_utils.h"
#include "components/sync/model/string_ordinal.h"
#endif  // BUILDFLAG(IS_CHROMEOS_ASH)

#if BUILDFLAG(IS_CHROMEOS_LACROS)
#include "chrome/browser/ui/startup/first_run_service.h"
#include "chromeos/crosapi/mojom/web_app_service.mojom.h"
#include "chromeos/lacros/lacros_service.h"
#include "mojo/public/cpp/bindings/remote.h"
#endif  // BUILDFLAG(IS_CHROMEOS_LACROS)

#if BUILDFLAG(IS_WIN)
#include "base/process/process.h"
#include "chrome/browser/browser_process.h"
#include "chrome/browser/web_applications/os_integration/os_integration_manager.h"
#include "chrome/browser/web_applications/os_integration/web_app_shortcut.h"
#include "chrome/browser/web_applications/web_app_install_finalizer.h"
#include "components/keep_alive_registry/keep_alive_types.h"
#include "components/keep_alive_registry/scoped_keep_alive.h"
#endif  // BUILDFLAG(IS_WIN)

namespace base {
class FilePath;
}

namespace web_app {

class AppLock;

namespace {

#if BUILDFLAG(IS_WIN)
void UninstallWebAppWithDialogFromStartupSwitch(
    std::unique_ptr<ScopedKeepAlive> scoped_keep_alive,
    const webapps::AppId& app_id,
    WebAppProvider* provider) {
  if (provider->registrar_unsafe().CanUserUninstallWebApp(app_id)) {
    provider->ui_manager().PresentUserUninstallDialog(
        app_id, webapps::WebappUninstallSource::kOsSettings,
        gfx::NativeWindow(),
        base::BindOnce(
            [](std::unique_ptr<ScopedKeepAlive> scoped_keep_alive,
               webapps::UninstallResultCode code) {
              // This ensures that the scoped_keep_alive will be deleted in the
              // next message loop, giving objects like DialogDelegate enough
              // time to shut itself down. See crbug.com/1506302 for more
              // information.
              base::SequencedTaskRunner::GetCurrentDefault()->DeleteSoon(
                  FROM_HERE, std::move(scoped_keep_alive));
            },
            std::move(scoped_keep_alive)));
  } else {
    // This is necessary to remove all OS integrations if the app has
    // been uninstalled.
    SynchronizeOsOptions synchronize_options;
    synchronize_options.force_unregister_os_integration = true;
    provider->scheduler().SynchronizeOsIntegration(
        app_id,
        base::BindOnce(
            [](std::unique_ptr<ScopedKeepAlive> scoped_keep_alive) {},
            std::move(scoped_keep_alive)),
        synchronize_options);
  }
}

#endif  // BUILDFLAG(IS_WIN)

#if BUILDFLAG(IS_CHROMEOS)
void ShowNonclosableAppToast(const web_app::WebAppRegistrar& registrar,
                             const webapps::AppId& app_id) {
#if BUILDFLAG(IS_CHROMEOS_LACROS)
  auto* const service = chromeos::LacrosService::Get();
  if (!service->IsRegistered<crosapi::mojom::NonclosableAppToastService>() ||
      !service->IsAvailable<crosapi::mojom::NonclosableAppToastService>()) {
    return;
  }

  crosapi::mojom::NonclosableAppToastService* const
      nonclosable_app_toast_service =
          service->GetRemote<crosapi::mojom::NonclosableAppToastService>()
              .get();
  nonclosable_app_toast_service->OnUserAttemptedClose(
      app_id, registrar.GetAppShortName(app_id));
#elif BUILDFLAG(IS_CHROMEOS_ASH)
  ash::ShowNonclosableAppToast(app_id, registrar.GetAppShortName(app_id));
#endif  // BUILDFLAG(IS_CHROMEOS_ASH)
}
#endif  // BUILDFLAG(IS_CHROMEOS)

}  // namespace

// static
std::unique_ptr<WebAppUiManager> WebAppUiManager::Create(Profile* profile) {}

WebAppUiManagerImpl::WebAppUiManagerImpl(Profile* profile)
    :{}

WebAppUiManagerImpl::~WebAppUiManagerImpl() = default;

void WebAppUiManagerImpl::Start() {}

void WebAppUiManagerImpl::Shutdown() {}

WebAppUiManagerImpl* WebAppUiManagerImpl::AsImpl() {}

size_t WebAppUiManagerImpl::GetNumWindowsForApp(const webapps::AppId& app_id) {}

void WebAppUiManagerImpl::CloseAppWindows(const webapps::AppId& app_id) {}

void WebAppUiManagerImpl::NotifyOnAllAppWindowsClosed(
    const webapps::AppId& app_id,
    base::OnceClosure callback) {}

void WebAppUiManagerImpl::OnExtensionSystemReady() {}

bool WebAppUiManagerImpl::CanAddAppToQuickLaunchBar() const {}

void WebAppUiManagerImpl::AddAppToQuickLaunchBar(const webapps::AppId& app_id) {}

bool WebAppUiManagerImpl::IsAppInQuickLaunchBar(
    const webapps::AppId& app_id) const {}

bool WebAppUiManagerImpl::IsInAppWindow(
    content::WebContents* web_contents) const {}

const webapps::AppId* WebAppUiManagerImpl::GetAppIdForWindow(
    const content::WebContents* web_contents) const {}

void WebAppUiManagerImpl::NotifyOnAssociatedAppChanged(
    content::WebContents* web_contents,
    const std::optional<webapps::AppId>& previous_app_id,
    const std::optional<webapps::AppId>& new_app_id) const {}

bool WebAppUiManagerImpl::CanReparentAppTabToWindow(
    const webapps::AppId& app_id,
    bool shortcut_created) const {}

Browser* WebAppUiManagerImpl::ReparentAppTabToWindow(
    content::WebContents* contents,
    const webapps::AppId& app_id,
    bool shortcut_created) {}

void WebAppUiManagerImpl::ShowWebAppFileLaunchDialog(
    const std::vector<base::FilePath>& file_paths,
    const webapps::AppId& app_id,
    WebAppLaunchAcceptanceCallback launch_callback) {}

void WebAppUiManagerImpl::ShowWebAppIdentityUpdateDialog(
    const std::string& app_id,
    bool title_change,
    bool icon_change,
    const std::u16string& old_title,
    const std::u16string& new_title,
    const SkBitmap& old_icon,
    const SkBitmap& new_icon,
    content::WebContents* web_contents,
    web_app::AppIdentityDialogCallback callback) {}

void WebAppUiManagerImpl::ShowWebAppSettings(const webapps::AppId& app_id) {}

void WebAppUiManagerImpl::LaunchWebApp(apps::AppLaunchParams params,
                                       LaunchWebAppWindowSetting launch_setting,
                                       Profile& profile,
                                       LaunchWebAppDebugValueCallback callback,
                                       WithAppResources& lock) {}

void WebAppUiManagerImpl::WaitForFirstRunService(
    Profile& profile,
    FirstRunServiceCompletedCallback callback) {}

#if BUILDFLAG(IS_CHROMEOS)
void WebAppUiManagerImpl::MigrateLauncherState(
    const webapps::AppId& from_app_id,
    const webapps::AppId& to_app_id,
    base::OnceClosure callback) {
#if BUILDFLAG(IS_CHROMEOS_LACROS)
  auto* lacros_service = chromeos::LacrosService::Get();
  if (!lacros_service ||
      lacros_service->GetInterfaceVersion<crosapi::mojom::WebAppService>() <
          int{crosapi::mojom::WebAppService::MethodMinVersions::
                  kMigrateLauncherStateMinVersion}) {
    LOG(WARNING) << "Ash version does not support MigrateLauncherState().";
    std::move(callback).Run();
    return;
  }
  // Forward the call to the Ash build of this method (see next #if branch).
  lacros_service->GetRemote<crosapi::mojom::WebAppService>()
      ->MigrateLauncherState(from_app_id, to_app_id, std::move(callback));
#elif BUILDFLAG(IS_CHROMEOS_ASH)
  auto* app_list_syncable_service =
      app_list::AppListSyncableServiceFactory::GetForProfile(profile_);
  bool to_app_in_shelf =
      app_list_syncable_service->GetPinPosition(to_app_id).IsValid();
  // If the new app is already pinned to the shelf don't transfer UI prefs
  // across as that could cause it to become unpinned.
  if (!to_app_in_shelf) {
    app_list_syncable_service->TransferItemAttributes(from_app_id, to_app_id);
  }
  std::move(callback).Run();
#else
  static_assert(false);
#endif
}

void WebAppUiManagerImpl::DisplayRunOnOsLoginNotification(
    const base::flat_map<webapps::AppId, RoolNotificationBehavior>& apps,
    base::WeakPtr<Profile> profile) {
  web_app::DisplayRunOnOsLoginNotification(apps, profile);
}
#endif  // BUILDFLAG(IS_CHROMEOS)

void WebAppUiManagerImpl::NotifyAppRelaunchState(
    const webapps::AppId& placeholder_app_id,
    const webapps::AppId& final_app_id,
    const std::u16string& final_app_name,
    base::WeakPtr<Profile> profile,
    AppRelaunchState relaunch_state) {}

content::WebContents* WebAppUiManagerImpl::CreateNewTab() {}

bool WebAppUiManagerImpl::IsWebContentsActiveTabInBrowser(
    content::WebContents* web_contents) {}

void WebAppUiManagerImpl::TriggerInstallDialog(
    content::WebContents* web_contents) {}

void WebAppUiManagerImpl::PresentUserUninstallDialog(
    const webapps::AppId& app_id,
    webapps::WebappUninstallSource uninstall_source,
    BrowserWindow* parent_window,
    UninstallCompleteCallback callback) {}

void WebAppUiManagerImpl::PresentUserUninstallDialog(
    const webapps::AppId& app_id,
    webapps::WebappUninstallSource uninstall_source,
    gfx::NativeWindow native_window,
    UninstallCompleteCallback callback) {}

void WebAppUiManagerImpl::PresentUserUninstallDialog(
    const webapps::AppId& app_id,
    webapps::WebappUninstallSource uninstall_source,
    gfx::NativeWindow parent_window,
    UninstallCompleteCallback uninstall_complete_callback,
    UninstallScheduledCallback uninstall_scheduled_callback) {}

void WebAppUiManagerImpl::LaunchOrFocusIsolatedWebAppInstaller(
    const base::FilePath& bundle_path) {}

void WebAppUiManagerImpl::OnIsolatedWebAppInstallerClosed(
    base::FilePath bundle_path) {}

void WebAppUiManagerImpl::MaybeCreateEnableSupportedLinksInfobar(
    content::WebContents* web_contents,
    const std::string& launch_name) {}

void WebAppUiManagerImpl::MaybeShowIPHPromoForAppsLaunchedViaLinkCapturing(
    content::WebContents* web_contents,
    Profile* profile,
    const std::string& app_id) {}

void WebAppUiManagerImpl::OnBrowserAdded(Browser* browser) {}

#if BUILDFLAG(IS_CHROMEOS)
void WebAppUiManagerImpl::OnBrowserCloseCancelled(Browser* browser,
                                                  BrowserClosingStatus reason) {
  DCHECK(started_);
  if (!IsBrowserForInstalledApp(browser) ||
      reason != BrowserClosingStatus::kDeniedByPolicy) {
    return;
  }

  ShowNonclosableAppToast(
      WebAppProvider::GetForWebApps(profile_)->registrar_unsafe(),
      GetAppIdForBrowser(browser));
}
#endif  // BUILDFLAG(IS_CHROMEOS)

void WebAppUiManagerImpl::OnBrowserRemoved(Browser* browser) {}

#if BUILDFLAG(IS_CHROMEOS)
void WebAppUiManagerImpl::TabCloseCancelled(
    const content::WebContents* contents) {
  const webapps::AppId* app_id = GetAppIdForWindow(contents);
  if (!app_id) {
    return;
  }

  ShowNonclosableAppToast(
      WebAppProvider::GetForWebApps(profile_)->registrar_unsafe(), *app_id);
}
#endif  // BUILDFLAG(IS_CHROMEOS)

#if BUILDFLAG(IS_WIN)
void WebAppUiManagerImpl::UninstallWebAppFromStartupSwitch(
    const webapps::AppId& app_id) {
  WebAppProvider* provider = WebAppProvider::GetForWebApps(profile_);
  // ScopedKeepAlive not only keeps the process from terminating early
  // during uninstall, it also ensures the process will terminate in the next
  // message loop if there are no active browser windows.
  provider->on_registry_ready().Post(
      FROM_HERE, base::BindOnce(&UninstallWebAppWithDialogFromStartupSwitch,
                                std::make_unique<ScopedKeepAlive>(
                                    KeepAliveOrigin::WEB_APP_UNINSTALL,
                                    KeepAliveRestartOption::DISABLED),
                                app_id, provider));
}
#endif  //  BUILDFLAG(IS_WIN)

bool WebAppUiManagerImpl::IsBrowserForInstalledApp(Browser* browser) {}

webapps::AppId WebAppUiManagerImpl::GetAppIdForBrowser(Browser* browser) {}

void WebAppUiManagerImpl::OnIconsReadForUninstall(
    const webapps::AppId& app_id,
    webapps::WebappUninstallSource uninstall_source,
    gfx::NativeWindow parent_window,
    std::unique_ptr<views::NativeWindowTracker> parent_window_tracker,
    UninstallCompleteCallback complete_callback,
    UninstallScheduledCallback uninstall_scheduled_callback,
    std::map<SquareSizePx, SkBitmap> icon_bitmaps) {}

void WebAppUiManagerImpl::ScheduleUninstallIfUserRequested(
    const webapps::AppId& app_id,
    webapps::WebappUninstallSource uninstall_source,
    UninstallCompleteCallback complete_callback,
    UninstallScheduledCallback uninstall_scheduled_callback,
    web_app::UninstallUserOptions uninstall_options) {}

void WebAppUiManagerImpl::OnUninstallCancelled(
    UninstallCompleteCallback complete_callback,
    UninstallScheduledCallback uninstall_scheduled_callback) {}

void WebAppUiManagerImpl::ClearWebAppSiteDataIfNeeded(
    const GURL app_start_url,
    UninstallCompleteCallback uninstall_complete_callback,
    webapps::UninstallResultCode uninstall_code) {}

#if BUILDFLAG(IS_WIN) || BUILDFLAG(IS_MAC) || BUILDFLAG(IS_LINUX)
void WebAppUiManagerImpl::ShowIPHPromoForAppsLaunchedViaLinkCapturing(
    const Browser* browser,
    const webapps::AppId& app_id,
    bool is_activated) {}

void WebAppUiManagerImpl::OnIPHPromoResponseForLinkCapturing(
    const Browser* browser,
    const webapps::AppId& app_id) {}

#endif  // BUILDFLAG(IS_WIN) || BUILDFLAG(IS_MAC) || BUILDFLAG(IS_LINUX)

}  // namespace web_app