blob: af1b98e1846fc8d4ff2a80f4c2ddab2339e43d23 [file] [log] [blame]
// Copyright 2020 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "chrome/browser/web_applications/os_integration_manager.h"
#include <memory>
#include <utility>
#include "base/auto_reset.h"
#include "base/bind.h"
#include "base/callback.h"
#include "base/callback_forward.h"
#include "base/callback_helpers.h"
#include "base/feature_list.h"
#include "base/memory/ref_counted.h"
#include "base/memory/weak_ptr.h"
#include "base/strings/string_util.h"
#include "base/task/post_task.h"
#include "base/task/task_traits.h"
#include "base/threading/sequenced_task_runner_handle.h"
#include "build/build_config.h"
#include "build/chromeos_buildflags.h"
#include "chrome/browser/profiles/profile.h"
#include "chrome/browser/web_applications/web_app_constants.h"
#include "chrome/browser/web_applications/web_app_id.h"
#include "chrome/browser/web_applications/web_app_registrar.h"
#include "chrome/browser/web_applications/web_app_shortcut.h"
#include "chrome/browser/web_applications/web_app_ui_manager.h"
#include "chrome/browser/web_applications/web_app_uninstallation_via_os_settings_registration.h"
#include "chrome/common/chrome_features.h"
#include "content/public/browser/browser_thread.h"
#include "third_party/abseil-cpp/absl/types/optional.h"
#if defined(OS_MAC)
#include "chrome/browser/web_applications/app_shim_registry_mac.h"
#endif
#if defined(OS_WIN)
#include "base/win/windows_version.h"
#endif
namespace {
// Used to disable os hooks globally when OsIntegrationManager::SuppressOsHooks
// can't be easily used.
bool g_suppress_os_hooks_for_testing_ = false;
} // namespace
namespace web_app {
// This barrier is designed to accumulate errors from calls to OS hook
// operations, and call the completion callback when all OS hook operations
// have completed. The |callback| is called when all copies of this object and
// all callbacks created using this object are destroyed.
class OsIntegrationManager::OsHooksBarrier
: public base::RefCounted<OsHooksBarrier> {
public:
explicit OsHooksBarrier(OsHooksErrors errors_default,
InstallOsHooksCallback callback)
: errors_(errors_default), callback_(std::move(callback)) {}
void OnError(OsHookType::Type type) { AddSuccess(type, false); }
base::OnceCallback<void(bool)> CreateSuccessBarrierCallbackForType(
OsHookType::Type type) {
return base::BindOnce(&OsHooksBarrier::AddSuccess, this, type);
}
private:
friend class base::RefCounted<OsHooksBarrier>;
~OsHooksBarrier() {
DCHECK(callback_);
base::SequencedTaskRunnerHandle::Get()->PostTask(
FROM_HERE, base::BindOnce(std::move(callback_), std::move(errors_)));
}
void AddSuccess(OsHookType::Type type, bool success) {
DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
bool error = !success;
errors_[type] = error;
}
OsHooksErrors errors_;
InstallOsHooksCallback callback_;
};
InstallOsHooksOptions::InstallOsHooksOptions() = default;
InstallOsHooksOptions::InstallOsHooksOptions(
const InstallOsHooksOptions& other) = default;
InstallOsHooksOptions& InstallOsHooksOptions::operator=(
const InstallOsHooksOptions& other) = default;
OsIntegrationManager::OsIntegrationManager(
Profile* profile,
std::unique_ptr<WebAppShortcutManager> shortcut_manager,
std::unique_ptr<WebAppFileHandlerManager> file_handler_manager,
std::unique_ptr<WebAppProtocolHandlerManager> protocol_handler_manager,
std::unique_ptr<UrlHandlerManager> url_handler_manager)
: profile_(profile),
shortcut_manager_(std::move(shortcut_manager)),
file_handler_manager_(std::move(file_handler_manager)),
protocol_handler_manager_(std::move(protocol_handler_manager)),
url_handler_manager_(std::move(url_handler_manager)) {}
OsIntegrationManager::~OsIntegrationManager() = default;
void OsIntegrationManager::SetSubsystems(WebAppRegistrar* registrar,
WebAppUiManager* ui_manager,
WebAppIconManager* icon_manager) {
registrar_ = registrar;
ui_manager_ = ui_manager;
file_handler_manager_->SetSubsystems(registrar);
shortcut_manager_->SetSubsystems(icon_manager, registrar);
if (protocol_handler_manager_)
protocol_handler_manager_->SetSubsystems(registrar);
if (url_handler_manager_)
url_handler_manager_->SetSubsystems(registrar);
}
void OsIntegrationManager::Start() {
DCHECK(registrar_);
DCHECK(file_handler_manager_);
#if defined(OS_MAC)
// Ensure that all installed apps are included in the AppShimRegistry when the
// profile is loaded. This is redundant, because apps are registered when they
// are installed. It is necessary, however, because app registration was added
// long after app installation launched. This should be removed after shipping
// for a few versions (whereupon it may be assumed that most applications have
// been registered).
std::vector<AppId> app_ids = registrar_->GetAppIds();
for (const auto& app_id : app_ids) {
AppShimRegistry::Get()->OnAppInstalledForProfile(app_id,
profile_->GetPath());
}
#endif
file_handler_manager_->Start();
if (protocol_handler_manager_)
protocol_handler_manager_->Start();
}
void OsIntegrationManager::InstallOsHooks(
const AppId& app_id,
InstallOsHooksCallback callback,
std::unique_ptr<WebApplicationInfo> web_app_info,
InstallOsHooksOptions options) {
if (g_suppress_os_hooks_for_testing_) {
OsHooksErrors os_hooks_errors;
base::SequencedTaskRunnerHandle::Get()->PostTask(
FROM_HERE, base::BindOnce(std::move(callback), os_hooks_errors));
return;
}
MacAppShimOnAppInstalledForProfile(app_id);
OsHooksErrors os_hooks_errors;
scoped_refptr<OsHooksBarrier> barrier = base::MakeRefCounted<OsHooksBarrier>(
os_hooks_errors, std::move(callback));
DCHECK(options.os_hooks[OsHookType::kShortcuts] ||
!options.os_hooks[OsHookType::kShortcutsMenu])
<< "Cannot install shortcuts menu without installing shortcuts.";
auto shortcuts_callback = base::BindOnce(
&OsIntegrationManager::OnShortcutsCreated, weak_ptr_factory_.GetWeakPtr(),
app_id, std::move(web_app_info), options, barrier);
// TODO(ortuno): Make adding a shortcut to the applications menu independent
// from adding a shortcut to desktop.
if (options.os_hooks[OsHookType::kShortcuts]) {
CreateShortcuts(app_id, options.add_to_desktop,
std::move(shortcuts_callback));
} else {
base::SequencedTaskRunnerHandle::Get()->PostTask(
FROM_HERE, base::BindOnce(std::move(shortcuts_callback),
/*shortcuts_created=*/false));
}
}
void OsIntegrationManager::UninstallAllOsHooks(
const AppId& app_id,
UninstallOsHooksCallback callback) {
OsHooksOptions os_hooks;
os_hooks.set();
UninstallOsHooks(app_id, os_hooks, std::move(callback));
}
void OsIntegrationManager::UninstallOsHooks(const AppId& app_id,
const OsHooksOptions& os_hooks,
UninstallOsHooksCallback callback) {
if (g_suppress_os_hooks_for_testing_) {
OsHooksErrors os_hooks_errors;
base::SequencedTaskRunnerHandle::Get()->PostTask(
FROM_HERE, base::BindOnce(std::move(callback), os_hooks_errors));
return;
}
OsHooksErrors os_hooks_errors;
scoped_refptr<OsHooksBarrier> barrier = base::MakeRefCounted<OsHooksBarrier>(
os_hooks_errors, std::move(callback));
if (os_hooks[OsHookType::kShortcutsMenu]) {
bool success = UnregisterShortcutsMenu(app_id);
if (!success)
barrier->OnError(OsHookType::kShortcutsMenu);
}
if (os_hooks[OsHookType::kShortcuts] || os_hooks[OsHookType::kRunOnOsLogin]) {
std::unique_ptr<ShortcutInfo> shortcut_info = BuildShortcutInfo(app_id);
base::FilePath shortcut_data_dir =
internals::GetShortcutDataDir(*shortcut_info);
if (os_hooks[OsHookType::kRunOnOsLogin] &&
base::FeatureList::IsEnabled(features::kDesktopPWAsRunOnOsLogin)) {
UnregisterRunOnOsLogin(app_id, shortcut_info->profile_path,
shortcut_info->title,
barrier->CreateSuccessBarrierCallbackForType(
OsHookType::kRunOnOsLogin));
}
if (os_hooks[OsHookType::kShortcuts]) {
DeleteShortcuts(
app_id, shortcut_data_dir, std::move(shortcut_info),
barrier->CreateSuccessBarrierCallbackForType(OsHookType::kShortcuts));
}
}
// unregistration and record errors during unregistration.
if (os_hooks[OsHookType::kFileHandlers]) {
UnregisterFileHandlers(app_id, barrier->CreateSuccessBarrierCallbackForType(
OsHookType::kFileHandlers));
}
if (os_hooks[OsHookType::kProtocolHandlers]) {
UnregisterProtocolHandlers(app_id,
barrier->CreateSuccessBarrierCallbackForType(
OsHookType::kProtocolHandlers));
}
if (os_hooks[OsHookType::kUrlHandlers])
UnregisterUrlHandlers(app_id);
// There is a chance uninstallation point was created with feature flag
// enabled so we need to clean it up regardless of feature flag state.
if (os_hooks[OsHookType::kUninstallationViaOsSettings])
UnregisterWebAppOsUninstallation(app_id);
}
void OsIntegrationManager::UpdateOsHooks(
const AppId& app_id,
base::StringPiece old_name,
FileHandlerUpdateAction file_handlers_need_os_update,
const WebApplicationInfo& web_app_info) {
if (g_suppress_os_hooks_for_testing_)
return;
UpdateFileHandlers(app_id, file_handlers_need_os_update, base::DoNothing());
UpdateShortcuts(app_id, old_name, base::DoNothing());
UpdateShortcutsMenu(app_id, web_app_info);
UpdateUrlHandlers(app_id, base::DoNothing());
UpdateProtocolHandlers(app_id, /*force_shortcut_updates_if_needed=*/false,
base::DoNothing());
}
void OsIntegrationManager::GetAppExistingShortCutLocation(
ShortcutLocationCallback callback,
std::unique_ptr<ShortcutInfo> shortcut_info) {
DCHECK(shortcut_manager_);
shortcut_manager_->GetAppExistingShortCutLocation(std::move(callback),
std::move(shortcut_info));
}
void OsIntegrationManager::GetShortcutInfoForApp(
const AppId& app_id,
WebAppShortcutManager::GetShortcutInfoCallback callback) {
DCHECK(shortcut_manager_);
return shortcut_manager_->GetShortcutInfoForApp(app_id, std::move(callback));
}
bool OsIntegrationManager::IsFileHandlingAPIAvailable(const AppId& app_id) {
DCHECK(file_handler_manager_);
return file_handler_manager_->IsFileHandlingAPIAvailable(app_id);
}
const apps::FileHandlers* OsIntegrationManager::GetEnabledFileHandlers(
const AppId& app_id) {
DCHECK(file_handler_manager_);
return file_handler_manager_->GetEnabledFileHandlers(app_id);
}
const absl::optional<GURL> OsIntegrationManager::GetMatchingFileHandlerURL(
const AppId& app_id,
const std::vector<base::FilePath>& launch_files) {
DCHECK(file_handler_manager_);
return file_handler_manager_->GetMatchingFileHandlerURL(app_id, launch_files);
}
void OsIntegrationManager::MaybeUpdateFileHandlingOriginTrialExpiry(
content::WebContents* web_contents,
const AppId& app_id) {
DCHECK(file_handler_manager_);
return file_handler_manager_->MaybeUpdateFileHandlingOriginTrialExpiry(
web_contents, app_id);
}
void OsIntegrationManager::ForceEnableFileHandlingOriginTrial(
const AppId& app_id) {
DCHECK(file_handler_manager_);
return file_handler_manager_->ForceEnableFileHandlingOriginTrial(app_id);
}
void OsIntegrationManager::DisableForceEnabledFileHandlingOriginTrial(
const AppId& app_id) {
DCHECK(file_handler_manager_);
return file_handler_manager_->DisableForceEnabledFileHandlingOriginTrial(
app_id);
}
absl::optional<GURL> OsIntegrationManager::TranslateProtocolUrl(
const AppId& app_id,
const GURL& protocol_url) {
if (!protocol_handler_manager_)
return absl::optional<GURL>();
return protocol_handler_manager_->TranslateProtocolUrl(app_id, protocol_url);
}
std::vector<ProtocolHandler> OsIntegrationManager::GetHandlersForProtocol(
const std::string& protocol) {
if (!protocol_handler_manager_)
return std::vector<ProtocolHandler>();
return protocol_handler_manager_->GetHandlersFor(protocol);
}
std::vector<ProtocolHandler> OsIntegrationManager::GetAppProtocolHandlers(
const AppId& app_id) {
if (!protocol_handler_manager_)
return std::vector<ProtocolHandler>();
return protocol_handler_manager_->GetAppProtocolHandlers(app_id);
}
std::vector<ProtocolHandler>
OsIntegrationManager::GetAllowedHandlersForProtocol(
const std::string& protocol) {
if (!protocol_handler_manager_)
return std::vector<ProtocolHandler>();
return protocol_handler_manager_->GetAllowedHandlersForProtocol(protocol);
}
std::vector<ProtocolHandler>
OsIntegrationManager::GetDisallowedHandlersForProtocol(
const std::string& protocol) {
if (!protocol_handler_manager_)
return std::vector<ProtocolHandler>();
return protocol_handler_manager_->GetDisallowedHandlersForProtocol(protocol);
}
WebAppFileHandlerManager&
OsIntegrationManager::file_handler_manager_for_testing() {
DCHECK(file_handler_manager_);
return *file_handler_manager_;
}
UrlHandlerManager& OsIntegrationManager::url_handler_manager_for_testing() {
DCHECK(url_handler_manager_);
return *url_handler_manager_;
}
WebAppProtocolHandlerManager&
OsIntegrationManager::protocol_handler_manager_for_testing() {
DCHECK(protocol_handler_manager_);
return *protocol_handler_manager_;
}
ScopedOsHooksSuppress OsIntegrationManager::ScopedSuppressOsHooksForTesting() {
// Creating OS hooks on ChromeOS doesn't write files to disk, so it's
// unnecessary to suppress and it provides better crash coverage.
#if !BUILDFLAG(IS_CHROMEOS_ASH)
return std::make_unique<base::AutoReset<bool>>(
&g_suppress_os_hooks_for_testing_, true);
#else
return std::make_unique<base::AutoReset<bool>>(
&g_suppress_os_hooks_for_testing_, false);
#endif
}
FakeOsIntegrationManager* OsIntegrationManager::AsTestOsIntegrationManager() {
return nullptr;
}
void OsIntegrationManager::CreateShortcuts(const AppId& app_id,
bool add_to_desktop,
CreateShortcutsCallback callback) {
if (shortcut_manager_->CanCreateShortcuts()) {
shortcut_manager_->CreateShortcuts(app_id, add_to_desktop,
std::move(callback));
} else {
std::move(callback).Run(false);
}
}
void OsIntegrationManager::RegisterFileHandlers(
const AppId& app_id,
base::OnceCallback<void(bool success)> callback) {
DCHECK(file_handler_manager_);
file_handler_manager_->EnableAndRegisterOsFileHandlers(app_id);
// TODO(crbug.com/1087219): callback should be run after all hooks are
// deployed, need to refactor filehandler to allow this.
std::move(callback).Run(true);
}
void OsIntegrationManager::RegisterProtocolHandlers(
const AppId& app_id,
base::OnceCallback<void(bool success)> callback) {
// Disable protocol handler unregistration on Win7 due to bad interactions
// between preinstalled app scenarios and the need for elevation to unregister
// protocol handlers on that platform. See crbug.com/1224327 for context.
#if defined(OS_WIN)
if (base::win::GetVersion() == base::win::Version::WIN7) {
std::move(callback).Run(true);
return;
}
#endif // defined(OS_WIN)
if (!protocol_handler_manager_) {
std::move(callback).Run(true);
return;
}
protocol_handler_manager_->RegisterOsProtocolHandlers(app_id,
std::move(callback));
}
void OsIntegrationManager::RegisterUrlHandlers(
const AppId& app_id,
base::OnceCallback<void(bool success)> callback) {
if (!url_handler_manager_) {
std::move(callback).Run(true);
return;
}
url_handler_manager_->RegisterUrlHandlers(app_id, std::move(callback));
}
void OsIntegrationManager::RegisterShortcutsMenu(
const AppId& app_id,
const std::vector<WebApplicationShortcutsMenuItemInfo>&
shortcuts_menu_item_infos,
const ShortcutsMenuIconBitmaps& shortcuts_menu_icon_bitmaps,
base::OnceCallback<void(bool success)> callback) {
if (!ShouldRegisterShortcutsMenuWithOs()) {
std::move(callback).Run(true);
return;
}
DCHECK(shortcut_manager_);
shortcut_manager_->RegisterShortcutsMenuWithOs(
app_id, shortcuts_menu_item_infos, shortcuts_menu_icon_bitmaps);
// TODO(https://crbug.com/1098471): fix RegisterShortcutsMenuWithOs to
// take callback.
std::move(callback).Run(true);
}
void OsIntegrationManager::ReadAllShortcutsMenuIconsAndRegisterShortcutsMenu(
const AppId& app_id,
base::OnceCallback<void(bool success)> callback) {
if (!ShouldRegisterShortcutsMenuWithOs()) {
std::move(callback).Run(true);
return;
}
shortcut_manager_->ReadAllShortcutsMenuIconsAndRegisterShortcutsMenu(
app_id, std::move(callback));
}
void OsIntegrationManager::RegisterRunOnOsLogin(
const AppId& app_id,
RegisterRunOnOsLoginCallback callback) {
GetShortcutInfoForApp(
app_id,
base::BindOnce(
&OsIntegrationManager::OnShortcutInfoRetrievedRegisterRunOnOsLogin,
weak_ptr_factory_.GetWeakPtr(), std::move(callback)));
}
void OsIntegrationManager::MacAppShimOnAppInstalledForProfile(
const AppId& app_id) {
#if defined(OS_MAC)
AppShimRegistry::Get()->OnAppInstalledForProfile(app_id, profile_->GetPath());
#endif
}
void OsIntegrationManager::AddAppToQuickLaunchBar(const AppId& app_id) {
DCHECK(ui_manager_);
if (ui_manager_->CanAddAppToQuickLaunchBar()) {
ui_manager_->AddAppToQuickLaunchBar(app_id);
}
}
void OsIntegrationManager::RegisterWebAppOsUninstallation(
const AppId& app_id,
const std::string& name) {
if (ShouldRegisterUninstallationViaOsSettingsWithOs()) {
RegisterUninstallationViaOsSettingsWithOs(app_id, name, profile_);
}
}
bool OsIntegrationManager::UnregisterShortcutsMenu(const AppId& app_id) {
if (!ShouldRegisterShortcutsMenuWithOs())
return true;
return UnregisterShortcutsMenuWithOs(app_id, profile_->GetPath());
}
void OsIntegrationManager::UnregisterRunOnOsLogin(
const AppId& app_id,
const base::FilePath& profile_path,
const std::u16string& shortcut_title,
UnregisterRunOnOsLoginCallback callback) {
ScheduleUnregisterRunOnOsLogin(app_id, profile_path, shortcut_title,
std::move(callback));
}
void OsIntegrationManager::DeleteShortcuts(
const AppId& app_id,
const base::FilePath& shortcuts_data_dir,
std::unique_ptr<ShortcutInfo> shortcut_info,
DeleteShortcutsCallback callback) {
if (shortcut_manager_->CanCreateShortcuts()) {
auto shortcuts_callback = base::BindOnce(
&OsIntegrationManager::OnShortcutsDeleted,
weak_ptr_factory_.GetWeakPtr(), app_id, std::move(callback));
shortcut_manager_->DeleteShortcuts(app_id, shortcuts_data_dir,
std::move(shortcut_info),
std::move(shortcuts_callback));
} else {
std::move(callback).Run(true);
}
}
void OsIntegrationManager::UnregisterFileHandlers(
const AppId& app_id,
base::OnceCallback<void(bool)> callback) {
DCHECK(file_handler_manager_);
file_handler_manager_->DisableAndUnregisterOsFileHandlers(
app_id, std::move(callback));
}
void OsIntegrationManager::UnregisterProtocolHandlers(
const AppId& app_id,
base::OnceCallback<void(bool)> callback) {
// Disable protocol handler unregistration on Win7 due to bad interactions
// between preinstalled app scenarios and the need for elevation to unregister
// protocol handlers on that platform. See crbug.com/1224327 for context.
#if defined(OS_WIN)
if (base::win::GetVersion() == base::win::Version::WIN7) {
std::move(callback).Run(true);
return;
}
#endif // defined(OS_WIN)
if (!protocol_handler_manager_) {
std::move(callback).Run(true);
return;
}
protocol_handler_manager_->UnregisterOsProtocolHandlers(app_id,
std::move(callback));
}
void OsIntegrationManager::UnregisterUrlHandlers(const AppId& app_id) {
if (!url_handler_manager_)
return;
url_handler_manager_->UnregisterUrlHandlers(app_id);
}
void OsIntegrationManager::UnregisterWebAppOsUninstallation(
const AppId& app_id) {
if (ShouldRegisterUninstallationViaOsSettingsWithOs())
UnegisterUninstallationViaOsSettingsWithOs(app_id, profile_);
}
void OsIntegrationManager::UpdateShortcuts(const AppId& app_id,
base::StringPiece old_name,
base::OnceClosure callback) {
DCHECK(shortcut_manager_);
shortcut_manager_->UpdateShortcuts(app_id, old_name, std::move(callback));
}
void OsIntegrationManager::UpdateShortcutsMenu(
const AppId& app_id,
const WebApplicationInfo& web_app_info) {
DCHECK(shortcut_manager_);
if (web_app_info.shortcuts_menu_item_infos.empty()) {
shortcut_manager_->UnregisterShortcutsMenuWithOs(app_id);
} else {
shortcut_manager_->RegisterShortcutsMenuWithOs(
app_id, web_app_info.shortcuts_menu_item_infos,
web_app_info.shortcuts_menu_icon_bitmaps);
}
}
void OsIntegrationManager::UpdateUrlHandlers(
const AppId& app_id,
base::OnceCallback<void(bool success)> callback) {
if (!url_handler_manager_)
return;
url_handler_manager_->UpdateUrlHandlers(app_id, std::move(callback));
}
void OsIntegrationManager::UpdateFileHandlers(
const AppId& app_id,
FileHandlerUpdateAction file_handlers_need_os_update,
base::OnceClosure finished_callback) {
if (!IsFileHandlingAPIAvailable(app_id) ||
file_handlers_need_os_update == FileHandlerUpdateAction::kNoUpdate) {
std::move(finished_callback).Run();
return;
}
// Convert `finished_callback` to a format that takes a bool.
auto finished_callback_with_bool =
base::BindOnce([](base::OnceClosure finished_callback,
bool success) { std::move(finished_callback).Run(); },
std::move(finished_callback));
base::OnceCallback<void(bool)> callback_after_removal;
if (file_handlers_need_os_update == FileHandlerUpdateAction::kUpdate) {
callback_after_removal = base::BindOnce(
[](base::WeakPtr<OsIntegrationManager> os_integration_manager,
const AppId& app_id,
base::OnceCallback<void(bool success)> finished_callback,
bool unregister_success) {
// Re-register file handlers regardless of `unregister_success`.
// TODO(https://crbug.com/1124047): Report `unregister_success` in
// an UMA metric.
if (!os_integration_manager) {
std::move(finished_callback).Run(false);
return;
}
os_integration_manager->RegisterFileHandlers(
app_id, std::move(finished_callback));
},
weak_ptr_factory_.GetWeakPtr(), app_id,
std::move(finished_callback_with_bool));
} else {
DCHECK_EQ(file_handlers_need_os_update, FileHandlerUpdateAction::kRemove);
callback_after_removal = std::move(finished_callback_with_bool);
}
// Update file handlers via complete uninstallation, then potential
// reinstallation.
UnregisterFileHandlers(app_id, std::move(callback_after_removal));
}
void OsIntegrationManager::UpdateProtocolHandlers(
const AppId& app_id,
bool force_shortcut_updates_if_needed,
base::OnceClosure callback) {
if (!protocol_handler_manager_) {
std::move(callback).Run();
return;
}
// Disable protocol handler unregistration on Win7 due to bad interactions
// between preinstalled app scenarios and the need for elevation to unregister
// protocol handlers on that platform. See crbug.com/1224327 for context.
#if defined(OS_WIN)
if (base::win::GetVersion() == base::win::Version::WIN7) {
std::move(callback).Run();
return;
}
#endif // defined(OS_WIN)
auto shortcuts_callback = base::BindOnce(
&OsIntegrationManager::OnShortcutsUpdatedForProtocolHandlers,
weak_ptr_factory_.GetWeakPtr(), app_id, std::move(callback));
#if !defined(OS_WIN)
// Windows handles protocol registration through the registry. For other
// OS's we also need to regenerate the shortcut file before we call into
// the OS. Since `UpdateProtocolHandlers` function is also called in
// `UpdateOSHooks`, which also recreates the shortcuts, only do it if
// required.
if (force_shortcut_updates_if_needed) {
UpdateShortcuts(app_id, "", std::move(shortcuts_callback));
return;
}
#endif
std::move(shortcuts_callback).Run();
}
void OsIntegrationManager::OnShortcutsUpdatedForProtocolHandlers(
const AppId& app_id,
base::OnceClosure update_finished_callback) {
// Update protocol handlers via complete uninstallation, then reinstallation.
base::OnceCallback<void(bool)> unregister_callback = base::BindOnce(
[](base::WeakPtr<OsIntegrationManager> os_integration_manager,
const AppId& app_id, base::OnceClosure update_finished_callback,
bool unregister_success) {
// Re-register protocol handlers regardless of `unregister_success`.
// TODO(https://crbug.com/1250728): Report a UMA metric when
// unregistering fails, either here, or at the point of failure. This
// might also mean we can remove `unregister_success`.
if (!os_integration_manager) {
std::move(update_finished_callback).Run();
return;
}
os_integration_manager->RegisterProtocolHandlers(
app_id, base::BindOnce(
[](base::OnceClosure update_finished_callback,
bool register_success) {
// TODO(https://crbug.com/1250728): Report
// |register_success| in an UMA metric.
std::move(update_finished_callback).Run();
},
std::move(update_finished_callback)));
},
weak_ptr_factory_.GetWeakPtr(), app_id,
std::move(update_finished_callback));
UnregisterProtocolHandlers(app_id, std::move(unregister_callback));
}
std::unique_ptr<ShortcutInfo> OsIntegrationManager::BuildShortcutInfo(
const AppId& app_id) {
DCHECK(shortcut_manager_);
return shortcut_manager_->BuildShortcutInfo(app_id);
}
void OsIntegrationManager::OnShortcutsCreated(
const AppId& app_id,
std::unique_ptr<WebApplicationInfo> web_app_info,
InstallOsHooksOptions options,
scoped_refptr<OsHooksBarrier> barrier,
bool shortcuts_created) {
DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
DCHECK(barrier);
bool shortcut_creation_failure =
!shortcuts_created && options.os_hooks[OsHookType::kShortcuts];
if (shortcut_creation_failure)
barrier->OnError(OsHookType::kShortcuts);
if (options.os_hooks[OsHookType::kFileHandlers]) {
RegisterFileHandlers(app_id, barrier->CreateSuccessBarrierCallbackForType(
OsHookType::kFileHandlers));
}
if (options.os_hooks[OsHookType::kProtocolHandlers]) {
RegisterProtocolHandlers(app_id,
barrier->CreateSuccessBarrierCallbackForType(
OsHookType::kProtocolHandlers));
}
if (options.os_hooks[OsHookType::kUrlHandlers]) {
RegisterUrlHandlers(app_id, barrier->CreateSuccessBarrierCallbackForType(
OsHookType::kUrlHandlers));
}
if (options.os_hooks[OsHookType::kShortcuts] &&
options.add_to_quick_launch_bar) {
AddAppToQuickLaunchBar(app_id);
}
if (shortcuts_created && options.os_hooks[OsHookType::kShortcutsMenu]) {
if (web_app_info) {
RegisterShortcutsMenu(app_id, web_app_info->shortcuts_menu_item_infos,
web_app_info->shortcuts_menu_icon_bitmaps,
barrier->CreateSuccessBarrierCallbackForType(
OsHookType::kShortcutsMenu));
} else {
ReadAllShortcutsMenuIconsAndRegisterShortcutsMenu(
app_id, barrier->CreateSuccessBarrierCallbackForType(
OsHookType::kShortcutsMenu));
}
}
if (options.os_hooks[OsHookType::kRunOnOsLogin] &&
base::FeatureList::IsEnabled(features::kDesktopPWAsRunOnOsLogin)) {
// TODO(crbug.com/1091964): Implement Run on OS Login mode selection.
// Currently it is set to be the default: RunOnOsLoginMode::kWindowed
RegisterRunOnOsLogin(app_id, barrier->CreateSuccessBarrierCallbackForType(
OsHookType::kRunOnOsLogin));
}
if (options.os_hooks[OsHookType::kUninstallationViaOsSettings] &&
base::FeatureList::IsEnabled(
features::kEnableWebAppUninstallFromOsSettings)) {
RegisterWebAppOsUninstallation(app_id, registrar_->GetAppShortName(app_id));
}
}
void OsIntegrationManager::OnShortcutsDeleted(const AppId& app_id,
DeleteShortcutsCallback callback,
bool shortcuts_deleted) {
#if defined(OS_MAC)
bool delete_multi_profile_shortcuts =
AppShimRegistry::Get()->OnAppUninstalledForProfile(app_id,
profile_->GetPath());
if (delete_multi_profile_shortcuts) {
internals::ScheduleDeleteMultiProfileShortcutsForApp(app_id,
std::move(callback));
}
#else
std::move(callback).Run(shortcuts_deleted);
#endif
}
void OsIntegrationManager::OnShortcutInfoRetrievedRegisterRunOnOsLogin(
RegisterRunOnOsLoginCallback callback,
std::unique_ptr<ShortcutInfo> info) {
ScheduleRegisterRunOnOsLogin(std::move(info), std::move(callback));
}
} // namespace web_app