blob: 378694d1c9e3a9702199d248f9e5120902e72a8f [file] [log] [blame]
// Copyright 2013 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_install_info.h"
#include <string_view>
#include <tuple>
#include <type_traits>
#include <utility>
#include "base/check.h"
#include "base/check_is_test.h"
#include "base/containers/flat_tree.h"
#include "base/strings/to_string.h"
#include "base/strings/utf_string_conversions.h"
#include "base/trace_event/trace_event.h"
#include "chrome/browser/web_applications/web_app_helpers.h"
#include "third_party/blink/public/common/manifest/manifest.h"
#include "third_party/blink/public/mojom/manifest/manifest.mojom.h"
#include "ui/gfx/skia_util.h"
#include "url/origin.h"
// This definition needs to be in the top-level namespace to be picked up by
// IconBitmaps::operator==().
static bool operator==(const SkBitmap& a, const SkBitmap& b) {
return gfx::BitmapsAreEqual(a, b);
}
namespace web_app {
apps::IconInfo::Purpose ManifestPurposeToIconInfoPurpose(
IconPurpose manifest_purpose) {
switch (manifest_purpose) {
case IconPurpose::ANY:
return apps::IconInfo::Purpose::kAny;
case IconPurpose::MONOCHROME:
return apps::IconInfo::Purpose::kMonochrome;
case IconPurpose::MASKABLE:
return apps::IconInfo::Purpose::kMaskable;
}
}
// IconBitmaps
IconBitmaps::IconBitmaps() = default;
IconBitmaps::~IconBitmaps() = default;
IconBitmaps::IconBitmaps(const IconBitmaps&) = default;
IconBitmaps::IconBitmaps(IconBitmaps&&) noexcept = default;
IconBitmaps& IconBitmaps::operator=(const IconBitmaps&) = default;
IconBitmaps& IconBitmaps::operator=(IconBitmaps&&) noexcept = default;
bool IconBitmaps::operator==(const IconBitmaps& other) const {
auto AsTuple = [](const IconBitmaps& icon_bitmaps) {
return std::make_tuple(icon_bitmaps.any, icon_bitmaps.maskable,
icon_bitmaps.monochrome);
};
return AsTuple(*this) == AsTuple(other);
}
const std::map<SquareSizePx, SkBitmap>& IconBitmaps::GetBitmapsForPurpose(
IconPurpose purpose) const {
switch (purpose) {
case IconPurpose::MONOCHROME:
return monochrome;
case IconPurpose::ANY:
return any;
case IconPurpose::MASKABLE:
return maskable;
}
}
void IconBitmaps::SetBitmapsForPurpose(
IconPurpose purpose,
std::map<SquareSizePx, SkBitmap> bitmaps) {
switch (purpose) {
case IconPurpose::ANY:
any = std::move(bitmaps);
return;
case IconPurpose::MONOCHROME:
monochrome = std::move(bitmaps);
return;
case IconPurpose::MASKABLE:
maskable = std::move(bitmaps);
return;
}
}
bool IconBitmaps::empty() const {
return any.empty() && maskable.empty() && monochrome.empty();
}
// IconSizes
IconSizes::IconSizes() = default;
IconSizes::~IconSizes() = default;
IconSizes::IconSizes(const IconSizes&) = default;
IconSizes::IconSizes(IconSizes&&) noexcept = default;
IconSizes& IconSizes::operator=(const IconSizes&) = default;
IconSizes& IconSizes::operator=(IconSizes&&) noexcept = default;
base::Value IconSizes::AsDebugValue() const {
auto ConvertList = [](const auto& list) {
base::Value::List list_json;
for (const auto& item : list) {
list_json.Append(item);
}
return list_json;
};
base::Value::Dict root;
for (IconPurpose purpose : kIconPurposes) {
root.Set(base::ToString(purpose), ConvertList(GetSizesForPurpose(purpose)));
}
return base::Value(std::move(root));
}
const std::vector<SquareSizePx>& IconSizes::GetSizesForPurpose(
IconPurpose purpose) const {
switch (purpose) {
case IconPurpose::MONOCHROME:
return monochrome;
case IconPurpose::ANY:
return any;
case IconPurpose::MASKABLE:
return maskable;
}
}
void IconSizes::SetSizesForPurpose(IconPurpose purpose,
std::vector<SquareSizePx> sizes) {
switch (purpose) {
case IconPurpose::ANY:
any = std::move(sizes);
return;
case IconPurpose::MONOCHROME:
monochrome = std::move(sizes);
return;
case IconPurpose::MASKABLE:
maskable = std::move(sizes);
return;
}
}
bool IconSizes::empty() const {
return any.empty() && maskable.empty() && monochrome.empty();
}
// WebAppShortcutsMenuItemInfo::Icon
WebAppShortcutsMenuItemInfo::Icon::Icon() = default;
WebAppShortcutsMenuItemInfo::Icon::Icon(
const WebAppShortcutsMenuItemInfo::Icon&) = default;
WebAppShortcutsMenuItemInfo::Icon::Icon(
WebAppShortcutsMenuItemInfo::Icon&&) noexcept = default;
WebAppShortcutsMenuItemInfo::Icon::~Icon() = default;
WebAppShortcutsMenuItemInfo::Icon& WebAppShortcutsMenuItemInfo::Icon::operator=(
const WebAppShortcutsMenuItemInfo::Icon&) = default;
WebAppShortcutsMenuItemInfo::Icon& WebAppShortcutsMenuItemInfo::Icon::operator=(
WebAppShortcutsMenuItemInfo::Icon&&) = default;
base::Value WebAppShortcutsMenuItemInfo::Icon::AsDebugValue() const {
base::Value::Dict root;
root.Set("url", url.spec());
root.Set("square_size_px", square_size_px);
return base::Value(std::move(root));
}
// WebAppShortcutsMenuItemInfo
WebAppShortcutsMenuItemInfo::WebAppShortcutsMenuItemInfo() = default;
WebAppShortcutsMenuItemInfo::WebAppShortcutsMenuItemInfo(
const WebAppShortcutsMenuItemInfo& other) = default;
WebAppShortcutsMenuItemInfo::WebAppShortcutsMenuItemInfo(
WebAppShortcutsMenuItemInfo&&) noexcept = default;
WebAppShortcutsMenuItemInfo::~WebAppShortcutsMenuItemInfo() = default;
WebAppShortcutsMenuItemInfo& WebAppShortcutsMenuItemInfo::operator=(
const WebAppShortcutsMenuItemInfo&) = default;
WebAppShortcutsMenuItemInfo& WebAppShortcutsMenuItemInfo::operator=(
WebAppShortcutsMenuItemInfo&&) noexcept = default;
const std::vector<WebAppShortcutsMenuItemInfo::Icon>&
WebAppShortcutsMenuItemInfo::GetShortcutIconInfosForPurpose(
IconPurpose purpose) const {
switch (purpose) {
case IconPurpose::MONOCHROME:
return monochrome;
case IconPurpose::ANY:
return any;
case IconPurpose::MASKABLE:
return maskable;
}
}
void WebAppShortcutsMenuItemInfo::SetShortcutIconInfosForPurpose(
IconPurpose purpose,
std::vector<WebAppShortcutsMenuItemInfo::Icon> shortcut_manifest_icons) {
switch (purpose) {
case IconPurpose::ANY:
any = std::move(shortcut_manifest_icons);
return;
case IconPurpose::MONOCHROME:
monochrome = std::move(shortcut_manifest_icons);
return;
case IconPurpose::MASKABLE:
maskable = std::move(shortcut_manifest_icons);
return;
}
}
base::Value WebAppShortcutsMenuItemInfo::AsDebugValue() const {
TRACE_EVENT0("ui", "WebAppShortcutsMenuItemInfo::AsDebugValue");
base::Value::Dict root;
root.Set("name", name);
root.Set("url", url.spec());
base::Value::Dict icons;
for (IconPurpose purpose : kIconPurposes) {
base::Value::List purpose_list;
for (const WebAppShortcutsMenuItemInfo::Icon& icon :
GetShortcutIconInfosForPurpose(purpose)) {
purpose_list.Append(icon.AsDebugValue());
}
icons.Set(base::ToString(purpose), std::move(purpose_list));
}
root.Set("icons", std::move(icons));
root.Set("downloaded_icons_sizes", downloaded_icon_sizes.AsDebugValue());
return base::Value(std::move(root));
}
// IconsWithSizeAny
IconsWithSizeAny::IconsWithSizeAny() = default;
IconsWithSizeAny::~IconsWithSizeAny() = default;
IconsWithSizeAny::IconsWithSizeAny(
const IconsWithSizeAny& icons_with_size_any) = default;
IconsWithSizeAny& IconsWithSizeAny::operator=(
const IconsWithSizeAny& icons_with_size_any) = default;
bool IconsWithSizeAny::operator==(
const IconsWithSizeAny& icons_with_size_any) const = default;
base::Value IconsWithSizeAny::ToDebugValue() const {
base::Value::Dict icons;
base::Value::Dict manifest;
for (const auto& icon : manifest_icons) {
manifest.Set(base::ToString(icon.first), icon.second.spec());
}
icons.Set("manifest_icons", base::Value(std::move(manifest)));
base::Value::List manifest_sizes;
for (const auto& size : manifest_icon_provided_sizes) {
manifest_sizes.Append(size.ToString());
}
icons.Set("manifest_provided_sizes", base::Value(std::move(manifest_sizes)));
base::Value::Dict shortcut_icon;
for (const auto& shicon : shortcut_menu_icons) {
shortcut_icon.Set(base::ToString(shicon.first), shicon.second.spec());
}
icons.Set("shortcut_icons", base::Value(std::move(shortcut_icon)));
base::Value::List shortcut_sizes;
for (const auto& size : shortcut_menu_icons_provided_sizes) {
shortcut_sizes.Append(size.ToString());
}
icons.Set("shortcut_menu_icons_provided_sizes",
base::Value(std::move(shortcut_sizes)));
base::Value::Dict file_handlers;
for (const auto& fhicon : file_handling_icons) {
file_handlers.Set(base::ToString(fhicon.first), fhicon.second.spec());
}
icons.Set("file_handling_icons", base::Value(std::move(file_handlers)));
base::Value::List file_handling_sizes;
for (const auto& size : file_handling_icon_provided_sizes) {
file_handling_sizes.Append(size.ToString());
}
icons.Set("file_handling_icons_manifest_provided_sizes",
base::Value(std::move(file_handling_sizes)));
base::Value::Dict tab_icons;
for (const auto& thicon : home_tab_icons) {
tab_icons.Set(base::ToString(thicon.first), thicon.second.spec());
}
icons.Set("home_tab_icons", base::Value(std::move(tab_icons)));
base::Value::List home_tab_sizes;
for (const auto& size : home_tab_icon_provided_sizes) {
home_tab_sizes.Append(size.ToString());
}
icons.Set("home_tab_icons_manifest_provided_sizes",
base::Value(std::move(home_tab_sizes)));
return base::Value(std::move(icons));
}
std::string IconsWithSizeAny::ToString() const {
return ToDebugValue().DebugString();
}
// WebAppInstallInfo
// static
std::unique_ptr<WebAppInstallInfo>
WebAppInstallInfo::CreateWithStartUrlForTesting(const GURL& start_url) {
CHECK_IS_TEST();
auto info = std::make_unique<WebAppInstallInfo>(
GenerateManifestIdFromStartUrlOnly(start_url), start_url);
info->scope = start_url.GetWithoutFilename();
CHECK(!info->scope.is_empty());
return info;
}
// static
std::unique_ptr<WebAppInstallInfo> WebAppInstallInfo::CreateForTesting(
const GURL& start_url,
blink::mojom::DisplayMode display,
mojom::UserDisplayMode user_mode,
std::optional<blink::mojom::ManifestLaunchHandler_ClientMode> client_mode) {
CHECK_IS_TEST();
auto info = WebAppInstallInfo::CreateWithStartUrlForTesting(start_url);
info->title = base::ASCIIToUTF16(start_url.PathForRequest());
info->display_mode = display;
info->user_display_mode = user_mode;
info->launch_handler = blink::Manifest::LaunchHandler(client_mode);
CHECK_EQ(info->launch_handler->client_mode_valid_and_specified(),
client_mode.has_value());
CHECK(!info->scope.is_empty());
return info;
}
// static
base::expected<WebAppInstallInfo, std::string> WebAppInstallInfo::Create(
const GURL& manifest_url,
const webapps::ManifestId& manifest_id,
const GURL& start_url) {
if (!manifest_id.is_valid()) {
return base::unexpected(
"Manifest `id` is not present or invalid. manifest_url: " +
manifest_url.possibly_invalid_spec());
}
if (!start_url.is_valid()) {
return base::unexpected(
"Manifest `start_url` is not present or invalid. manifest_url: " +
manifest_url.possibly_invalid_spec());
}
if (!url::Origin::Create(start_url).IsSameOriginWith(
url::Origin::Create(manifest_id))) {
return base::unexpected(
"Manifest `id` and `start_url` must have the same origin. "
"manifest_url: " +
manifest_url.possibly_invalid_spec());
}
WebAppInstallInfo info(manifest_id, start_url);
info.scope = start_url.GetWithoutFilename();
CHECK(!info.scope.is_empty());
return info;
}
namespace {
void CheckValidManifestIdAndStartUrl(const webapps::ManifestId& manifest_id,
const GURL& start_url) {
CHECK(manifest_id.is_valid());
CHECK(!manifest_id.has_ref());
CHECK(start_url.is_valid());
CHECK(url::Origin::Create(start_url).IsSameOriginWith(
url::Origin::Create(manifest_id)));
}
} // namespace
WebAppInstallInfo::WebAppInstallInfo(const webapps::ManifestId& manifest_id,
const GURL& start_url)
: manifest_id_(manifest_id), start_url_(start_url) {
CheckValidManifestIdAndStartUrl(manifest_id_, start_url_);
}
WebAppInstallInfo::WebAppInstallInfo(const WebAppInstallInfo& other) = default;
WebAppInstallInfo::WebAppInstallInfo(WebAppInstallInfo&&) = default;
WebAppInstallInfo& WebAppInstallInfo::operator=(WebAppInstallInfo&&) = default;
WebAppInstallInfo::~WebAppInstallInfo() = default;
WebAppInstallInfo WebAppInstallInfo::Clone() const {
return WebAppInstallInfo(*this);
}
void WebAppInstallInfo::SetManifestIdAndStartUrl(
const webapps::ManifestId& manifest_id,
const GURL& start_url) {
CheckValidManifestIdAndStartUrl(manifest_id, start_url);
manifest_id_ = manifest_id;
start_url_ = start_url;
}
bool operator==(const IconSizes& icon_sizes1, const IconSizes& icon_sizes2) {
return std::tie(icon_sizes1.any, icon_sizes1.maskable,
icon_sizes1.monochrome) == std::tie(icon_sizes2.any,
icon_sizes2.maskable,
icon_sizes2.monochrome);
}
bool operator==(const WebAppShortcutsMenuItemInfo::Icon& icon1,
const WebAppShortcutsMenuItemInfo::Icon& icon2) {
return std::tie(icon1.url, icon1.square_size_px) ==
std::tie(icon2.url, icon2.square_size_px);
}
bool operator==(const WebAppShortcutsMenuItemInfo& shortcut_info1,
const WebAppShortcutsMenuItemInfo& shortcut_info2) {
return std::tie(shortcut_info1.name, shortcut_info1.url, shortcut_info1.any,
shortcut_info1.maskable, shortcut_info1.monochrome) ==
std::tie(shortcut_info2.name, shortcut_info2.url, shortcut_info2.any,
shortcut_info2.maskable, shortcut_info2.monochrome);
}
} // namespace web_app