#include "chrome/browser/ash/borealis/borealis_util.h"
#include <unordered_set>
#include "base/base64.h"
#include "base/no_destructor.h"
#include "base/process/launch.h"
#include "base/strings/string_number_conversions.h"
#include "base/strings/string_split.h"
#include "base/task/task_traits.h"
#include "base/task/thread_pool.h"
#include "base/values.h"
#include "chrome/browser/ash/borealis/borealis_window_manager.h"
#include "chrome/browser/ash/guest_os/guest_os_registry_service.h"
#include "chrome/browser/ash/guest_os/guest_os_registry_service_factory.h"
#include "components/crx_file/id_util.h"
#include "components/exo/shell_surface_util.h"
#include "third_party/re2/src/re2/re2.h"
namespace borealis {
const char kInstallerAppId[] = "dkecggknbdokeipkgnhifhiokailichf";
const char kClientAppId[] = "epfhbkiklgmlkhfpbcdleadnhcfdjfmo";
const char kLauncherSearchAppId[] = "ceoplblcdaffnnflkkcagjpomjgedmdl";
const char kIgnoredAppIdPrefix[] = "org.chromium.guest_os.borealis.xid.";
const char kBorealisDlcName[] = "borealis-dlc";
const char kAllowedScheme[] = "steam";
const re2::LazyRE2 kURLAllowlistRegex[] = {
{"//store/[0-9]{1,32}"},
{"//run/[0-9]{1,32}"},
{"//subscriptioninstall/[0-9]{1,32}"},
{"//launch/[0-9]{1,32}/Dialog"}};
const char kCompatToolVersionGameMismatch[] = "UNKNOWN (GameID mismatch)";
const char kDeviceInformationKey[] = "entry.1613887985";
namespace {
static constexpr char kZenityId[] =
"borealis_anon:org.chromium.guest_os.borealis.wmclass.Zenity";
static constexpr char kSteamClientId[] =
"borealis_anon:org.chromium.guest_os.borealis.wmclass.steam";
static constexpr char kSteamBigPictureId[] =
"borealis_anon:org.chromium.guest_os.borealis.xprop.769";
const re2::LazyRE2 kSteamGameIdFromExecRegex = {
"steam:\\/\\/rungameid\\/(\\d+)"};
const re2::LazyRE2 kSteamGameIdFromWindowRegex = {
"org\\.chromium\\.guest_os\\.borealis\\.xprop\\.(\\d+)"};
std::optional<int> ParseGameIdFromWindowData(const std::string& data) {
int app_id;
if (RE2::PartialMatch(data, *kSteamGameIdFromWindowRegex, &app_id)) {
return app_id;
}
return std::nullopt;
}
const re2::LazyRE2 kSpuriousGameBlocklist[] = {
{"Proton [0-9.]+"},
{"Proton BattlEye Runtime"},
{"Proton EasyAntiCheat Runtime"},
{"Proton Experimental"},
{"Proton Hotfix"},
{"Proton Next"},
{"Steam Linux Runtime.*"},
};
bool IsSteamTool(int id) {
static const base::NoDestructor<std::unordered_set<int>> kSteamToolIds({
1070560,
1391110,
1628350,
858280,
930400,
961940,
996510,
1054830,
1113280,
1161040,
1245040,
1420170,
1493710,
1580130,
1826330,
1887720,
2180100,
2230260,
2348590,
});
return kSteamToolIds->contains(id);
}
}
std::optional<int> ParseSteamGameId(std::string exec) {
int app_id;
if (RE2::PartialMatch(exec, *kSteamGameIdFromExecRegex, &app_id)) {
return app_id;
}
return std::nullopt;
}
std::optional<int> SteamGameId(const aura::Window* window) {
const std::string* id = exo::GetShellApplicationId(window);
if (!id) {
return std::nullopt;
}
return ParseGameIdFromWindowData(*id);
}
std::optional<int> SteamGameId(Profile* profile, const std::string& app_id) {
if (BorealisWindowManager::IsAnonymousAppId(app_id)) {
return ParseGameIdFromWindowData(app_id);
}
guest_os::GuestOsRegistryService* registry =
guest_os::GuestOsRegistryServiceFactory::GetForProfile(profile);
if (!registry) {
return std::nullopt;
}
std::optional<guest_os::GuestOsRegistryService::Registration> reg =
registry->GetRegistration(app_id);
if (!reg) {
return std::nullopt;
}
return ParseSteamGameId(reg->Exec());
}
bool IsNonGameBorealisApp(const std::string& app_id) {
if (app_id.find(kIgnoredAppIdPrefix) != std::string::npos ||
app_id == kClientAppId) {
return true;
}
if (app_id == kZenityId || app_id == kSteamClientId ||
app_id == kSteamBigPictureId) {
return true;
}
return false;
}
bool ShouldHideIrrelevantApp(
const guest_os::GuestOsRegistryService::Registration& registration) {
std::optional<int> id = ParseSteamGameId(registration.Exec());
if (id && IsSteamTool(id.value())) {
return true;
}
for (auto& blocklist_regex : kSpuriousGameBlocklist) {
if (re2::RE2::FullMatch(registration.Name(), *blocklist_regex)) {
return true;
}
}
return false;
}
bool IsExternalURLAllowed(const GURL& url) {
if (url.GetScheme() != kAllowedScheme) {
return false;
}
for (auto& allowed_url : kURLAllowlistRegex) {
if (re2::RE2::FullMatch(url.GetContent(), *allowed_url)) {
return true;
}
}
return false;
}
bool GetCompatToolInfo(const std::string& owner_id, std::string* output) {
std::vector<std::string> command = {"/usr/bin/vsh", "--owner_id=" + owner_id,
"--vm_name=borealis", "--",
"/usr/bin/get_compat_tool_versions.py"};
return base::GetAppOutputAndError(command, output);
}
CompatToolInfo ParseCompatToolInfo(std::optional<int> game_id,
const std::string& output) {
std::string raw_info = output.substr(0, output.find("\n"));
CompatToolInfo compat_tool_info;
base::StringPairs tokenized_info;
base::SplitStringIntoKeyValuePairs(raw_info, ':', ',', &tokenized_info);
for (const auto& key_val_pair : tokenized_info) {
std::string key;
TrimWhitespaceASCII(key_val_pair.first, base::TRIM_ALL, &key);
std::string val;
TrimWhitespaceASCII(key_val_pair.second, base::TRIM_ALL, &val);
if (key == "GameID") {
int parsed_val;
bool ret = base::StringToInt(val, &parsed_val);
if (ret) {
compat_tool_info.game_id = parsed_val;
}
} else if (key == "Proton") {
compat_tool_info.proton = val;
} else if (key == "SLR") {
compat_tool_info.slr = val;
}
}
if (game_id.has_value() && compat_tool_info.game_id.has_value() &&
game_id.value() != compat_tool_info.game_id.value()) {
LOG(WARNING) << "Expected GameID " << game_id.value() << " got "
<< compat_tool_info.game_id.value();
compat_tool_info.proton = kCompatToolVersionGameMismatch;
compat_tool_info.slr = kCompatToolVersionGameMismatch;
}
return compat_tool_info;
}
}