[go: nahoru, domu]

blob: 84d987fc097722e315a0f5bb26525aa3777fad30 [file] [log] [blame]
// Copyright 2020 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/apps/app_service/launch_utils.h"
#include <memory>
#include <optional>
#include <utility>
#include "base/check.h"
#include "base/command_line.h"
#include "base/files/file_path.h"
#include "base/notreached.h"
#include "base/strings/string_piece.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/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 // BUILDFLAG(IS_CHROMEOS)
#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 // BUILDFLAG(IS_CHROMEOS_LACROS)
#if BUILDFLAG(IS_CHROMEOS)
namespace {
// Use manual mapping for launch container and window open disposition because
// we cannot use mojom traits for crosapi::mojom::LaunchParams yet. Move to auto
// mapping when the AppService Intent struct is converted to use FilePaths.
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();
return crosapi::mojom::LaunchContainer::kLaunchContainerNone;
}
NOTREACHED();
}
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();
}
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();
return crosapi::mojom::WindowOpenDisposition::kUnknown;
}
NOTREACHED();
}
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();
}
} // namespace
#endif // BUILDFLAG(IS_CHROMEOS)
namespace apps {
LaunchContainer ConvertWindowModeToAppLaunchContainer(WindowMode window_mode) {
switch (window_mode) {
case WindowMode::kBrowser:
return LaunchContainer::kLaunchContainerTab;
case WindowMode::kWindow:
case WindowMode::kTabbedWindow:
return LaunchContainer::kLaunchContainerWindow;
case WindowMode::kUnknown:
return LaunchContainer::kLaunchContainerNone;
}
}
std::vector<base::FilePath> GetLaunchFilesFromCommandLine(
const base::CommandLine& command_line) {
std::vector<base::FilePath> launch_files;
if (!command_line.HasSwitch(switches::kAppId)) {
return launch_files;
}
launch_files.reserve(command_line.GetArgs().size());
for (const auto& arg : command_line.GetArgs()) {
#if BUILDFLAG(IS_WIN)
GURL url(base::AsStringPiece16(arg));
#else
GURL url(arg);
#endif
if (url.is_valid() && !url.SchemeIsFile()) {
continue;
}
base::FilePath path(arg);
if (path.empty()) {
continue;
}
launch_files.push_back(path);
}
return launch_files;
}
Browser* CreateBrowserWithNewTabPage(Profile* profile) {
Browser::CreateParams create_params(profile, /*user_gesture=*/false);
Browser* browser = Browser::Create(create_params);
NavigateParams params(browser, GURL(chrome::kChromeUINewTabURL),
ui::PAGE_TRANSITION_AUTO_TOPLEVEL);
params.disposition = WindowOpenDisposition::NEW_FOREGROUND_TAB;
params.tabstrip_add_types = AddTabTypes::ADD_ACTIVE;
Navigate(&params);
browser->window()->Show();
return browser;
}
AppLaunchParams CreateAppIdLaunchParamsWithEventFlags(
const std::string& app_id,
int event_flags,
LaunchSource launch_source,
int64_t display_id,
LaunchContainer fallback_container) {
WindowOpenDisposition raw_disposition =
ui::DispositionFromEventFlags(event_flags);
LaunchContainer container;
WindowOpenDisposition disposition;
if (raw_disposition == WindowOpenDisposition::NEW_FOREGROUND_TAB ||
raw_disposition == WindowOpenDisposition::NEW_BACKGROUND_TAB) {
container = LaunchContainer::kLaunchContainerTab;
disposition = raw_disposition;
} else if (raw_disposition == WindowOpenDisposition::NEW_WINDOW) {
container = LaunchContainer::kLaunchContainerWindow;
disposition = raw_disposition;
} else {
// Look at preference to find the right launch container. If no preference
// is set, launch as a regular tab.
container = fallback_container;
disposition = WindowOpenDisposition::NEW_FOREGROUND_TAB;
}
return AppLaunchParams(app_id, container, disposition, launch_source,
display_id);
}
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) {
auto params = CreateAppIdLaunchParamsWithEventFlags(
app_id, event_flags, launch_source, display_id, fallback_container);
if (intent->url.has_value()) {
params.override_url = intent->url.value();
}
// On Lacros, the caller of this function attaches the intent files to the
// AppLaunchParams.
#if BUILDFLAG(IS_CHROMEOS_ASH)
if (!intent->files.empty()) {
std::vector<GURL> file_urls;
for (const auto& intent_file : intent->files) {
if (intent_file->url.SchemeIsFile()) {
DCHECK(file_urls.empty());
break;
}
file_urls.push_back(intent_file->url);
}
if (!file_urls.empty()) {
std::vector<storage::FileSystemURL> file_system_urls =
GetFileSystemURL(profile, file_urls);
for (const auto& file_system_url : file_system_urls) {
params.launch_files.push_back(file_system_url.path());
}
}
}
#endif // BUILDFLAG(IS_CHROMEOS_ASH)
params.intent = std::move(intent);
return params;
}
extensions::AppLaunchSource GetAppLaunchSource(LaunchSource launch_source) {
switch (launch_source) {
case LaunchSource::kUnknown:
case LaunchSource::kFromAppListGrid:
case LaunchSource::kFromAppListGridContextMenu:
case LaunchSource::kFromAppListQuery:
case LaunchSource::kFromAppListQueryContextMenu:
case LaunchSource::kFromAppListRecommendation:
case LaunchSource::kFromParentalControls:
case LaunchSource::kFromShelf:
case LaunchSource::kFromLink:
case LaunchSource::kFromOmnibox:
case LaunchSource::kFromOtherApp:
case LaunchSource::kFromSharesheet:
return extensions::AppLaunchSource::kSourceAppLauncher;
case LaunchSource::kFromMenu:
return extensions::AppLaunchSource::kSourceContextMenu;
case LaunchSource::kFromKeyboard:
return extensions::AppLaunchSource::kSourceKeyboard;
case LaunchSource::kFromFileManager:
return extensions::AppLaunchSource::kSourceFileHandler;
case LaunchSource::kFromChromeInternal:
case LaunchSource::kFromReleaseNotesNotification:
case LaunchSource::kFromFullRestore:
case LaunchSource::kFromSmartTextContextMenu:
case LaunchSource::kFromDiscoverTabNotification:
case LaunchSource::kFromFirstRun:
return extensions::AppLaunchSource::kSourceChromeInternal;
case LaunchSource::kFromInstalledNotification:
return extensions::AppLaunchSource::kSourceInstalledNotification;
case LaunchSource::kFromTest:
return extensions::AppLaunchSource::kSourceTest;
case LaunchSource::kFromArc:
return extensions::AppLaunchSource::kSourceArc;
case LaunchSource::kFromManagementApi:
return extensions::AppLaunchSource::kSourceManagementApi;
case LaunchSource::kFromKiosk:
return extensions::AppLaunchSource::kSourceKiosk;
case LaunchSource::kFromCommandLine:
return extensions::AppLaunchSource::kSourceCommandLine;
case LaunchSource::kFromBackgroundMode:
return extensions::AppLaunchSource::kSourceBackground;
case LaunchSource::kFromNewTabPage:
return extensions::AppLaunchSource::kSourceNewTabPage;
case LaunchSource::kFromIntentUrl:
return extensions::AppLaunchSource::kSourceIntentUrl;
case LaunchSource::kFromOsLogin:
return extensions::AppLaunchSource::kSourceRunOnOsLogin;
case LaunchSource::kFromProtocolHandler:
return extensions::AppLaunchSource::kSourceProtocolHandler;
case LaunchSource::kFromUrlHandler:
return extensions::AppLaunchSource::kSourceUrlHandler;
case LaunchSource::kFromLockScreen:
return extensions::AppLaunchSource::kSourceUntracked;
case LaunchSource::kFromAppHomePage:
return extensions::AppLaunchSource::kSourceAppHomePage;
// No equivalent extensions launch source or not needed in extensions:
case LaunchSource::kFromReparenting:
case LaunchSource::kFromProfileMenu:
case LaunchSource::kFromSysTrayCalendar:
case LaunchSource::kFromInstaller:
return extensions::AppLaunchSource::kSourceNone;
}
}
int GetEventFlags(WindowOpenDisposition disposition, bool prefer_container) {
if (prefer_container) {
return ui::EF_NONE;
}
switch (disposition) {
case WindowOpenDisposition::NEW_WINDOW:
return ui::EF_SHIFT_DOWN;
case WindowOpenDisposition::NEW_BACKGROUND_TAB:
return ui::EF_MIDDLE_MOUSE_BUTTON;
case WindowOpenDisposition::NEW_FOREGROUND_TAB:
return ui::EF_MIDDLE_MOUSE_BUTTON | ui::EF_SHIFT_DOWN;
default:
NOTREACHED();
return ui::EF_NONE;
}
}
int GetSessionIdForRestoreFromWebContents(
const content::WebContents* web_contents) {
if (!web_contents) {
return SessionID::InvalidValue().id();
}
Browser* browser = chrome::FindBrowserWithTab(web_contents);
if (!browser) {
return SessionID::InvalidValue().id();
}
return browser->session_id().id();
}
#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 // BUILDFLAG(IS_CHROMEOS_ASH)
#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;
// Both launch_files and override_url will be represent by intent in crosapi
// launch params. These info will normally represent in the intent field in
// the launch params, if not, then generate the intent from these fields
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,
/*fallback_container=*/
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, /*exclude_browsers=*/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,
/*event_flags=*/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(&params);
#endif // BUILDFLAG(IS_CHROMEOS_ASH)
}
#endif // BUILDFLAG(IS_CHROMEOS)
} // namespace apps