LibWeb: Implement Cache.match and Cache.matchAll
This commit is contained in:
parent
a478e3a30d
commit
e2d0ed4beb
7 changed files with 218 additions and 61 deletions
|
|
@ -42,6 +42,160 @@ void Cache::visit_edges(Visitor& visitor)
|
|||
visitor.visit(m_request_response_list);
|
||||
}
|
||||
|
||||
// https://w3c.github.io/ServiceWorker/#cache-match
|
||||
GC::Ref<WebIDL::Promise> Cache::match(Fetch::RequestInfo request, CacheQueryOptions options)
|
||||
{
|
||||
auto& realm = HTML::relevant_realm(*this);
|
||||
|
||||
// 1. Let promise be a new promise.
|
||||
auto promise = WebIDL::create_promise(realm);
|
||||
|
||||
// 2. Run these substeps in parallel:
|
||||
Platform::EventLoopPlugin::the().deferred_invoke(GC::create_function(realm.heap(), [this, &realm, promise, request = move(request), options]() {
|
||||
HTML::TemporaryExecutionContext context { realm, HTML::TemporaryExecutionContext::CallbacksEnabled::Yes };
|
||||
|
||||
// 1. Let p be the result of running the algorithm specified in matchAll(request, options) method with request and options.
|
||||
// 2. Wait until p settles.
|
||||
WebIDL::react_to_promise(match_all(move(request), options),
|
||||
// 4. Else if p resolves with an array, responses, then:
|
||||
GC::create_function(realm.heap(), [&realm, promise](JS::Value value) -> WebIDL::ExceptionOr<JS::Value> {
|
||||
HTML::TemporaryExecutionContext context { realm, HTML::TemporaryExecutionContext::CallbacksEnabled::Yes };
|
||||
|
||||
// 1. If responses is an empty array, then:
|
||||
if (auto& responses = value.as<JS::Array>(); responses.indexed_array_like_size() == 0) {
|
||||
// 1. Resolve promise with undefined.
|
||||
WebIDL::resolve_promise(realm, promise, JS::js_undefined());
|
||||
}
|
||||
// 2. Else:
|
||||
else {
|
||||
// 1. Resolve promise with the first element of responses.
|
||||
auto first_element = responses.indexed_get(0).release_value();
|
||||
WebIDL::resolve_promise(realm, promise, first_element.value);
|
||||
}
|
||||
|
||||
return JS::js_undefined();
|
||||
}),
|
||||
|
||||
// 3. If p rejects with an exception, then:
|
||||
GC::create_function(realm.heap(), [&realm, promise](JS::Value exception) -> WebIDL::ExceptionOr<JS::Value> {
|
||||
HTML::TemporaryExecutionContext context { realm, HTML::TemporaryExecutionContext::CallbacksEnabled::Yes };
|
||||
|
||||
// 1. Reject promise with that exception.
|
||||
WebIDL::reject_promise(realm, promise, exception);
|
||||
|
||||
return JS::js_undefined();
|
||||
}));
|
||||
}));
|
||||
|
||||
// 3. Return promise.
|
||||
return promise;
|
||||
}
|
||||
|
||||
// https://w3c.github.io/ServiceWorker/#cache-matchall
|
||||
GC::Ref<WebIDL::Promise> Cache::match_all(Optional<Fetch::RequestInfo> request, CacheQueryOptions options)
|
||||
{
|
||||
auto& realm = HTML::relevant_realm(*this);
|
||||
|
||||
// 1. Let r be null.
|
||||
GC::Ptr<Fetch::Infrastructure::Request> inner_request;
|
||||
|
||||
// 2. If the optional argument request is not omitted, then:
|
||||
if (request.has_value()) {
|
||||
TRY(request->visit(
|
||||
// 1. If request is a Request object, then:
|
||||
[&](GC::Root<Fetch::Request> const& request) -> ErrorOr<void, GC::Ref<WebIDL::Promise>> {
|
||||
// 1. Set r to request’s request.
|
||||
inner_request = request->request();
|
||||
|
||||
// 2. If r’s method is not `GET` and options.ignoreMethod is false, return a promise resolved with an
|
||||
// empty array.
|
||||
if (inner_request->method() != "GET"sv && !options.ignore_method)
|
||||
return WebIDL::create_resolved_promise(realm, MUST(JS::Array::create(realm, 0)));
|
||||
|
||||
return {};
|
||||
},
|
||||
// 2. Else if request is a string, then:
|
||||
[&](String const& request) -> ErrorOr<void, GC::Ref<WebIDL::Promise>> {
|
||||
// 1. Set r to the associated request of the result of invoking the initial value of Request as
|
||||
// constructor with request as its argument. If this throws an exception, return a promise rejected
|
||||
// with that exception.
|
||||
auto request_object = Fetch::Request::construct_impl(realm, request);
|
||||
if (request_object.is_error())
|
||||
return WebIDL::create_rejected_promise_from_exception(realm, request_object.release_error());
|
||||
|
||||
inner_request = request_object.value()->request();
|
||||
return {};
|
||||
}));
|
||||
}
|
||||
|
||||
// 3. Let realm be this’s relevant realm.
|
||||
// 4. Let promise be a new promise.
|
||||
auto promise = WebIDL::create_promise(realm);
|
||||
|
||||
// 5. Run these substeps in parallel:
|
||||
Platform::EventLoopPlugin::the().deferred_invoke(GC::create_function(realm.heap(), [this, &realm, inner_request, promise, request = move(request), options]() {
|
||||
// 1. Let responses be an empty list.
|
||||
auto responses = realm.heap().allocate<GC::HeapVector<GC::Ref<Fetch::Infrastructure::Response>>>();
|
||||
|
||||
// 2. If the optional argument request is omitted, then:
|
||||
if (!request.has_value()) {
|
||||
// 1. For each requestResponse of the relevant request response list:
|
||||
for (auto& request_response : m_request_response_list->elements()) {
|
||||
// 1. Add a copy of requestResponse’s response to responses.
|
||||
responses->elements().append(request_response->response->clone(realm));
|
||||
}
|
||||
}
|
||||
// 3. Else:
|
||||
else {
|
||||
// 1. Let requestResponses be the result of running Query Cache with r and options.
|
||||
auto request_responses = query_cache(*inner_request, options);
|
||||
|
||||
// 2. For each requestResponse of requestResponses:
|
||||
for (auto request_response : request_responses->elements()) {
|
||||
// 1. Add a copy of requestResponse’s response to responses.
|
||||
// NB: No need to copy. Query Cache creates a copy, and the requestResponses list is dropped hereafter.
|
||||
responses->elements().append(request_response->response);
|
||||
}
|
||||
}
|
||||
|
||||
// 3. For each response of responses:
|
||||
for (auto response : responses->elements()) {
|
||||
// 1. If response’s type is "opaque" and cross-origin resource policy check with promise’s relevant settings
|
||||
// object’s origin, promise’s relevant settings object, "", and response’s internal response returns
|
||||
// blocked, then reject promise with a TypeError and abort these steps.
|
||||
if (response->type() == Fetch::Infrastructure::Response::Type::Opaque) {
|
||||
// FIXME: Perform the cross-origin resource policy check.
|
||||
}
|
||||
}
|
||||
|
||||
// 4. Queue a task, on promise’s relevant settings object’s responsible event loop using the DOM manipulation
|
||||
// task source, to perform the following steps:
|
||||
HTML::queue_a_task(
|
||||
HTML::Task::Source::DOMManipulation,
|
||||
HTML::relevant_settings_object(promise->promise()).responsible_event_loop(),
|
||||
{},
|
||||
GC::create_function(realm.heap(), [&realm, promise, responses]() {
|
||||
HTML::TemporaryExecutionContext context { realm, HTML::TemporaryExecutionContext::CallbacksEnabled::Yes };
|
||||
|
||||
// 1. Let responseList be a list.
|
||||
auto response_list = realm.heap().allocate<GC::HeapVector<JS::Value>>();
|
||||
|
||||
// 2. For each response of responses:
|
||||
for (auto response : responses->elements()) {
|
||||
// 1. Add a new Response object associated with response and a new Headers object whose guard is
|
||||
// "immutable" to responseList.
|
||||
response_list->elements().append(Fetch::Response::create(realm, response, Fetch::Headers::Guard::Immutable));
|
||||
}
|
||||
|
||||
// 3. Resolve promise with a frozen array created from responseList, in realm.
|
||||
WebIDL::resolve_promise(realm, promise, JS::Array::create_from(realm, response_list->elements()));
|
||||
}));
|
||||
}));
|
||||
|
||||
// 6. Return promise.
|
||||
return promise;
|
||||
}
|
||||
|
||||
// https://w3c.github.io/ServiceWorker/#cache-add
|
||||
GC::Ref<WebIDL::Promise> Cache::add(Fetch::RequestInfo request)
|
||||
{
|
||||
|
|
|
|||
|
|
@ -51,6 +51,8 @@ class Cache : public Bindings::PlatformObject {
|
|||
GC_DECLARE_ALLOCATOR(Cache);
|
||||
|
||||
public:
|
||||
GC::Ref<WebIDL::Promise> match(Fetch::RequestInfo, CacheQueryOptions);
|
||||
GC::Ref<WebIDL::Promise> match_all(Optional<Fetch::RequestInfo>, CacheQueryOptions);
|
||||
GC::Ref<WebIDL::Promise> add(Fetch::RequestInfo);
|
||||
GC::Ref<WebIDL::Promise> add_all(ReadonlySpan<Fetch::RequestInfo>);
|
||||
GC::Ref<WebIDL::Promise> put(Fetch::RequestInfo, GC::Ref<Fetch::Response>);
|
||||
|
|
|
|||
|
|
@ -4,8 +4,8 @@
|
|||
// https://w3c.github.io/ServiceWorker/#cache-interface
|
||||
[SecureContext, Exposed=(Window,Worker)]
|
||||
interface Cache {
|
||||
// [NewObject] Promise<(Response or undefined)> match(RequestInfo request, optional CacheQueryOptions options = {});
|
||||
// [NewObject] Promise<FrozenArray<Response>> matchAll(optional RequestInfo request, optional CacheQueryOptions options = {});
|
||||
[NewObject] Promise<(Response or undefined)> match(RequestInfo request, optional CacheQueryOptions options = {});
|
||||
[NewObject] Promise<FrozenArray<Response>> matchAll(optional RequestInfo request, optional CacheQueryOptions options = {});
|
||||
[NewObject] Promise<undefined> add(RequestInfo request);
|
||||
[NewObject] Promise<undefined> addAll(sequence<RequestInfo> requests);
|
||||
[NewObject] Promise<undefined> put(RequestInfo request, Response response);
|
||||
|
|
|
|||
|
|
@ -2,10 +2,10 @@ Harness status: OK
|
|||
|
||||
Found 22 tests
|
||||
|
||||
14 Pass
|
||||
8 Fail
|
||||
18 Pass
|
||||
4 Fail
|
||||
Pass Cache.add called with no arguments
|
||||
Fail Cache.add called with relative URL specified as a string
|
||||
Pass Cache.add called with relative URL specified as a string
|
||||
Pass Cache.add called with non-HTTP/HTTPS URL
|
||||
Pass Cache.add called with Request object
|
||||
Pass Cache.add called with POST request
|
||||
|
|
@ -19,9 +19,9 @@ Pass Cache.add with request that results in a status of 500
|
|||
Pass Cache.addAll with no arguments
|
||||
Pass Cache.addAll with a mix of valid and undefined arguments
|
||||
Fail Cache.addAll with an empty array
|
||||
Fail Cache.addAll with string URL arguments
|
||||
Fail Cache.addAll with Request arguments
|
||||
Fail Cache.addAll with a mix of succeeding and failing requests
|
||||
Pass Cache.addAll with string URL arguments
|
||||
Pass Cache.addAll with Request arguments
|
||||
Pass Cache.addAll with a mix of succeeding and failing requests
|
||||
Pass Cache.addAll called with the same Request object specified twice
|
||||
Fail Cache.addAll should succeed when entries differ by vary header
|
||||
Fail Cache.addAll should reject when entries are duplicate by vary header
|
||||
|
|
|
|||
|
|
@ -2,29 +2,30 @@ Harness status: OK
|
|||
|
||||
Found 25 tests
|
||||
|
||||
25 Fail
|
||||
Fail Cache.match with no matching entries
|
||||
Fail Cache.match with URL
|
||||
Fail Cache.match with Request
|
||||
Fail Cache.match with multiple cache hits
|
||||
Fail Cache.match with new Request
|
||||
Fail Cache.match with HEAD
|
||||
Fail Cache.match with ignoreSearch option (request with no search parameters)
|
||||
Fail Cache.match with ignoreSearch option (request with search parameter)
|
||||
Fail Cache.match supports ignoreMethod
|
||||
Fail Cache.match supports ignoreVary
|
||||
Fail Cache.match does not support cacheName option
|
||||
Fail Cache.match with URL containing fragment
|
||||
Fail Cache.match with string fragment "http" as query
|
||||
Fail Cache.match with responses containing "Vary" header
|
||||
Fail Cache.match with Request and Response objects with different URLs
|
||||
Fail Cache.match invoked multiple times for the same Request/Response
|
||||
Fail Cache.match blob should be sliceable
|
||||
Fail Cache.match with POST Request
|
||||
Fail Cache.match with a non-2xx Response
|
||||
Fail Cache.match with a network error Response
|
||||
Fail Cache produces large Responses that can be cloned and read correctly.
|
||||
23 Pass
|
||||
2 Fail
|
||||
Pass Cache.match with no matching entries
|
||||
Pass Cache.match with URL
|
||||
Pass Cache.match with Request
|
||||
Pass Cache.match with multiple cache hits
|
||||
Pass Cache.match with new Request
|
||||
Pass Cache.match with HEAD
|
||||
Pass Cache.match with ignoreSearch option (request with no search parameters)
|
||||
Pass Cache.match with ignoreSearch option (request with search parameter)
|
||||
Pass Cache.match supports ignoreMethod
|
||||
Pass Cache.match supports ignoreVary
|
||||
Pass Cache.match does not support cacheName option
|
||||
Pass Cache.match with URL containing fragment
|
||||
Pass Cache.match with string fragment "http" as query
|
||||
Pass Cache.match with responses containing "Vary" header
|
||||
Pass Cache.match with Request and Response objects with different URLs
|
||||
Pass Cache.match invoked multiple times for the same Request/Response
|
||||
Pass Cache.match blob should be sliceable
|
||||
Pass Cache.match with POST Request
|
||||
Pass Cache.match with a non-2xx Response
|
||||
Pass Cache.match with a network error Response
|
||||
Pass Cache produces large Responses that can be cloned and read correctly.
|
||||
Fail cors-exposed header should be stored correctly.
|
||||
Fail MIME type should be set from content-header correctly.
|
||||
Fail MIME type should reflect Content-Type headers of response.
|
||||
Pass MIME type should be set from content-header correctly.
|
||||
Pass MIME type should reflect Content-Type headers of response.
|
||||
Fail Cache.match ignores vary headers on opaque response.
|
||||
|
|
@ -2,20 +2,20 @@ Harness status: OK
|
|||
|
||||
Found 16 tests
|
||||
|
||||
16 Fail
|
||||
Fail Cache.matchAll with no matching entries
|
||||
Fail Cache.matchAll with URL
|
||||
Fail Cache.matchAll with Request
|
||||
Fail Cache.matchAll with new Request
|
||||
Fail Cache.matchAll with HEAD
|
||||
Fail Cache.matchAll with ignoreSearch option (request with no search parameters)
|
||||
Fail Cache.matchAll with ignoreSearch option (request with search parameters)
|
||||
Fail Cache.matchAll supports ignoreMethod
|
||||
Fail Cache.matchAll supports ignoreVary
|
||||
Fail Cache.matchAll with URL containing fragment
|
||||
Fail Cache.matchAll with string fragment "http" as query
|
||||
Fail Cache.matchAll without parameters
|
||||
Fail Cache.matchAll with explicitly undefined request
|
||||
Fail Cache.matchAll with explicitly undefined request and empty options
|
||||
Fail Cache.matchAll with responses containing "Vary" header
|
||||
Fail Cache.matchAll with multiple vary pairs
|
||||
16 Pass
|
||||
Pass Cache.matchAll with no matching entries
|
||||
Pass Cache.matchAll with URL
|
||||
Pass Cache.matchAll with Request
|
||||
Pass Cache.matchAll with new Request
|
||||
Pass Cache.matchAll with HEAD
|
||||
Pass Cache.matchAll with ignoreSearch option (request with no search parameters)
|
||||
Pass Cache.matchAll with ignoreSearch option (request with search parameters)
|
||||
Pass Cache.matchAll supports ignoreMethod
|
||||
Pass Cache.matchAll supports ignoreVary
|
||||
Pass Cache.matchAll with URL containing fragment
|
||||
Pass Cache.matchAll with string fragment "http" as query
|
||||
Pass Cache.matchAll without parameters
|
||||
Pass Cache.matchAll with explicitly undefined request
|
||||
Pass Cache.matchAll with explicitly undefined request and empty options
|
||||
Pass Cache.matchAll with responses containing "Vary" header
|
||||
Pass Cache.matchAll with multiple vary pairs
|
||||
|
|
@ -2,24 +2,24 @@ Harness status: OK
|
|||
|
||||
Found 27 tests
|
||||
|
||||
13 Pass
|
||||
14 Fail
|
||||
23 Pass
|
||||
4 Fail
|
||||
Pass Cache.put called with simple Request and Response
|
||||
Fail Cache.put called with Request and Response from fetch()
|
||||
Pass Cache.put called with Request and Response from fetch()
|
||||
Pass Cache.put with Request without a body
|
||||
Pass Cache.put with Response without a body
|
||||
Fail Cache.put with a Response containing an empty URL
|
||||
Fail Cache.put with an empty response body
|
||||
Pass Cache.put with a Response containing an empty URL
|
||||
Pass Cache.put with an empty response body
|
||||
Pass Cache.put with synthetic 206 response
|
||||
Fail Cache.put with HTTP 206 response
|
||||
Fail Cache.put with opaque-filtered HTTP 206 response
|
||||
Pass Cache.put with opaque-filtered HTTP 206 response
|
||||
Fail Cache.put with HTTP 500 response
|
||||
Fail Cache.put called twice with matching Requests and different Responses
|
||||
Pass Cache.put called twice with matching Requests and different Responses
|
||||
Fail Cache.put called multiple times with request URLs that differ only by a fragment
|
||||
Fail Cache.put with a string request
|
||||
Pass Cache.put with a string request
|
||||
Pass Cache.put with an invalid response
|
||||
Pass Cache.put with a non-HTTP/HTTPS request
|
||||
Fail Cache.put with a relative URL
|
||||
Pass Cache.put with a relative URL
|
||||
Pass Cache.put with a non-GET request
|
||||
Pass Cache.put with a null response
|
||||
Pass Cache.put with a POST request
|
||||
|
|
@ -28,6 +28,6 @@ Pass getReader() after Cache.put
|
|||
Pass Cache.put with a VARY:* Response
|
||||
Pass Cache.put with an embedded VARY:* Response
|
||||
Fail Cache.put with a VARY:* opaque response should not reject
|
||||
Fail Cache.put should store Response.redirect() correctly
|
||||
Fail Cache.put called with simple Request and blob Response
|
||||
Fail Cache.put called with simple Request and form data Response
|
||||
Pass Cache.put should store Response.redirect() correctly
|
||||
Pass Cache.put called with simple Request and blob Response
|
||||
Pass Cache.put called with simple Request and form data Response
|
||||
Loading…
Reference in a new issue