Skip to content

Commit 9e8f76d

Browse files
committed
LibWeb: Wire up UniversalGlobalScopeMixin to ShadowRealmGlobalScope
1 parent 6ad30c3 commit 9e8f76d

19 files changed

+216
-110
lines changed
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
Shadow realm evaluation returned: SGVsbG8sIHdvcmxkIQ==
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
Shadow realm evaluation returned: example.com
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
<script src="../include.js"></script>
2+
<script>
3+
test(() => {
4+
const realm = new ShadowRealm();
5+
const result = realm.evaluate(`() => self.btoa('Hello, world!')`)();
6+
println(`Shadow realm evaluation returned: ${result}`);
7+
});
8+
</script>
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
<script src="../include.js"></script>
2+
<script>
3+
test(() => {
4+
const realm = new ShadowRealm();
5+
const result = realm.evaluate(`() => new URL('https://example.com/path?name=value').hostname`)();
6+
println(`Shadow realm evaluation returned: ${result}`);
7+
});
8+
</script>

Userland/Libraries/LibWeb/CMakeLists.txt

+1
Original file line numberDiff line numberDiff line change
@@ -482,6 +482,7 @@ set(SOURCES
482482
HTML/TokenizedFeatures.cpp
483483
HTML/TrackEvent.cpp
484484
HTML/TraversableNavigable.cpp
485+
HTML/UniversalGlobalScope.cpp
485486
HTML/UserActivation.cpp
486487
HTML/VideoTrack.cpp
487488
HTML/VideoTrackList.cpp

Userland/Libraries/LibWeb/HTML/ShadowRealmGlobalScope.cpp

+5-3
Original file line numberDiff line numberDiff line change
@@ -26,14 +26,16 @@ JS::NonnullGCPtr<ShadowRealmGlobalScope> ShadowRealmGlobalScope::create(JS::Real
2626

2727
void ShadowRealmGlobalScope::initialize(JS::Realm&)
2828
{
29-
// 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.
30-
// Base::initialize(realm);
31-
// WEB_SET_PROTOTYPE_FOR_INTERFACE(ShadowRealmGlobalScope);
3229
}
3330

3431
void ShadowRealmGlobalScope::initialize_web_interfaces()
3532
{
33+
auto& realm = this->realm();
34+
35+
WEB_SET_PROTOTYPE_FOR_INTERFACE(ShadowRealmGlobalScope);
36+
3637
add_shadow_realm_exposed_interfaces(*this);
38+
Bindings::ShadowRealmGlobalScopeGlobalMixin::initialize(realm, *this);
3739
}
3840

3941
void ShadowRealmGlobalScope::visit_edges(Cell::Visitor& visitor)

Userland/Libraries/LibWeb/HTML/ShadowRealmGlobalScope.h

+14-1
Original file line numberDiff line numberDiff line change
@@ -7,12 +7,17 @@
77
#pragma once
88

99
#include <LibWeb/Bindings/Intrinsics.h>
10+
#include <LibWeb/Bindings/ShadowRealmGlobalScopeGlobalMixin.h>
1011
#include <LibWeb/DOM/EventTarget.h>
12+
#include <LibWeb/HTML/UniversalGlobalScope.h>
1113

1214
namespace Web::HTML {
1315

1416
// https://whatpr.org/html/9893/webappapis.html#shadowrealmglobalscope
15-
class ShadowRealmGlobalScope : public DOM::EventTarget {
17+
class ShadowRealmGlobalScope
18+
: public DOM::EventTarget
19+
, public UniversalGlobalScopeMixin
20+
, public Bindings::ShadowRealmGlobalScopeGlobalMixin {
1621
WEB_PLATFORM_OBJECT(ShadowRealmGlobalScope, DOM::EventTarget);
1722
JS_DECLARE_ALLOCATOR(ShadowRealmGlobalScope);
1823

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

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

29+
virtual Bindings::PlatformObject& this_impl() override { return *this; }
30+
virtual Bindings::PlatformObject const& this_impl() const override { return *this; }
31+
2432
// https://whatpr.org/html/9893/webappapis.html#dom-shadowrealmglobalscope-self
2533
JS::NonnullGCPtr<ShadowRealmGlobalScope> self()
2634
{
2735
// The self getter steps are to return this.
2836
return *this;
2937
}
3038

39+
using UniversalGlobalScopeMixin::atob;
40+
using UniversalGlobalScopeMixin::btoa;
41+
using UniversalGlobalScopeMixin::queue_microtask;
42+
using UniversalGlobalScopeMixin::structured_clone;
43+
3144
void initialize_web_interfaces();
3245

3346
protected:
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,9 @@
1+
#import <HTML/UniversalGlobalScope.idl>
2+
13
// https://whatpr.org/html/9893/webappapis.html#shadowrealmglobalscope
24
[Global, Exposed=ShadowRealm]
35
interface ShadowRealmGlobalScope : EventTarget {
46
readonly attribute ShadowRealmGlobalScope self;
57
};
8+
9+
ShadowRealmGlobalScope includes UniversalGlobalScope;
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,104 @@
1+
/*
2+
* Copyright (c) 2022, Andrew Kaster <akaster@serenityos.org>
3+
* Copyright (c) 2023, Linus Groh <linusg@serenityos.org>
4+
* Copyright (c) 2023, Luke Wilde <lukew@serenityos.org>
5+
* Copyright (c) 2024, Shannon Booth <shannon@serenityos.org>
6+
*
7+
* SPDX-License-Identifier: BSD-2-Clause
8+
*/
9+
10+
#include <AK/Base64.h>
11+
#include <AK/String.h>
12+
#include <AK/Utf8View.h>
13+
#include <AK/Vector.h>
14+
#include <LibJS/Heap/HeapFunction.h>
15+
#include <LibWeb/HTML/Scripting/ExceptionReporter.h>
16+
#include <LibWeb/HTML/StructuredSerialize.h>
17+
#include <LibWeb/HTML/StructuredSerializeOptions.h>
18+
#include <LibWeb/HTML/UniversalGlobalScope.h>
19+
#include <LibWeb/HTML/Window.h>
20+
#include <LibWeb/Infra/Strings.h>
21+
#include <LibWeb/WebIDL/AbstractOperations.h>
22+
#include <LibWeb/WebIDL/DOMException.h>
23+
#include <LibWeb/WebIDL/ExceptionOr.h>
24+
#include <LibWeb/WebIDL/Types.h>
25+
26+
namespace Web::HTML {
27+
28+
UniversalGlobalScopeMixin::~UniversalGlobalScopeMixin() = default;
29+
30+
// https://html.spec.whatwg.org/multipage/webappapis.html#dom-btoa
31+
WebIDL::ExceptionOr<String> UniversalGlobalScopeMixin::btoa(String const& data) const
32+
{
33+
auto& vm = this_impl().vm();
34+
auto& realm = *vm.current_realm();
35+
36+
// The btoa(data) method must throw an "InvalidCharacterError" DOMException if data contains any character whose code point is greater than U+00FF.
37+
Vector<u8> byte_string;
38+
byte_string.ensure_capacity(data.bytes().size());
39+
for (u32 code_point : Utf8View(data)) {
40+
if (code_point > 0xff)
41+
return WebIDL::InvalidCharacterError::create(realm, "Data contains characters outside the range U+0000 and U+00FF"_string);
42+
byte_string.append(code_point);
43+
}
44+
45+
// 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,
46+
// and then must apply forgiving-base64 encode to that byte sequence and return the result.
47+
return TRY_OR_THROW_OOM(vm, encode_base64(byte_string.span()));
48+
}
49+
50+
// https://html.spec.whatwg.org/multipage/webappapis.html#dom-atob
51+
WebIDL::ExceptionOr<String> UniversalGlobalScopeMixin::atob(String const& data) const
52+
{
53+
auto& vm = this_impl().vm();
54+
auto& realm = *vm.current_realm();
55+
56+
// 1. Let decodedData be the result of running forgiving-base64 decode on data.
57+
auto decoded_data = decode_base64(data);
58+
59+
// 2. If decodedData is failure, then throw an "InvalidCharacterError" DOMException.
60+
if (decoded_data.is_error())
61+
return WebIDL::InvalidCharacterError::create(realm, "Input string is not valid base64 data"_string);
62+
63+
// 3. Return decodedData.
64+
// decode_base64() returns a byte buffer. LibJS uses UTF-8 for strings. Use isomorphic decoding to convert bytes to UTF-8.
65+
return Infra::isomorphic_decode(decoded_data.value());
66+
}
67+
68+
// https://html.spec.whatwg.org/multipage/timers-and-user-prompts.html#dom-queuemicrotask
69+
void UniversalGlobalScopeMixin::queue_microtask(WebIDL::CallbackType& callback)
70+
{
71+
auto& vm = this_impl().vm();
72+
auto& realm = *vm.current_realm();
73+
74+
JS::GCPtr<DOM::Document> document;
75+
if (is<Window>(this_impl()))
76+
document = &static_cast<Window&>(this_impl()).associated_document();
77+
78+
// The queueMicrotask(callback) method must queue a microtask to invoke callback, and if callback throws an exception, report the exception.
79+
HTML::queue_a_microtask(document, JS::create_heap_function(realm.heap(), [&callback, &realm] {
80+
auto result = WebIDL::invoke_callback(callback, {});
81+
if (result.is_error())
82+
HTML::report_exception(result, realm);
83+
}));
84+
}
85+
86+
// https://html.spec.whatwg.org/multipage/structured-data.html#dom-structuredclone
87+
WebIDL::ExceptionOr<JS::Value> UniversalGlobalScopeMixin::structured_clone(JS::Value value, StructuredSerializeOptions const& options) const
88+
{
89+
auto& vm = this_impl().vm();
90+
(void)options;
91+
92+
// 1. Let serialized be ? StructuredSerializeWithTransfer(value, options["transfer"]).
93+
// FIXME: Use WithTransfer variant of the AO
94+
auto serialized = TRY(structured_serialize(vm, value));
95+
96+
// 2. Let deserializeRecord be ? StructuredDeserializeWithTransfer(serialized, this's relevant realm).
97+
// FIXME: Use WithTransfer variant of the AO
98+
auto deserialized = TRY(structured_deserialize(vm, serialized, relevant_realm(this_impl()), {}));
99+
100+
// 3. Return deserializeRecord.[[Deserialized]].
101+
return deserialized;
102+
}
103+
104+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
/*
2+
* Copyright (c) 2023, Linus Groh <linusg@serenityos.org>
3+
* Copyright (c) 2023, Luke Wilde <lukew@serenityos.org>
4+
* Copyright (c) 2024, Shannon Booth <shannon@serenityos.org>
5+
*
6+
* SPDX-License-Identifier: BSD-2-Clause
7+
*/
8+
9+
#pragma once
10+
11+
#include <AK/Forward.h>
12+
#include <AK/String.h>
13+
#include <LibJS/Runtime/Value.h>
14+
#include <LibWeb/Forward.h>
15+
#include <LibWeb/WebIDL/ExceptionOr.h>
16+
17+
namespace Web::HTML {
18+
19+
// https://whatpr.org/html/9893/webappapis.html#universalglobalscope-mixin
20+
class UniversalGlobalScopeMixin {
21+
public:
22+
virtual ~UniversalGlobalScopeMixin();
23+
24+
virtual Bindings::PlatformObject& this_impl() = 0;
25+
virtual Bindings::PlatformObject const& this_impl() const = 0;
26+
27+
WebIDL::ExceptionOr<String> btoa(String const& data) const;
28+
WebIDL::ExceptionOr<String> atob(String const& data) const;
29+
void queue_microtask(WebIDL::CallbackType&);
30+
WebIDL::ExceptionOr<JS::Value> structured_clone(JS::Value, StructuredSerializeOptions const&) const;
31+
};
32+
33+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
#import <HTML/MessagePort.idl>
2+
3+
// FIXME: Support VoidFunction in the IDL parser
4+
callback VoidFunction = undefined ();
5+
6+
// https://whatpr.org/html/9893/webappapis.html#universalglobalscope-mixin
7+
interface mixin UniversalGlobalScope {
8+
// base64 utility methods
9+
DOMString btoa(DOMString data);
10+
ByteString atob(DOMString data);
11+
12+
// microtask queuing
13+
undefined queueMicrotask(VoidFunction callback);
14+
15+
// structured cloning
16+
any structuredClone(any value, optional StructuredSerializeOptions options = {});
17+
};

Userland/Libraries/LibWeb/HTML/Window.h

+6-4
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@
2424
#include <LibWeb/HTML/Scripting/ImportMap.h>
2525
#include <LibWeb/HTML/ScrollOptions.h>
2626
#include <LibWeb/HTML/StructuredSerializeOptions.h>
27+
#include <LibWeb/HTML/UniversalGlobalScope.h>
2728
#include <LibWeb/HTML/WindowEventHandlers.h>
2829
#include <LibWeb/HTML/WindowOrWorkerGlobalScope.h>
2930
#include <LibWeb/RequestIdleCallback/IdleRequest.h>
@@ -49,6 +50,7 @@ class Window final
4950
, public GlobalEventHandlers
5051
, public WindowEventHandlers
5152
, public WindowOrWorkerGlobalScopeMixin
53+
, public UniversalGlobalScopeMixin
5254
, public Bindings::WindowGlobalMixin {
5355
WEB_PLATFORM_OBJECT(Window, DOM::EventTarget);
5456
JS_DECLARE_ALLOCATOR(Window);
@@ -58,17 +60,17 @@ class Window final
5860

5961
~Window();
6062

61-
using WindowOrWorkerGlobalScopeMixin::atob;
62-
using WindowOrWorkerGlobalScopeMixin::btoa;
63+
using UniversalGlobalScopeMixin::atob;
64+
using UniversalGlobalScopeMixin::btoa;
65+
using UniversalGlobalScopeMixin::queue_microtask;
66+
using UniversalGlobalScopeMixin::structured_clone;
6367
using WindowOrWorkerGlobalScopeMixin::clear_interval;
6468
using WindowOrWorkerGlobalScopeMixin::clear_timeout;
6569
using WindowOrWorkerGlobalScopeMixin::create_image_bitmap;
6670
using WindowOrWorkerGlobalScopeMixin::fetch;
67-
using WindowOrWorkerGlobalScopeMixin::queue_microtask;
6871
using WindowOrWorkerGlobalScopeMixin::report_error;
6972
using WindowOrWorkerGlobalScopeMixin::set_interval;
7073
using WindowOrWorkerGlobalScopeMixin::set_timeout;
71-
using WindowOrWorkerGlobalScopeMixin::structured_clone;
7274

7375
// ^DOM::EventTarget
7476
virtual bool dispatch_event(DOM::Event&) override;

Userland/Libraries/LibWeb/HTML/Window.idl

+2
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
#import <HTML/History.idl>
1010
#import <HTML/Navigation.idl>
1111
#import <HTML/Navigator.idl>
12+
#import <HTML/UniversalGlobalScope.idl>
1213
#import <HTML/WindowLocalStorage.idl>
1314
#import <HTML/WindowOrWorkerGlobalScope.idl>
1415
#import <HTML/WindowSessionStorage.idl>
@@ -115,6 +116,7 @@ interface Window : EventTarget {
115116
};
116117
Window includes AnimationFrameProvider;
117118
Window includes GlobalEventHandlers;
119+
Window includes UniversalGlobalScope;
118120
Window includes WindowEventHandlers;
119121
Window includes WindowLocalStorage;
120122
Window includes WindowSessionStorage;

0 commit comments

Comments
 (0)