blob: 124d74922593f8700ee6a8d4813688afd327574c [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/web_applications/test/web_app_test_utils.h"
#include <algorithm>
#include <cstdint>
#include <ostream>
#include <random>
#include <set>
#include <string>
#include <type_traits>
#include <utility>
#include <vector>
#include "base/bind.h"
#include "base/check.h"
#include "base/check_op.h"
#include "base/containers/flat_map.h"
#include "base/containers/flat_set.h"
#include "base/json/json_reader.h"
#include "base/location.h"
#include "base/memory/scoped_refptr.h"
#include "base/numerics/clamped_math.h"
#include "base/numerics/safe_conversions.h"
#include "base/run_loop.h"
#include "base/strings/strcat.h"
#include "base/strings/string_number_conversions.h"
#include "base/strings/utf_string_conversions.h"
#include "base/task/single_thread_task_runner.h"
#include "base/test/bind.h"
#include "base/time/time.h"
#include "base/values.h"
#include "chrome/app/chrome_command_ids.h"
#include "chrome/browser/profiles/profile.h"
#include "chrome/browser/ui/browser.h"
#include "chrome/browser/ui/browser_commands.h"
#include "chrome/browser/ui/browser_dialogs.h"
#include "chrome/browser/web_applications/externally_installed_web_app_prefs.h"
#include "chrome/browser/web_applications/proto/web_app_os_integration_state.pb.h"
#include "chrome/browser/web_applications/test/web_app_test_observers.h"
#include "chrome/browser/web_applications/user_display_mode.h"
#include "chrome/browser/web_applications/web_app.h"
#include "chrome/browser/web_applications/web_app_chromeos_data.h"
#include "chrome/browser/web_applications/web_app_constants.h"
#include "chrome/browser/web_applications/web_app_helpers.h"
#include "chrome/browser/web_applications/web_app_install_info.h"
#include "chrome/browser/web_applications/web_app_provider.h"
#include "chrome/browser/web_applications/web_app_registry_update.h"
#include "chrome/browser/web_applications/web_app_sync_bridge.h"
#include "chrome/browser/web_applications/web_app_utils.h"
#include "chrome/common/pref_names.h"
#include "components/prefs/pref_service.h"
#include "components/services/app_service/public/cpp/file_handler.h"
#include "components/services/app_service/public/cpp/icon_info.h"
#include "components/services/app_service/public/cpp/protocol_handler_info.h"
#include "components/services/app_service/public/cpp/share_target.h"
#include "components/services/app_service/public/cpp/url_handler_info.h"
#include "components/webapps/browser/installable/installable_metrics.h"
#include "content/public/browser/service_worker_context.h"
#include "content/public/browser/storage_partition.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "third_party/abseil-cpp/absl/types/optional.h"
#include "third_party/abseil-cpp/absl/types/variant.h"
#include "third_party/blink/public/common/manifest/manifest.h"
#include "third_party/blink/public/common/permissions_policy/origin_with_possible_wildcards.h"
#include "third_party/blink/public/common/permissions_policy/permissions_policy.h"
#include "third_party/blink/public/common/permissions_policy/policy_helper_public.h"
#include "third_party/blink/public/common/storage_key/storage_key.h"
#include "third_party/blink/public/mojom/manifest/capture_links.mojom-shared.h"
#include "third_party/skia/include/core/SkColor.h"
#include "url/gurl.h"
#include "url/origin.h"
namespace {
std::vector<std::string> features = {
"default_on_feature", "default_self_feature", "default_disabled_feature"};
} // namespace
namespace web_app {
namespace test {
namespace {
class RandomHelper {
public:
explicit RandomHelper(const uint32_t seed)
: // Seed of 0 and 1 generate the same sequence, so skip 0.
generator_(seed + 1),
distribution_(0u, UINT32_MAX) {}
uint32_t next_uint() { return distribution_(generator_); }
// Return an unsigned int between 0 (inclusive) and bound (exclusive).
uint32_t next_uint(uint32_t bound) { return next_uint() % bound; }
bool next_bool() { return next_uint() & 1u; }
template <typename T>
T next_enum() {
constexpr uint32_t min = static_cast<uint32_t>(T::kMinValue);
constexpr uint32_t max = static_cast<uint32_t>(T::kMaxValue);
static_assert(min <= max, "min cannot be greater than max");
return static_cast<T>(min + next_uint(max - min));
}
private:
std::default_random_engine generator_;
std::uniform_int_distribution<uint32_t> distribution_;
};
apps::FileHandlers CreateRandomFileHandlers(uint32_t suffix) {
apps::FileHandlers file_handlers;
for (unsigned int i = 0; i < 5; ++i) {
std::string suffix_str =
base::NumberToString(suffix) + base::NumberToString(i);
apps::FileHandler::AcceptEntry accept_entry1;
accept_entry1.mime_type = "application/" + suffix_str + "+foo";
accept_entry1.file_extensions.insert("." + suffix_str + "a");
accept_entry1.file_extensions.insert("." + suffix_str + "b");
apps::FileHandler::AcceptEntry accept_entry2;
accept_entry2.mime_type = "application/" + suffix_str + "+bar";
accept_entry2.file_extensions.insert("." + suffix_str + "a");
accept_entry2.file_extensions.insert("." + suffix_str + "b");
apps::FileHandler file_handler;
file_handler.action = GURL("https://example.com/open-" + suffix_str);
file_handler.accept.push_back(std::move(accept_entry1));
file_handler.accept.push_back(std::move(accept_entry2));
file_handler.downloaded_icons.emplace_back(
GURL("https://example.com/image.png"), 16);
file_handler.downloaded_icons.emplace_back(
GURL("https://example.com/image2.png"), 48);
file_handler.display_name = base::ASCIIToUTF16(suffix_str) + u" file";
file_handlers.push_back(std::move(file_handler));
}
return file_handlers;
}
apps::ShareTarget CreateRandomShareTarget(uint32_t suffix) {
apps::ShareTarget share_target;
share_target.action =
GURL("https://example.com/path/target/" + base::NumberToString(suffix));
share_target.method = (suffix % 2 == 0) ? apps::ShareTarget::Method::kPost
: apps::ShareTarget::Method::kGet;
share_target.enctype = (suffix / 2 % 2 == 0)
? apps::ShareTarget::Enctype::kMultipartFormData
: apps::ShareTarget::Enctype::kFormUrlEncoded;
if (suffix % 3 != 0)
share_target.params.title = "title" + base::NumberToString(suffix);
if (suffix % 3 != 1)
share_target.params.text = "text" + base::NumberToString(suffix);
if (suffix % 3 != 2)
share_target.params.url = "url" + base::NumberToString(suffix);
for (uint32_t index = 0; index < suffix % 5; ++index) {
apps::ShareTarget::Files files;
files.name = "files" + base::NumberToString(index);
files.accept.push_back(".extension" + base::NumberToString(index));
files.accept.push_back("type/subtype" + base::NumberToString(index));
share_target.params.files.push_back(files);
}
return share_target;
}
blink::ParsedPermissionsPolicy CreateRandomPermissionsPolicy(
RandomHelper& random) {
const int num_permissions_policy_declarations =
random.next_uint(features.size());
std::vector<std::string> available_features = features;
const auto suffix = random.next_uint();
std::default_random_engine rng;
std::shuffle(available_features.begin(), available_features.end(), rng);
blink::ParsedPermissionsPolicy permissions_policy(
num_permissions_policy_declarations);
const auto& feature_name_map = blink::GetPermissionsPolicyNameToFeatureMap();
for (int i = 0; i < num_permissions_policy_declarations; ++i) {
permissions_policy[i].feature = feature_name_map.begin()->second;
for (unsigned int j = 0; j < 5; ++j) {
std::string suffix_str =
base::NumberToString(suffix) + base::NumberToString(j);
const auto origin =
url::Origin::Create(GURL("https://app-" + suffix_str + ".com/"));
permissions_policy[i].allowed_origins.emplace_back(
origin,
/*has_subdomain_wildcard=*/false);
}
}
return permissions_policy;
}
std::vector<apps::ProtocolHandlerInfo> CreateRandomProtocolHandlers(
uint32_t suffix) {
std::vector<apps::ProtocolHandlerInfo> protocol_handlers;
for (unsigned int i = 0; i < 5; ++i) {
std::string suffix_str =
base::NumberToString(suffix) + base::NumberToString(i);
apps::ProtocolHandlerInfo protocol_handler;
protocol_handler.protocol = "web+test" + suffix_str;
protocol_handler.url = GURL("https://example.com/").Resolve(suffix_str);
protocol_handlers.push_back(std::move(protocol_handler));
}
return protocol_handlers;
}
std::vector<apps::UrlHandlerInfo> CreateRandomUrlHandlers(uint32_t suffix) {
std::vector<apps::UrlHandlerInfo> url_handlers;
for (unsigned int i = 0; i < 3; ++i) {
std::string suffix_str =
base::NumberToString(suffix) + base::NumberToString(i);
apps::UrlHandlerInfo url_handler;
url_handler.origin =
url::Origin::Create(GURL("https://app-" + suffix_str + ".com/"));
url_handler.has_origin_wildcard = true;
url_handlers.push_back(std::move(url_handler));
}
return url_handlers;
}
std::vector<WebAppShortcutsMenuItemInfo> CreateRandomShortcutsMenuItemInfos(
const GURL& scope,
RandomHelper& random) {
const uint32_t suffix = random.next_uint();
std::vector<WebAppShortcutsMenuItemInfo> shortcuts_menu_item_infos;
for (int i = random.next_uint(4) + 1; i >= 0; --i) {
std::string suffix_str =
base::NumberToString(suffix) + base::NumberToString(i);
WebAppShortcutsMenuItemInfo shortcut_info;
shortcut_info.url = scope.Resolve("shortcut" + suffix_str);
shortcut_info.name = base::UTF8ToUTF16("shortcut" + suffix_str);
std::vector<WebAppShortcutsMenuItemInfo::Icon> shortcut_icons_any;
std::vector<WebAppShortcutsMenuItemInfo::Icon> shortcut_icons_maskable;
std::vector<WebAppShortcutsMenuItemInfo::Icon> shortcut_icons_monochrome;
for (int j = random.next_uint(4) + 1; j >= 0; --j) {
std::string icon_suffix_str = suffix_str + base::NumberToString(j);
WebAppShortcutsMenuItemInfo::Icon shortcut_icon;
shortcut_icon.url = scope.Resolve("/shortcuts/icon" + icon_suffix_str);
// Within each shortcut_icons_*, square_size_px must be unique.
shortcut_icon.square_size_px = (j * 10) + random.next_uint(10);
int icon_purpose = random.next_uint(3);
switch (icon_purpose) {
case 0:
shortcut_icons_any.push_back(std::move(shortcut_icon));
break;
case 1:
shortcut_icons_maskable.push_back(std::move(shortcut_icon));
break;
case 2:
shortcut_icons_monochrome.push_back(std::move(shortcut_icon));
break;
}
}
shortcut_info.SetShortcutIconInfosForPurpose(IconPurpose::ANY,
std::move(shortcut_icons_any));
shortcut_info.SetShortcutIconInfosForPurpose(
IconPurpose::MASKABLE, std::move(shortcut_icons_maskable));
shortcut_info.SetShortcutIconInfosForPurpose(
IconPurpose::MONOCHROME, std::move(shortcut_icons_monochrome));
shortcuts_menu_item_infos.push_back(std::move(shortcut_info));
}
return shortcuts_menu_item_infos;
}
std::vector<IconSizes> CreateRandomDownloadedShortcutsMenuIconsSizes(
RandomHelper& random) {
std::vector<IconSizes> results;
for (unsigned int i = 0; i < 3; ++i) {
IconSizes result;
std::vector<SquareSizePx> shortcuts_menu_icon_sizes_any;
std::vector<SquareSizePx> shortcuts_menu_icon_sizes_maskable;
std::vector<SquareSizePx> shortcuts_menu_icon_sizes_monochrome;
for (unsigned int j = 0; j < i; ++j) {
shortcuts_menu_icon_sizes_any.push_back(random.next_uint(256) + 1);
shortcuts_menu_icon_sizes_maskable.push_back(random.next_uint(256) + 1);
shortcuts_menu_icon_sizes_monochrome.push_back(random.next_uint(256) + 1);
}
result.SetSizesForPurpose(IconPurpose::ANY,
std::move(shortcuts_menu_icon_sizes_any));
result.SetSizesForPurpose(IconPurpose::MASKABLE,
std::move(shortcuts_menu_icon_sizes_maskable));
result.SetSizesForPurpose(IconPurpose::MONOCHROME,
std::move(shortcuts_menu_icon_sizes_monochrome));
results.push_back(std::move(result));
}
return results;
}
} // namespace
std::string GetExternalPrefMigrationTestName(
const ::testing::TestParamInfo<ExternalPrefMigrationTestCases>& info) {
switch (info.param) {
case ExternalPrefMigrationTestCases::kDisableMigrationReadPref:
return "DisableMigration_ReadFromPrefs";
case ExternalPrefMigrationTestCases::kDisableMigrationReadDB:
return "DisableMigration_ReadFromDB";
case ExternalPrefMigrationTestCases::kEnableMigrationReadPref:
return "EnableMigration_ReadFromPrefs";
case ExternalPrefMigrationTestCases::kEnableMigrationReadDB:
return "EnableMigration_ReadFromDB";
}
}
std::unique_ptr<WebApp> CreateWebApp(const GURL& start_url,
WebAppManagement::Type source_type) {
const AppId app_id = GenerateAppId(/*manifest_id=*/absl::nullopt, start_url);
auto web_app = std::make_unique<WebApp>(app_id);
web_app->SetStartUrl(start_url);
web_app->AddSource(source_type);
web_app->SetUserDisplayMode(UserDisplayMode::kStandalone);
web_app->SetName("Name");
return web_app;
}
std::unique_ptr<WebApp> CreateRandomWebApp(const GURL& base_url,
const uint32_t seed,
bool allow_system_source) {
RandomHelper random(seed);
const std::string seed_str = base::NumberToString(seed);
absl::optional<std::string> manifest_id;
if (random.next_bool())
manifest_id = "manifest_id_" + seed_str;
const GURL scope = base_url.Resolve("scope" + seed_str + "/");
const GURL start_url = scope.Resolve("start" + seed_str);
const AppId app_id = GenerateAppId(manifest_id, start_url);
const std::string name = "Name" + seed_str;
const std::string description = "Description" + seed_str;
const absl::optional<SkColor> theme_color = random.next_uint();
absl::optional<SkColor> dark_mode_theme_color;
const absl::optional<SkColor> background_color = random.next_uint();
absl::optional<SkColor> dark_mode_background_color;
const absl::optional<SkColor> synced_theme_color = random.next_uint();
auto app = std::make_unique<WebApp>(app_id);
std::vector<WebAppManagement::Type> management_types;
// Generate all possible permutations of field values in a random way:
if (allow_system_source && random.next_bool()) {
app->AddSource(WebAppManagement::kSystem);
management_types.push_back(WebAppManagement::kSystem);
}
if (random.next_bool()) {
app->AddSource(WebAppManagement::kPolicy);
management_types.push_back(WebAppManagement::kPolicy);
}
if (random.next_bool()) {
app->AddSource(WebAppManagement::kWebAppStore);
management_types.push_back(WebAppManagement::kWebAppStore);
}
if (random.next_bool()) {
app->AddSource(WebAppManagement::kSync);
management_types.push_back(WebAppManagement::kSync);
}
if (random.next_bool()) {
app->AddSource(WebAppManagement::kDefault);
management_types.push_back(WebAppManagement::kDefault);
}
if (random.next_bool()) {
app->AddSource(WebAppManagement::kSubApp);
management_types.push_back(WebAppManagement::kSubApp);
}
if (random.next_bool()) {
app->AddSource(WebAppManagement::kKiosk);
management_types.push_back(WebAppManagement::kKiosk);
}
if (random.next_bool()) {
app->AddSource(WebAppManagement::kCommandLine);
management_types.push_back(WebAppManagement::kCommandLine);
}
if (random.next_bool()) {
app->AddSource(WebAppManagement::kOem);
management_types.push_back(WebAppManagement::kOem);
}
if (random.next_bool()) {
app->AddSource(WebAppManagement::kOneDriveIntegration);
management_types.push_back(WebAppManagement::kOneDriveIntegration);
}
// Must always be at least one source.
if (!app->HasAnySources()) {
app->AddSource(WebAppManagement::kSync);
management_types.push_back(WebAppManagement::kSync);
}
if (random.next_bool()) {
dark_mode_theme_color = SkColorSetA(random.next_uint(), SK_AlphaOPAQUE);
}
if (random.next_bool()) {
dark_mode_background_color =
SkColorSetA(random.next_uint(), SK_AlphaOPAQUE);
}
app->SetName(name);
app->SetDescription(description);
app->SetManifestId(manifest_id);
app->SetStartUrl(GURL(start_url));
app->SetScope(GURL(scope));
app->SetThemeColor(theme_color);
app->SetDarkModeThemeColor(dark_mode_theme_color);
app->SetBackgroundColor(background_color);
app->SetDarkModeBackgroundColor(dark_mode_background_color);
app->SetIsLocallyInstalled(random.next_bool());
app->SetIsFromSyncAndPendingInstallation(random.next_bool());
const UserDisplayMode user_display_modes[3] = {UserDisplayMode::kBrowser,
UserDisplayMode::kStandalone,
UserDisplayMode::kTabbed};
app->SetUserDisplayMode(user_display_modes[random.next_uint(3)]);
const base::Time last_badging_time =
base::Time::UnixEpoch() + base::Milliseconds(random.next_uint());
app->SetLastBadgingTime(last_badging_time);
const base::Time last_launch_time =
base::Time::UnixEpoch() + base::Milliseconds(random.next_uint());
app->SetLastLaunchTime(last_launch_time);
const base::Time install_time =
base::Time::UnixEpoch() + base::Milliseconds(random.next_uint());
app->SetInstallTime(install_time);
const DisplayMode display_modes[4] = {
DisplayMode::kBrowser, DisplayMode::kMinimalUi, DisplayMode::kStandalone,
DisplayMode::kFullscreen};
app->SetDisplayMode(display_modes[random.next_uint(4)]);
// Add only unique display modes.
std::set<DisplayMode> display_mode_override;
int num_display_mode_override_tries = random.next_uint(5);
for (int i = 0; i < num_display_mode_override_tries; i++)
display_mode_override.insert(display_modes[random.next_uint(4)]);
app->SetDisplayModeOverride(std::vector<DisplayMode>(
display_mode_override.begin(), display_mode_override.end()));
if (random.next_bool())
app->SetLaunchQueryParams(base::NumberToString(random.next_uint()));
app->SetRunOnOsLoginMode(random.next_enum<RunOnOsLoginMode>());
app->SetRunOnOsLoginOsIntegrationState(RunOnOsLoginMode::kNotRun);
const SquareSizePx size = 256;
const int num_icons = random.next_uint(10);
std::vector<apps::IconInfo> manifest_icons(num_icons);
for (int i = 0; i < num_icons; i++) {
apps::IconInfo icon;
icon.url =
base_url.Resolve("/icon" + base::NumberToString(random.next_uint()));
if (random.next_bool())
icon.square_size_px = size;
int purpose = random.next_uint(4);
if (purpose == 0)
icon.purpose = apps::IconInfo::Purpose::kAny;
if (purpose == 1)
icon.purpose = apps::IconInfo::Purpose::kMaskable;
if (purpose == 2)
icon.purpose = apps::IconInfo::Purpose::kMonochrome;
// if (purpose == 3), leave purpose unset. Should default to ANY.
manifest_icons[i] = icon;
}
app->SetManifestIcons(manifest_icons);
if (random.next_bool())
app->SetDownloadedIconSizes(IconPurpose::ANY, {size});
if (random.next_bool())
app->SetDownloadedIconSizes(IconPurpose::MASKABLE, {size});
if (random.next_bool())
app->SetDownloadedIconSizes(IconPurpose::MONOCHROME, {size});
app->SetIsGeneratedIcon(random.next_bool());
app->SetFileHandlers(CreateRandomFileHandlers(random.next_uint()));
if (random.next_bool())
app->SetShareTarget(CreateRandomShareTarget(random.next_uint()));
app->SetProtocolHandlers(CreateRandomProtocolHandlers(random.next_uint()));
app->SetUrlHandlers(CreateRandomUrlHandlers(random.next_uint()));
if (random.next_bool()) {
app->SetLockScreenStartUrl(scope.Resolve(
"lock_screen_start_url" + base::NumberToString(random.next_uint())));
}
if (random.next_bool()) {
app->SetNoteTakingNewNoteUrl(
scope.Resolve("new_note" + base::NumberToString(random.next_uint())));
}
app->SetCaptureLinks(random.next_enum<blink::mojom::CaptureLinks>());
const int num_additional_search_terms = random.next_uint(8);
std::vector<std::string> additional_search_terms(num_additional_search_terms);
for (int i = 0; i < num_additional_search_terms; ++i) {
additional_search_terms[i] =
"Foo_" + seed_str + "_" + base::NumberToString(i);
}
app->SetAdditionalSearchTerms(std::move(additional_search_terms));
app->SetShortcutsMenuItemInfos(
CreateRandomShortcutsMenuItemInfos(scope, random));
app->SetDownloadedShortcutsMenuIconsSizes(
CreateRandomDownloadedShortcutsMenuIconsSizes(random));
app->SetManifestUrl(base_url.Resolve("/manifest" + seed_str + ".json"));
const int num_allowed_launch_protocols = random.next_uint(8);
std::vector<std::string> allowed_launch_protocols(
num_allowed_launch_protocols);
for (int i = 0; i < num_allowed_launch_protocols; ++i) {
allowed_launch_protocols[i] =
"web+test_" + seed_str + "_" + base::NumberToString(i);
}
app->SetAllowedLaunchProtocols(std::move(allowed_launch_protocols));
const int num_disallowed_launch_protocols = random.next_uint(8);
std::vector<std::string> disallowed_launch_protocols(
num_disallowed_launch_protocols);
for (int i = 0; i < num_disallowed_launch_protocols; ++i) {
disallowed_launch_protocols[i] =
"web+disallowed_" + seed_str + "_" + base::NumberToString(i);
}
app->SetDisallowedLaunchProtocols(std::move(disallowed_launch_protocols));
app->SetStorageIsolated(random.next_bool());
app->SetWindowControlsOverlayEnabled(false);
WebApp::SyncFallbackData sync_fallback_data;
sync_fallback_data.name = "Sync" + name;
sync_fallback_data.theme_color = synced_theme_color;
sync_fallback_data.scope = app->scope();
sync_fallback_data.icon_infos = app->manifest_icons();
app->SetSyncFallbackData(std::move(sync_fallback_data));
if (random.next_bool()) {
app->SetLaunchHandler(
LaunchHandler{random.next_enum<LaunchHandler::ClientMode>()});
}
const base::Time manifest_update_time =
base::Time::UnixEpoch() + base::Milliseconds(random.next_uint());
app->SetManifestUpdateTime(manifest_update_time);
if (random.next_bool())
app->SetParentAppId(base::NumberToString(random.next_uint()));
if (random.next_bool())
app->SetPermissionsPolicy(CreateRandomPermissionsPolicy(random));
uint32_t install_source =
random.next_uint(static_cast<int>(webapps::WebappInstallSource::COUNT));
app->SetInstallSourceForMetrics(
static_cast<webapps::WebappInstallSource>(install_source));
if (IsChromeOsDataMandatory()) {
// Use a separate random generator for CrOS so the result is deterministic
// across cros and non-cros builds.
RandomHelper cros_random(seed);
auto chromeos_data = absl::make_optional<WebAppChromeOsData>();
chromeos_data->show_in_launcher = cros_random.next_bool();
chromeos_data->show_in_search = cros_random.next_bool();
chromeos_data->show_in_management = cros_random.next_bool();
chromeos_data->is_disabled = cros_random.next_bool();
chromeos_data->oem_installed = cros_random.next_bool();
// Comply with DCHECK that system apps cannot be OEM installed.
if (app->IsSystemApp())
chromeos_data->oem_installed = false;
app->SetWebAppChromeOsData(std::move(chromeos_data));
}
WebApp::ExternalConfigMap management_to_external_config;
for (WebAppManagement::Type type : management_types) {
if (type == WebAppManagement::kSync)
continue;
base::flat_set<GURL> install_urls;
WebApp::ExternalManagementConfig config;
if (random.next_bool())
install_urls.emplace(base_url.Resolve("installer1_" + seed_str + "/"));
if (random.next_bool())
install_urls.emplace(base_url.Resolve("installer2_" + seed_str + "/"));
config.is_placeholder = random.next_bool();
config.install_urls = install_urls;
management_to_external_config.insert_or_assign(type, std::move(config));
}
app->SetWebAppManagementExternalConfigMap(management_to_external_config);
app->SetAppSizeInBytes(random.next_uint());
app->SetDataSizeInBytes(random.next_uint());
if (random.next_bool()) {
blink::Manifest::TabStrip tab_strip;
tab_strip.home_tab =
random.next_enum<blink::mojom::TabStripMemberVisibility>();
if (random.next_bool()) {
blink::Manifest::NewTabButtonParams new_tab_button_params;
if (random.next_bool()) {
new_tab_button_params.url = scope.Resolve(
"new_tab_button_url" + base::NumberToString(random.next_uint()));
}
tab_strip.new_tab_button = new_tab_button_params;
} else {
tab_strip.new_tab_button =
random.next_enum<blink::mojom::TabStripMemberVisibility>();
}
app->SetTabStrip(std::move(tab_strip));
}
app->SetAlwaysShowToolbarInFullscreen(random.next_bool());
if (random.next_bool()) {
proto::WebAppOsIntegrationState state;
app->SetCurrentOsIntegrationStates(state);
}
if (random.next_bool()) {
using IsolationDataContent = decltype(IsolationData::content);
constexpr size_t kNumContentTypes =
absl::variant_size<IsolationDataContent>::value;
auto path = base::FilePath::FromUTF8Unsafe(seed_str);
IsolationDataContent content_types[] = {
IsolationData::InstalledBundle{.path = path},
IsolationData::DevModeBundle{.path = path},
IsolationData::DevModeProxy{
.proxy_url = url::Origin::Create(
GURL(base::StrCat({"https://proxy-", seed_str, ".com/"})))},
};
static_assert(std::size(content_types) == kNumContentTypes);
IsolationData isolation_data(
content_types[random.next_uint(kNumContentTypes)]);
app->SetIsolationData(isolation_data);
}
return app;
}
void TestAcceptDialogCallback(
content::WebContents* initiator_web_contents,
std::unique_ptr<WebAppInstallInfo> web_app_info,
WebAppInstallationAcceptanceCallback acceptance_callback) {
base::SingleThreadTaskRunner::GetCurrentDefault()->PostTask(
FROM_HERE, base::BindOnce(std::move(acceptance_callback), true /*accept*/,
std::move(web_app_info)));
}
void TestDeclineDialogCallback(
content::WebContents* initiator_web_contents,
std::unique_ptr<WebAppInstallInfo> web_app_info,
WebAppInstallationAcceptanceCallback acceptance_callback) {
base::SingleThreadTaskRunner::GetCurrentDefault()->PostTask(
FROM_HERE, base::BindOnce(std::move(acceptance_callback),
false /*accept*/, std::move(web_app_info)));
}
AppId InstallPwaForCurrentUrl(Browser* browser) {
// Depending on the installability criteria, different dialogs can be used.
chrome::SetAutoAcceptWebAppDialogForTesting(true, true);
chrome::SetAutoAcceptPWAInstallConfirmationForTesting(true);
WebAppTestInstallObserver observer(browser->profile());
observer.BeginListening();
CHECK(chrome::ExecuteCommand(browser, IDC_INSTALL_PWA));
AppId app_id = observer.Wait();
chrome::SetAutoAcceptPWAInstallConfirmationForTesting(false);
chrome::SetAutoAcceptWebAppDialogForTesting(false, false);
return app_id;
}
void CheckServiceWorkerStatus(const GURL& url,
content::StoragePartition* storage_partition,
content::ServiceWorkerCapability status) {
base::RunLoop run_loop;
content::ServiceWorkerContext* service_worker_context =
storage_partition->GetServiceWorkerContext();
service_worker_context->CheckHasServiceWorker(
url, blink::StorageKey(url::Origin::Create(url)),
base::BindLambdaForTesting(
[&run_loop, status](content::ServiceWorkerCapability capability) {
CHECK_EQ(status, capability);
run_loop.Quit();
}));
run_loop.Run();
}
void SetWebAppSettingsListPref(Profile* profile, const base::StringPiece pref) {
auto result = base::JSONReader::ReadAndReturnValueWithError(
pref, base::JSONParserOptions::JSON_ALLOW_TRAILING_COMMAS);
DCHECK(result.has_value()) << result.error().message;
DCHECK(result->is_list());
profile->GetPrefs()->Set(prefs::kWebAppSettings, std::move(*result));
}
void AddInstallUrlData(PrefService* pref_service,
WebAppSyncBridge* sync_bridge,
const AppId& app_id,
const GURL& url,
const ExternalInstallSource& source) {
ScopedRegistryUpdate update(sync_bridge);
WebApp* app_to_update = update->UpdateApp(app_id);
DCHECK(app_to_update);
// Adding external app data (source and URL) to web_app DB.
app_to_update->AddInstallURLToManagementExternalConfigMap(
ConvertExternalInstallSourceToSource(source), url);
// Add to legacy external pref storage.
// TODO(crbug.com/1339965): Clean up after external pref migration is
// complete.
ExternallyInstalledWebAppPrefs(pref_service).Insert(url, app_id, source);
}
void AddInstallUrlAndPlaceholderData(PrefService* pref_service,
WebAppSyncBridge* sync_bridge,
const AppId& app_id,
const GURL& url,
const ExternalInstallSource& source,
bool is_placeholder) {
ScopedRegistryUpdate update(sync_bridge);
ExternallyInstalledWebAppPrefs prefs(pref_service);
WebApp* app_to_update = update->UpdateApp(app_id);
DCHECK(app_to_update);
// Adding install_url, source and placeholder information to the web_app DB.
app_to_update->AddExternalSourceInformation(
ConvertExternalInstallSourceToSource(source), url, is_placeholder);
// Add to legacy external pref storage.
// TODO(crbug.com/1339965): Clean up after external pref migration is
// complete.
prefs.Insert(url, app_id, source);
prefs.SetIsPlaceholder(url, is_placeholder);
}
#if BUILDFLAG(IS_CHROMEOS_LACROS)
ScopedSkipMainProfileCheck::ScopedSkipMainProfileCheck() {
EXPECT_FALSE(IsMainProfileCheckSkippedForTesting());
SetSkipMainProfileCheckForTesting(/*skip_check=*/true);
}
ScopedSkipMainProfileCheck::~ScopedSkipMainProfileCheck() {
SetSkipMainProfileCheckForTesting(/*skip_check=*/false);
}
#endif
} // namespace test
} // namespace web_app