Skip to content

Commit

Permalink
Web Manifest: support id member
Browse files Browse the repository at this point in the history
https://bugs.webkit.org/show_bug.cgi?id=230596
rdar://83656112

Implementation of Web Manifest's id member:
https://www.w3.org/TR/appmanifest/#id-member

The manifest's id member is a string that represents the identity for the application. The identity takes the form of a URL, which is same origin as the start URL.

Reviewed by Devin Rousso.

* Source/WebCore/Modules/applicationmanifest/ApplicationManifest.h:
* Source/WebCore/Modules/applicationmanifest/ApplicationManifestParser.cpp:
(WebCore::ApplicationManifestParser::parseManifest):
(WebCore::ApplicationManifestParser::parseId):
* Source/WebCore/Modules/applicationmanifest/ApplicationManifestParser.h:
* Source/WebKit/Shared/WebCoreArgumentCoders.serialization.in:
* Source/WebKit/UIProcess/API/Cocoa/_WKApplicationManifest.h:
* Source/WebKit/UIProcess/API/Cocoa/_WKApplicationManifest.mm:
(-[_WKApplicationManifest initWithCoder:]):
(-[_WKApplicationManifest encodeWithCoder:]):
(-[_WKApplicationManifest manifestId]):
* Tools/TestWebKitAPI/Tests/WebCore/ApplicationManifestParser.cpp:
(ApplicationManifestParserTest::testId):
(assertManifestHasDefaultValues):
(TEST_F):

Canonical link: https://commits.webkit.org/256182@main
  • Loading branch information
marcoscaceres authored and rreno committed Nov 1, 2022
1 parent 972d19f commit 7e44837
Show file tree
Hide file tree
Showing 7 changed files with 96 additions and 0 deletions.
Expand Up @@ -61,6 +61,7 @@ struct ApplicationManifest {
URL scope;
Display display;
URL startURL;
URL id;
Color themeColor;
Vector<Icon> icons;
};
Expand Down
Expand Up @@ -80,6 +80,7 @@ ApplicationManifest ApplicationManifestParser::parseManifest(const String& text,
parsedManifest.scope = parseScope(*manifest, documentURL, parsedManifest.startURL);
parsedManifest.themeColor = parseColor(*manifest, "theme_color"_s);
parsedManifest.icons = parseIcons(*manifest);
parsedManifest.id = parseId(*manifest, parsedManifest.startURL);

if (m_document)
m_document->processApplicationManifest(parsedManifest);
Expand Down Expand Up @@ -279,6 +280,36 @@ static bool isInScope(const URL& scopeURL, const URL& targetURL)
return false;
}

URL ApplicationManifestParser::parseId(const JSON::Object& manifest, const URL& startURL)
{
auto idValue = manifest.getValue("id"_s);
if (!idValue)
return startURL;

auto idStringValue = idValue->asString();
if (!idStringValue) {
logManifestPropertyNotAString("id"_s);
return startURL;
}

if (idStringValue.isEmpty())
return startURL;

auto baseOrigin = SecurityOrigin::create(startURL);

URL idURL(baseOrigin->toURL(), idStringValue);

if (!idURL.isValid()) {
logManifestPropertyInvalidURL("id"_s);
return startURL;
}

if (!protocolHostAndPortAreEqual(idURL, startURL))
return startURL;

return idURL;
}

