#include "chrome/browser/apps/app_service/launch_utils.h"
#include <memory>
#include <optional>
#include <string_view>
#include <utility>
#include "base/check.h"
#include "base/command_line.h"
#include "base/files/file_path.h"
#include "base/notreached.h"
#include "build/build_config.h"
#include "chrome/browser/apps/app_service/app_service_proxy.h"
#include "chrome/browser/apps/app_service/file_utils.h"
#include "chrome/browser/apps/app_service/intent_util.h"
#include "chrome/browser/profiles/profile.h"
#include "chrome/browser/ui/browser.h"
#include "chrome/browser/ui/browser_finder.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/tabs/tab_enums.h"
#include "chrome/common/chrome_switches.h"
#include "chrome/common/webui_url_constants.h"
#include "components/services/app_service/public/cpp/app_launch_util.h"
#include "components/services/app_service/public/cpp/app_registry_cache.h"
#include "components/services/app_service/public/cpp/app_types.h"
#include "components/services/app_service/public/cpp/app_update.h"
#include "components/services/app_service/public/cpp/intent_util.h"
#include "components/sessions/core/session_id.h"
#include "extensions/common/constants.h"
#include "mojo/public/cpp/bindings/struct_ptr.h"
#include "storage/browser/file_system/file_system_url.h"
#include "ui/base/page_transition_types.h"
#include "ui/base/window_open_disposition_utils.h"
#include "ui/events/event_constants.h"
#include "url/gurl.h"
#if BUILDFLAG(IS_CHROMEOS)
#include "chrome/browser/apps/app_service/app_service_proxy_factory.h"
#include "chromeos/crosapi/mojom/app_service_types.mojom-shared.h"
#include "chromeos/crosapi/mojom/app_service_types.mojom.h"
#endif
#if BUILDFLAG(IS_CHROMEOS_ASH)
#include "ash/components/arc/mojom/app.mojom.h"
#include "ash/public/cpp/new_window_delegate.h"
#endif
#if BUILDFLAG(IS_CHROMEOS_LACROS)
#include "chrome/browser/lacros/lacros_extensions_util.h"
#include "chrome/browser/profiles/profile.h"
#include "extensions/browser/extension_registry.h"
#include "extensions/common/extension.h"
#endif
#if BUILDFLAG(IS_CHROMEOS)
namespace {
crosapi::mojom::LaunchContainer ConvertAppServiceToCrosapiLaunchContainer(
apps::LaunchContainer input) {
switch (input) {
case apps::LaunchContainer::kLaunchContainerWindow:
return crosapi::mojom::LaunchContainer::kLaunchContainerWindow;
case apps::LaunchContainer::kLaunchContainerTab:
return crosapi::mojom::LaunchContainer::kLaunchContainerTab;
case apps::LaunchContainer::kLaunchContainerNone:
return crosapi::mojom::LaunchContainer::kLaunchContainerNone;
case apps::LaunchContainer::kLaunchContainerPanelDeprecated:
NOTREACHED_IN_MIGRATION();
return crosapi::mojom::LaunchContainer::kLaunchContainerNone;
}
NOTREACHED_IN_MIGRATION();
}
apps::LaunchContainer ConvertCrosapiToAppServiceLaunchContainer(
crosapi::mojom::LaunchContainer input) {
switch (input) {
case crosapi::mojom::LaunchContainer::kLaunchContainerWindow:
return apps::LaunchContainer::kLaunchContainerWindow;
case crosapi::mojom::LaunchContainer::kLaunchContainerTab:
return apps::LaunchContainer::kLaunchContainerTab;
case crosapi::mojom::LaunchContainer::kLaunchContainerNone:
return apps::LaunchContainer::kLaunchContainerNone;
}
NOTREACHED_IN_MIGRATION();
}
crosapi::mojom::WindowOpenDisposition ConvertWindowOpenDispositionToCrosapi(
WindowOpenDisposition input) {
switch (input) {
case WindowOpenDisposition::UNKNOWN:
return crosapi::mojom::WindowOpenDisposition::kUnknown;
case WindowOpenDisposition::CURRENT_TAB:
return crosapi::mojom::WindowOpenDisposition::kCurrentTab;
case WindowOpenDisposition::NEW_FOREGROUND_TAB:
return crosapi::mojom::WindowOpenDisposition::kNewForegroundTab;
case WindowOpenDisposition::NEW_BACKGROUND_TAB:
return crosapi::mojom::WindowOpenDisposition::kNewBackgroundTab;
case WindowOpenDisposition::NEW_WINDOW:
return crosapi::mojom::WindowOpenDisposition::kNewWindow;
case WindowOpenDisposition::NEW_POPUP:
return crosapi::mojom::WindowOpenDisposition::kNewPopup;
case WindowOpenDisposition::SINGLETON_TAB:
case WindowOpenDisposition::NEW_PICTURE_IN_PICTURE:
case WindowOpenDisposition::SAVE_TO_DISK:
case WindowOpenDisposition::OFF_THE_RECORD:
case WindowOpenDisposition::IGNORE_ACTION:
case WindowOpenDisposition::SWITCH_TO_TAB:
NOTREACHED_IN_MIGRATION();
return crosapi::mojom::WindowOpenDisposition::kUnknown;
}
NOTREACHED_IN_MIGRATION();
}
WindowOpenDisposition ConvertWindowOpenDispositionFromCrosapi(
crosapi::mojom::WindowOpenDisposition input) {
switch (input) {
case crosapi::mojom::WindowOpenDisposition::kUnknown:
return WindowOpenDisposition::UNKNOWN;
case crosapi::mojom::WindowOpenDisposition::kCurrentTab:
return WindowOpenDisposition::CURRENT_TAB;
case crosapi::mojom::WindowOpenDisposition::kNewForegroundTab:
return WindowOpenDisposition::NEW_FOREGROUND_TAB;
case crosapi::mojom::WindowOpenDisposition::kNewBackgroundTab:
return WindowOpenDisposition::NEW_BACKGROUND_TAB;
case crosapi::mojom::WindowOpenDisposition::kNewWindow:
return WindowOpenDisposition::NEW_WINDOW;
case crosapi::mojom::WindowOpenDisposition::kNewPopup:
return WindowOpenDisposition::NEW_POPUP;
}
NOTREACHED_IN_MIGRATION();
}
}
#endif
namespace apps {
LaunchContainer ConvertWindowModeToAppLaunchContainer(WindowMode window_mode) { … }
std::vector<base::FilePath> GetLaunchFilesFromCommandLine(
const base::CommandLine& command_line) { … }
Browser* CreateBrowserWithNewTabPage(Profile* profile) { … }
AppLaunchParams CreateAppIdLaunchParamsWithEventFlags(
const std::string& app_id,
int event_flags,
LaunchSource launch_source,
int64_t display_id,
LaunchContainer fallback_container) { … }
AppLaunchParams CreateAppLaunchParamsForIntent(
const std::string& app_id,
int32_t event_flags,
LaunchSource launch_source,
int64_t display_id,
LaunchContainer fallback_container,
IntentPtr&& intent,
Profile* profile) { … }
extensions::AppLaunchSource GetAppLaunchSource(LaunchSource launch_source) { … }
int GetEventFlags(WindowOpenDisposition disposition, bool prefer_container) { … }
int GetSessionIdForRestoreFromWebContents(
const content::WebContents* web_contents) { … }
#if BUILDFLAG(IS_CHROMEOS_ASH)
arc::mojom::WindowInfoPtr MakeArcWindowInfo(WindowInfoPtr window_info) {
if (!window_info) {
return nullptr;
}
arc::mojom::WindowInfoPtr arc_window_info = arc::mojom::WindowInfo::New();
arc_window_info->window_id = window_info->window_id;
arc_window_info->state = window_info->state;
arc_window_info->display_id = window_info->display_id;
if (window_info->bounds.has_value()) {
arc_window_info->bounds = std::move(window_info->bounds);
}
return arc_window_info;
}
#endif
#if BUILDFLAG(IS_CHROMEOS)
crosapi::mojom::LaunchParamsPtr ConvertLaunchParamsToCrosapi(
const AppLaunchParams& params,
Profile* profile) {
auto crosapi_params = crosapi::mojom::LaunchParams::New();
crosapi_params->app_id = params.app_id;
crosapi_params->launch_source = params.launch_source;
if (params.intent) {
crosapi_params->intent =
apps_util::ConvertAppServiceToCrosapiIntent(params.intent, profile);
} else if (!params.override_url.is_empty()) {
crosapi_params->intent = apps_util::ConvertAppServiceToCrosapiIntent(
std::make_unique<Intent>(apps_util::kIntentActionView,
params.override_url),
profile);
} else if (!params.launch_files.empty()) {
std::vector<base::FilePath> files = params.launch_files;
crosapi_params->intent =
apps_util::CreateCrosapiIntentForViewFiles(std::move(files));
}
crosapi_params->container =
ConvertAppServiceToCrosapiLaunchContainer(params.container);
crosapi_params->disposition =
ConvertWindowOpenDispositionToCrosapi(params.disposition);
crosapi_params->display_id = params.display_id;
return crosapi_params;
}
AppLaunchParams ConvertCrosapiToLaunchParams(
const crosapi::mojom::LaunchParamsPtr& crosapi_params,
Profile* profile) {
AppLaunchParams params(
crosapi_params->app_id,
ConvertCrosapiToAppServiceLaunchContainer(crosapi_params->container),
ConvertWindowOpenDispositionFromCrosapi(crosapi_params->disposition),
crosapi_params->launch_source, crosapi_params->display_id);
if (!crosapi_params->intent) {
return params;
}
if (crosapi_params->intent->url.has_value()) {
params.override_url = crosapi_params->intent->url.value();
}
if (crosapi_params->intent->files.has_value()) {
for (const auto& file : crosapi_params->intent->files.value()) {
params.launch_files.push_back(file->file_path);
}
}
params.intent = apps_util::CreateAppServiceIntentFromCrosapi(
crosapi_params->intent, profile);
return params;
}
crosapi::mojom::LaunchParamsPtr CreateCrosapiLaunchParamsWithEventFlags(
AppServiceProxy* proxy,
const std::string& app_id,
int event_flags,
LaunchSource launch_source,
int64_t display_id) {
WindowMode window_mode = WindowMode::kUnknown;
proxy->AppRegistryCache().ForOneApp(app_id,
[&window_mode](const AppUpdate& update) {
window_mode = update.WindowMode();
});
auto launch_params = apps::CreateAppIdLaunchParamsWithEventFlags(
app_id, event_flags, launch_source, display_id,
ConvertWindowModeToAppLaunchContainer(window_mode));
return apps::ConvertLaunchParamsToCrosapi(launch_params, proxy->profile());
}
AppIdsToLaunchForUrl::AppIdsToLaunchForUrl() = default;
AppIdsToLaunchForUrl::AppIdsToLaunchForUrl(AppIdsToLaunchForUrl&&) = default;
AppIdsToLaunchForUrl::~AppIdsToLaunchForUrl() = default;
AppIdsToLaunchForUrl FindAppIdsToLaunchForUrl(AppServiceProxy* proxy,
const GURL& url) {
AppIdsToLaunchForUrl result;
result.candidates = proxy->GetAppIdsForUrl(url, true);
if (result.candidates.empty()) {
return result;
}
std::optional<std::string> preferred =
proxy->PreferredAppsList().FindPreferredAppForUrl(url);
if (preferred && base::Contains(result.candidates, *preferred)) {
result.preferred = std::move(preferred);
}
return result;
}
void MaybeLaunchPreferredAppForUrl(Profile* profile,
const GURL& url,
LaunchSource launch_source) {
if (AppServiceProxyFactory::IsAppServiceAvailableForProfile(profile)) {
auto* proxy = AppServiceProxyFactory::GetForProfile(profile);
AppIdsToLaunchForUrl app_id_to_launch =
FindAppIdsToLaunchForUrl(proxy, url);
if (app_id_to_launch.preferred) {
proxy->LaunchAppWithUrl(*app_id_to_launch.preferred,
0, url, launch_source);
return;
}
}
#if BUILDFLAG(IS_CHROMEOS_ASH)
CHECK(ash::NewWindowDelegate::GetPrimary());
ash::NewWindowDelegate::GetPrimary()->OpenUrl(
url, ash::NewWindowDelegate::OpenUrlFrom::kUserInteraction,
ash::NewWindowDelegate::Disposition::kNewForegroundTab);
#elif BUILDFLAG(IS_CHROMEOS_LACROS)
NavigateParams params(profile, url, ui::PAGE_TRANSITION_LINK);
params.window_action = NavigateParams::SHOW_WINDOW;
Navigate(¶ms);
#endif
}
#endif
#if BUILDFLAG(IS_CHROMEOS_ASH)
void LaunchUrlInInstalledAppOrBrowser(Profile* profile,
const GURL& url,
LaunchSource launch_source) {
if (AppServiceProxyFactory::IsAppServiceAvailableForProfile(profile)) {
auto* proxy = AppServiceProxyFactory::GetForProfile(profile);
AppIdsToLaunchForUrl candidate_apps = FindAppIdsToLaunchForUrl(proxy, url);
std::optional<std::string> app_id = candidate_apps.preferred;
if (!app_id && candidate_apps.candidates.size() == 1) {
app_id = candidate_apps.candidates[0];
}
if (app_id) {
proxy->LaunchAppWithUrl(*app_id,
0, url, launch_source);
return;
}
}
CHECK(ash::NewWindowDelegate::GetPrimary());
ash::NewWindowDelegate::GetPrimary()->OpenUrl(
url, ash::NewWindowDelegate::OpenUrlFrom::kUserInteraction,
ash::NewWindowDelegate::Disposition::kNewForegroundTab);
}
#endif
}