blob: 698f80121664901d78db52f9f0d52cf0cf3713dd [file] [log] [blame]
// Copyright 2018 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/web_app.h"
#include <array>
#include <bitset>
#include <ostream>
#include <string>
#include <tuple>
#include <utility>
#include "base/check.h"
#include "base/check_is_test.h"
#include "base/check_op.h"
#include "base/containers/flat_tree.h"
#include "base/notreached.h"
#include "base/numerics/clamped_math.h"
#include "base/strings/strcat.h"
#include "base/strings/string_number_conversions.h"
#include "base/strings/to_string.h"
#include "base/values.h"
#include "chrome/browser/web_applications/mojom/user_display_mode.mojom-shared.h"
#include "chrome/browser/web_applications/proto/web_app_os_integration_state.pb.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_utils.h"
#include "components/sync/base/time.h"
#include "components/webapps/browser/installable/installable_metrics.h"
#include "third_party/abseil-cpp/absl/types/variant.h"
#include "third_party/blink/public/common/manifest/manifest_util.h"
#include "third_party/blink/public/common/permissions_policy/origin_with_possible_wildcards.h"
#include "third_party/blink/public/common/permissions_policy/policy_helper_public.h"
#include "third_party/blink/public/common/safe_url_pattern.h"
#include "third_party/blink/public/mojom/manifest/manifest.mojom-shared.h"
#include "third_party/blink/public/mojom/manifest/manifest_launch_handler.mojom-shared.h"
#include "third_party/liburlpattern/options.h"
#include "third_party/liburlpattern/pattern.h"
#include "third_party/protobuf/src/google/protobuf/repeated_field.h"
#include "ui/gfx/color_utils.h"
#include "ui/gfx/geometry/size.h"
#include "url/origin.h"
namespace web_app {
namespace {
// Converts an optional to a string wrapped in a `Value`, or an empty `Value` if
// absent.
template <typename T>
base::Value OptionalToStringValue(const absl::optional<T>& optional) {
if (optional.has_value()) {
return base::Value(base::ToString(optional.value()));
}
return base::Value();
}
// Converts an optional to a debug `Value`, or an empty `Value` if absent.
template <typename T>
base::Value OptionalAsDebugValue(const absl::optional<T>& optional) {
if (optional.has_value()) {
return optional.value().AsDebugValue();
}
return base::Value();
}
std::string ColorToString(absl::optional<SkColor> color) {
return color.has_value() ? color_utils::SkColorToRgbaString(color.value())
: "none";
}
std::string ApiApprovalStateToString(ApiApprovalState state) {
switch (state) {
case ApiApprovalState::kRequiresPrompt:
return "kRequiresPrompt";
case ApiApprovalState::kAllowed:
return "kAllowed";
case ApiApprovalState::kDisallowed:
return "kDisallowed";
}
}
std::string OsIntegrationStateToString(OsIntegrationState state) {
switch (state) {
case OsIntegrationState::kEnabled:
return "kEnabled";
case OsIntegrationState::kDisabled:
return "kDisabled";
}
}
std::string GetRunOnOsLoginMode(const proto::RunOnOsLoginMode& mode) {
switch (mode) {
case proto::RunOnOsLoginMode::RUN_ON_OS_LOGIN_MODE_UNSPECIFIED:
return "unspecified";
case proto::RunOnOsLoginMode::NOT_RUN:
return "not_run";
case proto::RunOnOsLoginMode::WINDOWED:
return "windowed";
case proto::RunOnOsLoginMode::MINIMIZED:
return "minimized";
}
}
base::Value OsStatesDebugValue(
const proto::WebAppOsIntegrationState& current_states) {
base::Value::Dict debug_dict;
if (current_states.has_shortcut()) {
base::Value::Dict shortcut_data;
shortcut_data.Set("title", current_states.shortcut().title());
shortcut_data.Set("description", current_states.shortcut().description());
base::Value::Dict icon_data;
for (const auto& data : current_states.shortcut().icon_data_any()) {
icon_data.Set(base::NumberToString(data.icon_size()),
base::ToString(syncer::ProtoTimeToTime(data.timestamp())));
}
shortcut_data.Set("icon_size_to_timestamp_map",
base::Value(std::move(icon_data)));
debug_dict.Set("shortcut_descriptions",
base::Value(std::move(shortcut_data)));
}
if (current_states.has_protocols_handled()) {
base::Value::Dict protocol_data;
for (const auto& data : current_states.protocols_handled().protocols()) {
protocol_data.Set(data.protocol(), data.url());
}
debug_dict.Set("protocols_handled", base::Value(std::move(protocol_data)));
}
if (current_states.has_run_on_os_login() &&
current_states.run_on_os_login().has_run_on_os_login_mode()) {
debug_dict.Set(
"run_on_os_login",
GetRunOnOsLoginMode(
current_states.run_on_os_login().run_on_os_login_mode()));
}
if (current_states.has_uninstall_registration()) {
base::Value::Dict state;
proto::OsUninstallRegistration os_uninstall =
current_states.uninstall_registration();
if (os_uninstall.has_registered_with_os()) {
state.Set("registered_with_os", os_uninstall.registered_with_os());
}
if (os_uninstall.has_display_name()) {
state.Set("display_name", os_uninstall.display_name());
}
debug_dict.Set("uninstall_registration", std::move(state));
}
if (current_states.has_shortcut_menus()) {
base::Value::List shortcut_menus_list;
for (const auto& shortcut_menu :
current_states.shortcut_menus().shortcut_menu_info()) {
base::Value::Dict icon_data_any_dict;
base::Value::Dict icon_data_maskable_dict;
base::Value::Dict icon_data_monochrome_dict;
for (const auto& icon_data_any : shortcut_menu.icon_data_any()) {
icon_data_any_dict.Set(
base::NumberToString(icon_data_any.icon_size()),
base::ToString(syncer::ProtoTimeToTime(icon_data_any.timestamp())));
}
for (const auto& icon_data_maskable : shortcut_menu.icon_data_any()) {
icon_data_maskable_dict.Set(
base::NumberToString(icon_data_maskable.icon_size()),
base::ToString(
syncer::ProtoTimeToTime(icon_data_maskable.timestamp())));
}
for (const auto& icon_data_monochrome : shortcut_menu.icon_data_any()) {
icon_data_monochrome_dict.Set(
base::NumberToString(icon_data_monochrome.icon_size()),
base::ToString(
syncer::ProtoTimeToTime(icon_data_monochrome.timestamp())));
}
base::Value::Dict shortcut_menu_dict;
shortcut_menu_dict.Set("shortcut_name", shortcut_menu.shortcut_name());
shortcut_menu_dict.Set("shortcut_launch_url",
shortcut_menu.shortcut_launch_url());
shortcut_menu_dict.Set("icon_data_any",
base::Value(std::move(icon_data_any_dict)));
shortcut_menu_dict.Set("icon_data_maskable",
base::Value(std::move(icon_data_maskable_dict)));
shortcut_menu_dict.Set("icon_data_monochrome",
base::Value(std::move(icon_data_monochrome_dict)));
shortcut_menus_list.Append(std::move(shortcut_menu_dict));
}
debug_dict.Set("shortcut_menus",
base::Value(std::move(shortcut_menus_list)));
}
if (current_states.has_file_handling()) {
base::Value::List file_handlers_list;
for (const auto& file_handler :
current_states.file_handling().file_handlers()) {
base::Value::Dict file_handler_dict;
file_handler_dict.Set("action", base::Value(file_handler.action()));
file_handler_dict.Set("display_name", file_handler.display_name());
base::Value::List accept_list;
for (const auto& accept : file_handler.accept()) {
base::Value::Dict accept_dict;
accept_dict.Set("mimetype", accept.mimetype());
base::Value::List file_extensions_list;
for (const auto& file_extension : accept.file_extensions()) {
file_extensions_list.Append(file_extension);
}
accept_dict.Set("file_extensions", std::move(file_extensions_list));
accept_list.Append(std::move(accept_dict));
}
file_handler_dict.Set("accept", std::move(accept_list));
file_handlers_list.Append(std::move(file_handler_dict));
}
debug_dict.Set("file_handling", std::move(file_handlers_list));
}
return base::Value(std::move(debug_dict));
}
base::Value::Dict ImageResourceDebugDict(
const blink::Manifest::ImageResource& icon) {
const char* const kPurposeStrings[] = {"Any", "Monochrome", "Maskable"};
base::Value::Dict root;
root.Set("src", icon.src.spec());
root.Set("type", icon.type);
base::Value::List sizes_json;
for (const auto& size : icon.sizes) {
std::string size_formatted = base::NumberToString(size.width()) + "x" +
base::NumberToString(size.height());
sizes_json.Append(base::Value(size_formatted));
}
root.Set("sizes", std::move(sizes_json));
base::Value::List purpose_json;
for (const auto& purpose : icon.purpose) {
purpose_json.Append(kPurposeStrings[static_cast<int>(purpose)]);
}
root.Set("purpose", std::move(purpose_json));
return root;
}
base::Value::Dict UrlPatternDebugValue(const blink::SafeUrlPattern& pattern) {
liburlpattern::Options options = {.delimiter_list = "/",
.prefix_list = "/",
.sensitive = true,
.strict = false};
liburlpattern::Pattern pathname(pattern.pathname, options, "[^/]+?");
base::Value::Dict pattern_dict;
pattern_dict.Set("pathname", pathname.GeneratePatternString());
return pattern_dict;
}
base::Value OptTabStripToDebugValue(
absl::optional<blink::Manifest::TabStrip> tab_strip) {
if (!tab_strip.has_value()) {
return base::Value();
}
base::Value::Dict result;
base::Value::Dict new_tab_button_json;
new_tab_button_json.Set(
"url", base::ToString(tab_strip->new_tab_button.url.value_or(GURL(""))));
result.Set("new_tab_button", std::move(new_tab_button_json));
if (absl::holds_alternative<TabStrip::Visibility>(tab_strip->home_tab)) {
result.Set(
"home_tab",
base::ToString(absl::get<TabStrip::Visibility>(tab_strip->home_tab)));
} else {
base::Value::Dict home_tab_json;
const blink::Manifest::HomeTabParams& home_tab_params =
absl::get<blink::Manifest::HomeTabParams>(tab_strip->home_tab);
base::Value::List icons_json;
absl::optional<std::vector<blink::Manifest::ImageResource>> icons =
home_tab_params.icons;
for (auto& icon : *icons) {
icons_json.Append(ImageResourceDebugDict(icon));
}
base::Value::List scope_patterns_json;
const std::vector<blink::SafeUrlPattern>& scope_patterns =
home_tab_params.scope_patterns;
for (const auto& scope_pattern : scope_patterns) {
scope_patterns_json.Append(UrlPatternDebugValue(scope_pattern));
}
home_tab_json.Set("icons", std::move(icons_json));
home_tab_json.Set("scope_patterns", std::move(scope_patterns_json));
result.Set("home_tab", std::move(home_tab_json));
}
return base::Value(std::move(result));
}
} // namespace
WebApp::WebApp(const AppId& app_id)
: app_id_(app_id),
chromeos_data_(IsChromeOsDataMandatory()
? absl::make_optional<WebAppChromeOsData>()
: absl::nullopt) {}
WebApp::~WebApp() = default;
WebApp::WebApp(const WebApp& web_app) = default;
WebApp& WebApp::operator=(WebApp&& web_app) = default;
const SortedSizesPx& WebApp::downloaded_icon_sizes(IconPurpose purpose) const {
switch (purpose) {
case IconPurpose::ANY:
return downloaded_icon_sizes_any_;
case IconPurpose::MONOCHROME:
return downloaded_icon_sizes_monochrome_;
case IconPurpose::MASKABLE:
return downloaded_icon_sizes_maskable_;
}
}
ManifestId WebApp::manifest_id() const {
// Almost all production use-cases should have the manifest_id set, but in
// some test it is not. If the manifest id is not set, then fall back to the
// start_url, as per the algorithm in
// https://www.w3.org/TR/appmanifest/#id-member.
if (manifest_id_.is_empty()) {
CHECK_IS_TEST();
// This is why the function must return a value instead of a const ref, as
// this object would be temporary.
return GenerateManifestIdFromStartUrlOnly(start_url_);
}
return manifest_id_;
}
void WebApp::AddSource(WebAppManagement::Type source) {
sources_.Put(source);
}
void WebApp::RemoveSource(WebAppManagement::Type source) {
sources_.Remove(source);
management_to_external_config_map_.erase(source);
}
bool WebApp::HasAnySources() const {
return !sources_.Empty();
}
bool WebApp::HasOnlySource(WebAppManagement::Type source) const {
WebAppManagementTypes specified_sources;
specified_sources.Put(source);
return HasAnySpecifiedSourcesAndNoOtherSources(sources_, specified_sources);
}
WebAppManagementTypes WebApp::GetSources() const {
return sources_;
}
bool WebApp::IsSynced() const {
return sources_.Has(WebAppManagement::kSync);
}
bool WebApp::IsPreinstalledApp() const {
return sources_.Has(WebAppManagement::kDefault);
}
bool WebApp::IsPolicyInstalledApp() const {
return sources_.Has(WebAppManagement::kPolicy);
}
bool WebApp::IsSystemApp() const {
return sources_.Has(WebAppManagement::kSystem);
}
bool WebApp::IsWebAppStoreInstalledApp() const {
return sources_.Has(WebAppManagement::kWebAppStore);
}
bool WebApp::IsSubAppInstalledApp() const {
return sources_.Has(WebAppManagement::kSubApp);
}
bool WebApp::IsKioskInstalledApp() const {
return sources_.Has(WebAppManagement::kKiosk);
}
bool WebApp::CanUserUninstallWebApp() const {
return web_app::CanUserUninstallWebApp(sources_);
}
bool WebApp::WasInstalledByUser() const {
return sources_.Has(WebAppManagement::kSync) ||
sources_.Has(WebAppManagement::kWebAppStore) ||
sources_.Has(WebAppManagement::kOneDriveIntegration);
}
WebAppManagement::Type WebApp::GetHighestPrioritySource() const {
// `WebAppManagementTypes` is iterated in order of priority.
// Top priority sources are iterated first.
for (WebAppManagement::Type source : WebAppManagementTypes::All()) {
if (sources_.Has(source)) {
return source;
}
}
NOTREACHED();
return WebAppManagement::kMaxValue;
}
void WebApp::SetName(const std::string& name) {
name_ = name;
}
void WebApp::SetDescription(const std::string& description) {
description_ = description;
}
void WebApp::SetStartUrl(const GURL& start_url) {
CHECK(start_url.is_valid());
if (manifest_id_.is_empty()) {
manifest_id_ = GenerateManifestIdFromStartUrlOnly(start_url);
}
CHECK(url::Origin::Create(manifest_id())
.IsSameOriginWith(url::Origin::Create(start_url)))
<< manifest_id().spec() << " " << start_url.spec();
start_url_ = start_url;
}
void WebApp::SetScope(const GURL& scope) {
DCHECK(scope.is_empty() || scope.is_valid());
scope_ = scope;
}
void WebApp::SetThemeColor(absl::optional<SkColor> theme_color) {
theme_color_ = theme_color;
}
void WebApp::SetDarkModeThemeColor(
absl::optional<SkColor> dark_mode_theme_color) {
dark_mode_theme_color_ = dark_mode_theme_color;
}
void WebApp::SetBackgroundColor(absl::optional<SkColor> background_color) {
background_color_ = background_color;
}
void WebApp::SetDarkModeBackgroundColor(
absl::optional<SkColor> dark_mode_background_color) {
dark_mode_background_color_ = dark_mode_background_color;
}
void WebApp::SetDisplayMode(DisplayMode display_mode) {
DCHECK_NE(DisplayMode::kUndefined, display_mode);
display_mode_ = display_mode;
}
void WebApp::SetUserDisplayMode(mojom::UserDisplayMode user_display_mode) {
user_display_mode_ = user_display_mode;
}
void WebApp::SetDisplayModeOverride(
std::vector<DisplayMode> display_mode_override) {
display_mode_override_ = std::move(display_mode_override);
}
void WebApp::SetUserPageOrdinal(syncer::StringOrdinal page_ordinal) {
user_page_ordinal_ = std::move(page_ordinal);
}
void WebApp::SetUserLaunchOrdinal(syncer::StringOrdinal launch_ordinal) {
user_launch_ordinal_ = std::move(launch_ordinal);
}
void WebApp::SetWebAppChromeOsData(
absl::optional<WebAppChromeOsData> chromeos_data) {
chromeos_data_ = std::move(chromeos_data);
}
void WebApp::SetIsLocallyInstalled(bool is_locally_installed) {
is_locally_installed_ = is_locally_installed;
}
void WebApp::SetIsFromSyncAndPendingInstallation(
bool is_from_sync_and_pending_installation) {
is_from_sync_and_pending_installation_ =
is_from_sync_and_pending_installation;
}
void WebApp::SetIsUninstalling(bool is_uninstalling) {
is_uninstalling_ = is_uninstalling;
}
void WebApp::SetManifestIcons(std::vector<apps::IconInfo> manifest_icons) {
manifest_icons_ = std::move(manifest_icons);
}
void WebApp::SetDownloadedIconSizes(IconPurpose purpose, SortedSizesPx sizes) {
switch (purpose) {
case IconPurpose::ANY:
downloaded_icon_sizes_any_ = std::move(sizes);
break;
case IconPurpose::MONOCHROME:
downloaded_icon_sizes_monochrome_ = std::move(sizes);
break;
case IconPurpose::MASKABLE:
downloaded_icon_sizes_maskable_ = std::move(sizes);
break;
}
}
void WebApp::SetIsGeneratedIcon(bool is_generated_icon) {
is_generated_icon_ = is_generated_icon;
}
void WebApp::SetFileHandlers(apps::FileHandlers file_handlers) {
file_handlers_ = std::move(file_handlers);
}
void WebApp::SetFileHandlerApprovalState(ApiApprovalState approval_state) {
file_handler_approval_state_ = approval_state;
}
void WebApp::SetFileHandlerOsIntegrationState(OsIntegrationState state) {
file_handler_os_integration_state_ = state;
}
void WebApp::SetShareTarget(absl::optional<apps::ShareTarget> share_target) {
share_target_ = std::move(share_target);
}
void WebApp::SetAdditionalSearchTerms(
std::vector<std::string> additional_search_terms) {
additional_search_terms_ = std::move(additional_search_terms);
}
void WebApp::SetProtocolHandlers(
std::vector<apps::ProtocolHandlerInfo> handlers) {
protocol_handlers_ = std::move(handlers);
}
void WebApp::SetAllowedLaunchProtocols(
base::flat_set<std::string> allowed_launch_protocols) {
allowed_launch_protocols_ = std::move(allowed_launch_protocols);
}
void WebApp::SetDisallowedLaunchProtocols(
base::flat_set<std::string> disallowed_launch_protocols) {
disallowed_launch_protocols_ = std::move(disallowed_launch_protocols);
}
void WebApp::SetUrlHandlers(apps::UrlHandlers url_handlers) {
url_handlers_ = std::move(url_handlers);
}
void WebApp::SetScopeExtensions(
base::flat_set<ScopeExtensionInfo> scope_extensions) {
scope_extensions_ = std::move(scope_extensions);
}
void WebApp::SetValidatedScopeExtensions(
base::flat_set<ScopeExtensionInfo> validated_scope_extensions) {
validated_scope_extensions_ = std::move(validated_scope_extensions);
}
void WebApp::SetLockScreenStartUrl(const GURL& lock_screen_start_url) {
DCHECK(lock_screen_start_url.is_empty() || lock_screen_start_url.is_valid());
lock_screen_start_url_ = lock_screen_start_url;
}
void WebApp::SetNoteTakingNewNoteUrl(const GURL& note_taking_new_note_url) {
DCHECK(note_taking_new_note_url.is_empty() ||
note_taking_new_note_url.is_valid());
note_taking_new_note_url_ = note_taking_new_note_url;
}
void WebApp::SetShortcutsMenuInfo(
std::vector<WebAppShortcutsMenuItemInfo> shortcuts_menu_item_infos) {
shortcuts_menu_item_infos_ = std::move(shortcuts_menu_item_infos);
}
void WebApp::SetLastBadgingTime(const base::Time& time) {
last_badging_time_ = time;
}
void WebApp::SetLastLaunchTime(const base::Time& time) {
last_launch_time_ = time;
}
void WebApp::SetFirstInstallTime(const base::Time& time) {
first_install_time_ = time;
}
void WebApp::SetManifestUpdateTime(const base::Time& time) {
manifest_update_time_ = time;
}
void WebApp::SetRunOnOsLoginMode(RunOnOsLoginMode mode) {
run_on_os_login_mode_ = mode;
}
void WebApp::SetRunOnOsLoginOsIntegrationState(RunOnOsLoginMode state) {
run_on_os_login_os_integration_state_ = state;
}
void WebApp::SetSyncFallbackData(SyncFallbackData sync_fallback_data) {
sync_fallback_data_ = std::move(sync_fallback_data);
}
void WebApp::SetCaptureLinks(blink::mojom::CaptureLinks capture_links) {
capture_links_ = capture_links;
}
void WebApp::SetLaunchQueryParams(
absl::optional<std::string> launch_query_params) {
launch_query_params_ = std::move(launch_query_params);
}
void WebApp::SetManifestUrl(const GURL& manifest_url) {
manifest_url_ = manifest_url;
}
void WebApp::SetManifestId(const ManifestId& manifest_id) {
CHECK(manifest_id.is_valid());
CHECK(start_url_.is_empty() ||
url::Origin::Create(start_url_)
.IsSameOriginWith(url::Origin::Create(manifest_id)))
<< start_url_.spec() << " vs " << manifest_id.spec();
manifest_id_ = manifest_id;
}
void WebApp::SetWindowControlsOverlayEnabled(bool enabled) {
window_controls_overlay_enabled_ = enabled;
}
void WebApp::SetLaunchHandler(absl::optional<LaunchHandler> launch_handler) {
launch_handler_ = std::move(launch_handler);
}
void WebApp::SetParentAppId(const absl::optional<AppId>& parent_app_id) {
parent_app_id_ = parent_app_id;
}
void WebApp::SetPermissionsPolicy(
blink::ParsedPermissionsPolicy permissions_policy) {
permissions_policy_ = std::move(permissions_policy);
}
void WebApp::SetLatestInstallSource(
absl::optional<webapps::WebappInstallSource> latest_install_source) {
latest_install_source_ = latest_install_source;
}
void WebApp::SetAppSizeInBytes(absl::optional<int64_t> app_size_in_bytes) {
app_size_in_bytes_ = app_size_in_bytes;
}
void WebApp::SetDataSizeInBytes(absl::optional<int64_t> data_size_in_bytes) {
data_size_in_bytes_ = data_size_in_bytes;
}
void WebApp::SetWebAppManagementExternalConfigMap(
ExternalConfigMap management_to_external_config_map) {
management_to_external_config_map_ =
std::move(management_to_external_config_map);
}
void WebApp::SetTabStrip(absl::optional<blink::Manifest::TabStrip> tab_strip) {
tab_strip_ = std::move(tab_strip);
}
void WebApp::SetCurrentOsIntegrationStates(
proto::WebAppOsIntegrationState current_os_integration_states) {
current_os_integration_states_ = std::move(current_os_integration_states);
}
void WebApp::SetIsolationData(IsolationData isolation_data) {
isolation_data_ = isolation_data;
}
void WebApp::SetIsUserSelectedAppForSupportedLinks(
bool is_user_selected_app_for_capturing_links) {
is_user_selected_app_for_capturing_links_ =
is_user_selected_app_for_capturing_links;
}
void WebApp::AddPlaceholderInfoToManagementExternalConfigMap(
WebAppManagement::Type type,
bool is_placeholder) {
DCHECK_NE(type, WebAppManagement::Type::kSync);
management_to_external_config_map_[type].is_placeholder = is_placeholder;
}
void WebApp::AddInstallURLToManagementExternalConfigMap(
WebAppManagement::Type type,
GURL install_url) {
DCHECK_NE(type, WebAppManagement::Type::kSync);
DCHECK(install_url.is_valid());
management_to_external_config_map_[type].install_urls.emplace(
std::move(install_url));
}
void WebApp::AddPolicyIdToManagementExternalConfigMap(
WebAppManagement::Type type,
std::string policy_id) {
DCHECK_NE(type, WebAppManagement::Type::kSync);
DCHECK(!policy_id.empty());
management_to_external_config_map_[type].additional_policy_ids.emplace(
std::move(policy_id));
}
void WebApp::AddExternalSourceInformation(WebAppManagement::Type type,
GURL install_url,
bool is_placeholder) {
AddInstallURLToManagementExternalConfigMap(type, std::move(install_url));
AddPlaceholderInfoToManagementExternalConfigMap(type, is_placeholder);
}
bool WebApp::RemoveInstallUrlForSource(WebAppManagement::Type type,
const GURL& install_url) {
if (!management_to_external_config_map_.count(type))
return false;
bool removed =
management_to_external_config_map_[type].install_urls.erase(install_url);
if (management_to_external_config_map_[type].install_urls.empty()) {
management_to_external_config_map_.erase(type);
}
return removed;
}
void WebApp::SetAlwaysShowToolbarInFullscreen(bool show) {
always_show_toolbar_in_fullscreen_ = show;
}
void WebApp::SetLatestInstallTime(const base::Time& latest_install_time) {
latest_install_time_ = latest_install_time;
}
WebApp::ClientData::ClientData() = default;
WebApp::ClientData::~ClientData() = default;
WebApp::ClientData::ClientData(const ClientData& client_data) = default;
base::Value WebApp::ClientData::AsDebugValue() const {
base::Value::Dict root;
#if BUILDFLAG(IS_CHROMEOS_ASH)
root.Set("system_web_app_data", OptionalAsDebugValue(system_web_app_data));
#endif
return base::Value(std::move(root));
}
WebApp::SyncFallbackData::SyncFallbackData() = default;
WebApp::SyncFallbackData::~SyncFallbackData() = default;
WebApp::SyncFallbackData::SyncFallbackData(
const SyncFallbackData& sync_fallback_data) = default;
WebApp::SyncFallbackData::SyncFallbackData(
SyncFallbackData&& sync_fallback_data) noexcept = default;
WebApp::SyncFallbackData& WebApp::SyncFallbackData::operator=(
SyncFallbackData&& sync_fallback_data) = default;
base::Value WebApp::SyncFallbackData::AsDebugValue() const {
base::Value::Dict root;
root.Set("name", name);
root.Set("theme_color", ColorToString(theme_color));
root.Set("scope", scope.spec());
base::Value::List manifest_icons_json;
for (const apps::IconInfo& icon_info : icon_infos)
manifest_icons_json.Append(icon_info.AsDebugValue());
root.Set("manifest_icons", std::move(manifest_icons_json));
return base::Value(std::move(root));
}
WebApp::ExternalManagementConfig::ExternalManagementConfig() = default;
WebApp::ExternalManagementConfig::ExternalManagementConfig(
bool is_placeholder,
const base::flat_set<GURL>& install_urls,
const base::flat_set<std::string>& additional_policy_ids)
: is_placeholder(is_placeholder),
install_urls(install_urls),
additional_policy_ids(additional_policy_ids) {}
WebApp::ExternalManagementConfig::~ExternalManagementConfig() = default;
WebApp::ExternalManagementConfig::ExternalManagementConfig(
const ExternalManagementConfig& external_management_config) = default;
WebApp::ExternalManagementConfig& WebApp::ExternalManagementConfig::operator=(
ExternalManagementConfig&& external_management_config) = default;
base::Value::Dict WebApp::ExternalManagementConfig::AsDebugValue() const {
base::Value::Dict root;
base::Value::List urls;
for (const auto& install_url : install_urls) {
urls.Append(install_url.spec());
}
base::Value::List policy_ids;
for (const auto& policy_id : additional_policy_ids) {
policy_ids.Append(policy_id);
}
root.Set("install_urls", std::move(urls));
root.Set("additional_policy_ids", std::move(policy_ids));
root.Set("is_placeholder", is_placeholder);
return root;
}
WebApp::IsolationData::IsolationData(IsolatedWebAppLocation location,
base::Version version)
: location(location), version(std::move(version)) {}
WebApp::IsolationData::IsolationData(
IsolatedWebAppLocation location,
base::Version version,
const std::set<std::string>& controlled_frame_partitions,
const absl::optional<PendingUpdateInfo>& pending_update_info)
: location(location),
version(std::move(version)),
controlled_frame_partitions(controlled_frame_partitions) {
SetPendingUpdateInfo(pending_update_info);
}
WebApp::IsolationData::~IsolationData() = default;
WebApp::IsolationData::IsolationData(const WebApp::IsolationData&) = default;
WebApp::IsolationData& WebApp::IsolationData::operator=(
const WebApp::IsolationData&) = default;
WebApp::IsolationData::IsolationData(WebApp::IsolationData&&) = default;
WebApp::IsolationData& WebApp::IsolationData::operator=(
WebApp::IsolationData&&) = default;
bool WebApp::IsolationData::operator==(
const WebApp::IsolationData& other) const {
return location == other.location && version == other.version &&
controlled_frame_partitions == other.controlled_frame_partitions &&
pending_update_info_ == other.pending_update_info_;
}
bool WebApp::IsolationData::operator!=(
const WebApp::IsolationData& other) const {
return !(*this == other);
}
base::Value WebApp::IsolationData::AsDebugValue() const {
auto value = base::Value::Dict()
.Set("isolated_web_app_location",
IsolatedWebAppLocationAsDebugValue(location))
.Set("version", version.GetString());
base::Value::List* partitions =
value.EnsureList("controlled_frame_partitions (on-disk)");
for (const std::string& partition : controlled_frame_partitions) {
partitions->Append(partition);
}
value.Set("pending_update_info", OptionalAsDebugValue(pending_update_info_));
return base::Value(std::move(value));
}
WebApp::IsolationData::PendingUpdateInfo::PendingUpdateInfo(
IsolatedWebAppLocation location,
base::Version version)
: location(std::move(location)), version(std::move(version)) {}
WebApp::IsolationData::PendingUpdateInfo::~PendingUpdateInfo() = default;
WebApp::IsolationData::PendingUpdateInfo::PendingUpdateInfo(
const PendingUpdateInfo&) = default;
WebApp::IsolationData::PendingUpdateInfo&
WebApp::IsolationData::PendingUpdateInfo::operator=(const PendingUpdateInfo&) =
default;
base::Value WebApp::IsolationData::PendingUpdateInfo::AsDebugValue() const {
auto value = base::Value::Dict()
.Set("isolated_web_app_location",
IsolatedWebAppLocationAsDebugValue(location))
.Set("version", version.GetString());
return base::Value(std::move(value));
}
void WebApp::IsolationData::SetPendingUpdateInfo(
const absl::optional<PendingUpdateInfo>& pending_update_info) {
if (pending_update_info.has_value()) {
CHECK_EQ(pending_update_info->location.index(), location.index());
}
pending_update_info_ = pending_update_info;
}
bool WebApp::IsolationData::PendingUpdateInfo::operator==(
const WebApp::IsolationData::PendingUpdateInfo& other) const = default;
bool WebApp::IsolationData::PendingUpdateInfo::operator!=(
const WebApp::IsolationData::PendingUpdateInfo& other) const = default;
bool WebApp::operator==(const WebApp& other) const {
auto AsTuple = [](const WebApp& app) {
// Keep in order declared in web_app.h.
return std::tie(
// Disable clang-format so diffs are clearer when fields are added.
// clang-format off
app.app_id_,
app.sources_,
app.name_,
app.description_,
app.start_url_,
app.launch_query_params_,
app.scope_,
app.theme_color_,
app.dark_mode_theme_color_,
app.background_color_,
app.dark_mode_background_color_,
app.display_mode_,
app.user_display_mode_,
app.display_mode_override_,
app.user_page_ordinal_,
app.user_launch_ordinal_,
app.chromeos_data_,
app.is_locally_installed_,
app.is_from_sync_and_pending_installation_,
app.is_uninstalling_,
app.manifest_icons_,
app.downloaded_icon_sizes_any_,
app.downloaded_icon_sizes_monochrome_,
app.downloaded_icon_sizes_maskable_,
app.is_generated_icon_,
app.shortcuts_menu_item_infos_,
app.file_handlers_,
app.share_target_,
app.additional_search_terms_,
app.protocol_handlers_,
app.allowed_launch_protocols_,
app.disallowed_launch_protocols_,
app.url_handlers_,
app.scope_extensions_,
app.validated_scope_extensions_,
app.lock_screen_start_url_,
app.note_taking_new_note_url_,
app.last_badging_time_,
app.last_launch_time_,
app.first_install_time_,
app.manifest_update_time_,
app.run_on_os_login_mode_,
app.run_on_os_login_os_integration_state_,
app.sync_fallback_data_,
app.capture_links_,
app.manifest_url_,
app.manifest_id_,
#if BUILDFLAG(IS_CHROMEOS_ASH)
app.client_data_.system_web_app_data,
#endif
app.file_handler_approval_state_,
app.file_handler_os_integration_state_,
app.window_controls_overlay_enabled_,
app.launch_handler_,
app.parent_app_id_,
app.permissions_policy_,
app.latest_install_source_,
app.app_size_in_bytes_,
app.data_size_in_bytes_,
app.management_to_external_config_map_,
app.tab_strip_,
app.always_show_toolbar_in_fullscreen_,
app.current_os_integration_states_,
app.isolation_data_,
app.is_user_selected_app_for_capturing_links_,
app.latest_install_time_
// clang-format on
);
};
return AsTuple(*this) == AsTuple(other);
}
bool WebApp::operator!=(const WebApp& other) const {
return !(*this == other);
}
base::Value WebApp::AsDebugValueWithOnlyPlatformAgnosticFields() const {
base::Value::Dict root;
auto ConvertList = [](const auto& list) {
base::Value::List list_json;
for (const auto& item : list)
list_json.Append(item);
return list_json;
};
auto ConvertDebugValueList = [](const auto& list) {
base::Value::List list_json;
for (const auto& item : list)
list_json.Append(item.AsDebugValue());
return list_json;
};
auto ConvertOptional = [](const auto& value) {
return value ? base::Value(*value) : base::Value();
};
// Prefix with a ! so these fields appear at the top when serialized.
root.Set("!app_id", app_id_);
root.Set("!name", name_);
root.Set("additional_search_terms", ConvertList(additional_search_terms_));
root.Set("app_service_icon_url",
base::StrCat({"chrome://app-icon/", app_id_, "/32"}));
root.Set("app_size_in_bytes", OptionalToStringValue(app_size_in_bytes_));
root.Set("allowed_launch_protocols", ConvertList(allowed_launch_protocols_));
root.Set("background_color", ColorToString(background_color_));
root.Set("capture_links", base::ToString(capture_links_));
root.Set("data_size_in_bytes", OptionalToStringValue(data_size_in_bytes_));
root.Set("dark_mode_background_color",
ColorToString(dark_mode_background_color_));
root.Set("dark_mode_theme_color", ColorToString(dark_mode_theme_color_));
root.Set("disallowed_launch_protocols",
ConvertList(disallowed_launch_protocols_));
root.Set("description", description_);
root.Set("display_mode", blink::DisplayModeToString(display_mode_));
base::Value::List display_override;
for (const DisplayMode& mode : display_mode_override_)
display_override.Append(blink::DisplayModeToString(mode));
root.Set("display_override", std::move(display_override));
base::Value::Dict downloaded_icon_sizes_json;
for (IconPurpose purpose : kIconPurposes) {
downloaded_icon_sizes_json.Set(base::ToString(purpose),
ConvertList(downloaded_icon_sizes(purpose)));
}
root.Set("downloaded_icon_sizes", std::move(downloaded_icon_sizes_json));
root.Set("file_handler_approval_state",
ApiApprovalStateToString(file_handler_approval_state_));
root.Set("file_handler_os_integration_state",
OsIntegrationStateToString(file_handler_os_integration_state_));
root.Set("file_handlers", ConvertDebugValueList(file_handlers_));
root.Set("manifest_icons", ConvertDebugValueList(manifest_icons_));
root.Set("latest_install_source",
OptionalToStringValue(latest_install_source_));
base::Value::Dict external_map;
for (auto it : management_to_external_config_map_) {
external_map.Set(base::ToString(it.first), it.second.AsDebugValue());
}
root.Set("management_type_to_external_configuration_map",
std::move(external_map));
root.Set("first_install_time", base::ToString(first_install_time_));
root.Set("is_generated_icon", is_generated_icon_);
root.Set("is_from_sync_and_pending_installation",
is_from_sync_and_pending_installation_);
root.Set("is_locally_installed", is_locally_installed_);
root.Set("is_uninstalling", is_uninstalling_);
root.Set("last_badging_time", base::ToString(last_badging_time_));
root.Set("last_launch_time", base::ToString(last_launch_time_));
if (launch_handler_) {
base::Value::Dict launch_handler_json;
launch_handler_json.Set("client_mode",
base::ToString(launch_handler_->client_mode));
root.Set("launch_handler", std::move(launch_handler_json));
} else {
root.Set("launch_handler", base::Value());
}
root.Set("launch_query_params", ConvertOptional(launch_query_params_));
root.Set("manifest_update_time", base::ToString(manifest_update_time_));
root.Set("manifest_url", base::ToString(manifest_url_));
root.Set("lock_screen_start_url", base::ToString(lock_screen_start_url_));
root.Set("note_taking_new_note_url",
base::ToString(note_taking_new_note_url_));
root.Set("parent_app_id", OptionalToStringValue(parent_app_id_));
if (!permissions_policy_.empty()) {
base::Value::List policy_list;
const auto& feature_to_name_map =
blink::GetPermissionsPolicyFeatureToNameMap();
for (const auto& decl : permissions_policy_) {
base::Value::Dict json_decl;
const auto& feature_name = feature_to_name_map.find(decl.feature);
if (feature_name == feature_to_name_map.end()) {
continue;
}
json_decl.Set("feature", feature_name->second);
base::Value::List allowlist_json;
for (const auto& allowlist_item : GetSerializedAllowedOrigins(decl)) {
allowlist_json.Append(allowlist_item);
}
json_decl.Set("allowed_origins", std::move(allowlist_json));
json_decl.Set("matches_all_origins", decl.matches_all_origins);
json_decl.Set("matches_opaque_src", decl.matches_opaque_src);
policy_list.Append(std::move(json_decl));
}
root.Set("permissions_policy", std::move(policy_list));
}
root.Set("protocol_handlers", ConvertDebugValueList(protocol_handlers_));
root.Set("run_on_os_login_mode", base::ToString(run_on_os_login_mode_));
root.Set("run_on_os_login_os_integration_state",
OptionalToStringValue(run_on_os_login_os_integration_state_));
root.Set("scope", base::ToString(scope_));
root.Set("share_target", OptionalAsDebugValue(share_target_));
root.Set("shortcuts_menu_item_infos",
ConvertDebugValueList(shortcuts_menu_item_infos_));
base::Value::List sources;
for (WebAppManagement::Type source : WebAppManagementTypes::All()) {
if (sources_.Has(source)) {
sources.Append(base::ToString(source));
}
}
root.Set("sources", std::move(sources));
root.Set("start_url", base::ToString(start_url_));
root.Set("sync_fallback_data", sync_fallback_data_.AsDebugValue());
root.Set("theme_color", ColorToString(theme_color_));
root.Set("manifest_id", manifest_id_.spec());
root.Set("url_handlers", ConvertDebugValueList(url_handlers_));
root.Set("scope_extensions", ConvertDebugValueList(scope_extensions_));
root.Set("scope_extensions_validated",
ConvertDebugValueList(validated_scope_extensions_));
root.Set("user_display_mode", OptionalToStringValue(user_display_mode_));
root.Set("user_launch_ordinal", user_launch_ordinal_.ToDebugString());
root.Set("user_page_ordinal", user_page_ordinal_.ToDebugString());
root.Set("window_controls_overlay_enabled", window_controls_overlay_enabled_);
root.Set("tab_strip", OptTabStripToDebugValue(tab_strip_));
root.Set("always_show_toolbar_in_fullscreen",
always_show_toolbar_in_fullscreen_);
root.Set("current_os_integration_states",
OsStatesDebugValue(current_os_integration_states_));
root.Set("isolation_data", OptionalAsDebugValue(isolation_data_));
root.Set("is_user_selected_app_for_capturing_links",
is_user_selected_app_for_capturing_links_);
root.Set("latest_install_time", base::ToString(latest_install_time_));
return base::Value(std::move(root));
}
base::Value WebApp::AsDebugValue() const {
base::Value value = AsDebugValueWithOnlyPlatformAgnosticFields();
auto& root = value.GetDict();
root.Set("chromeos_data", OptionalAsDebugValue(chromeos_data_));
root.Set("client_data", client_data_.AsDebugValue());
return value;
}
std::ostream& operator<<(std::ostream& out, const WebApp& app) {
return out << app.AsDebugValue();
}
bool operator==(const WebApp::SyncFallbackData& sync_fallback_data1,
const WebApp::SyncFallbackData& sync_fallback_data2) {
return std::tie(sync_fallback_data1.name, sync_fallback_data1.theme_color,
sync_fallback_data1.scope, sync_fallback_data1.icon_infos) ==
std::tie(sync_fallback_data2.name, sync_fallback_data2.theme_color,
sync_fallback_data2.scope, sync_fallback_data2.icon_infos);
}
bool operator!=(const WebApp::SyncFallbackData& sync_fallback_data1,
const WebApp::SyncFallbackData& sync_fallback_data2) {
return !(sync_fallback_data1 == sync_fallback_data2);
}
std::ostream& operator<<(
std::ostream& out,
const WebApp::ExternalManagementConfig& management_config) {
return out << management_config.AsDebugValue().DebugString();
}
bool operator==(const WebApp::ExternalManagementConfig& management_config1,
const WebApp::ExternalManagementConfig& management_config2) {
return std::tie(management_config1.install_urls,
management_config1.is_placeholder,
management_config1.additional_policy_ids) ==
std::tie(management_config2.install_urls,
management_config2.is_placeholder,
management_config2.additional_policy_ids);
}
bool operator!=(const WebApp::ExternalManagementConfig& management_config1,
const WebApp::ExternalManagementConfig& management_config2) {
return !(management_config1 == management_config2);
}
namespace proto {
bool operator==(const WebAppOsIntegrationState& os_integration_state1,
const WebAppOsIntegrationState& os_integration_state2) {
return os_integration_state1.SerializeAsString() ==
os_integration_state2.SerializeAsString();
}
bool operator!=(const WebAppOsIntegrationState& os_integration_state1,
const WebAppOsIntegrationState& os_integration_state2) {
return !(os_integration_state1 == os_integration_state2);
}
} // namespace proto
std::vector<std::string> GetSerializedAllowedOrigins(
const blink::ParsedPermissionsPolicyDeclaration
permissions_policy_declaration) {
std::vector<std::string> allowed_origins;
if (permissions_policy_declaration.self_if_matches) {
CHECK(!permissions_policy_declaration.self_if_matches->opaque());
allowed_origins.push_back(
permissions_policy_declaration.self_if_matches->Serialize());
}
for (const auto& origin_with_possible_wildcards :
permissions_policy_declaration.allowed_origins) {
allowed_origins.push_back(origin_with_possible_wildcards.Serialize());
}
return allowed_origins;
}
} // namespace web_app