LibWeb+LibWebView: Add an internal API to expire cookies with an offset

Cookies have a minimum expiry resolution of 1 second. So to test cookie
expiration, the test had to idle for at least a second, which is quite a
noticeable delay now that LibWeb tests are parallelized.

Instead, we can add an internal API to expire cookies with a time offset
to avoid this idle delay.
This commit is contained in:
Timothy Flynn 2024-10-13 08:56:46 -04:00 committed by Andreas Kling
parent 6ce473af72
commit e070ed5658
Notes: github-actions[bot] 2024-10-14 06:54:34 +00:00
13 changed files with 51 additions and 24 deletions

View file

@ -16,10 +16,10 @@ SameSite=Lax: "cookie=value"
SameSite=Strict: "cookie=value"
SameSite=None: ""
Max-Age (before expiration): "cookie-max-age1=value; cookie-max-age2=value"
Expires (before expiration): "cookie-expires=value; cookie-max-age1=value; cookie-max-age2=value"
Max-Age (after expiration): "cookie-max-age2=value"
Expires (after expiration): ""
Max-Age in past: ""
Expires (before expiration): "cookie-expires=value"
Expires (after expiration): ""
Expires in past: ""
Invalid expiry (date does not exist): "cookie=value"
Invalid expiry (missing time): "cookie=value"

View file

@ -122,13 +122,12 @@
deleteCookie("cookie");
};
const maxAgeTest1 = () => {
const maxAgeTest = () => {
document.cookie = "cookie-max-age1=value; max-age=1";
document.cookie = `cookie-max-age2=value; max-age=${"1".repeat(1024)}`;
printCookies("Max-Age (before expiration)");
};
const maxAgeTest2 = () => {
internals.expireCookiesWithTimeOffset(2);
printCookies("Max-Age (after expiration)");
deleteCookie("cookie-max-age2");
};
@ -139,15 +138,14 @@
printCookies("Max-Age in past");
};
const expiresTest1 = () => {
const expiresTest = () => {
let expiry = new Date(Date.now() + 1000);
expiry = expiry.toUTCString();
document.cookie = `cookie-expires=value; expires=${expiry}`;
printCookies("Expires (before expiration)");
};
const expiresTest2 = () => {
internals.expireCookiesWithTimeOffset(2);
printCookies("Expires (after expiration)");
};
@ -179,7 +177,7 @@
deleteCookie("cookie");
};
asyncTest(done => {
test(() => {
basicTest();
multipleCookiesTest();
@ -200,18 +198,11 @@
publicSuffixTest();
sameSiteTest();
maxAgeTest1();
expiresTest1();
maxAgeTest();
maxAgeInPastTest();
setTimeout(() => {
maxAgeTest2();
expiresTest2();
maxAgeInPastTest();
expiresInPastTest();
invalidExpiryTest();
done();
}, 1200);
expiresTest();
expiresInPastTest();
invalidExpiryTest();
});
</script>

View file

@ -184,4 +184,9 @@ void Internals::simulate_drop(double x, double y)
page.handle_drag_and_drop_event(DragEvent::Type::Drop, position, position, UIEvents::MouseButton::Primary, 0, 0, {});
}
void Internals::expire_cookies_with_time_offset(WebIDL::LongLong seconds)
{
internals_page().client().page_did_expire_cookies_with_time_offset(AK::Duration::from_seconds(seconds));
}
}

View file

@ -44,6 +44,8 @@ public:
void simulate_drag_move(double x, double y);
void simulate_drop(double x, double y);
void expire_cookies_with_time_offset(WebIDL::LongLong seconds);
private:
explicit Internals(JS::Realm&);
virtual void initialize(JS::Realm&) override;

View file

@ -33,4 +33,6 @@ interface Internals {
undefined simulateDragStart(double x, double y, DOMString mimeType, DOMString contents);
undefined simulateDragMove(double x, double y);
undefined simulateDrop(double x, double y);
undefined expireCookiesWithTimeOffset(long long seconds);
};

View file

