#include "chrome/browser/ui/startup/launch_mode_recorder.h"
#include <string>
#include <string_view>
#include <utility>
#include <vector>
#include "base/base_paths.h"
#include "base/command_line.h"
#include "base/functional/bind.h"
#include "base/functional/callback.h"
#include "base/i18n/case_conversion.h"
#include "base/metrics/histogram_functions.h"
#include "base/path_service.h"
#include "base/strings/string_util.h"
#include "base/strings/utf_string_conversions.h"
#include "base/task/thread_pool.h"
#include "components/url_formatter/url_formatter.h"
#include "url/gurl.h"
#if BUILDFLAG(IS_MAC)
#include "base/mac/mac_util.h"
#include "chrome/browser/mac/dock.h"
#include "chrome/browser/mac/install_from_dmg.h"
#endif
#if BUILDFLAG(IS_WIN)
#include "base/containers/fixed_flat_map.h"
#include "base/files/file_path.h"
#include "base/win/startup_information.h"
#include "chrome/common/chrome_switches.h"
#include "chrome/installer/util/taskbar_util.h"
#include "components/url_formatter/url_fixer.h"
#endif
namespace {
#if BUILDFLAG(IS_WIN)
enum class ArgType { kFile, kProtocol, kInvalid };
std::optional<int> GetShortcutLocation(const std::wstring& shortcut_path) {
const std::u16string shortcut(
base::i18n::ToLower(base::AsStringPiece16(shortcut_path)));
if (shortcut.find(u"\\quick launch\\") != std::u16string_view::npos) {
return base::DIR_TASKBAR_PINS;
}
constexpr int kPathKeys[] = {base::DIR_COMMON_START_MENU,
base::DIR_START_MENU, base::DIR_COMMON_DESKTOP,
base::DIR_USER_DESKTOP};
base::FilePath candidate;
for (int path_key : kPathKeys) {
if (base::PathService::Get(path_key, &candidate) &&
base::StartsWith(
shortcut,
base::i18n::ToLower(base::AsStringPiece16(candidate.value())),
base::CompareCase::SENSITIVE)) {
return path_key;
}
}
return std::nullopt;
}
ArgType GetArgType(const std::wstring& arg) {
GURL url(base::AsStringPiece16(arg));
if (url.is_valid() && !url.SchemeIsFile())
return ArgType::kProtocol;
if (!url.is_valid()) {
url =
url_formatter::FixupRelativeFile(base::FilePath(), base::FilePath(arg));
if (!url.is_valid())
return ArgType::kInvalid;
}
return url.SchemeIsFile() ? ArgType::kFile : ArgType::kProtocol;
}
std::optional<std::wstring> GetShortcutPath(
const base::CommandLine& command_line) {
if (command_line.HasSwitch(switches::kSourceShortcut))
return command_line.GetSwitchValueNative(switches::kSourceShortcut);
STARTUPINFOW si = {sizeof(si)};
GetStartupInfoW(&si);
return si.dwFlags & STARTF_TITLEISLINKNAME
? std::optional<std::wstring>(si.lpTitle)
: std::nullopt;
}
#endif
}
namespace {
void RecordLaunchMode(const base::CommandLine command_line,
std::optional<LaunchMode> mode) { … }
#if BUILDFLAG(IS_WIN)
std::optional<LaunchMode> GetLaunchModeSlow(
const base::CommandLine command_line) {
std::optional<std::wstring> shortcut_path = GetShortcutPath(command_line);
bool is_app_launch = command_line.HasSwitch(switches::kApp) ||
command_line.HasSwitch(switches::kAppId);
if (!shortcut_path.has_value()) {
bool single_argument_switch = command_line.HasSingleArgumentSwitch();
std::vector<base::CommandLine::StringType> args = command_line.GetArgs();
if (args.size() < 1)
return is_app_launch ? LaunchMode::kWebAppOther : LaunchMode::kOther;
auto arg_type = GetArgType(args[0]);
if (!is_app_launch) {
if (arg_type == ArgType::kFile) {
return single_argument_switch ? LaunchMode::kFileTypeHandler
: LaunchMode::kWithFile;
}
if (arg_type == ArgType::kProtocol) {
return single_argument_switch ? LaunchMode::kProtocolHandler
: LaunchMode::kWithUrl;
}
} else {
if (arg_type == ArgType::kFile)
return LaunchMode::kWebAppFileTypeHandler;
if (arg_type == ArgType::kProtocol)
return LaunchMode::kWebAppProtocolHandler;
}
} else {
if (shortcut_path.value().empty())
return LaunchMode::kShortcutNoName;
std::optional<int> shortcut_location =
GetShortcutLocation(shortcut_path.value());
if (!shortcut_location.has_value()) {
return is_app_launch ? LaunchMode::kWebAppShortcutUnknown
: LaunchMode::kShortcutUnknown;
}
if (!is_app_launch) {
static constexpr auto kDirToLaunchModeMap =
base::MakeFixedFlatMap<int, LaunchMode>({
{base::DIR_TASKBAR_PINS, LaunchMode::kShortcutTaskbar},
{base::DIR_COMMON_START_MENU, LaunchMode::kShortcutStartMenu},
{base::DIR_START_MENU, LaunchMode::kShortcutStartMenu},
{base::DIR_COMMON_DESKTOP, LaunchMode::kShortcutDesktop},
{base::DIR_USER_DESKTOP, LaunchMode::kShortcutDesktop},
});
return kDirToLaunchModeMap.at(shortcut_location.value());
}
static constexpr auto kDirToWebAppLaunchModeMap =
base::MakeFixedFlatMap<int, LaunchMode>({
{base::DIR_TASKBAR_PINS, LaunchMode::kWebAppShortcutTaskbar},
{base::DIR_COMMON_START_MENU, LaunchMode::kWebAppShortcutStartMenu},
{base::DIR_START_MENU, LaunchMode::kWebAppShortcutStartMenu},
{base::DIR_COMMON_DESKTOP, LaunchMode::kWebAppShortcutDesktop},
{base::DIR_USER_DESKTOP, LaunchMode::kWebAppShortcutDesktop},
});
return kDirToWebAppLaunchModeMap.at(shortcut_location.value());
}
return LaunchMode::kOther;
}
std::optional<LaunchMode> GetLaunchModeFast(
const base::CommandLine& command_line) {
static constexpr std::pair<const char*, LaunchMode> switch_to_mode[] = {
{switches::kRestart, LaunchMode::kNone},
{switches::kNoStartupWindow, LaunchMode::kNone},
{switches::kUninstallAppId, LaunchMode::kNone},
{switches::kListApps, LaunchMode::kNone},
{switches::kInstallChromeApp, LaunchMode::kNone},
{switches::kFromInstaller, LaunchMode::kNone},
{switches::kUninstall, LaunchMode::kNone},
{switches::kNotificationLaunchId, LaunchMode::kWinPlatformNotification},
};
for (const auto& [switch_val, mode] : switch_to_mode) {
if (command_line.HasSwitch(switch_val))
return mode;
}
return std::nullopt;
}
#elif BUILDFLAG(IS_MAC)
std::optional<LaunchMode> GetLaunchModeSlow(
const base::CommandLine command_line) {
NOTREACHED_IN_MIGRATION();
return std::nullopt;
}
std::optional<LaunchMode> GetLaunchModeFast(
const base::CommandLine& command_line) {
DiskImageStatus dmg_launch_status =
IsAppRunningFromReadOnlyDiskImage(nullptr);
dock::ChromeInDockStatus dock_launch_status = dock::ChromeIsInTheDock();
if (dock_launch_status == dock::ChromeInDockFailure &&
dmg_launch_status == DiskImageStatusFailure) {
return LaunchMode::kMacDockDMGStatusError;
}
if (dock_launch_status == dock::ChromeInDockFailure)
return LaunchMode::kMacDockStatusError;
if (dmg_launch_status == DiskImageStatusFailure)
return LaunchMode::kMacDMGStatusError;
bool dmg_launch = dmg_launch_status == DiskImageStatusTrue;
bool dock_launch = dock_launch_status == dock::ChromeInDockTrue;
if (dmg_launch && dock_launch)
return LaunchMode::kMacDockedDMGLaunch;
if (dmg_launch)
return LaunchMode::kMacUndockedDMGLaunch;
if (dock_launch)
return LaunchMode::kMacDockedDiskLaunch;
return LaunchMode::kMacUndockedDiskLaunch;
}
#else
std::optional<LaunchMode> GetLaunchModeSlow(
const base::CommandLine command_line) { … }
std::optional<LaunchMode> GetLaunchModeFast(
const base::CommandLine& command_line) { … }
#endif
}
void ComputeAndRecordLaunchMode(const base::CommandLine& command_line) { … }
void ComputeLaunchMode(
const base::CommandLine& command_line,
base::OnceCallback<void(std::optional<LaunchMode>)> result_callback) { … }
base::OnceCallback<void(std::optional<LaunchMode>)>
GetRecordLaunchModeForTesting() { … }