LibWeb: Add initial implementation for WorkerGlobalScope

This initial implementation stubs out the WorkerGlobalScope,
WorkerLocation and WorkerNavigator classes. It doesn't take into account
all the things that actually need passed into the constructors for these
objects, nor the extra abstract operations that need to be performed on
them by the rest of the Browser infrastructure. However, it does create
bindings that compile and link :^)
This commit is contained in:
Andrew Kaster 2022-02-06 19:12:57 -07:00 committed by Andreas Kling
parent 8b38df72a3
commit 820e99f97d
Notes: sideshowbarker 2024-07-17 19:06:15 +09:00
11 changed files with 527 additions and 0 deletions

View File

@ -3493,6 +3493,8 @@ void generate_prototype_implementation(IDL::Interface const& interface)
#include <LibWeb/Bindings/TextWrapper.h>
#include <LibWeb/Bindings/URLSearchParamsWrapper.h>
#include <LibWeb/Bindings/WindowObject.h>
#include <LibWeb/Bindings/WorkerLocationWrapper.h>
#include <LibWeb/Bindings/WorkerNavigatorWrapper.h>
#include <LibWeb/DOM/Element.h>
#include <LibWeb/DOM/EventListener.h>
#include <LibWeb/DOM/Range.h>

View File

@ -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)

View File

@ -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;

View File

@ -0,0 +1,153 @@
/*
* Copyright (c) 2022, Andrew Kaster <akaster@serenityos.org>
*
* SPDX-License-Identifier: BSD-2-Clause
*/
#include <AK/Base64.h>
#include <AK/String.h>
#include <AK/Utf8View.h>
#include <AK/Vector.h>
#include <LibTextCodec/Decoder.h>
#include <LibWeb/Bindings/WorkerGlobalScopeWrapper.h>
#include <LibWeb/DOM/DOMException.h>
#include <LibWeb/Forward.h>
#include <LibWeb/HTML/EventHandler.h>
#include <LibWeb/HTML/EventNames.h>
#include <LibWeb/HTML/WorkerGlobalScope.h>
#include <LibWeb/HTML/WorkerLocation.h>
#include <LibWeb/HTML/WorkerNavigator.h>
namespace Web::HTML {
WorkerGlobalScope::WorkerGlobalScope()
: m_navigator(make_ref_counted<WorkerNavigator>())
{
}
WorkerGlobalScope::~WorkerGlobalScope() { }
// https://html.spec.whatwg.org/multipage/workers.html#importing-scripts-and-libraries
DOM::ExceptionOr<void> WorkerGlobalScope::import_scripts(Vector<String> 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<WorkerLocation const> 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<WorkerNavigator const> 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<Bindings::CallbackType> 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<String> 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<u8> 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<String> 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());
}
}

View File

@ -0,0 +1,126 @@
/*
* Copyright (c) 2022, Andrew Kaster <akaster@serenityos.org>
*
* SPDX-License-Identifier: BSD-2-Clause
*/
#pragma once
#include <AK/Optional.h>
#include <AK/RefCounted.h>
#include <AK/URL.h>
#include <LibWeb/Bindings/Wrappable.h>
#include <LibWeb/DOM/EventTarget.h>
#include <LibWeb/DOM/ExceptionOr.h>
#include <LibWeb/Forward.h>
#include <LibWeb/HTML/WorkerLocation.h>
#include <LibWeb/HTML/WorkerNavigator.h>
#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<WorkerGlobalScope>
, 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<WorkerGlobalScope const> self() const { return *this; }
NonnullRefPtr<WorkerLocation const> location() const;
NonnullRefPtr<WorkerNavigator const> navigator() const;
DOM::ExceptionOr<void> import_scripts(Vector<String> urls);
#undef __ENUMERATE
#define __ENUMERATE(attribute_name, event_name) \
void set_##attribute_name(Optional<Bindings::CallbackType>); \
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<String> btoa(String const& data) const;
DOM::ExceptionOr<String> 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<WorkerLocation> loc) { m_location = move(loc); }
protected:
explicit WorkerGlobalScope();
private:
RefPtr<WorkerLocation> m_location;
// FIXME: Implement WorkerNavigator according to the spec
NonnullRefPtr<WorkerNavigator> 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<AK::URL> 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 };
};
}

View File

@ -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);
};

View File

@ -0,0 +1,124 @@
/*
* Copyright (c) 2022, Andrew Kaster <akaster@serenityos.org>
*
* SPDX-License-Identifier: BSD-2-Clause
*/
#include <AK/String.h>
#include <LibWeb/HTML/WorkerGlobalScope.h>
#include <LibWeb/HTML/WorkerLocation.h>
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)
{
}
}

View File

@ -0,0 +1,43 @@
/*
* Copyright (c) 2022, Andrew Kaster <akaster@serenityos.org>
*
* SPDX-License-Identifier: BSD-2-Clause
*/
#pragma once
#include <AK/RefCounted.h>
#include <LibWeb/Bindings/Wrappable.h>
#include <LibWeb/Forward.h>
namespace Web::HTML {
// https://html.spec.whatwg.org/multipage/workers.html#worker-locations
class WorkerLocation
: public RefCounted<WorkerLocation>
, public Bindings::Wrappable {
public:
using WrapperType = Bindings::WorkerLocationWrapper;
static NonnullRefPtr<WorkerLocation> 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;
};
}

View File

@ -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;
};

View File

@ -0,0 +1,23 @@
/*
* Copyright (c) 2022, Andrew Kaster <akaster@serenityos.org>
*
* SPDX-License-Identifier: BSD-2-Clause
*/
#pragma once
#include <AK/RefCounted.h>
#include <LibWeb/Bindings/Wrappable.h>
#include <LibWeb/Forward.h>
namespace Web::HTML {
// FIXME: Add Mixin APIs from https://html.spec.whatwg.org/multipage/workers.html#the-workernavigator-object
class WorkerNavigator
: public RefCounted<WorkerNavigator>
, public Bindings::Wrappable {
public:
using WrapperType = Bindings::WorkerNavigatorWrapper;
};
}

View File

@ -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;