URL ApplicationManifestParser::parseScope(const JSON::Object& manifest, const URL& documentURL, const URL& startURL)
{
URL defaultScope { startURL, "./"_s };
Expand Down
Expand Up @@ -51,6 +51,7 @@ class ApplicationManifestParser {
String parseShortName(const JSON::Object&);
URL parseScope(const JSON::Object&, const URL&, const URL&);
Vector<ApplicationManifest::Icon> parseIcons(const JSON::Object&);
URL parseId(const JSON::Object&, const URL&);

Color parseColor(const JSON::Object&, const String& propertyName);
String parseGenericString(const JSON::Object&, const String&);
Expand Down
Expand Up @@ -712,6 +712,7 @@ struct WebCore::ApplicationManifest {
URL scope
WebCore::ApplicationManifest::Display display
URL startURL
URL id
WebCore::Color themeColor
Vector<WebCore::ApplicationManifest::Icon> icons
}
Expand Down
1 change: 1 addition & 0 deletions Source/WebKit/UIProcess/API/Cocoa/_WKApplicationManifest.h
Expand Up @@ -56,6 +56,7 @@ WK_CLASS_AVAILABLE(macos(10.13.4), ios(11.3))
@property (nonatomic, readonly, nullable, copy) NSString *applicationDescription;
@property (nonatomic, readonly, nullable, copy) NSURL *scope;
@property (nonatomic, readonly, copy) NSURL *startURL;
@property (nonatomic, readonly, copy) NSURL *manifestId WK_API_AVAILABLE(macos(WK_MAC_TBA), ios(WK_IOS_TBA));
@property (nonatomic, readonly) _WKApplicationManifestDisplayMode displayMode;
@property (nonatomic, readonly, copy) NSArray<_WKApplicationManifestIcon *> *icons WK_API_AVAILABLE(macos(13.0), ios(16.0));

Expand Down
13 changes: 13 additions & 0 deletions Source/WebKit/UIProcess/API/Cocoa/_WKApplicationManifest.mm
Expand Up @@ -155,6 +155,7 @@ - (instancetype)initWithCoder:(NSCoder *)aDecoder
URL scopeURL = [aDecoder decodeObjectOfClass:[NSURL class] forKey:@"scope"];
NSInteger display = [aDecoder decodeIntegerForKey:@"display"];
URL startURL = [aDecoder decodeObjectOfClass:[NSURL class] forKey:@"start_url"];
URL manifestId = [aDecoder decodeObjectOfClass:[NSURL class] forKey:@"manifestId"];
WebCore::CocoaColor *themeColor = [aDecoder decodeObjectOfClass:[WebCore::CocoaColor class] forKey:@"theme_color"];
NSArray<_WKApplicationManifestIcon *> *icons = [aDecoder decodeObjectOfClasses:[NSSet setWithArray:@[[NSArray class], [_WKApplicationManifestIcon class]]] forKey:@"icons"];

Expand All @@ -165,6 +166,7 @@ - (instancetype)initWithCoder:(NSCoder *)aDecoder
WTFMove(scopeURL),
static_cast<WebCore::ApplicationManifest::Display>(display),
WTFMove(startURL),
WTFMove(manifestId),
WebCore::roundAndClampToSRGBALossy(themeColor.CGColor),
makeVector<WebCore::ApplicationManifest::Icon>(icons),
};
Expand Down Expand Up @@ -192,6 +194,7 @@ - (void)encodeWithCoder:(NSCoder *)aCoder
[aCoder encodeObject:self.scope forKey:@"scope"];
[aCoder encodeInteger:static_cast<NSInteger>(_applicationManifest->applicationManifest().display) forKey:@"display"];
[aCoder encodeObject:self.startURL forKey:@"start_url"];
[aCoder encodeObject:self.manifestId forKey:@"manifestId"];
[aCoder encodeObject:self.themeColor forKey:@"theme_color"];
[aCoder encodeObject:self.icons forKey:@"icons"];
}
Expand Down Expand Up @@ -265,6 +268,11 @@ - (_WKApplicationManifestDisplayMode)displayMode
}).autorelease();
}

- (NSURL *)manifestId
{
return _applicationManifest->applicationManifest().id;
}

#else // ENABLE(APPLICATION_MANIFEST)

+ (_WKApplicationManifest *)applicationManifestFromJSON:(NSString *)json manifestURL:(NSURL *)manifestURL documentURL:(NSURL *)documentURL
Expand Down Expand Up @@ -327,6 +335,11 @@ - (_WKApplicationManifestDisplayMode)displayMode
return nil;
}

- (NSURL *)manifestId
{
return nil;
}

