diff --git a/Tests/LibWeb/Text/expected/ServiceWorker/service-worker-register.txt b/Tests/LibWeb/Text/expected/ServiceWorker/service-worker-register.txt
new file mode 100644
index 00000000000..ad8ded6ef83
--- /dev/null
+++ b/Tests/LibWeb/Text/expected/ServiceWorker/service-worker-register.txt
@@ -0,0 +1 @@
+ServiceWorker registration failed: InternalError: TODO(ServiceWorkerContainer::start_register is not implemented in LibJS)
diff --git a/Tests/LibWeb/Text/input/ServiceWorker/service-worker-register.html b/Tests/LibWeb/Text/input/ServiceWorker/service-worker-register.html
new file mode 100644
index 00000000000..d0e5b0933fa
--- /dev/null
+++ b/Tests/LibWeb/Text/input/ServiceWorker/service-worker-register.html
@@ -0,0 +1,20 @@
+
+
+
diff --git a/Tests/LibWeb/Text/input/ServiceWorker/service-worker.js b/Tests/LibWeb/Text/input/ServiceWorker/service-worker.js
new file mode 100644
index 00000000000..e6733d97337
--- /dev/null
+++ b/Tests/LibWeb/Text/input/ServiceWorker/service-worker.js
@@ -0,0 +1,2 @@
+// FIXME: Add service worker code here
+console.log("hi from service worker");
diff --git a/Userland/Libraries/LibWeb/HTML/ServiceWorkerContainer.cpp b/Userland/Libraries/LibWeb/HTML/ServiceWorkerContainer.cpp
index 7d69059aad7..7a1ff4df393 100644
--- a/Userland/Libraries/LibWeb/HTML/ServiceWorkerContainer.cpp
+++ b/Userland/Libraries/LibWeb/HTML/ServiceWorkerContainer.cpp
@@ -8,8 +8,10 @@
#include
#include
#include
+#include
#include
#include
+#include
namespace Web::HTML {
@@ -40,6 +42,127 @@ JS::NonnullGCPtr ServiceWorkerContainer::create(JS::Real
return realm.heap().allocate(realm, realm);
}
+// https://w3c.github.io/ServiceWorker/#navigator-service-worker-register
+JS::NonnullGCPtr 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 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(*p->promise());
+}
+
+// https://w3c.github.io/ServiceWorker/#start-register-algorithm
+void ServiceWorkerContainer::start_register(Optional scope_url, URL::URL script_url, JS::NonnullGCPtr 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::ErrorType::NotImplemented, "ServiceWorkerContainer::start_register"sv).value());
+}
+
#undef __ENUMERATE
#define __ENUMERATE(attribute_name, event_name) \
void ServiceWorkerContainer::set_##attribute_name(WebIDL::CallbackType* value) \
diff --git a/Userland/Libraries/LibWeb/HTML/ServiceWorkerContainer.h b/Userland/Libraries/LibWeb/HTML/ServiceWorkerContainer.h
index 9576ac013be..18dc26ed418 100644
--- a/Userland/Libraries/LibWeb/HTML/ServiceWorkerContainer.h
+++ b/Userland/Libraries/LibWeb/HTML/ServiceWorkerContainer.h
@@ -7,7 +7,11 @@
#pragma once
+#include
+#include
#include
+#include
+#include
#define ENUMERATE_SERVICE_WORKER_CONTAINER_EVENT_HANDLERS(E) \
E(oncontrollerchange, HTML::EventNames::controllerchange) \
@@ -16,6 +20,12 @@
namespace Web::HTML {
+struct RegistrationOptions {
+ Optional 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 create(JS::Realm& realm);
virtual ~ServiceWorkerContainer() override;
+ JS::NonnullGCPtr 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 scope_url, URL::URL script_url, JS::NonnullGCPtr, EnvironmentSettingsObject&, URL::URL referrer, Bindings::WorkerType, Bindings::ServiceWorkerUpdateViaCache);
+
JS::NonnullGCPtr m_service_worker_client;
};
diff --git a/Userland/Libraries/LibWeb/HTML/ServiceWorkerContainer.idl b/Userland/Libraries/LibWeb/HTML/ServiceWorkerContainer.idl
index 3884eb7d987..c8567dc23f3 100644
--- a/Userland/Libraries/LibWeb/HTML/ServiceWorkerContainer.idl
+++ b/Userland/Libraries/LibWeb/HTML/ServiceWorkerContainer.idl
@@ -9,7 +9,8 @@ interface ServiceWorkerContainer : EventTarget {
[FIXME] readonly attribute ServiceWorker? controller;
[FIXME] readonly attribute Promise ready;
- [FIXME, NewObject] Promise register((TrustedScriptURL or USVString) scriptURL, optional RegistrationOptions options = {});
+ // FIXME: [NewObject] Promise register((TrustedScriptURL or USVString) scriptURL, optional RegistrationOptions options = {});
+ [NewObject, ImplementedAs=register_] Promise register(USVString scriptURL, optional RegistrationOptions options = {});
[FIXME, NewObject] Promise<(ServiceWorkerRegistration or undefined)> getRegistration(optional USVString clientURL = "");
[FIXME, NewObject] Promise> getRegistrations();
diff --git a/Userland/Libraries/LibWeb/HTML/ServiceWorkerRegistration.idl b/Userland/Libraries/LibWeb/HTML/ServiceWorkerRegistration.idl
index 59c3e2e756e..9f097e9f3e8 100644
--- a/Userland/Libraries/LibWeb/HTML/ServiceWorkerRegistration.idl
+++ b/Userland/Libraries/LibWeb/HTML/ServiceWorkerRegistration.idl
@@ -1,5 +1,6 @@
#import
#import
+#import
// https://w3c.github.io/ServiceWorker/#serviceworkerregistration-interface
[SecureContext, Exposed=(Window,Worker)]