| // Copyright 2021 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/chromeos/full_restore/app_launch_handler.h" |
| |
| #include <utility> |
| #include <vector> |
| |
| #include "base/bind.h" |
| #include "base/callback.h" |
| #include "base/threading/thread_task_runner_handle.h" |
| #include "chrome/browser/apps/app_service/app_platform_metrics.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/apps/app_service/browser_app_launcher.h" |
| #include "chrome/browser/profiles/profile.h" |
| #include "components/full_restore/full_restore_read_handler.h" |
| #include "components/services/app_service/public/cpp/types_util.h" |
| #include "extensions/common/constants.h" |
| |
| namespace chromeos { |
| |
| namespace { |
| |
| // Returns apps::AppTypeName used for metrics. |
| apps::AppTypeName GetHistogrameAppType(apps::mojom::AppType app_type) { |
| switch (app_type) { |
| case apps::mojom::AppType::kUnknown: |
| return apps::AppTypeName::kUnknown; |
| case apps::mojom::AppType::kArc: |
| return apps::AppTypeName::kArc; |
| case apps::mojom::AppType::kBuiltIn: |
| case apps::mojom::AppType::kCrostini: |
| return apps::AppTypeName::kUnknown; |
| case apps::mojom::AppType::kExtension: |
| return apps::AppTypeName::kChromeApp; |
| case apps::mojom::AppType::kWeb: |
| return apps::AppTypeName::kWeb; |
| case apps::mojom::AppType::kMacOs: |
| case apps::mojom::AppType::kPluginVm: |
| case apps::mojom::AppType::kStandaloneBrowser: |
| case apps::mojom::AppType::kRemote: |
| case apps::mojom::AppType::kBorealis: |
| return apps::AppTypeName::kUnknown; |
| case apps::mojom::AppType::kSystemWeb: |
| return apps::AppTypeName::kSystemWeb; |
| } |
| } |
| |
| } // namespace |
| |
| AppLaunchHandler::AppLaunchHandler(Profile* profile) : profile_(profile) {} |
| |
| AppLaunchHandler::~AppLaunchHandler() = default; |
| |
| bool AppLaunchHandler::HasRestoreData() { |
| return restore_data_ && !restore_data_->app_id_to_launch_list().empty(); |
| } |
| |
| void AppLaunchHandler::LaunchApps() { |
| // If there is no launch list from the restore data, we don't need to handle |
| // launching. |
| const auto& launch_list = restore_data_->app_id_to_launch_list(); |
| if (launch_list.empty()) |
| return; |
| |
| // Observe AppRegistryCache to get the notification when the app is ready. |
| DCHECK( |
| apps::AppServiceProxyFactory::IsAppServiceAvailableForProfile(profile_)); |
| auto* cache = &apps::AppServiceProxyFactory::GetForProfile(profile_) |
| ->AppRegistryCache(); |
| Observe(cache); |
| |
| // Add the app to `app_ids` if there is a launch list from the restore data |
| // for the app. |
| std::set<std::string> app_ids; |
| cache->ForEachApp([&app_ids, &launch_list](const apps::AppUpdate& update) { |
| if (update.Readiness() == apps::mojom::Readiness::kReady && |
| launch_list.find(update.AppId()) != launch_list.end()) { |
| app_ids.insert(update.AppId()); |
| } |
| }); |
| |
| for (const auto& app_id : app_ids) |
| LaunchApp(cache->GetAppType(app_id), app_id); |
| } |
| |
| void AppLaunchHandler::OnAppUpdate(const apps::AppUpdate& update) { |
| if (!restore_data_ || !update.ReadinessChanged()) |
| return; |
| |
| if (!apps_util::IsInstalled(update.Readiness())) { |
| restore_data_->RemoveApp(update.AppId()); |
| return; |
| } |
| |
| // If the app is not ready, don't launch the app for the restoration. |
| if (update.Readiness() != apps::mojom::Readiness::kReady) |
| return; |
| |
| // If there is no restore data or the launch list for the app is empty, don't |
| // launch the app. |
| const auto& app_id_to_launch_list = restore_data_->app_id_to_launch_list(); |
| if (app_id_to_launch_list.find(update.AppId()) == |
| app_id_to_launch_list.end()) { |
| return; |
| } |
| |
| base::ThreadTaskRunnerHandle::Get()->PostTask( |
| FROM_HERE, |
| base::BindOnce(&AppLaunchHandler::LaunchApp, GetWeakPtrAppLaunchHandler(), |
| update.AppType(), update.AppId())); |
| } |
| |
| void AppLaunchHandler::OnAppRegistryCacheWillBeDestroyed( |
| apps::AppRegistryCache* cache) { |
| apps::AppRegistryCache::Observer::Observe(nullptr); |
| } |
| |
| void AppLaunchHandler::LaunchApp(apps::mojom::AppType app_type, |
| const std::string& app_id) { |
| DCHECK(restore_data_); |
| |
| // For the Chrome browser, the browser session restore is used to restore the |
| // web pages, so we don't need to launch the app. |
| if (app_id == extension_misc::kChromeAppId) { |
| return; |
| } |
| |
| const auto it = restore_data_->app_id_to_launch_list().find(app_id); |
| if (it == restore_data_->app_id_to_launch_list().end() || |
| it->second.empty()) { |
| restore_data_->RemoveApp(app_id); |
| return; |
| } |
| |
| switch (app_type) { |
| case apps::mojom::AppType::kArc: |
| LaunchArcApp(app_id, it->second); |
| // ARC apps restoration could be delayed, so return to preserve the |
| // restore data for ARC apps. |
| return; |
| case apps::mojom::AppType::kExtension: |
| ::full_restore::FullRestoreReadHandler::GetInstance() |
| ->SetNextRestoreWindowIdForChromeApp(profile_->GetPath(), app_id); |
| // Deliberately fall through to apps::mojom::AppType::kWeb to launch the |
| // app. |
| FALLTHROUGH; |
| case apps::mojom::AppType::kWeb: |
| case apps::mojom::AppType::kSystemWeb: |
| LaunchSystemWebAppOrChromeApp(app_type, app_id, it->second); |
| break; |
| case apps::mojom::AppType::kBuiltIn: |
| case apps::mojom::AppType::kCrostini: |
| case apps::mojom::AppType::kPluginVm: |
| case apps::mojom::AppType::kUnknown: |
| case apps::mojom::AppType::kMacOs: |
| case apps::mojom::AppType::kStandaloneBrowser: |
| case apps::mojom::AppType::kRemote: |
| case apps::mojom::AppType::kBorealis: |
| NOTREACHED(); |
| break; |
| } |
| restore_data_->RemoveApp(app_id); |
| } |
| |
| void AppLaunchHandler::LaunchSystemWebAppOrChromeApp( |
| apps::mojom::AppType app_type, |
| const std::string& app_id, |
| const ::full_restore::RestoreData::LaunchList& launch_list) { |
| auto* launcher = apps::AppServiceProxyFactory::GetForProfile(profile_) |
| ->BrowserAppLauncher(); |
| if (!launcher) |
| return; |
| |
| for (const auto& it : launch_list) { |
| RecordRestoredAppLaunch(GetHistogrameAppType(app_type)); |
| |
| DCHECK(it.second->container.has_value()); |
| DCHECK(it.second->disposition.has_value()); |
| DCHECK(it.second->display_id.has_value()); |
| apps::mojom::IntentPtr intent; |
| apps::AppLaunchParams params( |
| app_id, |
| static_cast<apps::mojom::LaunchContainer>(it.second->container.value()), |
| static_cast<WindowOpenDisposition>(it.second->disposition.value()), |
| apps::mojom::AppLaunchSource::kSourceChromeInternal, |
| it.second->display_id.value(), |
| it.second->file_paths.has_value() ? it.second->file_paths.value() |
| : std::vector<base::FilePath>{}, |
| it.second->intent.has_value() ? it.second->intent.value() : intent); |
| params.restore_id = it.first; |
| launcher->LaunchAppWithParams(std::move(params)); |
| } |
| } |
| |
| } // namespace chromeos |