diff --git a/Meta/Lagom/Tools/CodeGenerators/LibWeb/WrapperGenerator.cpp b/Meta/Lagom/Tools/CodeGenerators/LibWeb/WrapperGenerator.cpp index db9ac20da4..551bbf6243 100644 --- a/Meta/Lagom/Tools/CodeGenerators/LibWeb/WrapperGenerator.cpp +++ b/Meta/Lagom/Tools/CodeGenerators/LibWeb/WrapperGenerator.cpp @@ -3493,6 +3493,8 @@ void generate_prototype_implementation(IDL::Interface const& interface) #include #include #include +#include +#include #include #include #include diff --git a/Userland/Libraries/LibWeb/CMakeLists.txt b/Userland/Libraries/LibWeb/CMakeLists.txt index 762a2c96ac..8b5c86c294 100644 --- a/Userland/Libraries/LibWeb/CMakeLists.txt +++ b/Userland/Libraries/LibWeb/CMakeLists.txt @@ -201,6 +201,8 @@ set(SOURCES HTML/TagNames.cpp HTML/TextMetrics.cpp HTML/WebSocket.cpp + HTML/WorkerGlobalScope.cpp + HTML/WorkerLocation.cpp HighResolutionTime/Performance.cpp ImageDecoding.cpp InProcessWebView.cpp @@ -498,6 +500,9 @@ libweb_js_wrapper(HTML/Storage) libweb_js_wrapper(HTML/SubmitEvent) libweb_js_wrapper(HTML/TextMetrics) libweb_js_wrapper(HTML/WebSocket) +libweb_js_wrapper(HTML/WorkerGlobalScope) +libweb_js_wrapper(HTML/WorkerLocation) +libweb_js_wrapper(HTML/WorkerNavigator) libweb_js_wrapper(HighResolutionTime/Performance) libweb_js_wrapper(IntersectionObserver/IntersectionObserver) libweb_js_wrapper(NavigationTiming/PerformanceTiming) diff --git a/Userland/Libraries/LibWeb/Forward.h b/Userland/Libraries/LibWeb/Forward.h index 48064f4e63..33fd353fc1 100644 --- a/Userland/Libraries/LibWeb/Forward.h +++ b/Userland/Libraries/LibWeb/Forward.h @@ -218,6 +218,9 @@ class SubmitEvent; class TextMetrics; class WebSocket; class WindowEnvironmentSettingsObject; +class WorkerGlobalScope; +class WorkerLocation; +class WorkerNavigator; } namespace Web::HighResolutionTime { @@ -465,6 +468,9 @@ class URLSearchParamsWrapper; class URLWrapper; class WebSocketWrapper; class WindowObject; +class WorkerGlobalScopeWrapper; +class WorkerLocationWrapper; +class WorkerNavigatorWrapper; class Wrappable; class Wrapper; class XMLHttpRequestConstructor; diff --git a/Userland/Libraries/LibWeb/HTML/WorkerGlobalScope.cpp b/Userland/Libraries/LibWeb/HTML/WorkerGlobalScope.cpp new file mode 100644 index 0000000000..312e589127 --- /dev/null +++ b/Userland/Libraries/LibWeb/HTML/WorkerGlobalScope.cpp @@ -0,0 +1,153 @@ +/* + * Copyright (c) 2022, Andrew Kaster + * + * SPDX-License-Identifier: BSD-2-Clause + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace Web::HTML { + +WorkerGlobalScope::WorkerGlobalScope() + : m_navigator(make_ref_counted()) +{ +} + +WorkerGlobalScope::~WorkerGlobalScope() { } + +// https://html.spec.whatwg.org/multipage/workers.html#importing-scripts-and-libraries +DOM::ExceptionOr WorkerGlobalScope::import_scripts(Vector urls) +{ + // The algorithm may optionally be customized by supplying custom perform the fetch hooks, + // which if provided will be used when invoking fetch a classic worker-imported script. + // NOTE: Service Workers is an example of a specification that runs this algorithm with its own options for the perform the fetch hook. + + // 1. FIXME: If worker global scope's type is "module", throw a TypeError exception. + // 2. FIXME: Let settings object be the current settings object. + + // 3. If urls is empty, return. + if (urls.is_empty()) + return {}; + + // 4. FIXME: Parse each value in urls relative to settings object. If any fail, throw a "SyntaxError" DOMException. + // 5. FIXME: For each url in the resulting URL records, run these substeps: + // 1. Fetch a classic worker-imported script given url and settings object, passing along any custom perform the fetch steps provided. + // If this succeeds, let script be the result. Otherwise, rethrow the exception. + // 2. Run the classic script script, with the rethrow errors argument set to true. + // NOTE: script will run until it either returns, fails to parse, fails to catch an exception, + // or gets prematurely aborted by the terminate a worker algorithm defined above. + // If an exception was thrown or if the script was prematurely aborted, then abort all these steps, + // letting the exception or aborting continue to be processed by the calling script. + + return {}; +} + +JS::Object* WorkerGlobalScope::create_wrapper(JS::GlobalObject& global_object) +{ + return wrap(global_object, *this); +} + +// https://html.spec.whatwg.org/multipage/workers.html#dom-workerglobalscope-location +NonnullRefPtr WorkerGlobalScope::location() const +{ + // The location attribute must return the WorkerLocation object whose associated WorkerGlobalScope object is the WorkerGlobalScope object. + return *m_location; +} + +// https://html.spec.whatwg.org/multipage/workers.html#dom-worker-navigator +NonnullRefPtr WorkerGlobalScope::navigator() const +{ + // The navigator attribute of the WorkerGlobalScope interface must return an instance of the WorkerNavigator interface, + // which represents the identity and state of the user agent (the client). + return *m_navigator; +} + +#undef __ENUMERATE +#define __ENUMERATE(attribute_name, event_name) \ + void WorkerGlobalScope::set_##attribute_name(Optional value) \ + { \ + set_event_handler_attribute(event_name, move(value)); \ + } \ + Bindings::CallbackType* WorkerGlobalScope::attribute_name() \ + { \ + return event_handler_attribute(event_name); \ + } +ENUMERATE_WORKER_GLOBAL_SCOPE_EVENT_HANDLERS(__ENUMERATE) +#undef __ENUMERATE + +// https://html.spec.whatwg.org/multipage/webappapis.html#dom-origin +String WorkerGlobalScope::origin() const +{ + // FIXME: The origin getter steps are to return this's relevant settings object's origin, serialized. + return {}; +} + +// https://html.spec.whatwg.org/multipage/webappapis.html#dom-issecurecontext +bool WorkerGlobalScope::is_secure_context() const +{ + // FIXME: The isSecureContext getter steps are to return true if this's relevant settings object is a secure context, or false otherwise. + return false; +} + +// https://html.spec.whatwg.org/multipage/webappapis.html#dom-crossoriginisolated +bool WorkerGlobalScope::cross_origin_isolated() const +{ + // The crossOriginIsolated getter steps are to return this's relevant settings object's cross-origin isolated capability. + // FIXME: Is this the same thing as https://html.spec.whatwg.org/multipage/workers.html#concept-workerglobalscope-cross-origin-isolated-capability? + // "A WorkerGlobalScope object has an associated cross-origin isolated capability boolean. It is initially false." + return m_cross_origin_isolated_capability; +} + +// https://html.spec.whatwg.org/multipage/webappapis.html#dom-btoa +DOM::ExceptionOr WorkerGlobalScope::btoa(String const& data) const +{ + // FIXME: This is the same as the implementation in Bindings/WindowObject.cpp + // Find a way to share this implementation, since they come from the same mixin. + + // The btoa(data) method must throw an "InvalidCharacterError" DOMException if data contains any character whose code point is greater than U+00FF. + Vector byte_string; + byte_string.ensure_capacity(data.length()); + for (u32 code_point : Utf8View(data)) { + if (code_point > 0xff) + return DOM::InvalidCharacterError::create("Data contains characters outside the range U+0000 and U+00FF"); + byte_string.append(code_point); + } + + // Otherwise, the user agent must convert data to a byte sequence whose nth byte is the eight-bit representation of the nth code point of data, + // and then must apply forgiving-base64 encode to that byte sequence and return the result. + return encode_base64(byte_string.span()); +} + +// https://html.spec.whatwg.org/multipage/webappapis.html#dom-atob +DOM::ExceptionOr WorkerGlobalScope::atob(String const& data) const +{ + // FIXME: This is the same as the implementation in Bindings/WindowObject.cpp + // Find a way to share this implementation, since they come from the same mixin. + + // 1. Let decodedData be the result of running forgiving-base64 decode on data. + auto decoded_data = decode_base64(data.view()); + + // 2. If decodedData is failure, then throw an "InvalidCharacterError" DOMException. + if (decoded_data.is_error()) + return DOM::InvalidCharacterError::create("Input string is not valid base64 data"); + + // 3. Return decodedData. + // decode_base64() returns a byte string. LibJS uses UTF-8 for strings. Use Latin1Decoder to convert bytes 128-255 to UTF-8. + auto* decoder = TextCodec::decoder_for("windows-1252"); + VERIFY(decoder); + return decoder->to_utf8(decoded_data.value()); +} + +} diff --git a/Userland/Libraries/LibWeb/HTML/WorkerGlobalScope.h b/Userland/Libraries/LibWeb/HTML/WorkerGlobalScope.h new file mode 100644 index 0000000000..ba2cdc3809 --- /dev/null +++ b/Userland/Libraries/LibWeb/HTML/WorkerGlobalScope.h @@ -0,0 +1,126 @@ +/* + * Copyright (c) 2022, Andrew Kaster + * + * SPDX-License-Identifier: BSD-2-Clause + */ + +#pragma once + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define ENUMERATE_WORKER_GLOBAL_SCOPE_EVENT_HANDLERS(E) \ + E(onerror, HTML::EventNames::error) \ + E(onlanguagechange, HTML::EventNames::languagechange) \ + E(ononline, HTML::EventNames::online) \ + E(onoffline, HTML::EventNames::offline) \ + E(onrejectionhandled, HTML::EventNames::rejectionhandled) \ + E(onunhandledrejection, HTML::EventNames::unhandledrejection) + +namespace Web::HTML { + +// https://html.spec.whatwg.org/multipage/workers.html#the-workerglobalscope-common-interface +// WorkerGlobalScope is the base class of each real WorkerGlobalScope that will be created when the +// user agent runs the run a worker algorithm. +class WorkerGlobalScope + : public RefCounted + , public DOM::EventTarget + , public Bindings::Wrappable { +public: + using WrapperType = Bindings::WorkerGlobalScopeWrapper; + + using RefCounted::ref; + using RefCounted::unref; + + virtual ~WorkerGlobalScope() override; + + // ^EventTarget + virtual void ref_event_target() override { ref(); } + virtual void unref_event_target() override { unref(); } + virtual JS::Object* create_wrapper(JS::GlobalObject&) override; + + // Following methods are from the WorkerGlobalScope IDL definition + // https://html.spec.whatwg.org/multipage/workers.html#the-workerglobalscope-common-interface + + // https://html.spec.whatwg.org/multipage/workers.html#dom-workerglobalscope-self + NonnullRefPtr self() const { return *this; } + + NonnullRefPtr location() const; + NonnullRefPtr navigator() const; + DOM::ExceptionOr import_scripts(Vector urls); + +#undef __ENUMERATE +#define __ENUMERATE(attribute_name, event_name) \ + void set_##attribute_name(Optional); \ + Bindings::CallbackType* attribute_name(); + ENUMERATE_WORKER_GLOBAL_SCOPE_EVENT_HANDLERS(__ENUMERATE) +#undef __ENUMERATE + + // Following methods are from the WindowOrWorkerGlobalScope mixin + // https://html.spec.whatwg.org/multipage/webappapis.html#windoworworkerglobalscope-mixin + + String origin() const; + bool is_secure_context() const; + bool cross_origin_isolated() const; + DOM::ExceptionOr btoa(String const& data) const; + DOM::ExceptionOr atob(String const& data) const; + + // Non-IDL public methods + + AK::URL const& url() const { return m_url.value(); } + void set_url(AK::URL const& url) { m_url = url; } + + // Spec note: While the WorkerLocation object is created after the WorkerGlobalScope object, + // this is not problematic as it cannot be observed from script. + void set_location(NonnullRefPtr loc) { m_location = move(loc); } + +protected: + explicit WorkerGlobalScope(); + +private: + RefPtr m_location; + + // FIXME: Implement WorkerNavigator according to the spec + NonnullRefPtr m_navigator; + + // FIXME: Add all these internal slots + + // https://html.spec.whatwg.org/multipage/workers.html#concept-WorkerGlobalScope-owner-set + // A WorkerGlobalScope object has an associated owner set (a set of Document and WorkerGlobalScope objects). It is initially empty and populated when the worker is created or obtained. + // Note: It is a set, instead of a single owner, to accommodate SharedWorkerGlobalScope objects. + + // https://html.spec.whatwg.org/multipage/workers.html#concept-workerglobalscope-type + // A WorkerGlobalScope object has an associated type ("classic" or "module"). It is set during creation. + + // https://html.spec.whatwg.org/multipage/workers.html#concept-workerglobalscope-url + // A WorkerGlobalScope object has an associated url (null or a URL). It is initially null. + Optional m_url; + + // https://html.spec.whatwg.org/multipage/workers.html#concept-workerglobalscope-name + // A WorkerGlobalScope object has an associated name (a string). It is set during creation. + // Note: The name can have different semantics for each subclass of WorkerGlobalScope. + // For DedicatedWorkerGlobalScope instances, it is simply a developer-supplied name, useful mostly for debugging purposes. + // For SharedWorkerGlobalScope instances, it allows obtaining a reference to a common shared worker via the SharedWorker() constructor. + // For ServiceWorkerGlobalScope objects, it doesn't make sense (and as such isn't exposed through the JavaScript API at all). + + // https://html.spec.whatwg.org/multipage/workers.html#concept-workerglobalscope-policy-container + // A WorkerGlobalScope object has an associated policy container (a policy container). It is initially a new policy container. + + // https://html.spec.whatwg.org/multipage/workers.html#concept-workerglobalscope-embedder-policy + // A WorkerGlobalScope object has an associated embedder policy (an embedder policy). + + // https://html.spec.whatwg.org/multipage/workers.html#concept-workerglobalscope-module-map + // A WorkerGlobalScope object has an associated module map. It is a module map, initially empty. + + // https://html.spec.whatwg.org/multipage/workers.html#concept-workerglobalscope-cross-origin-isolated-capability + bool m_cross_origin_isolated_capability { false }; +}; + +} diff --git a/Userland/Libraries/LibWeb/HTML/WorkerGlobalScope.idl b/Userland/Libraries/LibWeb/HTML/WorkerGlobalScope.idl new file mode 100644 index 0000000000..d827dc315a --- /dev/null +++ b/Userland/Libraries/LibWeb/HTML/WorkerGlobalScope.idl @@ -0,0 +1,25 @@ +[Exposed=Worker] +interface WorkerGlobalScope : EventTarget { + readonly attribute WorkerGlobalScope self; + readonly attribute WorkerLocation location; + readonly attribute WorkerNavigator navigator; + undefined importScripts(USVString... urls); + + // FIXME: Should be an OnErrorEventHandler + attribute EventHandler onerror; + + attribute EventHandler onlanguagechange; + attribute EventHandler onoffline; + attribute EventHandler ononline; + attribute EventHandler onrejectionhandled; + attribute EventHandler onunhandledrejection; + + // FIXME: These should all come from a WindowOrWorkerGlobalScope mixin + [Replaceable] readonly attribute USVString origin; + readonly attribute boolean isSecureContext; + readonly attribute boolean crossOriginIsolated; + + // base64 utility methods + DOMString btoa(DOMString data); + ByteString atob(DOMString data); +}; diff --git a/Userland/Libraries/LibWeb/HTML/WorkerLocation.cpp b/Userland/Libraries/LibWeb/HTML/WorkerLocation.cpp new file mode 100644 index 0000000000..4499aa0aa3 --- /dev/null +++ b/Userland/Libraries/LibWeb/HTML/WorkerLocation.cpp @@ -0,0 +1,124 @@ +/* + * Copyright (c) 2022, Andrew Kaster + * + * SPDX-License-Identifier: BSD-2-Clause + */ + +#include +#include +#include + +namespace Web::HTML { + +// https://html.spec.whatwg.org/multipage/workers.html#dom-workerlocation-href +String WorkerLocation::href() const +{ + // The href getter steps are to return this's WorkerGlobalScope object's url, serialized. + return m_global_scope.url().serialize(); +} + +// https://html.spec.whatwg.org/multipage/workers.html#dom-workerlocation-origin +String WorkerLocation::origin() const +{ + // The origin getter steps are to return the serialization of this's WorkerGlobalScope object's url's origin. + return m_global_scope.url().serialize_origin(); +} + +// https://html.spec.whatwg.org/multipage/workers.html#dom-workerlocation-protocol +String WorkerLocation::protocol() const +{ + // The protocol getter steps are to return this's WorkerGlobalScope object's url's scheme, followed by ":". + return String::formatted("{}:", m_global_scope.url().scheme()); +} + +// https://html.spec.whatwg.org/multipage/workers.html#dom-workerlocation-host +String WorkerLocation::host() const +{ + // The host getter steps are: + // 1. Let url be this's WorkerGlobalScope object's url. + auto const& url = m_global_scope.url(); + + // 2. If url's host is null, return the empty string. + if (url.host().is_empty()) + return ""; + + // 3. If url's port is null, return url's host, serialized. + if (!url.port().has_value()) + return url.host(); + + // 4. Return url's host, serialized, followed by ":" and url's port, serialized. + return String::formatted("{}:{}", url.host(), url.port().value()); +} + +// https://html.spec.whatwg.org/multipage/workers.html#dom-workerlocation-hostname +String WorkerLocation::hostname() const +{ + // The hostname getter steps are: + // 1. Let host be this's WorkerGlobalScope object's url's host. + auto const& host = m_global_scope.url().host(); + + // 2. If host is null, return the empty string. + if (host.is_empty()) + return ""; + + // 3. Return host, serialized. + return host; +} + +// https://html.spec.whatwg.org/multipage/workers.html#dom-workerlocation-port +String WorkerLocation::port() const +{ + // The port getter steps are: + // 1. Let port be this's WorkerGlobalScope object's url's port. + auto const& port = m_global_scope.url().port(); + + // 2. If port is null, return the empty string. + if (!port.has_value()) + return ""; + // 3. Return port, serialized. + return String::number(port.value()); +} + +// https://html.spec.whatwg.org/multipage/workers.html#dom-workerlocation-pathname +String WorkerLocation::pathname() const +{ + // The pathname getter steps are to return the result of URL path serializing this's WorkerGlobalScope object's url. + return m_global_scope.url().path(); +} + +// https://html.spec.whatwg.org/multipage/workers.html#dom-workerlocation-search +String WorkerLocation::search() const +{ + // The search getter steps are: + // 1. Let query be this's WorkerGlobalScope object's url's query. + auto const& query = m_global_scope.url().query(); + + // 2. If query is either null or the empty string, return the empty string. + if (query.is_empty()) + return ""; + + // 3. Return "?", followed by query. + return String::formatted("?{}", query); +} + +// https://html.spec.whatwg.org/multipage/workers.html#dom-workerlocation-hash +String WorkerLocation::hash() const +{ + // The hash getter steps are: + // 1. Let fragment be this's WorkerGlobalScope object's url's fragment. + auto const& fragment = m_global_scope.url().fragment(); + + // 2. If fragment is either null or the empty string, return the empty string. + if (fragment.is_empty()) + return ""; + + // 3. Return "#", followed by fragment. + return String::formatted("#{}", fragment); +} + +WorkerLocation::WorkerLocation(WorkerGlobalScope& global_scope) + : m_global_scope(global_scope) +{ +} + +} diff --git a/Userland/Libraries/LibWeb/HTML/WorkerLocation.h b/Userland/Libraries/LibWeb/HTML/WorkerLocation.h new file mode 100644 index 0000000000..384b74bfc7 --- /dev/null +++ b/Userland/Libraries/LibWeb/HTML/WorkerLocation.h @@ -0,0 +1,43 @@ +/* + * Copyright (c) 2022, Andrew Kaster + * + * SPDX-License-Identifier: BSD-2-Clause + */ + +#pragma once + +#include +#include +#include + +namespace Web::HTML { + +// https://html.spec.whatwg.org/multipage/workers.html#worker-locations +class WorkerLocation + : public RefCounted + , public Bindings::Wrappable { +public: + using WrapperType = Bindings::WorkerLocationWrapper; + + static NonnullRefPtr create(WorkerGlobalScope& global_scope) + { + return adopt_ref(*new WorkerLocation(global_scope)); + } + + String href() const; + String origin() const; + String protocol() const; + String host() const; + String hostname() const; + String port() const; + String pathname() const; + String search() const; + String hash() const; + +private: + WorkerLocation(WorkerGlobalScope&); + + WorkerGlobalScope& m_global_scope; +}; + +} diff --git a/Userland/Libraries/LibWeb/HTML/WorkerLocation.idl b/Userland/Libraries/LibWeb/HTML/WorkerLocation.idl new file mode 100644 index 0000000000..464ca0c321 --- /dev/null +++ b/Userland/Libraries/LibWeb/HTML/WorkerLocation.idl @@ -0,0 +1,12 @@ +[Exposed=Worker] +interface WorkerLocation { + stringifier readonly attribute USVString href; + readonly attribute USVString origin; + readonly attribute USVString protocol; + readonly attribute USVString host; + readonly attribute USVString hostname; + readonly attribute USVString port; + readonly attribute USVString pathname; + readonly attribute USVString search; + readonly attribute USVString hash; +}; diff --git a/Userland/Libraries/LibWeb/HTML/WorkerNavigator.h b/Userland/Libraries/LibWeb/HTML/WorkerNavigator.h new file mode 100644 index 0000000000..6d992e1305 --- /dev/null +++ b/Userland/Libraries/LibWeb/HTML/WorkerNavigator.h @@ -0,0 +1,23 @@ +/* + * Copyright (c) 2022, Andrew Kaster + * + * SPDX-License-Identifier: BSD-2-Clause + */ + +#pragma once + +#include +#include +#include + +namespace Web::HTML { + +// FIXME: Add Mixin APIs from https://html.spec.whatwg.org/multipage/workers.html#the-workernavigator-object +class WorkerNavigator + : public RefCounted + , public Bindings::Wrappable { +public: + using WrapperType = Bindings::WorkerNavigatorWrapper; +}; + +} diff --git a/Userland/Libraries/LibWeb/HTML/WorkerNavigator.idl b/Userland/Libraries/LibWeb/HTML/WorkerNavigator.idl new file mode 100644 index 0000000000..41558f18c2 --- /dev/null +++ b/Userland/Libraries/LibWeb/HTML/WorkerNavigator.idl @@ -0,0 +1,8 @@ +[Exposed=Worker] +interface WorkerNavigator {}; + +// FIXME: Add these mixins that are used to prevent duplication b/w WorkerNavigator and Navigator +// WorkerNavigator includes NavigatorID; +// WorkerNavigator includes NavigatorLanguage; +// WorkerNavigator includes NavigatorOnLine; +// WorkerNavigator includes NavigatorConcurrentHardware;