LibWeb: Implement CacheStorage.open

This commit is contained in:
Timothy Flynn 2026-04-01 19:24:57 -04:00 committed by Shannon Booth
parent 80b612b789
commit 0d2f0a6ed9
6 changed files with 127 additions and 3 deletions

View file

@ -905,6 +905,7 @@ set(SOURCES
ServiceWorker/CacheStorage.cpp
ServiceWorker/EventNames.cpp
ServiceWorker/Job.cpp
ServiceWorker/NameToCacheMap.cpp
ServiceWorker/Registration.cpp
ServiceWorker/ServiceWorker.cpp
ServiceWorker/ServiceWorkerContainer.cpp

View file

@ -1,11 +1,15 @@
/*
* Copyright (c) 2025, Aliaksandr Kalenik <kalenik.aliaksandr@gmail.com>
* Copyright (c) 2026, Tim Flynn <trflynn89@ladybird.org>
*
* SPDX-License-Identifier: BSD-2-Clause
*/
#include <LibWeb/Bindings/CacheStoragePrototype.h>
#include <LibWeb/Bindings/Intrinsics.h>
#include <LibWeb/HTML/Scripting/TemporaryExecutionContext.h>
#include <LibWeb/Platform/EventLoopPlugin.h>
#include <LibWeb/ServiceWorker/Cache.h>
#include <LibWeb/ServiceWorker/CacheStorage.h>
#include <LibWeb/WebIDL/Promise.h>
@ -24,10 +28,10 @@ void CacheStorage::initialize(JS::Realm& realm)
WEB_SET_PROTOTYPE_FOR_INTERFACE(CacheStorage);
}
// https://w3c.github.io/ServiceWorker/#cache-storage-open
GC::Ref<WebIDL::Promise> CacheStorage::open(String const&)
void CacheStorage::visit_edges(Visitor& visitor)
{
return WebIDL::create_rejected_promise(realm(), WebIDL::NotSupportedError::create(realm(), "CacheStorage.open() is not yet implemented"_utf16));
Base::visit_edges(visitor);
visitor.visit(m_relevant_name_to_cache_map);
}
// https://w3c.github.io/ServiceWorker/#cache-storage-has
@ -37,4 +41,54 @@ GC::Ref<WebIDL::Promise> CacheStorage::has(String const&)
return WebIDL::create_rejected_promise(realm(), JS::Value(false));
}
// https://w3c.github.io/ServiceWorker/#cache-storage-open
GC::Ref<WebIDL::Promise> CacheStorage::open(String const& cache_name)
{
auto& realm = HTML::relevant_realm(*this);
// 1. Let promise be a new promise.
auto promise = WebIDL::create_promise(realm);
// 2. Run the following substeps in parallel:
Platform::EventLoopPlugin::the().deferred_invoke(GC::create_function(realm.heap(), [this, &realm, promise, cache_name]() {
HTML::TemporaryExecutionContext context { realm, HTML::TemporaryExecutionContext::CallbacksEnabled::Yes };
auto& relevant_name_to_cache_map = this->relevant_name_to_cache_map();
// 1. For each key → value of the relevant name to cache map:
// 1. If cacheName matches key, then:
if (auto value = relevant_name_to_cache_map.get(cache_name); value.has_value()) {
// 1. Resolve promise with a new Cache object that represents value.
WebIDL::resolve_promise(realm, promise, realm.create<Cache>(realm, value.release_value()));
// 2. Abort these steps.
return;
}
// 2. Let cache be a new request response list.
auto cache = realm.heap().allocate<RequestResponseList>();
// 3. Set the relevant name to cache map[cacheName] to cache. If this cache write operation failed due to
// exceeding the granted quota limit, reject promise with a QuotaExceededError and abort these steps.
// FIXME: Handle cache quotas.
relevant_name_to_cache_map.set(cache_name, cache);
// 4. Resolve promise with a new Cache object that represents cache.
WebIDL::resolve_promise(realm, promise, realm.create<Cache>(realm, cache));
}));
// 3. Return promise.
return promise;
}
// https://w3c.github.io/ServiceWorker/#relevant-name-to-cache-map
NameToCacheMap& CacheStorage::relevant_name_to_cache_map()
{
// The relevant name to cache map for a CacheStorage object is the name to cache map associated with the result of
// running obtain a local storage bottle map with the objects relevant settings object and "caches".
// FIXME: We don't yet have a way to serialize request/response pairs for storage in the UI process. For now, we
// use an ephemeral name to cache map.
return m_relevant_name_to_cache_map;
}
}

