Skip to content

Commit 1fd68bc

Browse files
committed
Cookie Store API: add branch for empty string path
https://bugs.webkit.org/show_bug.cgi?id=297268 Reviewed by NOBODY (OOPS!). Implement whatwg/cookiestore#283 with tests upstreamed at web-platform-tests/wpt#54264
1 parent f1ea70a commit 1fd68bc

12 files changed

+96
-57
lines changed
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
2+
PASS CookieListItem - cookieStore.set with empty string path defaults to current URL
3+
PASS CookieListItem - cookieStore.set with empty string path defaults to current URL with __host- prefix
4+
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
<!-- This file is required for WebKit test infrastructure to run the templated test -->
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
// META: title=Cookie Store API: set()'s path option
2+
// META: script=/resources/testdriver.js
3+
// META: script=/resources/testdriver-vendor.js
4+
5+
promise_test(async testCase => {
6+
const currentUrl = new URL(self.location.href);
7+
const currentPath = currentUrl.pathname;
8+
const currentDirectory = currentPath.substr(0, currentPath.lastIndexOf('/'));
9+
10+
await cookieStore.set({ name: 'cookie-name', value: 'cookie-value', path: '' });
11+
testCase.add_cleanup(async () => {
12+
await cookieStore.delete({ name: 'cookie-name', path: currentDirectory });
13+
});
14+
15+
const internalCookie = await test_driver.get_named_cookie('cookie-name');
16+
assert_equals(internalCookie.path, currentDirectory);
17+
}, 'CookieListItem - cookieStore.set with empty string path defaults to current URL');
18+
19+
promise_test(async testCase => {
20+
const currentUrl = new URL(self.location.href);
21+
const currentPath = currentUrl.pathname;
22+
return promise_rejects_js(testCase, TypeError, cookieStore.set({ name: '__host-cookie-name', value: 'cookie-value', path: '' }));
23+
}, 'CookieListItem - cookieStore.set with empty string path defaults to current URL with __host- prefix');

LayoutTests/imported/w3c/web-platform-tests/cookiestore/cookieStore_special_names.https.any-expected.txt

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -15,25 +15,25 @@ PASS cookieStore.set with __Host- prefix and a domain option
1515
PASS cookieStore.set with __Host- prefix a path option
1616
PASS cookieStore.set with __host- prefix and a domain option
1717
PASS cookieStore.set with __host- prefix a path option
18-
FAIL cookieStore.set with __HostHttp- prefix rejects assert_unreached: Should have rejected: undefined Reached unreachable code
19-
FAIL cookieStore.set with __hosthttp- prefix rejects assert_unreached: Should have rejected: undefined Reached unreachable code
18+
FAIL cookieStore.set with __Host-Http- prefix rejects assert_unreached: Should have rejected: undefined Reached unreachable code
19+
FAIL cookieStore.set with __host-http- prefix rejects assert_unreached: Should have rejected: undefined Reached unreachable code
2020
FAIL cookieStore.set with __Http- prefix rejects assert_unreached: Should have rejected: undefined Reached unreachable code
2121
FAIL cookieStore.set with __http- prefix rejects assert_unreached: Should have rejected: undefined Reached unreachable code
2222
FAIL cookieStore.set with __Http- prefix rejects assert_unreached: Should have rejected: undefined Reached unreachable code
2323
FAIL cookieStore.set with __Http- prefix rejects assert_unreached: Should have rejected: undefined Reached unreachable code
24-
FAIL cookieStore.set with __HostHttp- prefix rejects assert_unreached: Should have rejected: undefined Reached unreachable code
25-
FAIL cookieStore.set with __HostHttp- prefix rejects assert_unreached: Should have rejected: undefined Reached unreachable code
24+
FAIL cookieStore.set with __Host-Http- prefix rejects assert_unreached: Should have rejected: undefined Reached unreachable code
25+
FAIL cookieStore.set with __Host-Http- prefix rejects assert_unreached: Should have rejected: undefined Reached unreachable code
2626
PASS cookieStore.set with malformed name.
2727
FAIL cookieStore.set a nameless cookie cannot have __Host- prefix promise_test: Unhandled rejection with value: object "TypeError: The cookie name and value must not both be empty."
2828
FAIL cookieStore.set a nameless cookie cannot have __Secure- prefix promise_test: Unhandled rejection with value: object "TypeError: The cookie name and value must not both be empty."
2929
FAIL cookieStore.set a nameless cookie cannot have __Http- prefix promise_test: Unhandled rejection with value: object "TypeError: The cookie name and value must not both be empty."
30-
FAIL cookieStore.set a nameless cookie cannot have __HostHttp- prefix promise_test: Unhandled rejection with value: object "TypeError: The cookie name and value must not both be empty."
30+
FAIL cookieStore.set a nameless cookie cannot have __Host-Http- prefix promise_test: Unhandled rejection with value: object "TypeError: The cookie name and value must not both be empty."
3131
FAIL cookieStore.set a nameless cookie cannot have __Host- prefix promise_test: Unhandled rejection with value: object "TypeError: The cookie name and value must not both be empty."
3232
FAIL cookieStore.set a nameless cookie cannot have __Host- prefix promise_test: Unhandled rejection with value: object "TypeError: The cookie name and value must not both be empty."
3333
FAIL cookieStore.set a nameless cookie cannot have __Secure- prefix promise_test: Unhandled rejection with value: object "TypeError: The cookie name and value must not both be empty."
3434
FAIL cookieStore.set a nameless cookie cannot have __Secure- prefix promise_test: Unhandled rejection with value: object "TypeError: The cookie name and value must not both be empty."
3535
FAIL cookieStore.set a nameless cookie cannot have __Http- prefix promise_test: Unhandled rejection with value: object "TypeError: The cookie name and value must not both be empty."
3636
FAIL cookieStore.set a nameless cookie cannot have __Http- prefix promise_test: Unhandled rejection with value: object "TypeError: The cookie name and value must not both be empty."
37-
FAIL cookieStore.set a nameless cookie cannot have __HostHttp- prefix promise_test: Unhandled rejection with value: object "TypeError: The cookie name and value must not both be empty."
38-
FAIL cookieStore.set a nameless cookie cannot have __HostHttp- prefix promise_test: Unhandled rejection with value: object "TypeError: The cookie name and value must not both be empty."
37+
FAIL cookieStore.set a nameless cookie cannot have __Host-Http- prefix promise_test: Unhandled rejection with value: object "TypeError: The cookie name and value must not both be empty."
38+
FAIL cookieStore.set a nameless cookie cannot have __Host-Http- prefix promise_test: Unhandled rejection with value: object "TypeError: The cookie name and value must not both be empty."
3939

