DPWA: parse manifest.id to web_app.manifest_id
Allow specifying "id" in web app manifest and parse it as a literal string to WebApp.manifest_id. After that, existing code takes care of generating WebApp.app_id from hash of {start_url_origin}/{manifest_id}. When id is not specified in the manifest, falls back to use start_url for app id. Bug: 1215724 Change-Id: Ia8c201e1f23e26d43e6e2e3c1c9cd8416ea3d4ae Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2935478 Commit-Queue: Phillis Tang <phillis@chromium.org> Reviewed-by: Dominick Ng <dominickn@chromium.org> Reviewed-by: Kentaro Hara <haraken@chromium.org> Reviewed-by: Daniel Murphy <dmurph@chromium.org> Cr-Commit-Position: refs/heads/master@{#894369}
This commit is contained in:

committed by
Chromium LUCI CQ

parent
35343463ad
commit
c6b38394f1
chrome
browser
about_flags.ccflag-metadata.jsonflag_descriptions.ccflag_descriptions.h
ui
web_applications
web_applications
test
data
banners
content/child
third_party/blink
common
public
common
mojom
manifest
platform
renderer
modules
platform
tools/metrics/histograms
@ -3510,6 +3510,10 @@ const FeatureEntry kFeatureEntries[] = {
|
||||
flag_descriptions::kDesktopPWAsLinkCapturingName,
|
||||
flag_descriptions::kDesktopPWAsLinkCapturingDescription, kOsDesktop,
|
||||
FEATURE_VALUE_TYPE(blink::features::kWebAppEnableLinkCapturing)},
|
||||
{"enable-desktop-pwas-manifest-id",
|
||||
flag_descriptions::kDesktopPWAsManifestIdName,
|
||||
flag_descriptions::kDesktopPWAsManifestIdDescription, kOsDesktop,
|
||||
FEATURE_VALUE_TYPE(blink::features::kWebAppEnableManifestId)},
|
||||
{"enable-desktop-pwas-run-on-os-login",
|
||||
flag_descriptions::kDesktopPWAsRunOnOsLoginName,
|
||||
flag_descriptions::kDesktopPWAsRunOnOsLoginDescription,
|
||||
|
@ -1670,6 +1670,11 @@
|
||||
"owners": [ "alancutter@chromium.org", "desktop-pwas-team@google.com" ],
|
||||
"expiry_milestone": 96
|
||||
},
|
||||
{
|
||||
"name": "enable-desktop-pwas-manifest-id",
|
||||
"owners": [ "phillis@chromium.org", "desktop-pwas-team@google.com" ],
|
||||
"expiry_milestone": 98
|
||||
},
|
||||
{
|
||||
"name": "enable-desktop-pwas-notification-icon-and-title",
|
||||
"owners": [ "loyso@chromium.org", "desktop-pwas-team@google.com" ],
|
||||
|
@ -774,6 +774,12 @@ const char kDesktopPWAsLinkCapturingDescription[] =
|
||||
"https://github.com/WICG/sw-launch/blob/master/"
|
||||
"declarative_link_capturing.md";
|
||||
|
||||
const char kDesktopPWAsManifestIdName[] = "Desktop PWA manifest id";
|
||||
const char kDesktopPWAsManifestIdDescription[] =
|
||||
"Enable web app manifests to declare id. Prototype "
|
||||
"implementation of: "
|
||||
"https://github.com/philloooo/pwa-unique-id/blob/main/explainer.md";
|
||||
|
||||
const char kDesktopPWAsTabStripName[] = "Desktop PWA tab strips";
|
||||
const char kDesktopPWAsTabStripDescription[] =
|
||||
"Experimental UI for exploring what PWA windows would look like with a tab "
|
||||
|
@ -465,6 +465,9 @@ extern const char kDesktopPWAsNotificationIconAndTitleDescription[];
|
||||
extern const char kDesktopPWAsLinkCapturingName[];
|
||||
extern const char kDesktopPWAsLinkCapturingDescription[];
|
||||
|
||||
extern const char kDesktopPWAsManifestIdName[];
|
||||
extern const char kDesktopPWAsManifestIdDescription[];
|
||||
|
||||
extern const char kDesktopPWAsTabStripName[];
|
||||
extern const char kDesktopPWAsTabStripDescription[];
|
||||
|
||||
|
@ -69,6 +69,7 @@
|
||||
#include "content/public/test/test_utils.h"
|
||||
#include "testing/gmock/include/gmock/gmock.h"
|
||||
#include "testing/gtest/include/gtest/gtest.h"
|
||||
#include "third_party/blink/public/common/features.h"
|
||||
#include "third_party/skia/include/core/SkColor.h"
|
||||
#include "ui/base/clipboard/clipboard.h"
|
||||
#include "ui/base/clipboard/clipboard_buffer.h"
|
||||
@ -1359,4 +1360,42 @@ IN_PROC_BROWSER_TEST_F(WebAppBrowserTest_NoDestroyProfile, Shutdown) {
|
||||
EXPECT_EQ(web_contents, nullptr);
|
||||
}
|
||||
|
||||
class WebAppBrowserTest_ManifestId : public WebAppBrowserTest {
|
||||
public:
|
||||
WebAppBrowserTest_ManifestId() = default;
|
||||
|
||||
private:
|
||||
base::test::ScopedFeatureList scoped_feature_list_{
|
||||
blink::features::kWebAppEnableManifestId};
|
||||
};
|
||||
|
||||
IN_PROC_BROWSER_TEST_F(WebAppBrowserTest_ManifestId, NoManifestId) {
|
||||
NavigateToURLAndWait(browser(), GetInstallableAppURL());
|
||||
|
||||
const AppId app_id = InstallPwaForCurrentUrl();
|
||||
auto* provider = WebAppProviderBase::GetProviderBase(profile());
|
||||
auto* app = provider->registrar().AsWebAppRegistrar()->GetAppById(app_id);
|
||||
|
||||
EXPECT_EQ(web_app::GenerateAppIdFromURL(
|
||||
provider->registrar().GetAppStartUrl(app_id)),
|
||||
app_id);
|
||||
EXPECT_EQ(app->start_url().spec().substr(
|
||||
app->start_url().GetOrigin().spec().size()),
|
||||
app->manifest_id());
|
||||
}
|
||||
|
||||
IN_PROC_BROWSER_TEST_F(WebAppBrowserTest_ManifestId, ManifestIdSpecified) {
|
||||
NavigateAndAwaitInstallabilityCheck(
|
||||
browser(),
|
||||
https_server()->GetURL(
|
||||
"/banners/manifest_test_page.html?manifest=manifest_with_id.json"));
|
||||
|
||||
const AppId app_id = InstallPwaForCurrentUrl();
|
||||
auto* provider = WebAppProviderBase::GetProviderBase(profile());
|
||||
auto* app = provider->registrar().AsWebAppRegistrar()->GetAppById(app_id);
|
||||
|
||||
EXPECT_EQ(web_app::GenerateAppId(app->manifest_id(), app->start_url()),
|
||||
app_id);
|
||||
EXPECT_NE(web_app::GenerateAppIdFromURL(app->start_url()), app_id);
|
||||
}
|
||||
} // namespace web_app
|
||||
|
@ -109,6 +109,8 @@ class AppRegistrar {
|
||||
virtual absl::optional<SkColor> GetAppBackgroundColor(
|
||||
const AppId& app_id) const = 0;
|
||||
virtual const GURL& GetAppStartUrl(const AppId& app_id) const = 0;
|
||||
virtual absl::optional<std::string> GetAppManifestId(
|
||||
const AppId& app_id) const = 0;
|
||||
virtual const std::string* GetAppLaunchQueryParams(
|
||||
const AppId& app_id) const = 0;
|
||||
virtual const apps::ShareTarget* GetAppShareTarget(
|
||||
|
@ -54,18 +54,24 @@ AppId GenerateAppIdFromURL(const GURL& url) {
|
||||
return crx_file::id_util::GenerateId(GenerateAppHashFromURL(url));
|
||||
}
|
||||
|
||||
AppId GenerateAppId(const absl::optional<std::string>& manifest_id,
|
||||
const GURL& start_url) {
|
||||
std::string GenerateAppIdUnhashed(
|
||||
const absl::optional<std::string>& manifest_id,
|
||||
const GURL& start_url) {
|
||||
// When manifest_id is specified, the app id is generated from
|
||||
// <start_url_origin>/<manifest_id>.
|
||||
// Note: start_url.GetOrigin().spec() returns the origin ending with slash.
|
||||
if (manifest_id.has_value()) {
|
||||
GURL app_id(start_url.GetOrigin().spec() + manifest_id.value());
|
||||
DCHECK(app_id.is_valid());
|
||||
return crx_file::id_util::GenerateId(
|
||||
crypto::SHA256HashString(app_id.spec()));
|
||||
return app_id.spec();
|
||||
}
|
||||
return GenerateAppIdFromURL(start_url);
|
||||
return start_url.spec();
|
||||
}
|
||||
|
||||
AppId GenerateAppId(const absl::optional<std::string>& manifest_id,
|
||||
const GURL& start_url) {
|
||||
return crx_file::id_util::GenerateId(
|
||||
crypto::SHA256HashString(GenerateAppIdUnhashed(manifest_id, start_url)));
|
||||
}
|
||||
|
||||
// Generate the public key for the fake extension that we synthesize to contain
|
||||
|
@ -42,6 +42,9 @@ AppId GetAppIdFromApplicationName(const std::string& app_name);
|
||||
//
|
||||
// App ID and App Key match Extension ID and Extension Key for migration.
|
||||
AppId GenerateAppIdFromURL(const GURL& url);
|
||||
std::string GenerateAppIdUnhashed(
|
||||
const absl::optional<std::string>& manifest_id,
|
||||
const GURL& start_url);
|
||||
AppId GenerateAppId(const absl::optional<std::string>& manifest_id,
|
||||
const GURL& start_url);
|
||||
|
||||
|
@ -213,6 +213,11 @@ void UpdateWebAppInfoFromManifest(const blink::Manifest& manifest,
|
||||
else if (manifest.short_name)
|
||||
web_app_info->title = *manifest.short_name;
|
||||
|
||||
if (manifest.id.has_value()) {
|
||||
web_app_info->manifest_id =
|
||||
absl::optional<std::string>(base::UTF16ToUTF8(manifest.id.value()));
|
||||
}
|
||||
|
||||
// Set the url based on the manifest value, if any.
|
||||
if (manifest.start_url.is_valid())
|
||||
web_app_info->start_url = manifest.start_url;
|
||||
|
@ -3003,4 +3003,105 @@ IN_PROC_BROWSER_TEST_F(ManifestUpdateManagerBrowserTestWithWebAppNoteTaking,
|
||||
EXPECT_TRUE(web_app->note_taking_new_note_url().is_empty());
|
||||
}
|
||||
|
||||
class ManifestUpdateManagerBrowserTest_ManifestId
|
||||
: public ManifestUpdateManagerBrowserTest {
|
||||
public:
|
||||
ManifestUpdateManagerBrowserTest_ManifestId() {
|
||||
scoped_feature_list_.InitAndEnableFeature(
|
||||
blink::features::kWebAppEnableManifestId);
|
||||
}
|
||||
|
||||
private:
|
||||
base::test::ScopedFeatureList scoped_feature_list_;
|
||||
};
|
||||
|
||||
IN_PROC_BROWSER_TEST_F(ManifestUpdateManagerBrowserTest_ManifestId,
|
||||
AllowStartUrlUpdate) {
|
||||
constexpr char kManifestTemplate[] = R"(
|
||||
{
|
||||
"name": "Test app name",
|
||||
"start_url": "$1",
|
||||
"scope": "/",
|
||||
"display": "minimal-ui",
|
||||
"id": "test",
|
||||
"icons": $2
|
||||
}
|
||||
)";
|
||||
OverrideManifest(kManifestTemplate, {"/startA", kInstallableIconList});
|
||||
AppId app_id = InstallWebApp();
|
||||
EXPECT_EQ(GetProvider().registrar().GetAppStartUrl(app_id).path(), "/startA");
|
||||
|
||||
OverrideManifest(kManifestTemplate, {"/startB", kInstallableIconList});
|
||||
EXPECT_EQ(GetResultAfterPageLoad(GetAppURL(), &app_id),
|
||||
ManifestUpdateResult::kAppUpdated);
|
||||
EXPECT_EQ(GetProvider().registrar().GetAppStartUrl(app_id).path(), "/startB");
|
||||
histogram_tester_.ExpectBucketCount(kUpdateHistogramName,
|
||||
ManifestUpdateResult::kAppUpdated, 1);
|
||||
}
|
||||
|
||||
IN_PROC_BROWSER_TEST_F(ManifestUpdateManagerBrowserTest_ManifestId,
|
||||
CheckIgnoresIdChange) {
|
||||
constexpr char kManifestTemplate[] = R"(
|
||||
{
|
||||
"name": "Test app name",
|
||||
"id": "$1",
|
||||
"start_url": "start",
|
||||
"scope": "/",
|
||||
"display": "standalone",
|
||||
"icons": $2
|
||||
}
|
||||
)";
|
||||
OverrideManifest(kManifestTemplate, {"test", kInstallableIconList});
|
||||
AppId app_id = InstallWebApp();
|
||||
|
||||
OverrideManifest(kManifestTemplate, {"testb", kInstallableIconList});
|
||||
EXPECT_EQ(GetResultAfterPageLoad(GetAppURL(), &app_id),
|
||||
ManifestUpdateResult::kAppIdMismatch);
|
||||
histogram_tester_.ExpectBucketCount(kUpdateHistogramName,
|
||||
ManifestUpdateResult::kAppIdMismatch, 1);
|
||||
}
|
||||
|
||||
IN_PROC_BROWSER_TEST_F(ManifestUpdateManagerBrowserTest_ManifestId,
|
||||
ChecksSettingIdMatchDefault) {
|
||||
constexpr char kManifestTemplate[] = R"(
|
||||
{
|
||||
"name": "Test app name",
|
||||
"start_url": "/start",
|
||||
"scope": "/",
|
||||
"display": "standalone",
|
||||
"icons": $1
|
||||
}
|
||||
)";
|
||||
OverrideManifest(kManifestTemplate, {kInstallableIconList});
|
||||
AppId app_id = InstallWebApp();
|
||||
|
||||
// manifest_id should default to start_url when it's not provided in manifest.
|
||||
EXPECT_EQ(GetProvider()
|
||||
.registrar()
|
||||
.AsWebAppRegistrar()
|
||||
->GetAppById(app_id)
|
||||
->manifest_id()
|
||||
.value(),
|
||||
"start");
|
||||
|
||||
constexpr char kManifestTemplate2[] = R"(
|
||||
{
|
||||
"name": "Test app name",
|
||||
"id": "$1",
|
||||
"start_url": "/start",
|
||||
"scope": "/",
|
||||
"display": "standalone",
|
||||
"icons": $2
|
||||
}
|
||||
)";
|
||||
|
||||
// Setting manifest id to match default value won't trigger update as the
|
||||
// parsed manifest is the same.
|
||||
OverrideManifest(kManifestTemplate2, {"start", kInstallableIconList});
|
||||
EXPECT_EQ(GetResultAfterPageLoad(GetAppURL(), &app_id),
|
||||
ManifestUpdateResult::kAppUpToDate);
|
||||
|
||||
histogram_tester_.ExpectBucketCount(kUpdateHistogramName,
|
||||
ManifestUpdateResult::kAppUpToDate, 1);
|
||||
}
|
||||
} // namespace web_app
|
||||
|
@ -257,7 +257,8 @@ void ManifestUpdateTask::OnDidGetInstallableData(
|
||||
// additionally the new app ID would get added to the sync profile. This has
|
||||
// the potential to flood the user sync profile with an infinite number of
|
||||
// apps should the site be serving a random start_url on every navigation.
|
||||
if (app_id_ != GenerateAppIdFromURL(web_application_info_->start_url)) {
|
||||
if (app_id_ != GenerateAppId(web_application_info_->manifest_id,
|
||||
web_application_info_->start_url)) {
|
||||
DestroySelf(ManifestUpdateResult::kAppIdMismatch);
|
||||
return;
|
||||
}
|
||||
@ -275,6 +276,20 @@ bool ManifestUpdateTask::IsUpdateNeededForManifest() const {
|
||||
const WebApp* app = registrar_.AsWebAppRegistrar()->GetAppById(app_id_);
|
||||
DCHECK(app);
|
||||
|
||||
// Allows updating start_url and manifest_id when kWebAppEnableManifestId is
|
||||
// enabled. Both fields are allowed to change as long as the app_id generated
|
||||
// from them doesn't change.
|
||||
if (base::FeatureList::IsEnabled(blink::features::kWebAppEnableManifestId)) {
|
||||
if (web_application_info_->manifest_id !=
|
||||
registrar_.GetAppManifestId(app_id_)) {
|
||||
return true;
|
||||
}
|
||||
if (web_application_info_->start_url !=
|
||||
registrar_.GetAppStartUrl(app_id_)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
if (web_application_info_->theme_color !=
|
||||
registrar_.GetAppThemeColor(app_id_))
|
||||
return true;
|
||||
|
@ -128,6 +128,12 @@ const GURL& TestAppRegistrar::GetAppStartUrl(const AppId& app_id) const {
|
||||
return iterator->second.launch_url;
|
||||
}
|
||||
|
||||
absl::optional<std::string> TestAppRegistrar::GetAppManifestId(
|
||||
const AppId& app_id) const {
|
||||
NOTIMPLEMENTED();
|
||||
return absl::nullopt;
|
||||
}
|
||||
|
||||
const std::string* TestAppRegistrar::GetAppLaunchQueryParams(
|
||||
const AppId& app_id) const {
|
||||
return nullptr;
|
||||
|
@ -65,6 +65,8 @@ class TestAppRegistrar : public AppRegistrar {
|
||||
absl::optional<SkColor> GetAppBackgroundColor(
|
||||
const AppId& app_id) const override;
|
||||
const GURL& GetAppStartUrl(const AppId& app_id) const override;
|
||||
absl::optional<std::string> GetAppManifestId(
|
||||
const AppId& app_id) const override;
|
||||
const std::string* GetAppLaunchQueryParams(
|
||||
const AppId& app_id) const override;
|
||||
const apps::ShareTarget* GetAppShareTarget(
|
||||
|
@ -14,6 +14,7 @@
|
||||
#include "base/strings/string_util.h"
|
||||
#include "chrome/browser/web_applications/components/web_app_chromeos_data.h"
|
||||
#include "chrome/browser/web_applications/components/web_app_constants.h"
|
||||
#include "chrome/browser/web_applications/components/web_app_helpers.h"
|
||||
#include "chrome/browser/web_applications/components/web_app_utils.h"
|
||||
#include "components/sync/base/time.h"
|
||||
#include "third_party/blink/public/common/manifest/manifest_util.h"
|
||||
@ -368,6 +369,9 @@ std::ostream& operator<<(std::ostream& out,
|
||||
std::ostream& operator<<(std::ostream& out, const WebApp& app) {
|
||||
out << "app_id: " << app.app_id_ << std::endl;
|
||||
|
||||
out << "unhashed app_id: "
|
||||
<< GenerateAppIdUnhashed(app.manifest_id_, app.start_url_) << std::endl;
|
||||
|
||||
out << "manifest_url: " << app.manifest_url_ << std::endl;
|
||||
|
||||
out << "manifest_id: " << app.manifest_id_ << std::endl;
|
||||
|
@ -134,14 +134,20 @@ void WebAppInstallFinalizer::FinalizeInstall(
|
||||
// A web app might be sync installed with id received from WebAppSpecifics
|
||||
// that's different from start_url hash, in this case we look up the app by
|
||||
// start_url and respect the app_id from the existing WebApp.
|
||||
if (!existing_web_app)
|
||||
if (!base::FeatureList::IsEnabled(blink::features::kWebAppEnableManifestId) &&
|
||||
!existing_web_app) {
|
||||
existing_web_app =
|
||||
GetWebAppRegistrar().GetAppByStartUrl(web_app_info.start_url);
|
||||
}
|
||||
std::unique_ptr<WebApp> web_app;
|
||||
if (existing_web_app) {
|
||||
app_id = existing_web_app->app_id();
|
||||
// Prepare copy-on-write:
|
||||
DCHECK_EQ(web_app_info.start_url, existing_web_app->start_url());
|
||||
// Allows changing manifest_id and start_url when manifest_id is enabled.
|
||||
if (!base::FeatureList::IsEnabled(
|
||||
blink::features::kWebAppEnableManifestId)) {
|
||||
DCHECK_EQ(web_app_info.start_url, existing_web_app->start_url());
|
||||
}
|
||||
web_app = std::make_unique<WebApp>(*existing_web_app);
|
||||
|
||||
// The UI may initiate a full install to overwrite the existing
|
||||
@ -315,7 +321,7 @@ void WebAppInstallFinalizer::FinalizeUpdate(
|
||||
const WebApp* existing_web_app = GetWebAppRegistrar().GetAppById(app_id);
|
||||
|
||||
if (!existing_web_app || existing_web_app->is_in_sync_install() ||
|
||||
web_app_info.start_url != existing_web_app->start_url()) {
|
||||
app_id != existing_web_app->app_id()) {
|
||||
base::ThreadTaskRunnerHandle::Get()->PostTask(
|
||||
FROM_HERE, base::BindOnce(std::move(callback), AppId(),
|
||||
InstallResultCode::kWebAppDisabled));
|
||||
|
@ -24,6 +24,7 @@
|
||||
#include "components/services/app_service/public/cpp/protocol_handler_info.h"
|
||||
#include "components/services/app_service/public/cpp/share_target.h"
|
||||
#include "third_party/abseil-cpp/absl/types/optional.h"
|
||||
#include "third_party/blink/public/common/features.h"
|
||||
#include "third_party/blink/public/common/manifest/manifest.h"
|
||||
#include "third_party/skia/include/core/SkBitmap.h"
|
||||
#include "third_party/skia/include/core/SkColor.h"
|
||||
@ -116,6 +117,10 @@ void SetWebAppManifestFields(const WebApplicationInfo& web_app_info,
|
||||
DCHECK(!web_app_info.title.empty());
|
||||
web_app.SetName(base::UTF16ToUTF8(web_app_info.title));
|
||||
|
||||
if (base::FeatureList::IsEnabled(blink::features::kWebAppEnableManifestId)) {
|
||||
web_app.SetStartUrl(web_app_info.start_url);
|
||||
web_app.SetManifestId(web_app_info.manifest_id);
|
||||
}
|
||||
web_app.SetDisplayMode(web_app_info.display_mode);
|
||||
web_app.SetDisplayModeOverride(web_app_info.display_override);
|
||||
|
||||
|
@ -139,6 +139,12 @@ const GURL& WebAppRegistrar::GetAppStartUrl(const AppId& app_id) const {
|
||||
return web_app ? web_app->start_url() : GURL::EmptyGURL();
|
||||
}
|
||||
|
||||
absl::optional<std::string> WebAppRegistrar::GetAppManifestId(
|
||||
const AppId& app_id) const {
|
||||
auto* web_app = GetAppById(app_id);
|
||||
return web_app ? web_app->manifest_id() : absl::nullopt;
|
||||
}
|
||||
|
||||
const std::string* WebAppRegistrar::GetAppLaunchQueryParams(
|
||||
const AppId& app_id) const {
|
||||
auto* web_app = GetAppById(app_id);
|
||||
|
@ -63,6 +63,8 @@ class WebAppRegistrar : public AppRegistrar, public ProfileManagerObserver {
|
||||
absl::optional<SkColor> GetAppBackgroundColor(
|
||||
const AppId& app_id) const override;
|
||||
const GURL& GetAppStartUrl(const AppId& app_id) const override;
|
||||
absl::optional<std::string> GetAppManifestId(
|
||||
const AppId& app_id) const override;
|
||||
const std::string* GetAppLaunchQueryParams(
|
||||
const AppId& app_id) const override;
|
||||
const apps::ShareTarget* GetAppShareTarget(
|
||||
|
@ -157,6 +157,7 @@ TEST(WebAppTest, EmptyAppToDebugString) {
|
||||
WebAppToPlatformAgnosticString(std::make_unique<WebApp>("empty_app"));
|
||||
EXPECT_EQ(debug_string,
|
||||
R"(app_id: empty_app
|
||||
unhashed app_id:
|
||||
manifest_url:
|
||||
manifest_id: nullopt
|
||||
name:
|
||||
@ -217,6 +218,7 @@ TEST(WebAppTest, SampleAppToDebugString) {
|
||||
test::CreateRandomWebApp(GURL("https://example.com/"), /*seed=*/1234));
|
||||
EXPECT_EQ(debug_string,
|
||||
R"(app_id: eajjdjobhihlgobdfaehiiheinneagde
|
||||
unhashed app_id: https://example.com/scope1234/start1234
|
||||
manifest_url: https://example.com/manifest1234.json
|
||||
manifest_id: nullopt
|
||||
name: Name1234
|
||||
|
39
chrome/test/data/banners/manifest_with_id.json
Normal file
39
chrome/test/data/banners/manifest_with_id.json
Normal file
@ -0,0 +1,39 @@
|
||||
{
|
||||
"name": "Manifest test app with id specified",
|
||||
"id": "testid",
|
||||
"icons": [
|
||||
{
|
||||
"src": "launcher-icon-1x.png",
|
||||
"sizes": "48x48",
|
||||
"type": "image/png"
|
||||
},
|
||||
{
|
||||
"src": "launcher-icon-1-5x.png",
|
||||
"sizes": "72x72",
|
||||
"type": "image/png"
|
||||
},
|
||||
{
|
||||
"src": "launcher-icon-2x.png",
|
||||
"sizes": "96x96",
|
||||
"type": "image/png",
|
||||
"purpose": "any monochrome"
|
||||
},
|
||||
{
|
||||
"src": "launcher-icon-3x.png",
|
||||
"sizes": "144x144",
|
||||
"type": "image/png"
|
||||
},
|
||||
{
|
||||
"src": "launcher-icon-4x.png",
|
||||
"sizes": "192x192",
|
||||
"type": "image/png"
|
||||
},
|
||||
{
|
||||
"src": "image-512px.png",
|
||||
"sizes": "512x512",
|
||||
"type": "image/png"
|
||||
}
|
||||
],
|
||||
"start_url": "start",
|
||||
"display": "fullscreen"
|
||||
}
|
@ -264,6 +264,7 @@ void SetRuntimeFeaturesFromChromiumFeatures() {
|
||||
kSetOnlyIfOverridden},
|
||||
{wf::EnableParseUrlProtocolHandler,
|
||||
blink::features::kWebAppEnableProtocolHandlers},
|
||||
{wf::EnableWebAppManifestId, blink::features::kWebAppEnableManifestId},
|
||||
{wf::EnablePaymentApp, features::kServiceWorkerPaymentApps},
|
||||
{wf::EnablePaymentHandlerMinimalUI, features::kWebPaymentsMinimalUI},
|
||||
{wf::EnablePaymentRequest, features::kWebPayments},
|
||||
|
6
third_party/blink/common/features.cc
vendored
6
third_party/blink/common/features.cc
vendored
@ -844,6 +844,12 @@ const base::Feature kWebAppEnableIsolatedStorage{
|
||||
const base::Feature kWebAppEnableLinkCapturing{
|
||||
"WebAppEnableLinkCapturing", base::FEATURE_DISABLED_BY_DEFAULT};
|
||||
|
||||
// Enables Unique ID feature in web apps. Controls parsing of "id" field in web
|
||||
// app manifests. See explainer for more information:
|
||||
// https://github.com/philloooo/pwa-unique-id
|
||||
const base::Feature kWebAppEnableManifestId{"WebAppEnableManifestId",
|
||||
base::FEATURE_DISABLED_BY_DEFAULT};
|
||||
|
||||
// Controls URL handling feature in web apps. Controls parsing of "url_handlers"
|
||||
// field in web app manifests. See explainer for more information:
|
||||
// https://github.com/WICG/pwa-url-handler/blob/master/explainer.md
|
||||
|
@ -40,7 +40,7 @@ Manifest::Manifest(const Manifest& other) = default;
|
||||
Manifest::~Manifest() = default;
|
||||
|
||||
bool Manifest::IsEmpty() const {
|
||||
return !name && !short_name && start_url.is_empty() &&
|
||||
return !name && !short_name && !id && start_url.is_empty() &&
|
||||
display == blink::mojom::DisplayMode::kUndefined &&
|
||||
display_override.empty() &&
|
||||
orientation == device::mojom::ScreenOrientationLockType::DEFAULT &&
|
||||
|
@ -86,6 +86,10 @@ bool StructTraits<blink::mojom::ManifestDataView, ::blink::Manifest>::Read(
|
||||
return false;
|
||||
out->description = std::move(string.string);
|
||||
|
||||
if (!data.ReadId(&string))
|
||||
return false;
|
||||
out->id = std::move(string.string);
|
||||
|
||||
if (!data.ReadGcmSenderId(&string))
|
||||
return false;
|
||||
out->gcm_sender_id = std::move(string.string);
|
||||
|
2
third_party/blink/public/common/features.h
vendored
2
third_party/blink/public/common/features.h
vendored
@ -339,6 +339,8 @@ BLINK_COMMON_EXPORT extern const base::Feature kWebAppEnableIsolatedStorage;
|
||||
|
||||
BLINK_COMMON_EXPORT extern const base::Feature kWebAppEnableLinkCapturing;
|
||||
|
||||
BLINK_COMMON_EXPORT extern const base::Feature kWebAppEnableManifestId;
|
||||
|
||||
BLINK_COMMON_EXPORT extern const base::Feature kWebAppEnableUrlHandlers;
|
||||
|
||||
BLINK_COMMON_EXPORT extern const base::Feature kWebAppEnableProtocolHandlers;
|
||||
|
@ -164,6 +164,10 @@ struct BLINK_COMMON_EXPORT Manifest {
|
||||
// Null if the parsing failed or the field was not present.
|
||||
absl::optional<std::u16string> description;
|
||||
|
||||
// Null if the start_url parsing failed or missing, otherwise defaults to
|
||||
// start_url with origin stripped when id field is not present.
|
||||
absl::optional<std::u16string> id;
|
||||
|
||||
// Empty if the parsing failed or the field was not present.
|
||||
GURL start_url;
|
||||
|
||||
|
@ -60,6 +60,11 @@ struct BLINK_COMMON_EXPORT
|
||||
return internal::TruncateOptionalString16(manifest.description);
|
||||
}
|
||||
|
||||
static absl::optional<base::StringPiece16> id(
|
||||
const ::blink::Manifest& manifest) {
|
||||
return internal::TruncateOptionalString16(manifest.id);
|
||||
}
|
||||
|
||||
static absl::optional<base::StringPiece16> gcm_sender_id(
|
||||
const ::blink::Manifest& manifest) {
|
||||
return internal::TruncateOptionalString16(manifest.gcm_sender_id);
|
||||
|
@ -24,6 +24,8 @@ struct Manifest {
|
||||
|
||||
mojo_base.mojom.String16? description;
|
||||
|
||||
mojo_base.mojom.String16? id;
|
||||
|
||||
url.mojom.Url start_url;
|
||||
|
||||
DisplayMode display;
|
||||
|
@ -167,6 +167,7 @@ class WebRuntimeFeatures {
|
||||
BLINK_PLATFORM_EXPORT static void EnableUserActivationSameOriginVisibility(
|
||||
bool);
|
||||
BLINK_PLATFORM_EXPORT static void EnableV8IdleTasks(bool);
|
||||
BLINK_PLATFORM_EXPORT static void EnableWebAppManifestId(bool);
|
||||
BLINK_PLATFORM_EXPORT static void EnableWebAuth(bool);
|
||||
BLINK_PLATFORM_EXPORT static void EnableWebBluetooth(bool);
|
||||
BLINK_PLATFORM_EXPORT static void
|
||||
|
@ -124,6 +124,7 @@ void ManifestParser::Parse() {
|
||||
manifest_->short_name = ParseShortName(root_object.get());
|
||||
manifest_->description = ParseDescription(root_object.get());
|
||||
manifest_->start_url = ParseStartURL(root_object.get());
|
||||
manifest_->id = ParseId(root_object.get(), manifest_->start_url);
|
||||
manifest_->scope = ParseScope(root_object.get(), manifest_->start_url);
|
||||
manifest_->display = ParseDisplay(root_object.get());
|
||||
manifest_->display_override = ParseDisplayOverride(root_object.get());
|
||||
@ -331,6 +332,20 @@ String ManifestParser::ParseDescription(const JSONObject* object) {
|
||||
return description.has_value() ? *description : String();
|
||||
}
|
||||
|
||||
String ManifestParser::ParseId(const JSONObject* object,
|
||||
const KURL& start_url) {
|
||||
if (!start_url.IsValid() ||
|
||||
!base::FeatureList::IsEnabled(blink::features::kWebAppEnableManifestId)) {
|
||||
return String();
|
||||
}
|
||||
absl::optional<String> id = ParseString(object, "id", NoTrim);
|
||||
|
||||
// Default to start_url with origin stripped.
|
||||
return id.has_value()
|
||||
? *id
|
||||
: start_url.GetString().Substring(start_url.PathStart() + 1);
|
||||
}
|
||||
|
||||
KURL ManifestParser::ParseStartURL(const JSONObject* object) {
|
||||
return ParseURL(object, "start_url", manifest_url_,
|
||||
ParseURLRestrictions::kSameOriginOnly);
|
||||
|
@ -120,6 +120,9 @@ class MODULES_EXPORT ManifestParser {
|
||||
// Returns the parsed string if any, a null string if the parsing failed.
|
||||
String ParseDescription(const JSONObject* object);
|
||||
|
||||
// Parses the 'id' field of the manifest.
|
||||
String ParseId(const JSONObject* object, const KURL& start_url);
|
||||
|
||||
// Parses the 'scope' field of the manifest, as defined in:
|
||||
// https://w3c.github.io/manifest/#scope-member. Returns the parsed KURL if
|
||||
// any, or start URL (falling back to document URL) without filename, path,
|
||||
|
@ -116,7 +116,7 @@ TEST_F(ManifestParserTest, ValidNoContentParses) {
|
||||
|
||||
TEST_F(ManifestParserTest, MultipleErrorsReporting) {
|
||||
auto& manifest = ParseManifest(
|
||||
"{ \"name\": 42, \"short_name\": 4,"
|
||||
"{ \"name\": 42, \"short_name\": 4, \"id\": 12,"
|
||||
"\"orientation\": {}, \"display\": \"foo\","
|
||||
"\"start_url\": null, \"icons\": {}, \"theme_color\": 42,"
|
||||
"\"background_color\": 42, \"shortcuts\": {} }");
|
||||
@ -259,6 +259,37 @@ TEST_F(ManifestParserTest, ShortNameParseRules) {
|
||||
}
|
||||
}
|
||||
|
||||
TEST_F(ManifestParserTest, IdParseRules) {
|
||||
base::test::ScopedFeatureList feature_list;
|
||||
feature_list.InitAndEnableFeature(blink::features::kWebAppEnableManifestId);
|
||||
// Empty manifest.
|
||||
{
|
||||
auto& manifest = ParseManifest("{ }");
|
||||
ASSERT_EQ(0u, GetErrorCount());
|
||||
EXPECT_EQ(String(), manifest->id);
|
||||
}
|
||||
// Does not contain id field.
|
||||
{
|
||||
auto& manifest = ParseManifest("{\"start_url\": \"/start?query=a\" }");
|
||||
ASSERT_EQ(0u, GetErrorCount());
|
||||
EXPECT_EQ("start?query=a", manifest->id);
|
||||
}
|
||||
// Empty string.
|
||||
{
|
||||
auto& manifest =
|
||||
ParseManifest("{ \"start_url\": \"/start?query=a\", \"id\": \"\" }");
|
||||
ASSERT_EQ(0u, GetErrorCount());
|
||||
EXPECT_EQ("", manifest->id);
|
||||
}
|
||||
// Smoke test.
|
||||
{
|
||||
auto& manifest =
|
||||
ParseManifest("{ \"start_url\": \"/start?query=a\", \"id\": \"foo\" }");
|
||||
ASSERT_EQ(0u, GetErrorCount());
|
||||
EXPECT_EQ("foo", manifest->id);
|
||||
}
|
||||
}
|
||||
|
||||
TEST_F(ManifestParserTest, StartURLParseRules) {
|
||||
// Smoke test.
|
||||
{
|
||||
@ -279,6 +310,7 @@ TEST_F(ManifestParserTest, StartURLParseRules) {
|
||||
{
|
||||
auto& manifest = ParseManifest("{ \"start_url\": {} }");
|
||||
ASSERT_TRUE(manifest->start_url.IsEmpty());
|
||||
ASSERT_EQ(String(), manifest->id);
|
||||
EXPECT_EQ(1u, GetErrorCount());
|
||||
EXPECT_EQ("property 'start_url' ignored, type string expected.",
|
||||
errors()[0]);
|
||||
|
@ -674,6 +674,10 @@ void WebRuntimeFeatures::EnableParseUrlProtocolHandler(bool enable) {
|
||||
RuntimeEnabledFeatures::SetParseUrlProtocolHandlerEnabled(enable);
|
||||
}
|
||||
|
||||
void WebRuntimeFeatures::EnableWebAppManifestId(bool enable) {
|
||||
RuntimeEnabledFeatures::SetWebAppManifestIdEnabled(enable);
|
||||
}
|
||||
|
||||
void WebRuntimeFeatures::EnableWebID(bool enable) {
|
||||
RuntimeEnabledFeatures::SetWebIDEnabled(enable);
|
||||
}
|
||||
|
@ -2214,6 +2214,13 @@
|
||||
origin_trial_feature_name: "WebAppLinkCapturing",
|
||||
origin_trial_os: ["chromeos"],
|
||||
},
|
||||
{
|
||||
// This flag enables the Manifest parser to handle id field.
|
||||
// Also enabled when blink::features::kWebAppEnableManifestId is
|
||||
// overridden on the command line (or via chrome://flags).
|
||||
name: "WebAppManifestId",
|
||||
status: "experimental",
|
||||
},
|
||||
{
|
||||
name:"WebAppsLockScreen",
|
||||
status:"experimental",
|
||||
|
@ -46159,6 +46159,7 @@ from previous Chrome versions.
|
||||
<int value="-1269084216" label="ash-md"/>
|
||||
<int value="-1268836676" label="disable-out-of-process-pdf"/>
|
||||
<int value="-1267958145" label="disable-pdf-material-ui"/>
|
||||
<int value="-1265627803" label="WebAppEnableManifestId:enabled"/>
|
||||
<int value="-1262730949" label="EnableDspHotword:enabled"/>
|
||||
<int value="-1262303946" label="SubresourceRedirectPreviews:disabled"/>
|
||||
<int value="-1262152606" label="disable-lock-screen-apps"/>
|
||||
@ -48003,6 +48004,7 @@ from previous Chrome versions.
|
||||
label="enable-experimental-accessibility-language-detection"/>
|
||||
<int value="324522065" label="app-menu-icon"/>
|
||||
<int value="324631366" label="enable-drive-search-in-app-launcher"/>
|
||||
<int value="325489620" label="WebAppEnableManifestId:disabled"/>
|
||||
<int value="325907076" label="SanitizerAPI:enabled"/>
|
||||
<int value="327045548" label="SafeSearchUrlReporting:enabled"/>
|
||||
<int value="328722396" label="NTPCondensedLayout:disabled"/>
|
||||
|
Reference in New Issue
Block a user