#endif // ENABLE(APPLICATION_MANIFEST)

@end
48 changes: 48 additions & 0 deletions Tools/TestWebKitAPI/Tests/WebCore/ApplicationManifestParser.cpp
Expand Up @@ -53,6 +53,7 @@ class ApplicationManifestParserTest : public testing::Test {
public:
URL m_manifestURL;
URL m_documentURL;
URL m_startURL;

virtual void SetUp()
{
Expand Down Expand Up @@ -176,6 +177,21 @@ class ApplicationManifestParserTest : public testing::Test {
EXPECT_EQ(expectedValues, value);
}

void testId(const String& rawJSON, const URL& expectedValue)
{
auto manifest = parseTopLevelProperty("id"_s, rawJSON);
auto value = manifest.id;
EXPECT_STREQ(expectedValue.string().utf8().data(), value.string().utf8().data());
}

void testId(const String& rawJSON, const URL& startURL, const String& expectedValue)
{
String manifestContent = "{ \"id\" : \"" + rawJSON + "\", \"start_url\" : \"" + startURL.string() + "\" }";
auto manifest = parseString(manifestContent);
auto value = manifest.id;
EXPECT_STREQ(expectedValue.utf8().data(), value.string().utf8().data());
}

};

static void assertManifestHasDefaultValues(const URL& manifestURL, const URL& documentURL, const ApplicationManifest& manifest)
Expand All @@ -185,6 +201,7 @@ static void assertManifestHasDefaultValues(const URL& manifestURL, const URL& do
EXPECT_TRUE(manifest.description.isNull());
EXPECT_STREQ("https://example.com/", manifest.scope.string().utf8().data());
EXPECT_STREQ(documentURL.string().utf8().data(), manifest.startURL.string().utf8().data());
EXPECT_STREQ(manifest.id.string().utf8().data(), manifest.startURL.string().utf8().data());
}

TEST_F(ApplicationManifestParserTest, DefaultManifest)
Expand All @@ -195,6 +212,37 @@ TEST_F(ApplicationManifestParserTest, DefaultManifest)
assertManifestHasDefaultValues(m_manifestURL, m_documentURL, parseString("This is 100% not JSON."_s));
}

TEST_F(ApplicationManifestParserTest, Id)
{
m_documentURL = URL { "https://example.com/home"_s };
m_manifestURL = URL { "https://example.com/manifest.json"_s };

testId("123"_s, m_documentURL);
testId("null"_s, m_documentURL);
testId("true"_s, m_documentURL);
testId("{ }"_s, m_documentURL);
testId("[ ]"_s, m_documentURL);
testId("[ \"http://example.com/somepage\" ]"_s, m_documentURL);
testId("\"\""_s, m_documentURL);
testId("\"http:?\""_s, m_documentURL);

testId("\"https://other-domain.com\""_s, m_documentURL);
testId("\"https://invalid.com:a\""_s, m_documentURL);

m_startURL = URL { "https://example.com/my-app/start?query=q#fragment"_s };
testId(""_s, m_startURL , m_startURL.string());
testId("/"_s, m_startURL, "https://example.com/"_s);
testId("foo"_s, m_startURL, "https://example.com/foo"_s);
testId("./foo"_s, m_startURL, "https://example.com/foo"_s);
testId("foo/"_s, m_startURL, "https://example.com/foo/"_s);
testId("../../foo/bar"_s, m_startURL, "https://example.com/foo/bar"_s);
testId("../../foo/bar?query=hi#hi"_s, m_startURL, "https://example.com/foo/bar?query=hi#hi"_s);

testId("https://example.com/foo"_s, m_startURL, "https://example.com/foo"_s);
testId("https://anothersite.com/foo"_s, m_startURL, m_startURL.string());
testId("https://invalid.com:a"_s, m_startURL, m_startURL.string());
}

TEST_F(ApplicationManifestParserTest, StartURL)
{
m_documentURL = URL { "https://example.com/home"_s };
Expand Down

0 comments on commit 7e44837

Please sign in to comment.