Commit 68e87810 authored by Alan Cutter's avatar Alan Cutter Committed by Chromium LUCI CQ

desktop-pwas: Store capture_links manifest field in web app installations

This CL enables parsing and storing the capture_links field in web app
manifests while the kWebAppEnableLinkCapturing feature flag is enabled.

This CL is implementing the parsing side of this spec:
https://github.com/WICG/sw-launch/blob/master/declarative_link_capturing.md#proposal
including the following pending pull request to that spec:
https://github.com/WICG/sw-launch/pull/26

This CL does not implement the link capturing behaviours, that will
come in follow up CLs.

Bug: 1163398
Change-Id: I803ad87ea219ebceed35a547695a517020918c46
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2623453Reviewed-by: default avatarKentaro Hara <haraken@chromium.org>
Reviewed-by: default avatarDominick Ng <dominickn@chromium.org>
Reviewed-by: default avatarMatt Giuca <mgiuca@chromium.org>
Reviewed-by: default avatarDaniel Murphy <dmurph@chromium.org>
Commit-Queue: Alan Cutter <alancutter@chromium.org>
Cr-Commit-Position: refs/heads/master@{#843335}
parent 11e4dc99
...@@ -285,6 +285,8 @@ void UpdateWebAppInfoFromManifest(const blink::Manifest& manifest, ...@@ -285,6 +285,8 @@ void UpdateWebAppInfoFromManifest(const blink::Manifest& manifest,
web_app_info->shortcuts_menu_item_infos = web_app_info->shortcuts_menu_item_infos =
UpdateShortcutsMenuItemInfosFromManifest(manifest.shortcuts); UpdateShortcutsMenuItemInfosFromManifest(manifest.shortcuts);
} }
web_app_info->capture_links = manifest.capture_links;
} }
std::vector<GURL> GetValidIconUrlsToDownload( std::vector<GURL> GetValidIconUrlsToDownload(
......
...@@ -189,6 +189,11 @@ struct WebApplicationInfo { ...@@ -189,6 +189,11 @@ struct WebApplicationInfo {
// User preference as to whether to auto run the app on OS login. // User preference as to whether to auto run the app on OS login.
// Currently only supported in Windows platform. // Currently only supported in Windows platform.
bool run_on_os_login = false; bool run_on_os_login = false;
// The link capturing behaviour to use for navigations into in the app's
// scope.
blink::mojom::CaptureLinks capture_links =
blink::mojom::CaptureLinks::kUndefined;
}; };
std::ostream& operator<<(std::ostream& out, std::ostream& operator<<(std::ostream& out,
......
...@@ -193,4 +193,15 @@ message WebAppProto { ...@@ -193,4 +193,15 @@ message WebAppProto {
repeated WebAppUrlHandlerProto url_handlers = 27; repeated WebAppUrlHandlerProto url_handlers = 27;
optional ClientDataProto client_data = 28; optional ClientDataProto client_data = 28;
// This enum should be synced with |ManifestCaptureLinks| in
// third_party/blink/public/mojom/manifest/manifest.mojom
enum CaptureLinks {
// UNDEFINED if optional |capture_links| is absent.
NONE = 1;
NEW_CLIENT = 2;
EXISTING_CLIENT_NAVIGATE = 3;
}
optional CaptureLinks capture_links = 29;
} }
...@@ -266,6 +266,10 @@ void WebApp::SetSyncFallbackData(SyncFallbackData sync_fallback_data) { ...@@ -266,6 +266,10 @@ void WebApp::SetSyncFallbackData(SyncFallbackData sync_fallback_data) {
sync_fallback_data_ = std::move(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( void WebApp::SetLaunchQueryParams(
base::Optional<std::string> launch_query_params) { base::Optional<std::string> launch_query_params) {
launch_query_params_ = std::move(launch_query_params); launch_query_params_ = std::move(launch_query_params);
...@@ -363,6 +367,7 @@ std::ostream& operator<<(std::ostream& out, const WebApp& app) { ...@@ -363,6 +367,7 @@ std::ostream& operator<<(std::ostream& out, const WebApp& app) {
} }
for (const apps::UrlHandlerInfo& url_handler : app.url_handlers_) for (const apps::UrlHandlerInfo& url_handler : app.url_handlers_)
out << " url_handler: " << url_handler << std::endl; out << " url_handler: " << url_handler << std::endl;
out << " capture_links: " << app.capture_links_ << std::endl;
out << " chromeos_data: " << app.chromeos_data_.has_value() << std::endl; out << " chromeos_data: " << app.chromeos_data_.has_value() << std::endl;
if (app.chromeos_data_.has_value()) if (app.chromeos_data_.has_value())
...@@ -429,6 +434,7 @@ bool WebApp::operator==(const WebApp& other) const { ...@@ -429,6 +434,7 @@ bool WebApp::operator==(const WebApp& other) const {
app.run_on_os_login_mode_, app.run_on_os_login_mode_,
app.sync_fallback_data_, app.sync_fallback_data_,
app.url_handlers_, app.url_handlers_,
app.capture_links_,
app.client_data_.system_web_app_data app.client_data_.system_web_app_data
// clang-format on // clang-format on
); );
......
...@@ -171,6 +171,8 @@ class WebApp { ...@@ -171,6 +171,8 @@ class WebApp {
return downloaded_shortcuts_menu_icons_sizes_; return downloaded_shortcuts_menu_icons_sizes_;
} }
blink::mojom::CaptureLinks capture_links() const { return capture_links_; }
// A Web App can be installed from multiple sources simultaneously. Installs // A Web App can be installed from multiple sources simultaneously. Installs
// add a source to the app. Uninstalls remove a source from the app. // add a source to the app. Uninstalls remove a source from the app.
void AddSource(Source::Type source); void AddSource(Source::Type source);
...@@ -223,6 +225,7 @@ class WebApp { ...@@ -223,6 +225,7 @@ class WebApp {
void SetInstallTime(const base::Time& time); void SetInstallTime(const base::Time& time);
void SetRunOnOsLoginMode(RunOnOsLoginMode mode); void SetRunOnOsLoginMode(RunOnOsLoginMode mode);
void SetSyncFallbackData(SyncFallbackData sync_fallback_data); void SetSyncFallbackData(SyncFallbackData sync_fallback_data);
void SetCaptureLinks(blink::mojom::CaptureLinks capture_links);
// For logging and debug purposes. // For logging and debug purposes.
bool operator==(const WebApp&) const; bool operator==(const WebApp&) const;
...@@ -274,6 +277,8 @@ class WebApp { ...@@ -274,6 +277,8 @@ class WebApp {
RunOnOsLoginMode run_on_os_login_mode_ = RunOnOsLoginMode::kUndefined; RunOnOsLoginMode run_on_os_login_mode_ = RunOnOsLoginMode::kUndefined;
SyncFallbackData sync_fallback_data_; SyncFallbackData sync_fallback_data_;
apps::UrlHandlers url_handlers_; apps::UrlHandlers url_handlers_;
blink::mojom::CaptureLinks capture_links_ =
blink::mojom::CaptureLinks::kUndefined;
ClientData client_data_; ClientData client_data_;
// New fields must be added to |operator==| and |operator<<|. // New fields must be added to |operator==| and |operator<<|.
}; };
......
...@@ -74,6 +74,33 @@ apps::ShareTarget::Enctype ProtoToEnctype(ShareTarget_Enctype enctype) { ...@@ -74,6 +74,33 @@ apps::ShareTarget::Enctype ProtoToEnctype(ShareTarget_Enctype enctype) {
} }
} }
blink::mojom::CaptureLinks ProtoToCaptureLinks(
WebAppProto::CaptureLinks capture_links) {
switch (capture_links) {
case WebAppProto_CaptureLinks_NONE:
return blink::mojom::CaptureLinks::kNone;
case WebAppProto_CaptureLinks_NEW_CLIENT:
return blink::mojom::CaptureLinks::kNewClient;
case WebAppProto_CaptureLinks_EXISTING_CLIENT_NAVIGATE:
return blink::mojom::CaptureLinks::kExistingClientNavigate;
}
}
WebAppProto::CaptureLinks CaptureLinksToProto(
blink::mojom::CaptureLinks capture_links) {
switch (capture_links) {
case blink::mojom::CaptureLinks::kUndefined:
NOTREACHED();
FALLTHROUGH;
case blink::mojom::CaptureLinks::kNone:
return WebAppProto_CaptureLinks_NONE;
case blink::mojom::CaptureLinks::kNewClient:
return WebAppProto_CaptureLinks_NEW_CLIENT;
case blink::mojom::CaptureLinks::kExistingClientNavigate:
return WebAppProto_CaptureLinks_EXISTING_CLIENT_NAVIGATE;
}
}
} // anonymous namespace } // anonymous namespace
WebAppDatabase::WebAppDatabase(AbstractWebAppDatabaseFactory* database_factory, WebAppDatabase::WebAppDatabase(AbstractWebAppDatabaseFactory* database_factory,
...@@ -316,6 +343,11 @@ std::unique_ptr<WebAppProto> WebAppDatabase::CreateWebAppProto( ...@@ -316,6 +343,11 @@ std::unique_ptr<WebAppProto> WebAppDatabase::CreateWebAppProto(
url_handler_proto->set_origin(url_handler.origin.Serialize()); url_handler_proto->set_origin(url_handler.origin.Serialize());
} }
if (web_app.capture_links() != blink::mojom::CaptureLinks::kUndefined)
local_data->set_capture_links(CaptureLinksToProto(web_app.capture_links()));
else
local_data->clear_capture_links();
return local_data; return local_data;
} }
...@@ -649,6 +681,11 @@ std::unique_ptr<WebApp> WebAppDatabase::CreateWebApp( ...@@ -649,6 +681,11 @@ std::unique_ptr<WebApp> WebAppDatabase::CreateWebApp(
} }
web_app->SetUrlHandlers(std::move(url_handlers)); web_app->SetUrlHandlers(std::move(url_handlers));
if (local_data.has_capture_links())
web_app->SetCaptureLinks(ProtoToCaptureLinks(local_data.capture_links()));
else
web_app->SetCaptureLinks(blink::mojom::CaptureLinks::kUndefined);
return web_app; return web_app;
} }
......
...@@ -163,6 +163,11 @@ class WebAppDatabaseTest : public WebAppTest { ...@@ -163,6 +163,11 @@ class WebAppDatabaseTest : public WebAppTest {
return url_handlers; return url_handlers;
} }
static blink::mojom::CaptureLinks CreateCaptureLinks(uint32_t suffix) {
return static_cast<blink::mojom::CaptureLinks>(
suffix % static_cast<uint32_t>(blink::mojom::CaptureLinks::kMaxValue));
}
static std::vector<WebApplicationShortcutsMenuItemInfo> static std::vector<WebApplicationShortcutsMenuItemInfo>
CreateShortcutsMenuItemInfos(const std::string& base_url, uint32_t suffix) { CreateShortcutsMenuItemInfos(const std::string& base_url, uint32_t suffix) {
std::vector<WebApplicationShortcutsMenuItemInfo> shortcuts_menu_item_infos; std::vector<WebApplicationShortcutsMenuItemInfo> shortcuts_menu_item_infos;
...@@ -302,6 +307,7 @@ class WebAppDatabaseTest : public WebAppTest { ...@@ -302,6 +307,7 @@ class WebAppDatabaseTest : public WebAppTest {
app->SetShareTarget(CreateShareTarget(random.next_uint())); app->SetShareTarget(CreateShareTarget(random.next_uint()));
app->SetProtocolHandlers(CreateProtocolHandlers(random.next_uint())); app->SetProtocolHandlers(CreateProtocolHandlers(random.next_uint()));
app->SetUrlHandlers(CreateUrlHandlers(random.next_uint())); app->SetUrlHandlers(CreateUrlHandlers(random.next_uint()));
app->SetCaptureLinks(CreateCaptureLinks(random.next_uint()));
const int num_additional_search_terms = random.next_uint(8); const int num_additional_search_terms = random.next_uint(8);
std::vector<std::string> additional_search_terms( std::vector<std::string> additional_search_terms(
...@@ -755,4 +761,19 @@ TEST_F(WebAppDatabaseTest, WebAppWithShareTargetRoundTrip) { ...@@ -755,4 +761,19 @@ TEST_F(WebAppDatabaseTest, WebAppWithShareTargetRoundTrip) {
EXPECT_TRUE(IsRegistryEqual(mutable_registrar().registry(), registry)); EXPECT_TRUE(IsRegistryEqual(mutable_registrar().registry(), registry));
} }
TEST_F(WebAppDatabaseTest, WebAppWithCaptureLinksRoundTrip) {
controller().Init();
const std::string base_url = "https://example.com/path";
auto app = CreateWebApp(base_url, 0);
auto app_id = app->app_id();
app->SetCaptureLinks(blink::mojom::CaptureLinks::kExistingClientNavigate);
controller().RegisterApp(std::move(app));
Registry registry = database_factory().ReadRegistry();
EXPECT_TRUE(IsRegistryEqual(mutable_registrar().registry(), registry));
}
} // namespace web_app } // namespace web_app
...@@ -150,6 +150,8 @@ void SetWebAppManifestFields(const WebApplicationInfo& web_app_info, ...@@ -150,6 +150,8 @@ void SetWebAppManifestFields(const WebApplicationInfo& web_app_info,
// default (windowed). // default (windowed).
web_app.SetRunOnOsLoginMode(RunOnOsLoginMode::kWindowed); web_app.SetRunOnOsLoginMode(RunOnOsLoginMode::kWindowed);
} }
web_app.SetCaptureLinks(web_app_info.capture_links);
} }
} // namespace web_app } // namespace web_app
...@@ -113,6 +113,9 @@ bool StructTraits<blink::mojom::ManifestDataView, ::blink::Manifest>::Read( ...@@ -113,6 +113,9 @@ bool StructTraits<blink::mojom::ManifestDataView, ::blink::Manifest>::Read(
if (!data.ReadScope(&out->scope)) if (!data.ReadScope(&out->scope))
return false; return false;
if (!data.ReadCaptureLinks(&out->capture_links))
return false;
return true; return true;
} }
......
...@@ -5,6 +5,7 @@ ...@@ -5,6 +5,7 @@
#include "third_party/blink/public/common/manifest/manifest_util.h" #include "third_party/blink/public/common/manifest/manifest_util.h"
#include "base/strings/string_util.h" #include "base/strings/string_util.h"
#include "third_party/blink/public/mojom/manifest/capture_links.mojom.h"
#include "third_party/blink/public/mojom/manifest/display_mode.mojom.h" #include "third_party/blink/public/mojom/manifest/display_mode.mojom.h"
namespace blink { namespace blink {
...@@ -98,4 +99,14 @@ device::mojom::ScreenOrientationLockType WebScreenOrientationLockTypeFromString( ...@@ -98,4 +99,14 @@ device::mojom::ScreenOrientationLockType WebScreenOrientationLockTypeFromString(
return device::mojom::ScreenOrientationLockType::DEFAULT; return device::mojom::ScreenOrientationLockType::DEFAULT;
} }
mojom::CaptureLinks CaptureLinksFromString(const std::string& capture_links) {
if (base::LowerCaseEqualsASCII(capture_links, "none"))
return mojom::CaptureLinks::kNone;
if (base::LowerCaseEqualsASCII(capture_links, "new-client"))
return mojom::CaptureLinks::kNewClient;
if (base::LowerCaseEqualsASCII(capture_links, "existing-client-navigate"))
return mojom::CaptureLinks::kExistingClientNavigate;
return mojom::CaptureLinks::kUndefined;
}
} // namespace blink } // namespace blink
...@@ -4,6 +4,7 @@ ...@@ -4,6 +4,7 @@
#include "third_party/blink/public/common/manifest/manifest_util.h" #include "third_party/blink/public/common/manifest/manifest_util.h"
#include "testing/gtest/include/gtest/gtest.h" #include "testing/gtest/include/gtest/gtest.h"
#include "third_party/blink/public/mojom/manifest/capture_links.mojom.h"
#include "third_party/blink/public/mojom/manifest/display_mode.mojom.h" #include "third_party/blink/public/mojom/manifest/display_mode.mojom.h"
#include "url/gurl.h" #include "url/gurl.h"
...@@ -79,4 +80,22 @@ TEST(ManifestUtilTest, WebScreenOrientationLockTypeConversions) { ...@@ -79,4 +80,22 @@ TEST(ManifestUtilTest, WebScreenOrientationLockTypeConversions) {
WebScreenOrientationLockTypeFromString("random")); WebScreenOrientationLockTypeFromString("random"));
} }
TEST(ManifestUtilTest, CaptureLinksFromString) {
EXPECT_EQ(blink::mojom::CaptureLinks::kUndefined, CaptureLinksFromString(""));
EXPECT_EQ(blink::mojom::CaptureLinks::kNone, CaptureLinksFromString("none"));
EXPECT_EQ(blink::mojom::CaptureLinks::kNewClient,
CaptureLinksFromString("new-client"));
EXPECT_EQ(blink::mojom::CaptureLinks::kExistingClientNavigate,
CaptureLinksFromString("existing-client-navigate"));
// CaptureLinksFromString() should work with non-lowercase strings.
EXPECT_EQ(blink::mojom::CaptureLinks::kNewClient,
CaptureLinksFromString("NEW-CLIENT"));
// CaptureLinksFromString() should return CaptureLinks::kUndefined if the
// string isn't known.
EXPECT_EQ(blink::mojom::CaptureLinks::kUndefined,
CaptureLinksFromString("unknown-value"));
}
} // namespace blink } // namespace blink
...@@ -237,6 +237,8 @@ struct BLINK_COMMON_EXPORT Manifest { ...@@ -237,6 +237,8 @@ struct BLINK_COMMON_EXPORT Manifest {
// document URL if start URL isn't present) with filename, query, and fragment // document URL if start URL isn't present) with filename, query, and fragment
// removed. // removed.
GURL scope; GURL scope;
mojom::CaptureLinks capture_links = mojom::CaptureLinks::kUndefined;
}; };
} // namespace blink } // namespace blink
......
...@@ -152,6 +152,11 @@ struct BLINK_COMMON_EXPORT ...@@ -152,6 +152,11 @@ struct BLINK_COMMON_EXPORT
return manifest.prefer_related_applications; return manifest.prefer_related_applications;
} }
static blink::mojom::CaptureLinks capture_links(
const ::blink::Manifest& manifest) {
return manifest.capture_links;
}
static bool Read(blink::mojom::ManifestDataView data, ::blink::Manifest* out); static bool Read(blink::mojom::ManifestDataView data, ::blink::Manifest* out);
}; };
......
...@@ -9,6 +9,7 @@ ...@@ -9,6 +9,7 @@
#include "services/device/public/mojom/screen_orientation_lock_types.mojom-shared.h" #include "services/device/public/mojom/screen_orientation_lock_types.mojom-shared.h"
#include "third_party/blink/public/common/common_export.h" #include "third_party/blink/public/common/common_export.h"
#include "third_party/blink/public/mojom/manifest/capture_links.mojom-forward.h"
#include "third_party/blink/public/mojom/manifest/display_mode.mojom-forward.h" #include "third_party/blink/public/mojom/manifest/display_mode.mojom-forward.h"
namespace blink { namespace blink {
...@@ -48,6 +49,9 @@ BLINK_COMMON_EXPORT std::string WebScreenOrientationLockTypeToString( ...@@ -48,6 +49,9 @@ BLINK_COMMON_EXPORT std::string WebScreenOrientationLockTypeToString(
BLINK_COMMON_EXPORT device::mojom::ScreenOrientationLockType BLINK_COMMON_EXPORT device::mojom::ScreenOrientationLockType
WebScreenOrientationLockTypeFromString(const std::string& orientation); WebScreenOrientationLockTypeFromString(const std::string& orientation);
BLINK_COMMON_EXPORT mojom::CaptureLinks CaptureLinksFromString(
const std::string& capture_links);
} // namespace blink } // namespace blink
#endif // THIRD_PARTY_BLINK_PUBLIC_COMMON_MANIFEST_MANIFEST_UTIL_H_ #endif // THIRD_PARTY_BLINK_PUBLIC_COMMON_MANIFEST_MANIFEST_UTIL_H_
...@@ -106,6 +106,7 @@ mojom("mojom_platform") { ...@@ -106,6 +106,7 @@ mojom("mojom_platform") {
"loader/transferrable_url_loader.mojom", "loader/transferrable_url_loader.mojom",
"loader/url_loader_factory_bundle.mojom", "loader/url_loader_factory_bundle.mojom",
"locks/lock_manager.mojom", "locks/lock_manager.mojom",
"manifest/capture_links.mojom",
"manifest/display_mode.mojom", "manifest/display_mode.mojom",
"manifest/manifest.mojom", "manifest/manifest.mojom",
"manifest/manifest_manager.mojom", "manifest/manifest_manager.mojom",
......
// Copyright 2021 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
module blink.mojom;
enum CaptureLinks {
// Used when unset or invalid.
kUndefined,
// Standard link capturing behaviours described by:
// https://github.com/WICG/sw-launch/blob/master/declarative_link_capturing.md#proposal
// No link capturing.
kNone,
// Navigations into app scope are captured into a new app tab/window.
kNewClient,
// Navigations into app scope navigate an existing app tab/window if one
// exists otherwise falls back to kNewClient.
kExistingClientNavigate,
};
...@@ -7,6 +7,7 @@ module blink.mojom; ...@@ -7,6 +7,7 @@ module blink.mojom;
import "services/device/public/mojom/screen_orientation_lock_types.mojom"; import "services/device/public/mojom/screen_orientation_lock_types.mojom";
import "mojo/public/mojom/base/string16.mojom"; import "mojo/public/mojom/base/string16.mojom";
import "third_party/blink/public/mojom/manifest/display_mode.mojom"; import "third_party/blink/public/mojom/manifest/display_mode.mojom";
import "third_party/blink/public/mojom/manifest/capture_links.mojom";
import "ui/gfx/geometry/mojom/geometry.mojom"; import "ui/gfx/geometry/mojom/geometry.mojom";
import "url/mojom/url.mojom"; import "url/mojom/url.mojom";
import "url/mojom/origin.mojom"; import "url/mojom/origin.mojom";
...@@ -44,7 +45,7 @@ struct Manifest { ...@@ -44,7 +45,7 @@ struct Manifest {
// TODO(crbug.com/829689): This field is non-standard and part of a Chrome // TODO(crbug.com/829689): This field is non-standard and part of a Chrome
// experiment. See: // experiment. See:
// https://github.com/WICG/file-handling/blob/master/explainer.md // https://github.com/WICG/file-handling/blob/master/explainer.md
// As such, this field should not be exposed to the drive-by web. // As such, this field should not be exposed by default.
array<ManifestFileHandler> file_handlers; array<ManifestFileHandler> file_handlers;
// TODO(crbug.com/1019239): This is going into the mainline manifest spec, // TODO(crbug.com/1019239): This is going into the mainline manifest spec,
...@@ -73,6 +74,12 @@ struct Manifest { ...@@ -73,6 +74,12 @@ struct Manifest {
mojo_base.mojom.String16? gcm_sender_id; mojo_base.mojom.String16? gcm_sender_id;
url.mojom.Url scope; url.mojom.Url scope;
// TODO(crbug.com/1163398): This field is non-standard and part of a Chrome
// experiment. See:
// https://github.com/WICG/sw-launch/blob/master/declarative_link_capturing.md#proposal
// As such, this field should not be exposed by default.
CaptureLinks capture_links;
}; };
// Structure representing a Shortcut Item per the Manifest specification, see: // Structure representing a Shortcut Item per the Manifest specification, see:
......
...@@ -117,6 +117,7 @@ void ManifestParser::Parse() { ...@@ -117,6 +117,7 @@ void ManifestParser::Parse() {
manifest_->gcm_sender_id = ParseGCMSenderID(root_object.get()); manifest_->gcm_sender_id = ParseGCMSenderID(root_object.get());
manifest_->shortcuts = ParseShortcuts(root_object.get()); manifest_->shortcuts = ParseShortcuts(root_object.get());
manifest_->capture_links = ParseCaptureLinks(root_object.get());
ManifestUmaUtil::ParseSucceeded(manifest_); ManifestUmaUtil::ParseSucceeded(manifest_);
} }
...@@ -1214,6 +1215,48 @@ String ManifestParser::ParseGCMSenderID(const JSONObject* object) { ...@@ -1214,6 +1215,48 @@ String ManifestParser::ParseGCMSenderID(const JSONObject* object) {
return gcm_sender_id.has_value() ? *gcm_sender_id : String(); return gcm_sender_id.has_value() ? *gcm_sender_id : String();
} }
mojom::blink::CaptureLinks ManifestParser::ParseCaptureLinks(
const JSONObject* object) {
if (!base::FeatureList::IsEnabled(features::kWebAppEnableLinkCapturing))
return mojom::blink::CaptureLinks::kUndefined;
String capture_links_string;
if (object->GetString("capture_links", &capture_links_string)) {
mojom::blink::CaptureLinks capture_links =
CaptureLinksFromString(capture_links_string.Utf8());
if (capture_links == mojom::blink::CaptureLinks::kUndefined) {
AddErrorInfo("capture_links value '" + capture_links_string +
"' ignored, unknown value.");
}
return capture_links;
}
if (JSONArray* list = object->GetArray("capture_links")) {
for (wtf_size_t i = 0; i < list->size(); ++i) {
const JSONValue* item = list->at(i);
if (!item->AsString(&capture_links_string)) {
AddErrorInfo("capture_links value '" + item->ToJSONString() +
"' ignored, string expected.");
continue;
}
mojom::blink::CaptureLinks capture_links =
CaptureLinksFromString(capture_links_string.Utf8());
if (capture_links != mojom::blink::CaptureLinks::kUndefined)
return capture_links;
AddErrorInfo("capture_links value '" + capture_links_string +
"' ignored, unknown value.");
}
return mojom::blink::CaptureLinks::kUndefined;
}
AddErrorInfo(
"property 'capture_links' ignored, type string or array of strings "
"expected.");
return mojom::blink::CaptureLinks::kUndefined;
}
void ManifestParser::AddErrorInfo(const String& error_msg, void ManifestParser::AddErrorInfo(const String& error_msg,
bool critical, bool critical,
int error_line, int error_line,
......
...@@ -387,6 +387,11 @@ class MODULES_EXPORT ManifestParser { ...@@ -387,6 +387,11 @@ class MODULES_EXPORT ManifestParser {
// Returns the parsed string if any, a null string if the parsing failed. // Returns the parsed string if any, a null string if the parsing failed.
String ParseGCMSenderID(const JSONObject* object); String ParseGCMSenderID(const JSONObject* object);
// Parses the 'capture_links' field of the manifest.
// This specifies how navigations into the web app's scope should be captured.
// https://github.com/WICG/sw-launch/blob/master/declarative_link_capturing.md#proposal
mojom::blink::CaptureLinks ParseCaptureLinks(const JSONObject* object);
void AddErrorInfo(const String& error_msg, void AddErrorInfo(const String& error_msg,
bool critical = false, bool critical = false,
int error_line = 0, int error_line = 0,
......
...@@ -4050,4 +4050,120 @@ TEST_F(ManifestParserTest, GCMSenderIDParseRules) { ...@@ -4050,4 +4050,120 @@ TEST_F(ManifestParserTest, GCMSenderIDParseRules) {
} }
} }
TEST_F(ManifestParserTest, CaptureLinksParseRules) {
// Feature not enabled, should not be parsed.
{
auto& manifest = ParseManifest(R"({ "capture_links": "none" })");
EXPECT_EQ(manifest->capture_links, mojom::blink::CaptureLinks::kUndefined);
EXPECT_EQ(0u, GetErrorCount());
}
}
class ManifestCaptureLinksParserTest : public ManifestParserTest {
public:
ManifestCaptureLinksParserTest() {
scoped_feature_list_.InitAndEnableFeature(
features::kWebAppEnableLinkCapturing);
}
private:
base::test::ScopedFeatureList scoped_feature_list_;
};
TEST_F(ManifestCaptureLinksParserTest, CaptureLinksParseRules) {
// Smoke test.
{
auto& manifest = ParseManifest(R"({ "capture_links": "none" })");
EXPECT_EQ(manifest->capture_links, mojom::blink::CaptureLinks::kNone);
EXPECT_EQ(0u, GetErrorCount());
}
{
auto& manifest = ParseManifest(R"({ "capture_links": ["new-client"] })");
EXPECT_EQ(manifest->capture_links, mojom::blink::CaptureLinks::kNewClient);
EXPECT_EQ(0u, GetErrorCount());
}
// Empty array is fine.
{
auto& manifest = ParseManifest(R"({ "capture_links": [] })");
EXPECT_EQ(manifest->capture_links, mojom::blink::CaptureLinks::kUndefined);
EXPECT_EQ(0u, GetErrorCount());
}
// Unknown single string.
{
auto& manifest = ParseManifest(R"({ "capture_links": "unknown" })");
EXPECT_EQ(manifest->capture_links, mojom::blink::CaptureLinks::kUndefined);
EXPECT_EQ(1u, GetErrorCount());
EXPECT_EQ("capture_links value 'unknown' ignored, unknown value.",
errors()[0]);
}
// First known value in array is used.
{
auto& manifest = ParseManifest(
R"({ "capture_links": ["none", "existing-client-navigate"] })");
EXPECT_EQ(manifest->capture_links, mojom::blink::CaptureLinks::kNone);
EXPECT_EQ(0u, GetErrorCount());
}
{
auto& manifest = ParseManifest(R"({
"capture_links": [
"unknown",
"existing-client-navigate",
"also-unknown",
"none"
]
})");
EXPECT_EQ(manifest->capture_links,
mojom::blink::CaptureLinks::kExistingClientNavigate);
EXPECT_EQ(1u, GetErrorCount());
EXPECT_EQ("capture_links value 'unknown' ignored, unknown value.",
errors()[0]);
}
{
auto& manifest = ParseManifest(R"({
"capture_links": [
1234,
"new-client",
null,
"none"
]
})");
EXPECT_EQ(manifest->capture_links, mojom::blink::CaptureLinks::kNewClient);
EXPECT_EQ(1u, GetErrorCount());
EXPECT_EQ("capture_links value '1234' ignored, string expected.",
errors()[0]);
}
// Don't parse if the property isn't a string or array of strings.
{
auto& manifest = ParseManifest(R"({ "capture_links": null })");
EXPECT_EQ(manifest->capture_links, mojom::blink::CaptureLinks::kUndefined);
EXPECT_EQ(1u, GetErrorCount());
EXPECT_EQ(
"property 'capture_links' ignored, type string or array of strings "
"expected.",
errors()[0]);
}
{
auto& manifest = ParseManifest(R"({ "capture_links": 1234 })");
EXPECT_EQ(manifest->capture_links, mojom::blink::CaptureLinks::kUndefined);
EXPECT_EQ(1u, GetErrorCount());
EXPECT_EQ(
"property 'capture_links' ignored, type string or array of strings "
"expected.",
errors()[0]);
}
{
auto& manifest = ParseManifest(R"({ "capture_links": [12, 34] })");
EXPECT_EQ(manifest->capture_links, mojom::blink::CaptureLinks::kUndefined);
EXPECT_EQ(2u, GetErrorCount());
EXPECT_EQ("capture_links value '12' ignored, string expected.",
errors()[0]);
EXPECT_EQ("capture_links value '34' ignored, string expected.",
errors()[1]);
}
}
} // namespace blink } // namespace blink
...@@ -79,6 +79,8 @@ TypeConverter<blink::Manifest, blink::mojom::blink::ManifestPtr>::Convert( ...@@ -79,6 +79,8 @@ TypeConverter<blink::Manifest, blink::mojom::blink::ManifestPtr>::Convert(
if (!input->scope.IsEmpty()) if (!input->scope.IsEmpty())
output.scope = input->scope; output.scope = input->scope;
output.capture_links = input->capture_links;
return output; return output;
} }
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment