diff --git a/Libraries/LibWeb/Bindings/MainThreadVM.cpp b/Libraries/LibWeb/Bindings/MainThreadVM.cpp index 003adef48f0..7cafd1a7b7f 100644 --- a/Libraries/LibWeb/Bindings/MainThreadVM.cpp +++ b/Libraries/LibWeb/Bindings/MainThreadVM.cpp @@ -31,13 +31,13 @@ #include #include #include -#include #include #include #include #include #include #include +#include #include #include #include @@ -76,7 +76,7 @@ ErrorOr initialize_main_thread_vm(HTML::EventLoop::Type type) { VERIFY(!s_main_thread_vm); - s_main_thread_vm = TRY(JS::VM::create(make())); + s_main_thread_vm = TRY(JS::VM::create(make())); auto& agent = as(*s_main_thread_vm->agent()); agent.event_loop = s_main_thread_vm->heap().allocate(type); @@ -659,7 +659,7 @@ JS::VM& main_thread_vm() void queue_mutation_observer_microtask(DOM::Document const& document) { auto& vm = main_thread_vm(); - auto& surrounding_agent = as(*vm.agent()); + auto& surrounding_agent = as(*vm.agent()); // 1. If the surrounding agent’s mutation observer microtask queued is true, then return. if (surrounding_agent.mutation_observer_microtask_queued) diff --git a/Libraries/LibWeb/CMakeLists.txt b/Libraries/LibWeb/CMakeLists.txt index d57fd088114..d040a2905f8 100644 --- a/Libraries/LibWeb/CMakeLists.txt +++ b/Libraries/LibWeb/CMakeLists.txt @@ -504,6 +504,7 @@ set(SOURCES HTML/Scripting/ModuleMap.cpp HTML/Scripting/ModuleScript.cpp HTML/Scripting/Script.cpp + HTML/Scripting/SimilarOriginWindowAgent.cpp HTML/Scripting/SyntheticRealmSettings.cpp HTML/Scripting/TemporaryExecutionContext.cpp HTML/Scripting/WindowEnvironmentSettingsObject.cpp diff --git a/Libraries/LibWeb/DOM/Element.cpp b/Libraries/LibWeb/DOM/Element.cpp index 963e22c3789..4ad66bdb650 100644 --- a/Libraries/LibWeb/DOM/Element.cpp +++ b/Libraries/LibWeb/DOM/Element.cpp @@ -63,7 +63,7 @@ #include #include #include -#include +#include #include #include #include @@ -2487,7 +2487,7 @@ bool Element::include_in_accessibility_tree() const void Element::enqueue_an_element_on_the_appropriate_element_queue() { // 1. Let reactionsStack be element's relevant agent's custom element reactions stack. - auto& relevant_agent = HTML::relevant_agent(*this); + auto& relevant_agent = HTML::relevant_similar_origin_window_agent(*this); auto& reactions_stack = relevant_agent.custom_element_reactions_stack; // 2. If reactionsStack is empty, then: @@ -2505,7 +2505,7 @@ void Element::enqueue_an_element_on_the_appropriate_element_queue() // 4. Queue a microtask to perform the following steps: // NOTE: `this` is protected by GC::Function HTML::queue_a_microtask(&document(), GC::create_function(heap(), [this]() { - auto& reactions_stack = HTML::relevant_agent(*this).custom_element_reactions_stack; + auto& reactions_stack = HTML::relevant_similar_origin_window_agent(*this).custom_element_reactions_stack; // 1. Invoke custom element reactions in reactionsStack's backup element queue. Bindings::invoke_custom_element_reactions(reactions_stack.backup_element_queue); diff --git a/Libraries/LibWeb/DOM/MutationObserver.cpp b/Libraries/LibWeb/DOM/MutationObserver.cpp index 3c19f552309..5f30c900953 100644 --- a/Libraries/LibWeb/DOM/MutationObserver.cpp +++ b/Libraries/LibWeb/DOM/MutationObserver.cpp @@ -9,7 +9,7 @@ #include #include #include -#include +#include namespace Web::DOM { @@ -30,7 +30,7 @@ MutationObserver::MutationObserver(JS::Realm& realm, GC::Ptr #include #include +#include namespace Web::DOM { @@ -207,7 +208,7 @@ void assign_a_slot(Slottable const& slottable) void signal_a_slot_change(GC::Ref slottable) { // 1. Append slot to slot’s relevant agent’s signal slots. - HTML::relevant_agent(slottable).signal_slots.append(slottable); + HTML::relevant_similar_origin_window_agent(slottable).signal_slots.append(slottable); // 2. Queue a mutation observer microtask. Bindings::queue_mutation_observer_microtask(slottable->document()); diff --git a/Libraries/LibWeb/HTML/Parser/HTMLParser.cpp b/Libraries/LibWeb/HTML/Parser/HTMLParser.cpp index 144061d5856..1607c6c8fcd 100644 --- a/Libraries/LibWeb/HTML/Parser/HTMLParser.cpp +++ b/Libraries/LibWeb/HTML/Parser/HTMLParser.cpp @@ -38,8 +38,8 @@ #include #include #include -#include #include +#include #include #include #include @@ -801,7 +801,7 @@ GC::Ref HTMLParser::create_element_for(HTMLToken const& token, Opt perform_a_microtask_checkpoint(); // 3. Push a new element queue onto document's relevant agent's custom element reactions stack. - relevant_agent(document).custom_element_reactions_stack.element_queue_stack.append({}); + relevant_similar_origin_window_agent(document).custom_element_reactions_stack.element_queue_stack.append({}); } // 9. Let element be the result of creating an element given document, localName, given namespace, null, is, and willExecuteScript. @@ -826,7 +826,7 @@ GC::Ref HTMLParser::create_element_for(HTMLToken const& token, Opt // 11. If willExecuteScript is true: if (will_execute_script) { // 1. Let queue be the result of popping from document's relevant agent's custom element reactions stack. (This will be the same element queue as was pushed above.) - auto queue = relevant_agent(document).custom_element_reactions_stack.element_queue_stack.take_last(); + auto queue = relevant_similar_origin_window_agent(document).custom_element_reactions_stack.element_queue_stack.take_last(); // 2. Invoke custom element reactions in queue. Bindings::invoke_custom_element_reactions(queue); @@ -5240,7 +5240,7 @@ void HTMLParser::insert_an_element_at_the_adjusted_insertion_location(GC::Ref> goal_condition) { Platform::EventLoopPlugin::the().spin_until(move(goal_condition)); diff --git a/Libraries/LibWeb/HTML/Scripting/Agent.h b/Libraries/LibWeb/HTML/Scripting/Agent.h index ccc3273403b..13303c55918 100644 --- a/Libraries/LibWeb/HTML/Scripting/Agent.h +++ b/Libraries/LibWeb/HTML/Scripting/Agent.h @@ -6,45 +6,20 @@ #pragma once -#include -#include #include #include #include -#include #include -#include namespace Web::HTML { -// https://html.spec.whatwg.org/multipage/webappapis.html#similar-origin-window-agent struct Agent : public JS::Agent { + // https://html.spec.whatwg.org/multipage/webappapis.html#window-event-loop + // The event loop of a similar-origin window agent is known as a window event loop. + // The event loop of a dedicated worker agent, shared worker agent, or service worker agent is known as a worker event loop. + // And the event loop of a worklet agent is known as a worklet event loop. GC::Root event_loop; - // FIXME: These should only be on similar-origin window agents, but we don't currently differentiate agent types. - - // https://dom.spec.whatwg.org/#mutation-observer-compound-microtask-queued-flag - bool mutation_observer_microtask_queued { false }; - - // https://dom.spec.whatwg.org/#mutation-observer-list - DOM::MutationObserver::List mutation_observers; - - // https://html.spec.whatwg.org/multipage/custom-elements.html#custom-element-reactions-stack - // Each similar-origin window agent has a custom element reactions stack, which is initially empty. - CustomElementReactionsStack custom_element_reactions_stack {}; - - // https://dom.spec.whatwg.org/#signal-slot-list - // Each similar-origin window agent has signal slots (a set of slots), which is initially empty. [HTML] - Vector> signal_slots; - - // https://html.spec.whatwg.org/multipage/custom-elements.html#current-element-queue - // A similar-origin window agent's current element queue is the element queue at the top of its custom element reactions stack. - Vector>& current_element_queue() { return custom_element_reactions_stack.element_queue_stack.last(); } - Vector> const& current_element_queue() const { return custom_element_reactions_stack.element_queue_stack.last(); } - - // [[CanBlock]] - virtual bool can_block() const override; - virtual void spin_event_loop_until(GC::Root> goal_condition) override; }; diff --git a/Libraries/LibWeb/HTML/Scripting/SimilarOriginWindowAgent.cpp b/Libraries/LibWeb/HTML/Scripting/SimilarOriginWindowAgent.cpp new file mode 100644 index 00000000000..95e64a3016b --- /dev/null +++ b/Libraries/LibWeb/HTML/Scripting/SimilarOriginWindowAgent.cpp @@ -0,0 +1,28 @@ +/* + * Copyright (c) 2025, Shannon Booth + * + * SPDX-License-Identifier: BSD-2-Clause + */ + +#include +#include +#include +#include + +namespace Web::HTML { + +bool SimilarOriginWindowAgent::can_block() const +{ + // similar-origin window agents can not block, see: https://html.spec.whatwg.org/multipage/webappapis.html#obtain-similar-origin-window-agent + return false; +} + +// https://html.spec.whatwg.org/multipage/webappapis.html#relevant-agent +SimilarOriginWindowAgent& relevant_similar_origin_window_agent(JS::Object const& object) +{ + // The relevant agent for a platform object platformObject is platformObject's relevant Realm's agent. + // Spec Note: This pointer is not yet defined in the JavaScript specification; see tc39/ecma262#1357. + return as(*relevant_realm(object).vm().agent()); +} + +} diff --git a/Libraries/LibWeb/HTML/Scripting/SimilarOriginWindowAgent.h b/Libraries/LibWeb/HTML/Scripting/SimilarOriginWindowAgent.h new file mode 100644 index 00000000000..7e1ffd16b56 --- /dev/null +++ b/Libraries/LibWeb/HTML/Scripting/SimilarOriginWindowAgent.h @@ -0,0 +1,50 @@ +/* + * Copyright (c) 2025, Shannon Booth + * + * SPDX-License-Identifier: BSD-2-Clause + */ + +#pragma once + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace Web::HTML { + +// https://html.spec.whatwg.org/multipage/webappapis.html#similar-origin-window-agent +struct SimilarOriginWindowAgent : public Agent { + // https://dom.spec.whatwg.org/#mutation-observer-compound-microtask-queued-flag + // Each similar-origin window agent has a mutation observer microtask queued (a boolean), which is initially false. [HTML] + bool mutation_observer_microtask_queued { false }; + + // https://dom.spec.whatwg.org/#mutation-observer-list + // Each similar-origin window agent also has pending mutation observers (a set of zero or more MutationObserver objects), which is initially empty. + DOM::MutationObserver::List mutation_observers; + + // https://html.spec.whatwg.org/multipage/custom-elements.html#custom-element-reactions-stack + // Each similar-origin window agent has a custom element reactions stack, which is initially empty. + CustomElementReactionsStack custom_element_reactions_stack {}; + + // https://dom.spec.whatwg.org/#signal-slot-list + // Each similar-origin window agent has signal slots (a set of slots), which is initially empty. [HTML] + Vector> signal_slots; + + // https://html.spec.whatwg.org/multipage/custom-elements.html#current-element-queue + // A similar-origin window agent's current element queue is the element queue at the top of its custom element reactions stack. + Vector>& current_element_queue() { return custom_element_reactions_stack.element_queue_stack.last(); } + Vector> const& current_element_queue() const { return custom_element_reactions_stack.element_queue_stack.last(); } + + // [[CanBlock]] + virtual bool can_block() const override; +}; + +SimilarOriginWindowAgent& relevant_similar_origin_window_agent(JS::Object const&); + +} diff --git a/Meta/Lagom/Tools/CodeGenerators/LibWeb/BindingsGenerator/IDLGenerators.cpp b/Meta/Lagom/Tools/CodeGenerators/LibWeb/BindingsGenerator/IDLGenerators.cpp index 46dae457dec..e8d9e47ad30 100644 --- a/Meta/Lagom/Tools/CodeGenerators/LibWeb/BindingsGenerator/IDLGenerators.cpp +++ b/Meta/Lagom/Tools/CodeGenerators/LibWeb/BindingsGenerator/IDLGenerators.cpp @@ -2181,7 +2181,7 @@ JS_DEFINE_NATIVE_FUNCTION(@class_name@::@function.name:snakecase@@overload_suffi if (function.extended_attributes.contains("CEReactions")) { // 1. Push a new element queue onto this object's relevant agent's custom element reactions stack. function_generator.append(R"~~~( - auto& reactions_stack = HTML::relevant_agent(*impl).custom_element_reactions_stack; + auto& reactions_stack = HTML::relevant_similar_origin_window_agent(*impl).custom_element_reactions_stack; reactions_stack.element_queue_stack.append({}); )~~~"); } @@ -3779,7 +3779,7 @@ JS_DEFINE_NATIVE_FUNCTION(@class_name@::@attribute.getter_callback@) if (attribute.extended_attributes.contains("CEReactions")) { // 1. Push a new element queue onto this object's relevant agent's custom element reactions stack. attribute_generator.append(R"~~~( - auto& reactions_stack = HTML::relevant_agent(*impl).custom_element_reactions_stack; + auto& reactions_stack = HTML::relevant_similar_origin_window_agent(*impl).custom_element_reactions_stack; reactions_stack.element_queue_stack.append({}); )~~~"); } @@ -4136,7 +4136,7 @@ JS_DEFINE_NATIVE_FUNCTION(@class_name@::@attribute.setter_callback@) if (attribute.extended_attributes.contains("CEReactions")) { // 1. Push a new element queue onto this object's relevant agent's custom element reactions stack. attribute_generator.append(R"~~~( - auto& reactions_stack = HTML::relevant_agent(*impl).custom_element_reactions_stack; + auto& reactions_stack = HTML::relevant_similar_origin_window_agent(*impl).custom_element_reactions_stack; reactions_stack.element_queue_stack.append({}); )~~~"); } @@ -5178,8 +5178,8 @@ void generate_prototype_implementation(IDL::Interface const& interface, StringBu #include #include #include -#include #include +#include #include #include #include