1
Fork 0
mirror of https://github.com/RGBCube/serenity synced 2025-07-27 04:07:45 +00:00

LibWeb: Replace GlobalObject with Realm in wrapper functions

Similar to create() in LibJS, wrap() et al. are on a low enough level to
warrant passing a Realm directly instead of relying on the current realm
from the VM, as a wrapper may need to be allocated while no JS is being
executed.
This commit is contained in:
Linus Groh 2022-08-22 18:31:08 +01:00
parent 56b2ae5ac0
commit 40a70461a0
60 changed files with 261 additions and 235 deletions

View file

@ -19,9 +19,9 @@ AbortSignal::AbortSignal()
{
}
JS::Object* AbortSignal::create_wrapper(JS::GlobalObject& global_object)
JS::Object* AbortSignal::create_wrapper(JS::Realm& realm)
{
return wrap(global_object, *this);
return wrap(realm, *this);
}
// https://dom.spec.whatwg.org/#abortsignal-add
@ -38,6 +38,10 @@ void AbortSignal::add_abort_algorithm(Function<void()> abort_algorithm)
// https://dom.spec.whatwg.org/#abortsignal-signal-abort
void AbortSignal::signal_abort(JS::Value reason)
{
VERIFY(wrapper());
auto& vm = wrapper()->vm();
auto& realm = *vm.current_realm();
// 1. If signal is aborted, then return.
if (aborted())
return;
@ -46,7 +50,7 @@ void AbortSignal::signal_abort(JS::Value reason)
if (!reason.is_undefined())
m_abort_reason = reason;
else
m_abort_reason = wrap(wrapper()->global_object(), AbortError::create("Aborted without reason"));
m_abort_reason = wrap(realm, AbortError::create("Aborted without reason"));
// 3. For each algorithm in signals abort algorithms: run algorithm.
for (auto& algorithm : m_abort_algorithms)

View file

@ -60,7 +60,7 @@ public:
// ^EventTarget
virtual void ref_event_target() override { ref(); }
virtual void unref_event_target() override { unref(); }
virtual JS::Object* create_wrapper(JS::GlobalObject&) override;
virtual JS::Object* create_wrapper(JS::Realm&) override;
private:
AbortSignal();

View file

@ -58,6 +58,6 @@ private:
namespace Web::Bindings {
DOMTokenListWrapper* wrap(JS::GlobalObject&, DOM::DOMTokenList&);
DOMTokenListWrapper* wrap(JS::Realm&, DOM::DOMTokenList&);
}

View file

@ -88,7 +88,8 @@ bool EventDispatcher::inner_invoke(Event& event, Vector<NonnullRefPtr<DOM::DOMEv
// 6. Let global be listener callbacks associated Realms global object.
auto& callback = listener->callback->callback();
auto& global = callback.callback.cell()->global_object();
auto& realm = callback.callback->shape().realm();
auto& global = realm.global_object();
// 7. Let currentEvent be undefined.
RefPtr<Event> current_event;
@ -112,8 +113,8 @@ bool EventDispatcher::inner_invoke(Event& event, Vector<NonnullRefPtr<DOM::DOMEv
// 10. Call a user objects operation with listeners callback, "handleEvent", « event », and events currentTarget attribute value. If this throws an exception, then:
// FIXME: These should be wrapped for us in call_user_object_operation, but it currently doesn't do that.
auto* this_value = Bindings::wrap(global, *event.current_target());
auto* wrapped_event = Bindings::wrap(global, event);
auto* this_value = Bindings::wrap(realm, *event.current_target());
auto* wrapped_event = Bindings::wrap(realm, event);
auto result = Bindings::IDL::call_user_object_operation(callback, "handleEvent", this_value, wrapped_event);
// If this throws an exception, then:

View file

@ -384,10 +384,10 @@ Bindings::CallbackType* EventTarget::get_current_value_of_event_handler(FlyStrin
return nullptr;
}
auto& global_object = settings_object.global_object();
auto& vm = Bindings::main_thread_vm();
// 8. Push settings object's realm execution context onto the JavaScript execution context stack; it is now the running JavaScript execution context.
global_object.vm().push_execution_context(settings_object.realm_execution_context());
vm.push_execution_context(settings_object.realm_execution_context());
// 9. Let function be the result of calling OrdinaryFunctionCreate, with arguments:
// functionPrototype
@ -419,19 +419,19 @@ Bindings::CallbackType* EventTarget::get_current_value_of_event_handler(FlyStrin
// 3. If eventHandler is an element's event handler, then set scope to NewObjectEnvironment(document, true, scope).
// (Otherwise, eventHandler is a Window object's event handler.)
if (is<Element>(this)) {
auto* wrapped_document = Bindings::wrap(global_object, *document);
auto* wrapped_document = Bindings::wrap(realm, *document);
scope = JS::new_object_environment(*wrapped_document, true, scope);
}
// 4. If form owner is not null, then set scope to NewObjectEnvironment(form owner, true, scope).
if (form_owner) {
auto* wrapped_form_owner = Bindings::wrap(global_object, *form_owner);
auto* wrapped_form_owner = Bindings::wrap(realm, *form_owner);
scope = JS::new_object_environment(*wrapped_form_owner, true, scope);
}
// 5. If element is not null, then set scope to NewObjectEnvironment(element, true, scope).
if (element) {
auto* wrapped_element = Bindings::wrap(global_object, *element);
auto* wrapped_element = Bindings::wrap(realm, *element);
scope = JS::new_object_environment(*wrapped_element, true, scope);
}
@ -441,8 +441,8 @@ Bindings::CallbackType* EventTarget::get_current_value_of_event_handler(FlyStrin
VERIFY(function);
// 10. Remove settings object's realm execution context from the JavaScript execution context stack.
VERIFY(global_object.vm().execution_context_stack().last() == &settings_object.realm_execution_context());
global_object.vm().pop_execution_context();
VERIFY(vm.execution_context_stack().last() == &settings_object.realm_execution_context());
vm.pop_execution_context();
// 11. Set function.[[ScriptOrModule]] to null.
function->set_script_or_module({});
@ -602,6 +602,7 @@ JS::ThrowCompletionOr<void> EventTarget::process_event_handler_for_event(FlyStri
// Needed for wrapping.
auto* callback_object = callback->callback.cell();
auto& realm = callback_object->shape().realm();
if (special_error_event_handling) {
// -> If special error event handling is true
@ -619,7 +620,7 @@ JS::ThrowCompletionOr<void> EventTarget::process_event_handler_for_event(FlyStri
// NOTE: current_target is always non-null here, as the event dispatcher takes care to make sure it's non-null (and uses it as the this value for the callback!)
// FIXME: This is rewrapping the this value of the callback defined in activate_event_handler. While I don't think this is observable as the event dispatcher
// calls directly into the callback without considering things such as proxies, it is a waste. However, if it observable, then we must reuse the this_value that was given to the callback.
auto* this_value = Bindings::wrap(callback_object->global_object(), *error_event.current_target());
auto* this_value = Bindings::wrap(realm, *error_event.current_target());
return_value_or_error = Bindings::IDL::invoke_callback(*callback, this_value, wrapped_message, wrapped_filename, wrapped_lineno, wrapped_colno, error_event.error());
} else {
@ -627,10 +628,10 @@ JS::ThrowCompletionOr<void> EventTarget::process_event_handler_for_event(FlyStri
// Invoke callback with one argument, the value of which is the Event object event, with the callback this value set to event's currentTarget. Let return value be the callback's return value. [WEBIDL]
// FIXME: This has the same rewrapping issue as this_value.
auto* wrapped_event = Bindings::wrap(callback_object->global_object(), event);
auto* wrapped_event = Bindings::wrap(realm, event);
// FIXME: The comments about this in the special_error_event_handling path also apply here.
auto* this_value = Bindings::wrap(callback_object->global_object(), *event.current_target());
auto* this_value = Bindings::wrap(realm, *event.current_target());
return_value_or_error = Bindings::IDL::invoke_callback(*callback, this_value, wrapped_event);
}

View file

@ -39,7 +39,7 @@ public:
virtual bool dispatch_event(NonnullRefPtr<Event>);
ExceptionOr<bool> dispatch_event_binding(NonnullRefPtr<Event>);
virtual JS::Object* create_wrapper(JS::GlobalObject&) = 0;
virtual JS::Object* create_wrapper(JS::Realm&) = 0;
virtual EventTarget* get_parent(Event const&) { return nullptr; }

View file

@ -64,6 +64,6 @@ private:
namespace Web::Bindings {
HTMLCollectionWrapper* wrap(JS::GlobalObject&, DOM::HTMLCollection&);
HTMLCollectionWrapper* wrap(JS::Realm&, DOM::HTMLCollection&);
}

View file

@ -64,6 +64,6 @@ private:
namespace Web::Bindings {
NamedNodeMapWrapper* wrap(JS::GlobalObject&, DOM::NamedNodeMap&);
NamedNodeMapWrapper* wrap(JS::Realm&, DOM::NamedNodeMap&);
}

View file

@ -806,9 +806,9 @@ bool Node::is_editable() const
return parent() && parent()->is_editable();
}
JS::Object* Node::create_wrapper(JS::GlobalObject& global_object)
JS::Object* Node::create_wrapper(JS::Realm& realm)
{
return wrap(global_object, *this);
return wrap(realm, *this);
}
void Node::removed_last_ref()

View file

@ -56,7 +56,7 @@ public:
// ^EventTarget
virtual void ref_event_target() final { ref(); }
virtual void unref_event_target() final { unref(); }
virtual JS::Object* create_wrapper(JS::GlobalObject&) override;
virtual JS::Object* create_wrapper(JS::Realm&) override;
virtual ~Node();

View file

@ -38,7 +38,7 @@ private:
Bindings::CallbackType m_callback;
};
inline JS::Object* wrap(JS::GlobalObject&, Web::DOM::NodeFilter& filter)
inline JS::Object* wrap(JS::Realm&, Web::DOM::NodeFilter& filter)
{
return filter.callback().callback.cell();
}

View file

@ -118,12 +118,12 @@ JS::ThrowCompletionOr<RefPtr<Node>> NodeIterator::traverse(Direction direction)
JS::ThrowCompletionOr<NodeFilter::Result> NodeIterator::filter(Node& node)
{
VERIFY(wrapper());
auto& global_object = wrapper()->global_object();
auto& vm = wrapper()->vm();
auto& realm = *vm.current_realm();
// 1. If traversers active flag is set, then throw an "InvalidStateError" DOMException.
if (m_active)
return JS::throw_completion(wrap(global_object, InvalidStateError::create("NodeIterator is already active")));
return JS::throw_completion(wrap(realm, InvalidStateError::create("NodeIterator is already active")));
// 2. Let n be nodes nodeType attribute value 1.
auto n = node.node_type() - 1;
@ -141,7 +141,7 @@ JS::ThrowCompletionOr<NodeFilter::Result> NodeIterator::filter(Node& node)
// 6. Let result be the return value of call a user objects operation with traversers filter, "acceptNode", and « node ».
// If this throws an exception, then unset traversers active flag and rethrow the exception.
auto result = Bindings::IDL::call_user_object_operation(m_filter->callback(), "acceptNode", {}, wrap(global_object, node));
auto result = Bindings::IDL::call_user_object_operation(m_filter->callback(), "acceptNode", {}, wrap(realm, node));
if (result.is_abrupt()) {
m_active = false;
return result;

View file

@ -221,12 +221,12 @@ JS::ThrowCompletionOr<RefPtr<Node>> TreeWalker::next_node()
JS::ThrowCompletionOr<NodeFilter::Result> TreeWalker::filter(Node& node)
{
VERIFY(wrapper());
auto& global_object = wrapper()->global_object();
auto& vm = wrapper()->vm();
auto& realm = *vm.current_realm();
// 1. If traversers active flag is set, then throw an "InvalidStateError" DOMException.
if (m_active)
return JS::throw_completion(wrap(global_object, InvalidStateError::create("NodeIterator is already active")));
return JS::throw_completion(wrap(realm, InvalidStateError::create("NodeIterator is already active")));
// 2. Let n be nodes nodeType attribute value 1.
auto n = node.node_type() - 1;
@ -244,7 +244,7 @@ JS::ThrowCompletionOr<NodeFilter::Result> TreeWalker::filter(Node& node)
// 6. Let result be the return value of call a user objects operation with traversers filter, "acceptNode", and « node ».
// If this throws an exception, then unset traversers active flag and rethrow the exception.
auto result = Bindings::IDL::call_user_object_operation(m_filter->callback(), "acceptNode", {}, wrap(global_object, node));
auto result = Bindings::IDL::call_user_object_operation(m_filter->callback(), "acceptNode", {}, wrap(realm, node));
if (result.is_abrupt()) {
m_active = false;
return result;