Skip to content

Commit

Permalink
LibWeb: Wire up UniversalGlobalScopeMixin to ShadowRealmGlobalScope
Browse files Browse the repository at this point in the history
  • Loading branch information
shannonbooth committed Nov 8, 2024
1 parent 7fc2762 commit 7f673ea
Show file tree
Hide file tree
Showing 19 changed files with 216 additions and 110 deletions.
1 change: 1 addition & 0 deletions Tests/LibWeb/Text/expected/HTML/shadow-realm-btoa.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Shadow realm evaluation returned: SGVsbG8sIHdvcmxkIQ==
1 change: 1 addition & 0 deletions Tests/LibWeb/Text/expected/HTML/shadow-realm-url.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Shadow realm evaluation returned: example.com
8 changes: 8 additions & 0 deletions Tests/LibWeb/Text/input/HTML/shadow-realm-btoa.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
<script src="../include.js"></script>
<script>
test(() => {
const realm = new ShadowRealm();
const result = realm.evaluate(`() => self.btoa('Hello, world!')`)();
println(`Shadow realm evaluation returned: ${result}`);
});
</script>
8 changes: 8 additions & 0 deletions Tests/LibWeb/Text/input/HTML/shadow-realm-url.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
<script src="../include.js"></script>
<script>
test(() => {
const realm = new ShadowRealm();
const result = realm.evaluate(`() => new URL('https://example.com/path?name=value').hostname`)();
println(`Shadow realm evaluation returned: ${result}`);
});
</script>
1 change: 1 addition & 0 deletions Userland/Libraries/LibWeb/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -482,6 +482,7 @@ set(SOURCES
HTML/TokenizedFeatures.cpp
HTML/TrackEvent.cpp
HTML/TraversableNavigable.cpp
HTML/UniversalGlobalScope.cpp
HTML/UserActivation.cpp
HTML/VideoTrack.cpp
HTML/VideoTrackList.cpp
Expand Down
8 changes: 5 additions & 3 deletions Userland/Libraries/LibWeb/HTML/ShadowRealmGlobalScope.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -26,14 +26,16 @@ JS::NonnullGCPtr<ShadowRealmGlobalScope> ShadowRealmGlobalScope::create(JS::Real

void ShadowRealmGlobalScope::initialize(JS::Realm&)
{
// FIXME: This does not work as we do not have any intrinsics in the [HostDefined] of a shadow realm. Figure out how this _should_ work.
// Base::initialize(realm);
// WEB_SET_PROTOTYPE_FOR_INTERFACE(ShadowRealmGlobalScope);
}

void ShadowRealmGlobalScope::initialize_web_interfaces()
{
auto& realm = this->realm();

WEB_SET_PROTOTYPE_FOR_INTERFACE(ShadowRealmGlobalScope);

add_shadow_realm_exposed_interfaces(*this);
Bindings::ShadowRealmGlobalScopeGlobalMixin::initialize(realm, *this);
}

void ShadowRealmGlobalScope::visit_edges(Cell::Visitor& visitor)
Expand Down
15 changes: 14 additions & 1 deletion Userland/Libraries/LibWeb/HTML/ShadowRealmGlobalScope.h
Original file line number Diff line number Diff line change
Expand Up @@ -7,12 +7,17 @@
#pragma once

#include <LibWeb/Bindings/Intrinsics.h>
#include <LibWeb/Bindings/ShadowRealmGlobalScopeGlobalMixin.h>
#include <LibWeb/DOM/EventTarget.h>
#include <LibWeb/HTML/UniversalGlobalScope.h>

namespace Web::HTML {

// https://whatpr.org/html/9893/webappapis.html#shadowrealmglobalscope
class ShadowRealmGlobalScope : public DOM::EventTarget {
class ShadowRealmGlobalScope
: public DOM::EventTarget
, public UniversalGlobalScopeMixin
, public Bindings::ShadowRealmGlobalScopeGlobalMixin {
WEB_PLATFORM_OBJECT(ShadowRealmGlobalScope, DOM::EventTarget);
JS_DECLARE_ALLOCATOR(ShadowRealmGlobalScope);

Expand All @@ -21,13 +26,21 @@ class ShadowRealmGlobalScope : public DOM::EventTarget {

static JS::NonnullGCPtr<ShadowRealmGlobalScope> create(JS::Realm&);

virtual Bindings::PlatformObject& this_impl() override { return *this; }
virtual Bindings::PlatformObject const& this_impl() const override { return *this; }

// https://whatpr.org/html/9893/webappapis.html#dom-shadowrealmglobalscope-self
JS::NonnullGCPtr<ShadowRealmGlobalScope> self()
{
// The self getter steps are to return this.
return *this;
}

using UniversalGlobalScopeMixin::atob;
using UniversalGlobalScopeMixin::btoa;
using UniversalGlobalScopeMixin::queue_microtask;
using UniversalGlobalScopeMixin::structured_clone;

void initialize_web_interfaces();

protected:
Expand Down
4 changes: 4 additions & 0 deletions Userland/Libraries/LibWeb/HTML/ShadowRealmGlobalScope.idl
Original file line number Diff line number Diff line change
@@ -1,5 +1,9 @@
#import <HTML/UniversalGlobalScope.idl>

// https://whatpr.org/html/9893/webappapis.html#shadowrealmglobalscope
[Global, Exposed=ShadowRealm]
interface ShadowRealmGlobalScope : EventTarget {
readonly attribute ShadowRealmGlobalScope self;
};

ShadowRealmGlobalScope includes UniversalGlobalScope;
104 changes: 104 additions & 0 deletions Userland/Libraries/LibWeb/HTML/UniversalGlobalScope.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,104 @@
/*
* Copyright (c) 2022, Andrew Kaster <akaster@serenityos.org>
* Copyright (c) 2023, Linus Groh <linusg@serenityos.org>
* Copyright (c) 2023, Luke Wilde <lukew@serenityos.org>
* Copyright (c) 2024, Shannon Booth <shannon@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 <LibJS/Heap/HeapFunction.h>
#include <LibWeb/HTML/Scripting/ExceptionReporter.h>
#include <LibWeb/HTML/StructuredSerialize.h>
#include <LibWeb/HTML/StructuredSerializeOptions.h>
#include <LibWeb/HTML/UniversalGlobalScope.h>
#include <LibWeb/HTML/Window.h>
#include <LibWeb/Infra/Strings.h>
#include <LibWeb/WebIDL/AbstractOperations.h>
#include <LibWeb/WebIDL/DOMException.h>
#include <LibWeb/WebIDL/ExceptionOr.h>
#include <LibWeb/WebIDL/Types.h>

namespace Web::HTML {

UniversalGlobalScopeMixin::~UniversalGlobalScopeMixin() = default;

// https://html.spec.whatwg.org/multipage/webappapis.html#dom-btoa
WebIDL::ExceptionOr<String> UniversalGlobalScopeMixin::btoa(String const& data) const
{
auto& vm = this_impl().vm();
auto& realm = *vm.current_realm();

// 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.bytes().size());
for (u32 code_point : Utf8View(data)) {
if (code_point > 0xff)
return WebIDL::InvalidCharacterError::create(realm, "Data contains characters outside the range U+0000 and U+00FF"_string);
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 TRY_OR_THROW_OOM(vm, encode_base64(byte_string.span()));
}

// https://html.spec.whatwg.org/multipage/webappapis.html#dom-atob
WebIDL::ExceptionOr<String> UniversalGlobalScopeMixin::atob(String const& data) const
{
auto& vm = this_impl().vm();
auto& realm = *vm.current_realm();

// 1. Let decodedData be the result of running forgiving-base64 decode on data.
auto decoded_data = decode_base64(data);

// 2. If decodedData is failure, then throw an "InvalidCharacterError" DOMException.
if (decoded_data.is_error())
return WebIDL::InvalidCharacterError::create(realm, "Input string is not valid base64 data"_string);

// 3. Return decodedData.
// decode_base64() returns a byte buffer. LibJS uses UTF-8 for strings. Use isomorphic decoding to convert bytes to UTF-8.
return Infra::isomorphic_decode(decoded_data.value());
}

// https://html.spec.whatwg.org/multipage/timers-and-user-prompts.html#dom-queuemicrotask
void UniversalGlobalScopeMixin::queue_microtask(WebIDL::CallbackType& callback)
{
auto& vm = this_impl().vm();
auto& realm = *vm.current_realm();

JS::GCPtr<DOM::Document> document;
if (is<Window>(this_impl()))
document = &static_cast<Window&>(this_impl()).associated_document();

// The queueMicrotask(callback) method must queue a microtask to invoke callback, and if callback throws an exception, report the exception.
HTML::queue_a_microtask(document, JS::create_heap_function(realm.heap(), [&callback, &realm] {
auto result = WebIDL::invoke_callback(callback, {});
if (result.is_error())
HTML::report_exception(result, realm);
}));
}

// https://html.spec.whatwg.org/multipage/structured-data.html#dom-structuredclone
WebIDL::ExceptionOr<JS::Value> UniversalGlobalScopeMixin::structured_clone(JS::Value value, StructuredSerializeOptions const& options) const
{
auto& vm = this_impl().vm();
(void)options;

// 1. Let serialized be ? StructuredSerializeWithTransfer(value, options["transfer"]).
// FIXME: Use WithTransfer variant of the AO
auto serialized = TRY(structured_serialize(vm, value));

// 2. Let deserializeRecord be ? StructuredDeserializeWithTransfer(serialized, this's relevant realm).
// FIXME: Use WithTransfer variant of the AO
auto deserialized = TRY(structured_deserialize(vm, serialized, relevant_realm(this_impl()), {}));

// 3. Return deserializeRecord.[[Deserialized]].
return deserialized;
}

}
33 changes: 33 additions & 0 deletions Userland/Libraries/LibWeb/HTML/UniversalGlobalScope.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
/*
* Copyright (c) 2023, Linus Groh <linusg@serenityos.org>
* Copyright (c) 2023, Luke Wilde <lukew@serenityos.org>
* Copyright (c) 2024, Shannon Booth <shannon@serenityos.org>
*
* SPDX-License-Identifier: BSD-2-Clause
*/

#pragma once

#include <AK/Forward.h>
#include <AK/String.h>
#include <LibJS/Runtime/Value.h>
#include <LibWeb/Forward.h>
#include <LibWeb/WebIDL/ExceptionOr.h>

namespace Web::HTML {

// https://whatpr.org/html/9893/webappapis.html#universalglobalscope-mixin
class UniversalGlobalScopeMixin {
public:
virtual ~UniversalGlobalScopeMixin();

virtual Bindings::PlatformObject& this_impl() = 0;
virtual Bindings::PlatformObject const& this_impl() const = 0;

WebIDL::ExceptionOr<String> btoa(String const& data) const;
WebIDL::ExceptionOr<String> atob(String const& data) const;
void queue_microtask(WebIDL::CallbackType&);
WebIDL::ExceptionOr<JS::Value> structured_clone(JS::Value, StructuredSerializeOptions const&) const;
};

}
17 changes: 17 additions & 0 deletions Userland/Libraries/LibWeb/HTML/UniversalGlobalScope.idl
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
#import <HTML/MessagePort.idl>

// FIXME: Support VoidFunction in the IDL parser
callback VoidFunction = undefined ();

// https://whatpr.org/html/9893/webappapis.html#universalglobalscope-mixin
interface mixin UniversalGlobalScope {
// base64 utility methods
DOMString btoa(DOMString data);
ByteString atob(DOMString data);

// microtask queuing
undefined queueMicrotask(VoidFunction callback);

// structured cloning
any structuredClone(any value, optional StructuredSerializeOptions options = {});
};
10 changes: 6 additions & 4 deletions Userland/Libraries/LibWeb/HTML/Window.h
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@
#include <LibWeb/HTML/Scripting/ImportMap.h>
#include <LibWeb/HTML/ScrollOptions.h>
#include <LibWeb/HTML/StructuredSerializeOptions.h>
#include <LibWeb/HTML/UniversalGlobalScope.h>
#include <LibWeb/HTML/WindowEventHandlers.h>
#include <LibWeb/HTML/WindowOrWorkerGlobalScope.h>
#include <LibWeb/RequestIdleCallback/IdleRequest.h>
Expand All @@ -49,6 +50,7 @@ class Window final
, public GlobalEventHandlers
, public WindowEventHandlers
, public WindowOrWorkerGlobalScopeMixin
, public UniversalGlobalScopeMixin
, public Bindings::WindowGlobalMixin {
WEB_PLATFORM_OBJECT(Window, DOM::EventTarget);
JS_DECLARE_ALLOCATOR(Window);
Expand All @@ -58,17 +60,17 @@ class Window final

~Window();

using WindowOrWorkerGlobalScopeMixin::atob;
using WindowOrWorkerGlobalScopeMixin::btoa;
using UniversalGlobalScopeMixin::atob;
using UniversalGlobalScopeMixin::btoa;
using UniversalGlobalScopeMixin::queue_microtask;
using UniversalGlobalScopeMixin::structured_clone;
using WindowOrWorkerGlobalScopeMixin::clear_interval;
using WindowOrWorkerGlobalScopeMixin::clear_timeout;
using WindowOrWorkerGlobalScopeMixin::create_image_bitmap;
using WindowOrWorkerGlobalScopeMixin::fetch;
using WindowOrWorkerGlobalScopeMixin::queue_microtask;
using WindowOrWorkerGlobalScopeMixin::report_error;
using WindowOrWorkerGlobalScopeMixin::set_interval;
using WindowOrWorkerGlobalScopeMixin::set_timeout;
using WindowOrWorkerGlobalScopeMixin::structured_clone;

// ^DOM::EventTarget
virtual bool dispatch_event(DOM::Event&) override;
Expand Down
2 changes: 2 additions & 0 deletions Userland/Libraries/LibWeb/HTML/Window.idl
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
#import <HTML/History.idl>
#import <HTML/Navigation.idl>
#import <HTML/Navigator.idl>
#import <HTML/UniversalGlobalScope.idl>
#import <HTML/WindowLocalStorage.idl>
#import <HTML/WindowOrWorkerGlobalScope.idl>
#import <HTML/WindowSessionStorage.idl>
Expand Down Expand Up @@ -115,6 +116,7 @@ interface Window : EventTarget {
};
Window includes AnimationFrameProvider;
Window includes GlobalEventHandlers;
Window includes UniversalGlobalScope;
Window includes WindowEventHandlers;
Window includes WindowLocalStorage;
Window includes WindowSessionStorage;
Expand Down
Loading

0 comments on commit 7f673ea

Please sign in to comment.