LibWebView: Add async cookie deletion internals

Add Internals.deleteAllCookies(), backed by an async WebContent to
browser request and ack pair. CookieJar can now clear transient and
persisted cookies. Note that we only delete all cookies associated
with the current URL so that tests are able to run in parallel with
one another without impacting shared cookie state.
This commit is contained in:
Shannon Booth 2026-06-10 17:37:14 +02:00 committed by Shannon Booth
parent 5b856595b3
commit 790b9bd36a
15 changed files with 72 additions and 0 deletions

View file

@ -478,6 +478,16 @@ void Internals::expire_cookies_with_time_offset(WebIDL::LongLong seconds)
page().client().page_did_expire_cookies_with_time_offset(AK::Duration::from_seconds(seconds));
}
GC::Ref<WebIDL::Promise> Internals::delete_all_cookies()
{
auto& realm = this->realm();
auto promise = WebIDL::create_promise(realm);
auto const& document = as<HTML::Window>(HTML::relevant_global_object(*this)).associated_document();
page().client().page_did_delete_all_cookies(document.url(), promise);
return promise;
}
bool Internals::set_http_memory_cache_enabled(bool enabled)
{
auto was_enabled = Web::Fetch::Fetching::http_memory_cache_enabled();

View file

@ -82,6 +82,7 @@ public:
void simulate_drop(double x, double y);
void expire_cookies_with_time_offset(WebIDL::LongLong seconds);
GC::Ref<WebIDL::Promise> delete_all_cookies();
bool set_http_memory_cache_enabled(bool enabled);
WebIDL::ExceptionOr<void> set_content_blockers(String const& patterns);

View file

@ -70,6 +70,7 @@ interface Internals {
undefined simulateDrop(double x, double y);
undefined expireCookiesWithTimeOffset(long long seconds);
Promise<undefined> deleteAllCookies();
boolean setHttpMemoryCacheEnabled(boolean enabled);
undefined setContentBlockers(DOMString patterns);

View file

@ -481,6 +481,7 @@ public:
virtual void page_did_set_cookie(URL::URL const&, HTTP::Cookie::ParsedCookie const&, HTTP::Cookie::Source) { }
virtual void page_did_update_cookie(HTTP::Cookie::Cookie const&) { }
virtual void page_did_expire_cookies_with_time_offset(AK::Duration) { }
virtual void page_did_delete_all_cookies(URL::URL const&, GC::Ref<WebIDL::Promise>) { }
virtual void page_did_store_hsts_policy(String const&, HTTP::HSTS::ParsedHSTSPolicy const&) { }
virtual bool page_did_is_known_hsts_host(String const&) { return false; }
virtual Optional<String> page_did_request_storage_item([[maybe_unused]] Web::StorageAPI::StorageEndpointType storage_endpoint, [[maybe_unused]] String const& storage_key, [[maybe_unused]] String const& bottle_key) { return {}; }

View file

@ -540,6 +540,14 @@ void CookieJar::expire_cookies_with_time_offset(AK::Duration offset)
m_transient_storage.purge_expired_cookies(offset);
}
void CookieJar::delete_all_cookies(URL::URL const& url)
{
for (auto& cookie : get_all_cookies_webdriver(url)) {
cookie.expiry_time = UnixDateTime::earliest();
update_cookie(move(cookie));
}
}
void CookieJar::expire_cookies_accessed_since(UnixDateTime since)
{
m_transient_storage.expire_and_purge_cookies_accessed_since(since);

View file

@ -48,6 +48,7 @@ public:
Vector<HTTP::Cookie::Cookie> get_all_cookies_cookiestore(URL::URL const& url);
Optional<HTTP::Cookie::Cookie> get_named_cookie(URL::URL const& url, StringView name);
void expire_cookies_with_time_offset(AK::Duration);
void delete_all_cookies(URL::URL const&);
void expire_cookies_accessed_since(UnixDateTime since);
Requests::CacheSizes estimate_storage_size_accessed_since(UnixDateTime since) const;

View file

@ -982,6 +982,12 @@ void WebContentClient::did_expire_cookies_with_time_offset(AK::Duration offset)
Application::cookie_jar().expire_cookies_with_time_offset(offset);
}
void WebContentClient::did_request_delete_all_cookies(u64 page_id, u64 request_id, URL::URL url)
{
Application::cookie_jar().delete_all_cookies(url);
async_did_delete_all_cookies(page_id, request_id);
}
void WebContentClient::did_store_hsts_policy(String domain, HTTP::HSTS::ParsedHSTSPolicy policy)
{
Application::hsts_store().store_policy(domain, policy);

View file

@ -153,6 +153,7 @@ private:
virtual void did_set_cookie(URL::URL, HTTP::Cookie::ParsedCookie, HTTP::Cookie::Source) override;
virtual void did_update_cookie(HTTP::Cookie::Cookie) override;
virtual void did_expire_cookies_with_time_offset(AK::Duration) override;
virtual void did_request_delete_all_cookies(u64 page_id, u64 request_id, URL::URL) override;
virtual void did_store_hsts_policy(String, HTTP::HSTS::ParsedHSTSPolicy) override;
virtual Messages::WebContentClient::DidIsKnownHstsHostResponse did_is_known_hsts_host(String) override;
virtual Messages::WebContentClient::DidRequestStorageItemResponse did_request_storage_item(Web::StorageAPI::StorageEndpointType storage_endpoint, String storage_key, String bottle_key) override;

View file

@ -2053,6 +2053,12 @@ void ConnectionFromClient::retrieved_clipboard_entries(u64 page_id, u64 request_
page->page().retrieved_clipboard_entries(request_id, move(items));
}
void ConnectionFromClient::did_delete_all_cookies(u64 page_id, u64 request_id)
{
if (auto page = this->page(page_id); page.has_value())
page->did_delete_all_cookies(request_id);
}
void ConnectionFromClient::toggle_media_play_state(u64 page_id)
{
if (auto page = this->page(page_id); page.has_value())

View file

@ -147,6 +147,7 @@ private:
virtual void set_window_size(u64 page_id, Web::DevicePixelSize) override;
virtual void did_update_window_rect(u64 page_id) override;
virtual void handle_file_return(u64 page_id, i32 error, Optional<IPC::File> file, i32 request_id) override;
virtual void did_delete_all_cookies(u64 page_id, u64 request_id) override;
virtual void set_system_visibility_state(u64 page_id, Web::HTML::VisibilityState) override;
virtual void reset_zoom(u64 page_id) override;

View file

@ -32,11 +32,13 @@
#include <LibWeb/HTML/EventLoop/EventLoop.h>
#include <LibWeb/HTML/Navigable.h>
#include <LibWeb/HTML/Scripting/ClassicScript.h>
#include <LibWeb/HTML/Scripting/TemporaryExecutionContext.h>
#include <LibWeb/HTML/TraversableNavigable.h>
#include <LibWeb/HighResolutionTime/TimeOrigin.h>
#include <LibWeb/InvalidateDisplayList.h>
#include <LibWeb/Layout/Viewport.h>
#include <LibWeb/Painting/PaintableBox.h>
#include <LibWeb/WebIDL/Promise.h>
#include <LibWebView/SiteIsolation.h>
#include <LibWebView/ViewImplementation.h>
#include <WebContent/ConnectionFromClient.h>
@ -110,6 +112,8 @@ void PageClient::visit_edges(JS::Cell::Visitor& visitor)
Base::visit_edges(visitor);
visitor.visit(m_page);
visitor.visit(m_top_level_document_console_client);
for (auto& promise : m_pending_delete_all_cookies_promises)
visitor.visit(promise.value);
m_pending_dom_mutations.for_each([&](auto& pending_mutation) {
visitor.visit(pending_mutation.target);
});
@ -634,6 +638,28 @@ void PageClient::page_did_expire_cookies_with_time_offset(AK::Duration offset)
document->reset_cookie_version();
}
void PageClient::page_did_delete_all_cookies(URL::URL const& url, GC::Ref<Web::WebIDL::Promise> promise)
{
auto request_id = m_next_delete_all_cookies_request_id++;
m_pending_delete_all_cookies_promises.set(request_id, promise);
client().async_did_request_delete_all_cookies(m_id, request_id, url);
if (auto* document = page().top_level_browsing_context().active_document())
document->reset_cookie_version();
}
void PageClient::did_delete_all_cookies(u64 request_id)
{
auto maybe_promise = m_pending_delete_all_cookies_promises.take(request_id);
if (!maybe_promise.has_value())
return;
auto promise = maybe_promise.release_value();
auto& realm = promise->promise()->shape().realm();
Web::HTML::TemporaryExecutionContext execution_context { realm, Web::HTML::TemporaryExecutionContext::CallbacksEnabled::Yes };
Web::WebIDL::resolve_promise(realm, promise);
}
void PageClient::page_did_store_hsts_policy(String const& domain, HTTP::HSTS::ParsedHSTSPolicy const& policy)
{
client().async_did_store_hsts_policy(domain, policy);

View file

@ -112,6 +112,7 @@ public:
void queue_screenshot_task(Optional<Web::UniqueNodeID> node_id);
void send_current_needs_beforeunload_check();
void clear_pending_dom_mutations();
void did_delete_all_cookies(u64 request_id);
private:
struct PendingDOMMutation {
@ -179,6 +180,7 @@ private:
virtual void page_did_set_cookie(URL::URL const&, HTTP::Cookie::ParsedCookie const&, HTTP::Cookie::Source) override;
virtual void page_did_update_cookie(HTTP::Cookie::Cookie const&) override;
virtual void page_did_expire_cookies_with_time_offset(AK::Duration) override;
virtual void page_did_delete_all_cookies(URL::URL const&, GC::Ref<Web::WebIDL::Promise>) override;
virtual void page_did_store_hsts_policy(String const&, HTTP::HSTS::ParsedHSTSPolicy const&) override;
virtual bool page_did_is_known_hsts_host(String const&) override;
virtual Optional<String> page_did_request_storage_item(Web::StorageAPI::StorageEndpointType storage_endpoint, String const& storage_key, String const& bottle_key) override;
@ -235,6 +237,8 @@ private:
double m_zoom_level { 1.0 };
double m_maximum_frames_per_second { 60.0 };
u64 m_id { 0 };
u64 m_next_delete_all_cookies_request_id { 1 };
HashMap<u64, GC::Ref<Web::WebIDL::Promise>> m_pending_delete_all_cookies_promises;
bool m_has_focus { true };
Web::CSS::PreferredColorScheme m_preferred_color_scheme { Web::CSS::PreferredColorScheme::Auto };

View file

@ -96,6 +96,7 @@ endpoint WebContentClient
did_set_cookie(URL::URL url, HTTP::Cookie::ParsedCookie cookie, HTTP::Cookie::Source source) => ()
did_update_cookie(HTTP::Cookie::Cookie cookie) =|
did_expire_cookies_with_time_offset(AK::Duration offset) =|
did_request_delete_all_cookies(u64 page_id, u64 request_id, URL::URL url) =|
did_store_hsts_policy(String domain, HTTP::HSTS::ParsedHSTSPolicy policy) =|
did_is_known_hsts_host(String domain) => (bool result)

View file

@ -138,6 +138,7 @@ endpoint WebContentServer
reset_zoom(u64 page_id) =|
handle_file_return(u64 page_id, i32 error, Optional<IPC::File> file, i32 request_id) =|
did_delete_all_cookies(u64 page_id, u64 request_id) =|
set_system_visibility_state(u64 page_id, Web::HTML::VisibilityState visibility_state) =|

View file

@ -21,6 +21,10 @@ window.test_driver_internal.get_computed_role = async function(element) {
return await window.internals.getComputedRole(element);
};
window.test_driver_internal.delete_all_cookies = function(context) {
return window.internals.deleteAllCookies();
};
window.test_driver_internal.action_sequence = function(actions, context) {
// Modifier key codes from WebDriver spec
const SHIFT = "\uE008";