ladybird/Libraries/LibWeb/CookieStore/CookieStore.cpp
2025-08-08 13:09:58 -04:00

132 lines
4.8 KiB
C++
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/*
* Copyright (c) 2025, Idan Horowitz <idan.horowitz@serenityos.org>
*
* SPDX-License-Identifier: BSD-2-Clause
*/
#include <LibWeb/Bindings/CookieStorePrototype.h>
#include <LibWeb/Bindings/Intrinsics.h>
#include <LibWeb/CookieStore/CookieStore.h>
#include <LibWeb/HTML/Scripting/Environments.h>
#include <LibWeb/HTML/Scripting/TemporaryExecutionContext.h>
#include <LibWeb/Page/Page.h>
#include <LibWeb/Platform/EventLoopPlugin.h>
#include <LibWeb/WebIDL/DOMException.h>
#include <LibWeb/WebIDL/Promise.h>
namespace Web::CookieStore {
GC_DEFINE_ALLOCATOR(CookieStore);
CookieStore::CookieStore(JS::Realm& realm, PageClient& client)
: DOM::EventTarget(realm)
, m_client(client)
{
}
void CookieStore::initialize(JS::Realm& realm)
{
WEB_SET_PROTOTYPE_FOR_INTERFACE(CookieStore);
Base::initialize(realm);
}
void CookieStore::visit_edges(Cell::Visitor& visitor)
{
Base::visit_edges(visitor);
visitor.visit(m_client);
}
// https://cookiestore.spec.whatwg.org/#create-a-cookielistitem
static CookieListItem create_a_cookie_list_item(Cookie::Cookie const& cookie)
{
// 1. Let name be the result of running UTF-8 decode without BOM on cookies name.
// 2. Let value be the result of running UTF-8 decode without BOM on cookies value.
// 3. Return «[ "name" → name, "value" → value ]»
return CookieListItem {
.name = cookie.name,
.value = cookie.value,
};
}
// https://cookiestore.spec.whatwg.org/#query-cookies
static Vector<CookieListItem> query_cookies(PageClient& client, URL::URL const& url, Optional<String> const& name)
{
// 1. Perform the steps defined in Cookies § Retrieval Model to compute the "cookie-string from a given cookie store"
// with url as request-uri. The cookie-string itself is ignored, but the intermediate cookie-list is used in subsequent steps.
// For the purposes of the steps, the cookie-string is being generated for a "non-HTTP" API.
auto cookie_list = client.page_did_request_all_cookies_cookiestore(url);
// 2. Let list be a new list.
Vector<CookieListItem> list;
// 3. For each cookie in cookie-list, run these steps:
for (auto const& cookie : cookie_list) {
// 1. Assert: cookies http-only-flag is false.
VERIFY(!cookie.http_only);
// 2. If name is given, then run these steps:
if (name.has_value()) {
// 1. Let cookieName be the result of running UTF-8 decode without BOM on cookies name.
// 2. If cookieName does not equal name, then continue.
if (cookie.name != name.value())
continue;
}
// 3. Let item be the result of running create a CookieListItem from cookie.
auto item = create_a_cookie_list_item(cookie);
// 4. Append item to list.
list.append(move(item));
}
// 4. Return list.
return list;
}
// https://cookiestore.spec.whatwg.org/#dom-cookiestore-get
GC::Ref<WebIDL::Promise> CookieStore::get(String name)
{
auto& realm = this->realm();
// 1. Let settings be thiss relevant settings object.
auto const& settings = HTML::relevant_settings_object(*this);
// 2. Let origin be settingss origin.
auto const& origin = settings.origin();
// 3. If origin is an opaque origin, then return a promise rejected with a "SecurityError" DOMException.
if (origin.is_opaque())
return WebIDL::create_rejected_promise(realm, WebIDL::SecurityError::create(realm, "Document origin is opaque"_string));
// 4. Let url be settingss creation URL.
auto url = settings.creation_url;
// 5. Let p be a new promise.
auto promise = WebIDL::create_promise(realm);
// 6. Run the following steps in parallel:
Platform::EventLoopPlugin::the().deferred_invoke(GC::create_function(realm.heap(), [&realm, client = m_client, promise, url = move(url), name = move(name)]() {
// 1. Let list be the results of running query cookies with url and name.
auto list = query_cookies(client, url, name);
// AD-HOC: Queue a global task to perform the next steps
// Spec issue: https://github.com/whatwg/cookiestore/issues/239
queue_global_task(HTML::Task::Source::Unspecified, realm.global_object(), GC::create_function(realm.heap(), [&realm, promise, list = move(list)]() {
HTML::TemporaryExecutionContext execution_context { realm };
// 2. If list is failure, then reject p with a TypeError and abort these steps.
// 3. If list is empty, then resolve p with null.
if (list.is_empty())
WebIDL::resolve_promise(realm, promise, JS::js_null());
// 4. Otherwise, resolve p with the first item of list.
else
WebIDL::resolve_promise(realm, promise, Bindings::cookie_list_item_to_value(realm, list[0]));
}));
}));
// 7. Return p.
return promise;
}
}