LibWeb+LibWebView+WebContent: Perform cookie URL filtering in the UI
When cookies change or expire, we currently send a list of all changed cookies to all WebContent processes. We then filter that list in the WebContent process for cookies that match the page's URL before sending out cookie change events to JS. We now perform this filtering in the UI process, so each WebContent process only receives the cookies it would be interested in, if any. This serves two purposes: 1. Less IPC chatter. 2. This will let each ViewImplementation know that its cookie value has actually changed. (2) is for an upcoming change that will introduce a cookie cache, and will allow each view to know it should bust that cache. Note that for this filtering to work, we must iterate ViewImplementation instances rather than WebContentClient in order to have the view's URL. We must then associate the IPC with the view's page ID. No changes to the /cookiestore WPT subtests.
This commit is contained in:
parent
c810e99842
commit
76eb5b2fa6
9 changed files with 62 additions and 44 deletions
|
|
@ -747,28 +747,24 @@ struct CookieChange {
|
|||
};
|
||||
|
||||
// https://cookiestore.spec.whatwg.org/#observable-changes
|
||||
static Vector<CookieChange> observable_changes(URL::URL const& url, Vector<Cookie::Cookie> const& changes)
|
||||
static Vector<CookieChange> observable_changes(Vector<Cookie::Cookie> changes)
|
||||
{
|
||||
// The observable changes for url are the set of cookie changes to cookies in a cookie store which meet the
|
||||
// requirements in step 1 of Cookies § Retrieval Algorithm’s steps to compute the "cookie-string from a given
|
||||
// cookie store" with url as request-uri, for a "non-HTTP" API.
|
||||
auto retrieval_host_canonical = Cookie::canonicalize_domain(url);
|
||||
if (!retrieval_host_canonical.has_value())
|
||||
return {};
|
||||
|
||||
Vector<CookieChange> observable_changes;
|
||||
observable_changes.ensure_capacity(changes.size());
|
||||
|
||||
auto now = UnixDateTime::now();
|
||||
|
||||
for (auto const& cookie : changes) {
|
||||
if (!Cookie::cookie_matches_url(cookie, url, *retrieval_host_canonical))
|
||||
continue;
|
||||
|
||||
for (auto& cookie : changes) {
|
||||
// A cookie change is a cookie and a type (either changed or deleted):
|
||||
// - A cookie which is removed due to an insertion of another cookie with the same name, domain, and path is ignored.
|
||||
// - A newly-created cookie which is not immediately evicted is considered changed.
|
||||
// - A newly-created cookie which is immediately evicted is considered deleted.
|
||||
// - A cookie which is otherwise evicted or removed is considered deleted
|
||||
observable_changes.append({ cookie, cookie.expiry_time < now ? CookieChange::Type::Deleted : CookieChange::Type::Changed });
|
||||
auto type = cookie.expiry_time < now ? CookieChange::Type::Deleted : CookieChange::Type::Changed;
|
||||
observable_changes.unchecked_empend(move(cookie), type);
|
||||
}
|
||||
|
||||
return observable_changes;
|
||||
|
|
@ -812,19 +808,16 @@ static PreparedLists prepare_lists(Vector<CookieChange> const& changes)
|
|||
}
|
||||
|
||||
// https://cookiestore.spec.whatwg.org/#process-cookie-changes
|
||||
void CookieStore::process_cookie_changes(Vector<Cookie::Cookie> const& all_changes)
|
||||
void CookieStore::process_cookie_changes(Vector<Cookie::Cookie> all_changes)
|
||||
{
|
||||
auto& realm = this->realm();
|
||||
|
||||
// 1. Let url be window’s relevant settings object’s creation URL.
|
||||
auto url = HTML::relevant_settings_object(*this).creation_url;
|
||||
|
||||
// 2. Let changes be the observable changes for url.
|
||||
auto changes = observable_changes(url, all_changes);
|
||||
|
||||
// 3. If changes is empty, then continue.
|
||||
if (changes.is_empty())
|
||||
return;
|
||||
// NB: We perform the URL-based filtering in the UI process so that we don't have to send all changed cookies over
|
||||
// IPC to every tab.
|
||||
auto changes = observable_changes(move(all_changes));
|
||||
|
||||
// 4. Queue a global task on the DOM manipulation task source given window to fire a change event named "change"
|
||||
// with changes at window’s CookieStore.
|
||||
|
|
|
|||
|
|
@ -67,7 +67,7 @@ public:
|
|||
void set_onchange(WebIDL::CallbackType*);
|
||||
WebIDL::CallbackType* onchange();
|
||||
|
||||
void process_cookie_changes(Vector<Cookie::Cookie> const&);
|
||||
void process_cookie_changes(Vector<Cookie::Cookie>);
|
||||
|
||||
private:
|
||||
CookieStore(JS::Realm&, PageClient&);
|
||||
|
|
|
|||
|
|
@ -15,7 +15,7 @@
|
|||
#include <LibURL/URL.h>
|
||||
#include <LibWeb/Cookie/ParsedCookie.h>
|
||||
#include <LibWebView/CookieJar.h>
|
||||
#include <LibWebView/WebContentClient.h>
|
||||
#include <LibWebView/ViewImplementation.h>
|
||||
|
||||
namespace WebView {
|
||||
|
||||
|
|
@ -554,25 +554,22 @@ void CookieJar::TransientStorage::set_cookies(Cookies cookies)
|
|||
purge_expired_cookies();
|
||||
}
|
||||
|
||||
static void notify_cookies_changed(Vector<Web::Cookie::Cookie> cookies)
|
||||
{
|
||||
WebContentClient::for_each_client([&](WebContentClient& client) {
|
||||
client.async_cookies_changed(move(cookies));
|
||||
return IterationDecision::Continue;
|
||||
});
|
||||
}
|
||||
|
||||
void CookieJar::TransientStorage::set_cookie(CookieStorageKey key, Web::Cookie::Cookie cookie)
|
||||
{
|
||||
auto now = UnixDateTime::now();
|
||||
// AD-HOC: Skip adding immediately-expiring cookies (i.e., only allow updating to immediately-expiring) to prevent firing deletion events for them
|
||||
// Spec issue: https://github.com/whatwg/cookiestore/issues/282
|
||||
|
||||
// AD-HOC: Skip adding immediately-expiring cookies (i.e., only allow updating to immediately-expiring) to prevent
|
||||
// firing deletion events for them.
|
||||
// Spec issue: https://github.com/whatwg/cookiestore/issues/282
|
||||
if (cookie.expiry_time < now && !m_cookies.contains(key))
|
||||
return;
|
||||
|
||||
m_cookies.set(key, cookie);
|
||||
|
||||
// We skip notifying about updating expired cookies, as they will be notified as being expired immediately after instead
|
||||
if (cookie.expiry_time >= now)
|
||||
notify_cookies_changed({ cookie });
|
||||
send_cookie_changed_notifications({ { CookieEntry { {}, cookie } } });
|
||||
|
||||
m_dirty_cookies.set(move(key), move(cookie));
|
||||
}
|
||||
|
||||
|
|
@ -592,14 +589,9 @@ UnixDateTime CookieJar::TransientStorage::purge_expired_cookies(Optional<AK::Dur
|
|||
}
|
||||
|
||||
auto is_expired = [&](auto const&, auto const& cookie) { return cookie.expiry_time < now; };
|
||||
auto removed_entries = m_cookies.take_all_matching(is_expired);
|
||||
if (!removed_entries.is_empty()) {
|
||||
Vector<Web::Cookie::Cookie> removed_cookies;
|
||||
removed_cookies.ensure_capacity(removed_entries.size());
|
||||
for (auto const& entry : removed_entries)
|
||||
removed_cookies.unchecked_append(move(entry.value));
|
||||
notify_cookies_changed(move(removed_cookies));
|
||||
}
|
||||
|
||||
if (auto removed_entries = m_cookies.take_all_matching(is_expired); !removed_entries.is_empty())
|
||||
send_cookie_changed_notifications(removed_entries);
|
||||
|
||||
return now;
|
||||
}
|
||||
|
|
@ -631,6 +623,27 @@ Requests::CacheSizes CookieJar::TransientStorage::estimate_storage_size_accessed
|
|||
return sizes;
|
||||
}
|
||||
|
||||
void CookieJar::TransientStorage::send_cookie_changed_notifications(ReadonlySpan<CookieEntry> cookies)
|
||||
{
|
||||
ViewImplementation::for_each_view([&](ViewImplementation& view) {
|
||||
auto retrieval_host_canonical = Web::Cookie::canonicalize_domain(view.url());
|
||||
if (!retrieval_host_canonical.has_value())
|
||||
return IterationDecision::Continue;
|
||||
|
||||
Vector<Web::Cookie::Cookie> matching_cookies;
|
||||
|
||||
for (auto const& cookie : cookies) {
|
||||
if (Web::Cookie::cookie_matches_url(cookie.value, view.url(), *retrieval_host_canonical))
|
||||
matching_cookies.append(cookie.value);
|
||||
}
|
||||
|
||||
if (!matching_cookies.is_empty())
|
||||
view.notify_cookies_changed(matching_cookies);
|
||||
|
||||
return IterationDecision::Continue;
|
||||
});
|
||||
}
|
||||
|
||||
void CookieJar::PersistedStorage::insert_cookie(Web::Cookie::Cookie const& cookie)
|
||||
{
|
||||
database.execute_statement(
|
||||
|
|
|
|||
|
|
@ -90,6 +90,9 @@ private:
|
|||
}
|
||||
|
||||
private:
|
||||
using CookieEntry = decltype(declval<Cookies>().take_all_matching(nullptr))::ValueType;
|
||||
static void send_cookie_changed_notifications(ReadonlySpan<CookieEntry>);
|
||||
|
||||
Cookies m_cookies;
|
||||
Cookies m_dirty_cookies;
|
||||
};
|
||||
|
|
|
|||
|
|
@ -259,6 +259,11 @@ void ViewImplementation::set_preferred_motion(Web::CSS::PreferredMotion motion)
|
|||
client().async_set_preferred_motion(page_id(), motion);
|
||||
}
|
||||
|
||||
void ViewImplementation::notify_cookies_changed(ReadonlySpan<Web::Cookie::Cookie> cookies)
|
||||
{
|
||||
client().async_cookies_changed(page_id(), cookies);
|
||||
}
|
||||
|
||||
ByteString ViewImplementation::selected_text()
|
||||
{
|
||||
return client().get_selected_text(page_id());
|
||||
|
|
|
|||
|
|
@ -86,6 +86,8 @@ public:
|
|||
void set_preferred_contrast(Web::CSS::PreferredContrast);
|
||||
void set_preferred_motion(Web::CSS::PreferredMotion);
|
||||
|
||||
void notify_cookies_changed(ReadonlySpan<Web::Cookie::Cookie>);
|
||||
|
||||
ByteString selected_text();
|
||||
Optional<String> selected_text_with_whitespace_collapsed();
|
||||
void select_all();
|
||||
|
|
|
|||
|
|
@ -1354,13 +1354,14 @@ void ConnectionFromClient::system_time_zone_changed()
|
|||
Unicode::clear_system_time_zone_cache();
|
||||
}
|
||||
|
||||
void ConnectionFromClient::cookies_changed(Vector<Web::Cookie::Cookie> cookies)
|
||||
void ConnectionFromClient::cookies_changed(u64 page_id, Vector<Web::Cookie::Cookie> cookies)
|
||||
{
|
||||
for (auto& navigable : Web::HTML::all_navigables()) {
|
||||
auto window = navigable->active_window();
|
||||
if (auto page = this->page(page_id); page.has_value()) {
|
||||
auto window = page->page().top_level_traversable()->active_window();
|
||||
if (!window)
|
||||
return;
|
||||
window->cookie_store()->process_cookie_changes(cookies);
|
||||
|
||||
window->cookie_store()->process_cookie_changes(move(cookies));
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -162,7 +162,7 @@ private:
|
|||
virtual void paste(u64 page_id, Utf16String text) override;
|
||||
|
||||
virtual void system_time_zone_changed() override;
|
||||
virtual void cookies_changed(Vector<Web::Cookie::Cookie>) override;
|
||||
virtual void cookies_changed(u64 page_id, Vector<Web::Cookie::Cookie>) override;
|
||||
|
||||
NonnullOwnPtr<PageHost> m_page_host;
|
||||
|
||||
|
|
|
|||
|
|
@ -133,5 +133,6 @@ endpoint WebContentServer
|
|||
set_user_style(u64 page_id, String source) =|
|
||||
|
||||
system_time_zone_changed() =|
|
||||
cookies_changed(Vector<Web::Cookie::Cookie> cookies) =|
|
||||
|
||||
cookies_changed(u64 page_id, Vector<Web::Cookie::Cookie> cookies) =|
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in a new issue