mirror of
https://github.com/LadybirdBrowser/ladybird.git
synced 2025-05-09 04:32:51 +00:00
LibWeb: Start implementing serviceWorker.register
This is mostly the fun boilerplate. Actually creating the Job queue to do the heavy lifting is next.
This commit is contained in:
parent
acd604c5e1
commit
c77d9a2732
Notes:
github-actions[bot]
2024-09-20 21:42:18 +00:00
Author: https://github.com/ADKaster
Commit: c77d9a2732
Pull-request: https://github.com/LadybirdBrowser/ladybird/pull/1404
Reviewed-by: https://github.com/tcl3 ✅
7 changed files with 163 additions and 1 deletions
|
@ -0,0 +1 @@
|
|||
ServiceWorker registration failed: InternalError: TODO(ServiceWorkerContainer::start_register is not implemented in LibJS)
|
|
@ -0,0 +1,20 @@
|
|||
<!DOCTYPE html>
|
||||
<script src="../include.js"></script>
|
||||
<script>
|
||||
asyncTest(done => {
|
||||
|
||||
spoofCurrentURL("https://example.com/service-worker-register.html");
|
||||
|
||||
let swPromise = navigator.serviceWorker.register("service-worker.js");
|
||||
|
||||
swPromise
|
||||
.then(registration => {
|
||||
println(`ServiceWorker registration successful with scope: ${registration.scope}`);
|
||||
done();
|
||||
})
|
||||
.catch(err => {
|
||||
println(`ServiceWorker registration failed: ${err}`);
|
||||
done();
|
||||
});
|
||||
});
|
||||
</script>
|
2
Tests/LibWeb/Text/input/ServiceWorker/service-worker.js
Normal file
2
Tests/LibWeb/Text/input/ServiceWorker/service-worker.js
Normal file
|
@ -0,0 +1,2 @@
|
|||
// FIXME: Add service worker code here
|
||||
console.log("hi from service worker");
|
|
@ -8,8 +8,10 @@
|
|||
#include <LibJS/Runtime/Realm.h>
|
||||
#include <LibWeb/Bindings/Intrinsics.h>
|
||||
#include <LibWeb/Bindings/ServiceWorkerContainerPrototype.h>
|
||||
#include <LibWeb/DOMURL/DOMURL.h>
|
||||
#include <LibWeb/HTML/EventNames.h>
|
||||
#include <LibWeb/HTML/ServiceWorkerContainer.h>
|
||||
#include <LibWeb/StorageAPI/StorageKey.h>
|
||||
|
||||
namespace Web::HTML {
|
||||
|
||||
|
@ -40,6 +42,127 @@ JS::NonnullGCPtr<ServiceWorkerContainer> ServiceWorkerContainer::create(JS::Real
|
|||
return realm.heap().allocate<ServiceWorkerContainer>(realm, realm);
|
||||
}
|
||||
|
||||
// https://w3c.github.io/ServiceWorker/#navigator-service-worker-register
|
||||
JS::NonnullGCPtr<JS::Promise> ServiceWorkerContainer::register_(String script_url, RegistrationOptions const& options)
|
||||
{
|
||||
auto& realm = this->realm();
|
||||
// Note: The register(scriptURL, options) method creates or updates a service worker registration for the given scope url.
|
||||
// If successful, a service worker registration ties the provided scriptURL to a scope url,
|
||||
// which is subsequently used for navigation matching.
|
||||
|
||||
// 1. Let p be a promise.
|
||||
auto p = WebIDL::create_promise(realm);
|
||||
|
||||
// FIXME: 2. Set scriptURL to the result of invoking Get Trusted Type compliant string with TrustedScriptURL,
|
||||
// this's relevant global object, scriptURL, "ServiceWorkerContainer register", and "script".
|
||||
|
||||
// 3 Let client be this's service worker client.
|
||||
auto client = m_service_worker_client;
|
||||
|
||||
// 4. Let scriptURL be the result of parsing scriptURL with this's relevant settings object’s API base URL.
|
||||
auto base_url = relevant_settings_object(*this).api_base_url();
|
||||
auto parsed_script_url = DOMURL::parse(script_url, base_url);
|
||||
|
||||
// 5. Let scopeURL be null.
|
||||
Optional<URL::URL> scope_url;
|
||||
|
||||
// 6. If options["scope"] exists, set scopeURL to the result of parsing options["scope"] with this's relevant settings object’s API base URL.
|
||||
if (options.scope.has_value()) {
|
||||
scope_url = DOMURL::parse(options.scope.value(), base_url);
|
||||
}
|
||||
|
||||
// 7. Invoke Start Register with scopeURL, scriptURL, p, client, client’s creation URL, options["type"], and options["updateViaCache"].
|
||||
start_register(scope_url, parsed_script_url, p, client, client->creation_url, options.type, options.update_via_cache);
|
||||
|
||||
// 8. Return p.
|
||||
return verify_cast<JS::Promise>(*p->promise());
|
||||
}
|
||||
|
||||
// https://w3c.github.io/ServiceWorker/#start-register-algorithm
|
||||
void ServiceWorkerContainer::start_register(Optional<URL::URL> scope_url, URL::URL script_url, JS::NonnullGCPtr<WebIDL::Promise> promise, EnvironmentSettingsObject& client, URL::URL, Bindings::WorkerType, Bindings::ServiceWorkerUpdateViaCache)
|
||||
{
|
||||
auto& realm = this->realm();
|
||||
auto& vm = realm.vm();
|
||||
|
||||
// 1. If scriptURL is failure, reject promise with a TypeError and abort these steps.
|
||||
if (!script_url.is_valid()) {
|
||||
WebIDL::reject_promise(realm, promise, JS::TypeError::create(realm, "scriptURL is not a valid URL"sv));
|
||||
return;
|
||||
}
|
||||
|
||||
// 2. Set scriptURL’s fragment to null.
|
||||
// Note: The user agent does not store the fragment of the script’s url.
|
||||
// This means that the fragment does not have an effect on identifying service workers.
|
||||
script_url.set_fragment({});
|
||||
|
||||
// 3. If scriptURL’s scheme is not one of "http" and "https", reject promise with a TypeError and abort these steps.
|
||||
if (!script_url.scheme().is_one_of("http"sv, "https"sv)) {
|
||||
WebIDL::reject_promise(realm, promise, JS::TypeError::create(realm, "scriptURL must have a scheme of 'http' or 'https'"sv));
|
||||
return;
|
||||
}
|
||||
|
||||
// 4. If any of the strings in scriptURL’s path contains either ASCII case-insensitive "%2f" or ASCII case-insensitive "%5c",
|
||||
// reject promise with a TypeError and abort these steps.
|
||||
auto invalid_path = script_url.paths().first_matching([&](auto& path) {
|
||||
return path.contains("%2f"sv, CaseSensitivity::CaseInsensitive) || path.contains("%5c"sv, CaseSensitivity::CaseInsensitive);
|
||||
});
|
||||
if (invalid_path.has_value()) {
|
||||
WebIDL::reject_promise(realm, promise, JS::TypeError::create(realm, "scriptURL path must not contain '%2f' or '%5c'"sv));
|
||||
return;
|
||||
}
|
||||
|
||||
// 5. If scopeURL is null, set scopeURL to the result of parsing the string "./" with scriptURL.
|
||||
// Note: The scope url for the registration is set to the location of the service worker script by default.
|
||||
if (!scope_url.has_value()) {
|
||||
scope_url = DOMURL::parse("./"sv, script_url);
|
||||
}
|
||||
|
||||
// 6. If scopeURL is failure, reject promise with a TypeError and abort these steps.
|
||||
if (!scope_url->is_valid()) {
|
||||
WebIDL::reject_promise(realm, promise, JS::TypeError::create(realm, "scopeURL is not a valid URL"sv));
|
||||
return;
|
||||
}
|
||||
|
||||
// 7. Set scopeURL’s fragment to null.
|
||||
// Note: The user agent does not store the fragment of the scope url.
|
||||
// This means that the fragment does not have an effect on identifying service worker registrations.
|
||||
scope_url->set_fragment({});
|
||||
|
||||
// 8. If scopeURL’s scheme is not one of "http" and "https", reject promise with a TypeError and abort these steps.
|
||||
if (!scope_url->scheme().is_one_of("http"sv, "https"sv)) {
|
||||
WebIDL::reject_promise(realm, promise, JS::TypeError::create(realm, "scopeURL must have a scheme of 'http' or 'https'"sv));
|
||||
return;
|
||||
}
|
||||
|
||||
// 9. If any of the strings in scopeURL’s path contains either ASCII case-insensitive "%2f" or ASCII case-insensitive "%5c",
|
||||
// reject promise with a TypeError and abort these steps.
|
||||
invalid_path = scope_url->paths().first_matching([&](auto& path) {
|
||||
return path.contains("%2f"sv, CaseSensitivity::CaseInsensitive) || path.contains("%5c"sv, CaseSensitivity::CaseInsensitive);
|
||||
});
|
||||
if (invalid_path.has_value()) {
|
||||
WebIDL::reject_promise(realm, promise, JS::TypeError::create(realm, "scopeURL path must not contain '%2f' or '%5c'"sv));
|
||||
return;
|
||||
}
|
||||
|
||||
// 10. Let storage key be the result of running obtain a storage key given client.
|
||||
auto storage_key = StorageAPI::obtain_a_storage_key(client);
|
||||
|
||||
// FIXME: Ad-Hoc. Spec should handle this failure here, or earlier.
|
||||
if (!storage_key.has_value()) {
|
||||
WebIDL::reject_promise(realm, promise, JS::TypeError::create(realm, "Failed to obtain a storage key"sv));
|
||||
return;
|
||||
}
|
||||
|
||||
// FIXME: Schedule the job
|
||||
// 11. Let job be the result of running Create Job with register, storage key, scopeURL, scriptURL, promise, and client.
|
||||
// 12. Set job’s worker type to workerType.
|
||||
// 13. Set job’s update via cache to updateViaCache.
|
||||
// 14. Set job’s referrer to referrer.
|
||||
// 15. Invoke Schedule Job with job.
|
||||
|
||||
WebIDL::reject_promise(realm, promise, *vm.throw_completion<JS::InternalError>(JS::ErrorType::NotImplemented, "ServiceWorkerContainer::start_register"sv).value());
|
||||
}
|
||||
|
||||
#undef __ENUMERATE
|
||||
#define __ENUMERATE(attribute_name, event_name) \
|
||||
void ServiceWorkerContainer::set_##attribute_name(WebIDL::CallbackType* value) \
|
||||
|
|
|
@ -7,7 +7,11 @@
|
|||
|
||||
#pragma once
|
||||
|
||||
#include <LibWeb/Bindings/ServiceWorkerRegistrationPrototype.h>
|
||||
#include <LibWeb/Bindings/WorkerPrototype.h>
|
||||
#include <LibWeb/DOM/EventTarget.h>
|
||||
#include <LibWeb/WebIDL/ExceptionOr.h>
|
||||
#include <LibWeb/WebIDL/Promise.h>
|
||||
|
||||
#define ENUMERATE_SERVICE_WORKER_CONTAINER_EVENT_HANDLERS(E) \
|
||||
E(oncontrollerchange, HTML::EventNames::controllerchange) \
|
||||
|
@ -16,6 +20,12 @@
|
|||
|
||||
namespace Web::HTML {
|
||||
|
||||
struct RegistrationOptions {
|
||||
Optional<String> scope;
|
||||
Bindings::WorkerType type = Bindings::WorkerType::Classic;
|
||||
Bindings::ServiceWorkerUpdateViaCache update_via_cache = Bindings::ServiceWorkerUpdateViaCache::Imports;
|
||||
};
|
||||
|
||||
class ServiceWorkerContainer : public DOM::EventTarget {
|
||||
WEB_PLATFORM_OBJECT(ServiceWorkerContainer, DOM::EventTarget);
|
||||
JS_DECLARE_ALLOCATOR(ServiceWorkerContainer);
|
||||
|
@ -24,6 +34,8 @@ public:
|
|||
[[nodiscard]] static JS::NonnullGCPtr<ServiceWorkerContainer> create(JS::Realm& realm);
|
||||
virtual ~ServiceWorkerContainer() override;
|
||||
|
||||
JS::NonnullGCPtr<JS::Promise> register_(String script_url, RegistrationOptions const& options);
|
||||
|
||||
#undef __ENUMERATE
|
||||
#define __ENUMERATE(attribute_name, event_name) \
|
||||
void set_##attribute_name(WebIDL::CallbackType*); \
|
||||
|
@ -37,6 +49,8 @@ private:
|
|||
virtual void initialize(JS::Realm&) override;
|
||||
virtual void visit_edges(Cell::Visitor&) override;
|
||||
|
||||
void start_register(Optional<URL::URL> scope_url, URL::URL script_url, JS::NonnullGCPtr<WebIDL::Promise>, EnvironmentSettingsObject&, URL::URL referrer, Bindings::WorkerType, Bindings::ServiceWorkerUpdateViaCache);
|
||||
|
||||
JS::NonnullGCPtr<EnvironmentSettingsObject> m_service_worker_client;
|
||||
};
|
||||
|
||||
|
|
|
@ -9,7 +9,8 @@ interface ServiceWorkerContainer : EventTarget {
|
|||
[FIXME] readonly attribute ServiceWorker? controller;
|
||||
[FIXME] readonly attribute Promise<ServiceWorkerRegistration> ready;
|
||||
|
||||
[FIXME, NewObject] Promise<ServiceWorkerRegistration> register((TrustedScriptURL or USVString) scriptURL, optional RegistrationOptions options = {});
|
||||
// FIXME: [NewObject] Promise<ServiceWorkerRegistration> register((TrustedScriptURL or USVString) scriptURL, optional RegistrationOptions options = {});
|
||||
[NewObject, ImplementedAs=register_] Promise<ServiceWorkerRegistration> register(USVString scriptURL, optional RegistrationOptions options = {});
|
||||
|
||||
[FIXME, NewObject] Promise<(ServiceWorkerRegistration or undefined)> getRegistration(optional USVString clientURL = "");
|
||||
[FIXME, NewObject] Promise<FrozenArray<ServiceWorkerRegistration>> getRegistrations();
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
#import <DOM/EventTarget.idl>
|
||||
#import <DOM/EventHandler.idl>
|
||||
#import <HTML/ServiceWorker.idl>
|
||||
|
||||
// https://w3c.github.io/ServiceWorker/#serviceworkerregistration-interface
|
||||
[SecureContext, Exposed=(Window,Worker)]
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue