LibWeb: Split out SimilarOriginWindowAgent from HTML::Agent

To allow for adding the concept of a WorkerAgent to be reused
between shared and dedicated workers. An event loop is the
commonality between the different agent types, though, there
are some differences between those event loops which we customize
on the construction of the HTML::EventLoop.
This commit is contained in:
Shannon Booth 2025-04-24 15:04:13 +12:00 committed by Andreas Kling
commit 084cceab5c
Notes: github-actions[bot] 2025-04-25 14:45:55 +00:00
11 changed files with 103 additions and 54 deletions

View file

@ -31,13 +31,13 @@
#include <LibWeb/HTML/HTMLSlotElement.h>
#include <LibWeb/HTML/Location.h>
#include <LibWeb/HTML/PromiseRejectionEvent.h>
#include <LibWeb/HTML/Scripting/Agent.h>
#include <LibWeb/HTML/Scripting/ClassicScript.h>
#include <LibWeb/HTML/Scripting/Environments.h>
#include <LibWeb/HTML/Scripting/ExceptionReporter.h>
#include <LibWeb/HTML/Scripting/Fetching.h>
#include <LibWeb/HTML/Scripting/ModuleScript.h>
#include <LibWeb/HTML/Scripting/Script.h>
#include <LibWeb/HTML/Scripting/SimilarOriginWindowAgent.h>
#include <LibWeb/HTML/Scripting/SyntheticRealmSettings.h>
#include <LibWeb/HTML/Scripting/TemporaryExecutionContext.h>
#include <LibWeb/HTML/ShadowRealmGlobalScope.h>
@ -76,7 +76,7 @@ ErrorOr<void> initialize_main_thread_vm(HTML::EventLoop::Type type)
{
VERIFY(!s_main_thread_vm);
s_main_thread_vm = TRY(JS::VM::create(make<HTML::Agent>()));
s_main_thread_vm = TRY(JS::VM::create(make<HTML::SimilarOriginWindowAgent>()));
auto& agent = as<HTML::Agent>(*s_main_thread_vm->agent());
agent.event_loop = s_main_thread_vm->heap().allocate<HTML::EventLoop>(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<HTML::Agent>(*vm.agent());
auto& surrounding_agent = as<HTML::SimilarOriginWindowAgent>(*vm.agent());
// 1. If the surrounding agents mutation observer microtask queued is true, then return.
if (surrounding_agent.mutation_observer_microtask_queued)

View file

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

View file

@ -63,7 +63,7 @@
#include <LibWeb/HTML/HTMLUListElement.h>
#include <LibWeb/HTML/Numbers.h>
#include <LibWeb/HTML/Parser/HTMLParser.h>
#include <LibWeb/HTML/Scripting/Agent.h>
#include <LibWeb/HTML/Scripting/SimilarOriginWindowAgent.h>
#include <LibWeb/HTML/Scripting/TemporaryExecutionContext.h>
#include <LibWeb/HTML/TraversableNavigable.h>
#include <LibWeb/HTML/Window.h>
@ -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);

View file

@ -9,7 +9,7 @@
#include <LibWeb/Bindings/MutationObserverPrototype.h>
#include <LibWeb/DOM/MutationObserver.h>
#include <LibWeb/DOM/Node.h>
#include <LibWeb/HTML/Scripting/Agent.h>
#include <LibWeb/HTML/Scripting/SimilarOriginWindowAgent.h>
namespace Web::DOM {
@ -30,7 +30,7 @@ MutationObserver::MutationObserver(JS::Realm& realm, GC::Ptr<WebIDL::CallbackTyp
// 1. Set thiss callback to callback.
// 2. Append this to thiss relevant agents mutation observers.
HTML::relevant_agent(*this).mutation_observers.append(*this);
HTML::relevant_similar_origin_window_agent(*this).mutation_observers.append(*this);
}
MutationObserver::~MutationObserver()
@ -39,7 +39,7 @@ MutationObserver::~MutationObserver()
void MutationObserver::finalize()
{
HTML::relevant_agent(*this).mutation_observers.remove(*this);
HTML::relevant_similar_origin_window_agent(*this).mutation_observers.remove(*this);
}
void MutationObserver::initialize(JS::Realm& realm)

View file

@ -11,6 +11,7 @@
#include <LibWeb/DOM/Slottable.h>
#include <LibWeb/DOM/Text.h>
#include <LibWeb/HTML/HTMLSlotElement.h>
#include <LibWeb/HTML/Scripting/SimilarOriginWindowAgent.h>
namespace Web::DOM {
@ -207,7 +208,7 @@ void assign_a_slot(Slottable const& slottable)
void signal_a_slot_change(GC::Ref<HTML::HTMLSlotElement> slottable)
{
// 1. Append slot to slots relevant agents 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());

View file

@ -38,8 +38,8 @@
#include <LibWeb/HTML/Parser/HTMLEncodingDetection.h>
#include <LibWeb/HTML/Parser/HTMLParser.h>
#include <LibWeb/HTML/Parser/HTMLToken.h>
#include <LibWeb/HTML/Scripting/Agent.h>
#include <LibWeb/HTML/Scripting/ExceptionReporter.h>
#include <LibWeb/HTML/Scripting/SimilarOriginWindowAgent.h>
#include <LibWeb/HTML/Window.h>
#include <LibWeb/HighResolutionTime/TimeOrigin.h>
#include <LibWeb/Infra/CharacterTypes.h>
@ -801,7 +801,7 @@ GC::Ref<DOM::Element> 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<DOM::Element> 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<DO
// 3. If the parser was not created as part of the HTML fragment parsing algorithm,
// then push a new element queue onto element's relevant agent's custom element reactions stack.
if (!m_parsing_fragment) {
relevant_agent(*element).custom_element_reactions_stack.element_queue_stack.append({});
relevant_similar_origin_window_agent(*element).custom_element_reactions_stack.element_queue_stack.append({});
}
// 4. Insert element at the adjusted insertion location.
@ -5249,7 +5249,7 @@ void HTMLParser::insert_an_element_at_the_adjusted_insertion_location(GC::Ref<DO
// 5. If the parser was not created as part of the HTML fragment parsing algorithm,
// then pop the element queue from element's relevant agent's custom element reactions stack, and invoke custom element reactions in that queue.
if (!m_parsing_fragment) {
auto queue = relevant_agent(*element).custom_element_reactions_stack.element_queue_stack.take_last();
auto queue = relevant_similar_origin_window_agent(*element).custom_element_reactions_stack.element_queue_stack.take_last();
Bindings::invoke_custom_element_reactions(queue);
}
}

View file

@ -11,12 +11,6 @@
namespace Web::HTML {
bool Agent::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;
}
void Agent::spin_event_loop_until(GC::Root<GC::Function<bool()>> goal_condition)
{
Platform::EventLoopPlugin::the().spin_until(move(goal_condition));

View file

@ -6,45 +6,20 @@
#pragma once
#include <AK/OwnPtr.h>
#include <AK/Vector.h>
#include <LibGC/Root.h>
#include <LibJS/Forward.h>
#include <LibJS/Runtime/Agent.h>
#include <LibWeb/DOM/MutationObserver.h>
#include <LibWeb/Forward.h>
#include <LibWeb/HTML/CustomElements/CustomElementReactionsStack.h>
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<HTML::EventLoop> 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<GC::Root<HTML::HTMLSlotElement>> 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<GC::Root<DOM::Element>>& current_element_queue() { return custom_element_reactions_stack.element_queue_stack.last(); }
Vector<GC::Root<DOM::Element>> 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<GC::Function<bool()>> goal_condition) override;
};

View file

@ -0,0 +1,28 @@
/*
* Copyright (c) 2025, Shannon Booth <shannon@serenityos.org>
*
* SPDX-License-Identifier: BSD-2-Clause
*/
#include <LibWeb/Bindings/MainThreadVM.h>
#include <LibWeb/HTML/Scripting/Environments.h>
#include <LibWeb/HTML/Scripting/SimilarOriginWindowAgent.h>
#include <LibWeb/Platform/EventLoopPlugin.h>
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<SimilarOriginWindowAgent>(*relevant_realm(object).vm().agent());
}
}

View file

@ -0,0 +1,50 @@
/*
* Copyright (c) 2025, Shannon Booth <shannon@serenityos.org>
*
* SPDX-License-Identifier: BSD-2-Clause
*/
#pragma once
#include <AK/OwnPtr.h>
#include <AK/Vector.h>
#include <LibGC/Root.h>
#include <LibJS/Forward.h>
#include <LibJS/Runtime/Agent.h>
#include <LibWeb/DOM/MutationObserver.h>
#include <LibWeb/Forward.h>
#include <LibWeb/HTML/CustomElements/CustomElementReactionsStack.h>
#include <LibWeb/HTML/Scripting/Agent.h>
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<GC::Root<HTML::HTMLSlotElement>> 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<GC::Root<DOM::Element>>& current_element_queue() { return custom_element_reactions_stack.element_queue_stack.last(); }
Vector<GC::Root<DOM::Element>> 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&);
}

View file

@ -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 <LibWeb/DOM/NodeFilter.h>
#include <LibWeb/DOM/Range.h>
#include <LibWeb/HTML/Numbers.h>
#include <LibWeb/HTML/Scripting/Agent.h>
#include <LibWeb/HTML/Scripting/Environments.h>
#include <LibWeb/HTML/Scripting/SimilarOriginWindowAgent.h>
#include <LibWeb/HTML/Window.h>
#include <LibWeb/HTML/WindowProxy.h>
#include <LibWeb/Infra/Strings.h>