View file

@ -1,5 +1,6 @@
/*
* Copyright (c) 2025, Aliaksandr Kalenik <kalenik.aliaksandr@gmail.com>
* Copyright (c) 2026, Tim Flynn <trflynn89@ladybird.org>
*
* SPDX-License-Identifier: BSD-2-Clause
*/
@ -7,6 +8,7 @@
#pragma once
#include <LibWeb/Bindings/PlatformObject.h>
#include <LibWeb/ServiceWorker/NameToCacheMap.h>
#include <LibWeb/WebIDL/Promise.h>
namespace Web::ServiceWorker {
@ -24,6 +26,11 @@ private:
explicit CacheStorage(JS::Realm&);
virtual void initialize(JS::Realm&) override;
virtual void visit_edges(Visitor&) override;
NameToCacheMap& relevant_name_to_cache_map();
NameToCacheMap m_relevant_name_to_cache_map;
};
}

View file

@ -1,4 +1,5 @@
#import <Fetch/Request.idl>
#import <ServiceWorker/Cache.idl>
// https://w3c.github.io/ServiceWorker/#cachestorage-interface
[SecureContext, Exposed=(Window,Worker), Experimental]

View file

@ -0,0 +1,22 @@
/*
* Copyright (c) 2026, Tim Flynn <trflynn89@ladybird.org>
*
* SPDX-License-Identifier: BSD-2-Clause
*/
#include <LibWeb/Fetch/Infrastructure/HTTP/Requests.h>
#include <LibWeb/Fetch/Infrastructure/HTTP/Responses.h>
#include <LibWeb/ServiceWorker/NameToCacheMap.h>
namespace Web::ServiceWorker {
GC_DEFINE_ALLOCATOR(RequestResponse);
void RequestResponse::visit_edges(Visitor& visitor)
{
Base::visit_edges(visitor);
visitor.visit(request);
visitor.visit(response);
}
}

View file

@ -0,0 +1,39 @@
/*
* Copyright (c) 2026, Tim Flynn <trflynn89@ladybird.org>
*
* SPDX-License-Identifier: BSD-2-Clause
*/
#pragma once
#include <LibGC/Cell.h>
#include <LibGC/CellAllocator.h>
#include <LibGC/HeapVector.h>
#include <LibWeb/Forward.h>
namespace Web::ServiceWorker {
// https://w3c.github.io/ServiceWorker/#dfn-request-response-list
struct RequestResponse : public GC::Cell {
GC_CELL(RequestResponse, GC::Cell);
GC_DECLARE_ALLOCATOR(RequestResponse);
RequestResponse(GC::Ref<Fetch::Infrastructure::Request> request, GC::Ref<Fetch::Infrastructure::Response> response)
: request(request)
, response(response)
{
}
virtual void visit_edges(Visitor&) override;
GC::Ref<Fetch::Infrastructure::Request> request;
GC::Ref<Fetch::Infrastructure::Response> response;
};
// https://w3c.github.io/ServiceWorker/#dfn-request-response-list
using RequestResponseList = GC::HeapVector<GC::Ref<RequestResponse>>;
// https://w3c.github.io/ServiceWorker/#dfn-name-to-cache-map
using NameToCacheMap = OrderedHashMap<String, GC::Ref<RequestResponseList>>;
}