@ -343,6 +343,7 @@ public:
virtual String page_did_request_cookie(URL::URL const&, Cookie::Source) { return {}; }
virtual void page_did_set_cookie(URL::URL const&, Cookie::ParsedCookie const&, Cookie::Source) { }
virtual void page_did_update_cookie(Web::Cookie::Cookie) { }
virtual void page_did_expire_cookies_with_time_offset(AK::Duration) { }
virtual void page_did_update_resource_count(i32) { }
struct NewWebViewResult {
JS::GCPtr<Page> page;

View file

@ -213,6 +213,11 @@ Optional<Web::Cookie::Cookie> CookieJar::get_named_cookie(URL::URL const& url, S
return {};
}
void CookieJar::expire_cookies_with_time_offset(AK::Duration offset)
{
m_transient_storage.purge_expired_cookies(offset);
}
// https://www.ietf.org/archive/id/draft-ietf-httpbis-rfc6265bis-15.html#section-5.1.2
Optional<String> CookieJar::canonicalize_domain(const URL::URL& url)
{
@ -643,12 +648,19 @@ Optional<Web::Cookie::Cookie> CookieJar::TransientStorage::get_cookie(CookieStor
return m_cookies.get(key);
}
UnixDateTime CookieJar::TransientStorage::purge_expired_cookies()
UnixDateTime CookieJar::TransientStorage::purge_expired_cookies(Optional<AK::Duration> offset)
{
auto now = UnixDateTime::now();
auto is_expired = [&](auto const&, auto const& cookie) { return cookie.expiry_time < now; };
if (offset.has_value()) {
now += *offset;
for (auto& cookie : m_dirty_cookies)
cookie.value.expiry_time -= *offset;
}
auto is_expired = [&](auto const&, auto const& cookie) { return cookie.expiry_time < now; };
m_cookies.remove_all_matching(is_expired);
return now;
}

View file

@ -47,7 +47,7 @@ class CookieJar {
size_t size() const { return m_cookies.size(); }
UnixDateTime purge_expired_cookies();
UnixDateTime purge_expired_cookies(Optional<AK::Duration> offset = {});
auto take_dirty_cookies() { return move(m_dirty_cookies); }
@ -94,6 +94,7 @@ public:
Vector<Web::Cookie::Cookie> get_all_cookies();
Vector<Web::Cookie::Cookie> get_all_cookies(URL::URL const& url);
Optional<Web::Cookie::Cookie> get_named_cookie(URL::URL const& url, StringView name);
void expire_cookies_with_time_offset(AK::Duration);
private:
explicit CookieJar(Optional<PersistedStorage>);

View file

@ -445,6 +445,11 @@ void WebContentClient::did_update_cookie(Web::Cookie::Cookie const& cookie)
Application::cookie_jar().update_cookie(cookie);
}
void WebContentClient::did_expire_cookies_with_time_offset(AK::Duration offset)
{
Application::cookie_jar().expire_cookies_with_time_offset(offset);
}
Messages::WebContentClient::DidRequestNewWebViewResponse WebContentClient::did_request_new_web_view(u64 page_id, Web::HTML::ActivateTab const& activate_tab, Web::HTML::WebViewHints const& hints, Optional<u64> const& page_index)
{
if (auto view = view_for_page_id(page_id); view.has_value()) {

View file

@ -93,6 +93,7 @@ private:
virtual Messages::WebContentClient::DidRequestCookieResponse did_request_cookie(URL::URL const&, Web::Cookie::Source) override;
virtual void did_set_cookie(URL::URL const&, Web::Cookie::ParsedCookie const&, Web::Cookie::Source) override;
virtual void did_update_cookie(Web::Cookie::Cookie const&) override;
virtual void did_expire_cookies_with_time_offset(AK::Duration) override;
virtual Messages::WebContentClient::DidRequestNewWebViewResponse did_request_new_web_view(u64 page_id, Web::HTML::ActivateTab const&, Web::HTML::WebViewHints const&, Optional<u64> const& page_index) override;
virtual void did_request_activate_tab(u64 page_id) override;
virtual void did_close_browsing_context(u64 page_id) override;

View file

@ -502,6 +502,11 @@ void PageClient::page_did_update_cookie(Web::Cookie::Cookie cookie)
client().async_did_update_cookie(move(cookie));
}
void PageClient::page_did_expire_cookies_with_time_offset(AK::Duration offset)
{
client().async_did_expire_cookies_with_time_offset(offset);
}
void PageClient::page_did_update_resource_count(i32 count_waiting)
{
client().async_did_update_resource_count(m_id, count_waiting);

View file

@ -146,6 +146,7 @@ private:
virtual String page_did_request_cookie(URL::URL const&, Web::Cookie::Source) override;
virtual void page_did_set_cookie(URL::URL const&, Web::Cookie::ParsedCookie const&, Web::Cookie::Source) override;
virtual void page_did_update_cookie(Web::Cookie::Cookie) override;
virtual void page_did_expire_cookies_with_time_offset(AK::Duration) override;
virtual void page_did_update_resource_count(i32) override;
virtual NewWebViewResult page_did_request_new_web_view(Web::HTML::ActivateTab, Web::HTML::WebViewHints, Web::HTML::TokenizedFeature::NoOpener) override;
virtual void page_did_request_activate_tab() override;

View file

@ -71,6 +71,7 @@ endpoint WebContentClient
did_request_cookie(URL::URL url, Web::Cookie::Source source) => (String cookie)
did_set_cookie(URL::URL url, Web::Cookie::ParsedCookie cookie, Web::Cookie::Source source) => ()
did_update_cookie(Web::Cookie::Cookie cookie) =|
did_expire_cookies_with_time_offset(AK::Duration offset) =|
did_update_resource_count(u64 page_id, i32 count_waiting) =|
did_request_new_web_view(u64 page_id, Web::HTML::ActivateTab activate_tab, Web::HTML::WebViewHints hints, Optional<u64> page_index) => (String handle)
did_request_activate_tab(u64 page_id) =|