LayoutTests/imported/w3c/web-platform-tests/cookiestore/cookieStore_special_names.https.any.js

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -56,8 +56,8 @@
5656
}, `cookieStore.set with ${prefix} prefix a path option`);
5757
});
5858

59-
['__HostHttp-', '__hosthttp-', '__Http-', '__http-', ' __Http-', '\t__Http-',
60-
' __HostHttp-', '\t__HostHttp-'].forEach(prefix => {
59+
['__Host-Http-', '__host-http-', '__Http-', '__http-', ' __Http-', '\t__Http-',
60+
' __Host-Http-', '\t__Host-Http-'].forEach(prefix => {
6161
promise_test(async testCase => {
6262
await promise_rejects_js(testCase, TypeError,
6363
cookieStore.set({ name: `${prefix}cookie-name`, value: 'cookie-value'}));
@@ -75,8 +75,8 @@ promise_test(async testCase => {
7575
assert_true(exceptionThrown, "No exception thrown.");
7676
}, 'cookieStore.set with malformed name.');
7777

78-
['__Host-', '__Secure-', '__Http-', '__HostHttp-', ' __Host-', '\t__Host-', ' __Secure-',
79-
'\t__Secure-', ' __Http-', '\t__Http-', ' __HostHttp-', '\t__HostHttp-'].forEach(prefix => {
78+
['__Host-', '__Secure-', '__Http-', '__Host-Http-', ' __Host-', '\t__Host-', ' __Secure-',
79+
'\t__Secure-', ' __Http-', '\t__Http-', ' __Host-Http-', '\t__Host-Http-'].forEach(prefix => {
8080
promise_test(async testCase => {
8181
// Nameless cookies cannot have special prefixes
8282
await cookieStore.delete('');

LayoutTests/imported/w3c/web-platform-tests/cookiestore/cookieStore_special_names.https.any.serviceworker-expected.txt

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -15,25 +15,25 @@ PASS cookieStore.set with __Host- prefix and a domain option
1515
PASS cookieStore.set with __Host- prefix a path option
1616
PASS cookieStore.set with __host- prefix and a domain option
1717
PASS cookieStore.set with __host- prefix a path option
18-
FAIL cookieStore.set with __HostHttp- prefix rejects assert_unreached: Should have rejected: undefined Reached unreachable code
19-
FAIL cookieStore.set with __hosthttp- prefix rejects assert_unreached: Should have rejected: undefined Reached unreachable code
18+
FAIL cookieStore.set with __Host-Http- prefix rejects assert_unreached: Should have rejected: undefined Reached unreachable code
19+
FAIL cookieStore.set with __host-http- prefix rejects assert_unreached: Should have rejected: undefined Reached unreachable code
2020
FAIL cookieStore.set with __Http- prefix rejects assert_unreached: Should have rejected: undefined Reached unreachable code
2121
FAIL cookieStore.set with __http- prefix rejects assert_unreached: Should have rejected: undefined Reached unreachable code
2222
FAIL cookieStore.set with __Http- prefix rejects assert_unreached: Should have rejected: undefined Reached unreachable code
2323
FAIL cookieStore.set with __Http- prefix rejects assert_unreached: Should have rejected: undefined Reached unreachable code
24-
FAIL cookieStore.set with __HostHttp- prefix rejects assert_unreached: Should have rejected: undefined Reached unreachable code
25-
FAIL cookieStore.set with __HostHttp- prefix rejects assert_unreached: Should have rejected: undefined Reached unreachable code
24+
FAIL cookieStore.set with __Host-Http- prefix rejects assert_unreached: Should have rejected: undefined Reached unreachable code
25+
FAIL cookieStore.set with __Host-Http- prefix rejects assert_unreached: Should have rejected: undefined Reached unreachable code
2626
PASS cookieStore.set with malformed name.
2727
FAIL cookieStore.set a nameless cookie cannot have __Host- prefix promise_test: Unhandled rejection with value: object "TypeError: The cookie name and value must not both be empty."
2828
FAIL cookieStore.set a nameless cookie cannot have __Secure- prefix promise_test: Unhandled rejection with value: object "TypeError: The cookie name and value must not both be empty."
2929
FAIL cookieStore.set a nameless cookie cannot have __Http- prefix promise_test: Unhandled rejection with value: object "TypeError: The cookie name and value must not both be empty."
30-
FAIL cookieStore.set a nameless cookie cannot have __HostHttp- prefix promise_test: Unhandled rejection with value: object "TypeError: The cookie name and value must not both be empty."
30+
FAIL cookieStore.set a nameless cookie cannot have __Host-Http- prefix promise_test: Unhandled rejection with value: object "TypeError: The cookie name and value must not both be empty."
3131
FAIL cookieStore.set a nameless cookie cannot have __Host- prefix promise_test: Unhandled rejection with value: object "TypeError: The cookie name and value must not both be empty."
3232
FAIL cookieStore.set a nameless cookie cannot have __Host- prefix promise_test: Unhandled rejection with value: object "TypeError: The cookie name and value must not both be empty."
3333
FAIL cookieStore.set a nameless cookie cannot have __Secure- prefix promise_test: Unhandled rejection with value: object "TypeError: The cookie name and value must not both be empty."
3434
FAIL cookieStore.set a nameless cookie cannot have __Secure- prefix promise_test: Unhandled rejection with value: object "TypeError: The cookie name and value must not both be empty."
3535
FAIL cookieStore.set a nameless cookie cannot have __Http- prefix promise_test: Unhandled rejection with value: object "TypeError: The cookie name and value must not both be empty."
3636
FAIL cookieStore.set a nameless cookie cannot have __Http- prefix promise_test: Unhandled rejection with value: object "TypeError: The cookie name and value must not both be empty."
37-
FAIL cookieStore.set a nameless cookie cannot have __HostHttp- prefix promise_test: Unhandled rejection with value: object "TypeError: The cookie name and value must not both be empty."
38-
FAIL cookieStore.set a nameless cookie cannot have __HostHttp- prefix promise_test: Unhandled rejection with value: object "TypeError: The cookie name and value must not both be empty."
37+
FAIL cookieStore.set a nameless cookie cannot have __Host-Http- prefix promise_test: Unhandled rejection with value: object "TypeError: The cookie name and value must not both be empty."
38+
FAIL cookieStore.set a nameless cookie cannot have __Host-Http- prefix promise_test: Unhandled rejection with value: object "TypeError: The cookie name and value must not both be empty."
3939

LayoutTests/imported/w3c/web-platform-tests/cookiestore/w3c-import.log

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,7 @@ List of files:
5353
/LayoutTests/imported/w3c/web-platform-tests/cookiestore/cookieStore_set_domain_parsing.sub.https.html
5454
/LayoutTests/imported/w3c/web-platform-tests/cookiestore/cookieStore_set_domain_parsing.tentative.sub.https.html
5555
/LayoutTests/imported/w3c/web-platform-tests/cookiestore/cookieStore_set_limit.https.any.js
56+
/LayoutTests/imported/w3c/web-platform-tests/cookiestore/cookieStore_set_path.https.window.js
5657
/LayoutTests/imported/w3c/web-platform-tests/cookiestore/cookieStore_special_names.https.any.js
5758
/LayoutTests/imported/w3c/web-platform-tests/cookiestore/cookieStore_subscribe_arguments.https.any.js
5859
/LayoutTests/imported/w3c/web-platform-tests/cookiestore/cookieStore_subscriptions_empty.https.window.js

Source/WebCore/Modules/cookie-store/CookieStore.cpp

Lines changed: 22 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -442,18 +442,13 @@ void CookieStore::set(CookieInit&& options, Ref<DeferredPromise>&& promise)
442442
}
443443
}
444444

445-
if (cookie.name.startsWithIgnoringASCIICase("__Host-"_s)) {
446-
if (!options.domain.isNull()) {
447-
promise->reject(Exception { ExceptionCode::TypeError, "If the cookie name begins with \"__Host-\", the domain must not be specified."_s });
448-
return;
449-
}
450-
451-
if (!options.path.isNull() && options.path != "/"_s) {
452-
promise->reject(Exception { ExceptionCode::TypeError, "If the cookie name begins with \"__Host-\", the path must either not be specified or be \"/\"."_s });
453-
return;
454-
}
445+
// FIXME: This should be further down.
446+
if (!options.domain.isNull() && cookie.name.startsWithIgnoringASCIICase("__Host-"_s)) {
447+
promise->reject(Exception { ExceptionCode::TypeError, "If the cookie name begins with \"__Host-\", the domain must not be specified."_s });
448+
return;
455449
}
456450

451+
// FIXME: The specification does not perform this initialization of domain.
457452
cookie.domain = options.domain.isNull() ? domain : options.domain;
458453
if (!cookie.domain.isNull()) {
459454
if (cookie.domain.startsWith('.')) {
@@ -485,17 +480,24 @@ void CookieStore::set(CookieInit&& options, Ref<DeferredPromise>&& promise)
485480
}
486481

487482
cookie.path = WTFMove(options.path);
488-
if (!cookie.path.isNull()) {
489-
if (!cookie.path.startsWith('/')) {
490-
promise->reject(Exception { ExceptionCode::TypeError, "The path must begin with a '/'"_s });
491-
return;
492-
}
483+
ASSERT(!cookie.path.isNull());
484+
if (cookie.path.isEmpty())
485+
cookie.path = CookieUtil::defaultPathForURL(url);
493486

494-
// FIXME: <rdar://85515842> Obtain the encoded length without allocating and encoding.
495-
if (cookie.path.utf8().length() > maximumAttributeValueSize) {
496-
promise->reject(Exception { ExceptionCode::TypeError, makeString("The size of the path must not be greater than "_s, maximumAttributeValueSize, " bytes"_s) });
497-
return;
498-
}
487+
if (!cookie.path.startsWith('/')) {
488+
promise->reject(Exception { ExceptionCode::TypeError, "The path must begin with a '/'"_s });
489+
return;
490+
}
491+
492+
if (cookie.path != "/"_s && cookie.name.startsWithIgnoringASCIICase("__Host-"_s)) {
493+
promise->reject(Exception { ExceptionCode::TypeError, "If the cookie name begins with \"__Host-\", the eventual path must be \"/\"."_s });
494+
return;
495+
}
496+
497+
// FIXME: <rdar://85515842> Obtain the encoded length without allocating and encoding.
498+
if (cookie.path.utf8().length() > maximumAttributeValueSize) {
499+
promise->reject(Exception { ExceptionCode::TypeError, makeString("The size of the path must not be greater than "_s, maximumAttributeValueSize, " bytes"_s) });
500+
return;
499501
}
500502

501503
if (options.expires) {

Source/WebCore/platform/Cookie.h

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -137,7 +137,13 @@ struct CookieHash {
137137
static const bool safeToCompareToEmptyOrDeleted = false;
138138
};
139139

140-
}
140+
namespace CookieUtil {
141+
142+
WEBCORE_EXPORT String defaultPathForURL(const URL&);
143+
144+
} // namespace CookieUtil
145+
146+
} // namespace WebCore
141147

142148
namespace WTF {
143149
template<typename T> struct DefaultHash;

Source/WebCore/platform/network/Cookie.cpp

Lines changed: 20 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,25 @@ unsigned Cookie::hash() const
4242
return StringHash::hash(name) + StringHash::hash(domain) + StringHash::hash(path) + secure;
4343
}
4444
#endif
45-
45+
46+
namespace CookieUtil {
47+
48+
String defaultPathForURL(const URL& url)
49+
{
50+
// Algorithm to generate the default path is outlined in https://tools.ietf.org/html/rfc6265#section-5.1.4
51+
52+
String path = url.path().toString();
53+
if (path.isEmpty() || !path.startsWith('/'))
54+
return "/"_s;
55+
56+
auto lastSlashPosition = path.reverseFind('/');
57+
if (!lastSlashPosition)
58+
return "/"_s;
59+
60+
return path.left(lastSlashPosition);
61+
}
62+
63+
} // namespace CookieUtil
64+
4665
} // namespace WebCore
4766

0 commit comments

Comments
 (0)