Explorar el Código

LibWeb: Make CallbackType take a realm instead of settings object

In line with the ShadowRealm proposal changes in the WebIDL spec:
webidl#1437 and supporting changes in HTML spec.

This is required for ShadowRealms as they have no relevant settings
object on the shadow realm, so fixes a crash in the QueueingStrategy
test in this commit.
Shannon Booth hace 7 meses
padre
commit
d6bcd3fb0b

+ 1 - 1
Libraries/LibWeb/Bindings/MainThreadVM.cpp

@@ -673,7 +673,7 @@ void queue_mutation_observer_microtask(DOM::Document const& document)
             // 4. If records is not empty, then invoke mo’s callback with « records, mo », and mo. If this throws an exception, catch it, and report the exception.
             if (!records.is_empty()) {
                 auto& callback = mutation_observer->callback();
-                auto& realm = callback.callback_context->realm();
+                auto& realm = callback.callback_context;
 
                 auto wrapped_records = MUST(JS::Array::create(realm, 0));
                 for (size_t i = 0; i < records.size(); ++i) {

+ 1 - 1
Libraries/LibWeb/DOM/Document.cpp

@@ -4203,7 +4203,7 @@ void Document::start_intersection_observing_a_lazy_loading_element(Element& elem
         // Spec Note: This allows for fetching the image during scrolling, when it does not yet — but is about to — intersect the viewport.
         auto options = IntersectionObserver::IntersectionObserverInit {};
 
-        auto wrapped_callback = realm.heap().allocate<WebIDL::CallbackType>(callback, Bindings::principal_host_defined_environment_settings_object(realm));
+        auto wrapped_callback = realm.heap().allocate<WebIDL::CallbackType>(callback, realm);
         m_lazy_load_intersection_observer = IntersectionObserver::IntersectionObserver::construct_impl(realm, wrapped_callback, options).release_value_but_fixme_should_propagate_errors();
     }
 

+ 3 - 2
Libraries/LibWeb/DOM/EventTarget.cpp

@@ -492,7 +492,8 @@ WebIDL::CallbackType* EventTarget::get_current_value_of_event_handler(FlyString
         function->set_script_or_module({});
 
         // 12. Set eventHandler's value to the result of creating a Web IDL EventHandler callback function object whose object reference is function and whose callback context is settings object.
-        event_handler->value = GC::Ptr(realm.heap().allocate<WebIDL::CallbackType>(*function, settings_object));
+        // FIXME: Update this comment once the ShadowRealm proposal is merged to pass realm.
+        event_handler->value = GC::Ptr(realm.heap().allocate<WebIDL::CallbackType>(*function, realm));
     }
 
     // 4. Return eventHandler's value.
@@ -584,7 +585,7 @@ void EventTarget::activate_event_handler(FlyString const& name, HTML::EventHandl
         0, "", &realm);
 
     // NOTE: As per the spec, the callback context is arbitrary.
-    auto callback = realm.heap().allocate<WebIDL::CallbackType>(*callback_function, Bindings::principal_host_defined_environment_settings_object(realm));
+    auto callback = realm.heap().allocate<WebIDL::CallbackType>(*callback_function, realm);
 
     // 5. Let listener be a new event listener whose type is the event handler event type corresponding to eventHandler and callback is callback.
     auto listener = realm.heap().allocate<DOMEventListener>();

+ 3 - 2
Libraries/LibWeb/HTML/CustomElements/CustomElementRegistry.cpp

@@ -44,6 +44,7 @@ void CustomElementRegistry::visit_edges(Visitor& visitor)
 }
 
 // https://webidl.spec.whatwg.org/#es-callback-function
+// https://github.com/whatwg/html/pull/9893
 static JS::ThrowCompletionOr<GC::Ref<WebIDL::CallbackType>> convert_value_to_callback_function(JS::VM& vm, JS::Value value)
 {
     // FIXME: De-duplicate this from the IDL generator.
@@ -51,8 +52,8 @@ static JS::ThrowCompletionOr<GC::Ref<WebIDL::CallbackType>> convert_value_to_cal
     if (!value.is_function())
         return vm.throw_completion<JS::TypeError>(JS::ErrorType::NotAFunction, value.to_string_without_side_effects());
 
-    // 2. Return the IDL callback function type value that represents a reference to the same object that V represents, with the incumbent settings object as the callback context.
-    return vm.heap().allocate<WebIDL::CallbackType>(value.as_object(), HTML::incumbent_settings_object());
+    // 2. Return the IDL callback function type value that represents a reference to the same object that V represents, with the incumbent realm as the callback context.
+    return vm.heap().allocate<WebIDL::CallbackType>(value.as_object(), HTML::incumbent_realm());
 }
 
 // https://webidl.spec.whatwg.org/#es-sequence

+ 2 - 2
Libraries/LibWeb/HTML/HTMLDialogElement.cpp

@@ -215,7 +215,7 @@ WebIDL::ExceptionOr<void> HTMLDialogElement::show_modal()
             return JS::js_undefined();
         },
         0, "", &realm());
-    auto cancel_callback = realm().heap().allocate<WebIDL::CallbackType>(*cancel_callback_function, Bindings::principal_host_defined_environment_settings_object(realm()));
+    auto cancel_callback = realm().heap().allocate<WebIDL::CallbackType>(*cancel_callback_function, realm());
     m_close_watcher->add_event_listener_without_options(HTML::EventNames::cancel, DOM::IDLEventListener::create(realm(), cancel_callback));
     // - closeAction being to close the dialog given this and null.
     auto close_callback_function = JS::NativeFunction::create(
@@ -225,7 +225,7 @@ WebIDL::ExceptionOr<void> HTMLDialogElement::show_modal()
             return JS::js_undefined();
         },
         0, "", &realm());
-    auto close_callback = realm().heap().allocate<WebIDL::CallbackType>(*close_callback_function, Bindings::principal_host_defined_environment_settings_object(realm()));
+    auto close_callback = realm().heap().allocate<WebIDL::CallbackType>(*close_callback_function, realm());
     m_close_watcher->add_event_listener_without_options(HTML::EventNames::close, DOM::IDLEventListener::create(realm(), close_callback));
 
     // FIXME: 16. Set this's previously focused element to the focused element.

+ 9 - 9
Libraries/LibWeb/HTML/HTMLInputElement.cpp

@@ -899,7 +899,7 @@ void HTMLInputElement::create_text_input_shadow_tree()
                 return JS::js_undefined();
             },
             0, "", &realm());
-        auto mouseup_callback = realm().heap().allocate<WebIDL::CallbackType>(*mouseup_callback_function, Bindings::principal_host_defined_environment_settings_object(realm()));
+        auto mouseup_callback = realm().heap().allocate<WebIDL::CallbackType>(*mouseup_callback_function, realm());
         DOM::AddEventListenerOptions mouseup_listener_options;
         mouseup_listener_options.once = true;
 
@@ -912,7 +912,7 @@ void HTMLInputElement::create_text_input_shadow_tree()
                 return JS::js_undefined();
             },
             0, "", &realm());
-        auto step_up_callback = realm().heap().allocate<WebIDL::CallbackType>(*up_callback_function, Bindings::principal_host_defined_environment_settings_object(realm()));
+        auto step_up_callback = realm().heap().allocate<WebIDL::CallbackType>(*up_callback_function, realm());
         up_button->add_event_listener_without_options(UIEvents::EventNames::mousedown, DOM::IDLEventListener::create(realm(), step_up_callback));
         up_button->add_event_listener_without_options(UIEvents::EventNames::mouseup, DOM::IDLEventListener::create(realm(), mouseup_callback));
 
@@ -934,7 +934,7 @@ void HTMLInputElement::create_text_input_shadow_tree()
                 return JS::js_undefined();
             },
             0, "", &realm());
-        auto step_down_callback = realm().heap().allocate<WebIDL::CallbackType>(*down_callback_function, Bindings::principal_host_defined_environment_settings_object(realm()));
+        auto step_down_callback = realm().heap().allocate<WebIDL::CallbackType>(*down_callback_function, realm());
         down_button->add_event_listener_without_options(UIEvents::EventNames::mousedown, DOM::IDLEventListener::create(realm(), step_down_callback));
         down_button->add_event_listener_without_options(UIEvents::EventNames::mouseup, DOM::IDLEventListener::create(realm(), mouseup_callback));
     }
@@ -993,7 +993,7 @@ void HTMLInputElement::create_file_input_shadow_tree()
     };
 
     auto on_button_click_function = JS::NativeFunction::create(realm, move(on_button_click), 0, "", &realm);
-    auto on_button_click_callback = realm.heap().allocate<WebIDL::CallbackType>(on_button_click_function, Bindings::principal_host_defined_environment_settings_object(realm));
+    auto on_button_click_callback = realm.heap().allocate<WebIDL::CallbackType>(on_button_click_function, realm);
     m_file_button->add_event_listener_without_options(UIEvents::EventNames::click, DOM::IDLEventListener::create(realm, on_button_click_callback));
 
     update_file_input_shadow_tree();
@@ -1065,7 +1065,7 @@ void HTMLInputElement::create_range_input_shadow_tree()
             return JS::js_undefined();
         },
         0, "", &realm());
-    auto keydown_callback = realm().heap().allocate<WebIDL::CallbackType>(*keydown_callback_function, Bindings::principal_host_defined_environment_settings_object(realm()));
+    auto keydown_callback = realm().heap().allocate<WebIDL::CallbackType>(*keydown_callback_function, realm());
     add_event_listener_without_options(UIEvents::EventNames::keydown, DOM::IDLEventListener::create(realm(), keydown_callback));
 
     auto wheel_callback_function = JS::NativeFunction::create(
@@ -1080,7 +1080,7 @@ void HTMLInputElement::create_range_input_shadow_tree()
             return JS::js_undefined();
         },
         0, "", &realm());
-    auto wheel_callback = realm().heap().allocate<WebIDL::CallbackType>(*wheel_callback_function, Bindings::principal_host_defined_environment_settings_object(realm()));
+    auto wheel_callback = realm().heap().allocate<WebIDL::CallbackType>(*wheel_callback_function, realm());
     add_event_listener_without_options(UIEvents::EventNames::wheel, DOM::IDLEventListener::create(realm(), wheel_callback));
 
     auto update_slider_by_mouse = [this](JS::VM& vm) {
@@ -1103,7 +1103,7 @@ void HTMLInputElement::create_range_input_shadow_tree()
                     return JS::js_undefined();
                 },
                 0, "", &realm());
-            auto mousemove_callback = realm().heap().allocate<WebIDL::CallbackType>(*mousemove_callback_function, Bindings::principal_host_defined_environment_settings_object(realm()));
+            auto mousemove_callback = realm().heap().allocate<WebIDL::CallbackType>(*mousemove_callback_function, realm());
             auto mousemove_listener = DOM::IDLEventListener::create(realm(), mousemove_callback);
             auto& window = static_cast<HTML::Window&>(relevant_global_object(*this));
             window.add_event_listener_without_options(UIEvents::EventNames::mousemove, mousemove_listener);
@@ -1115,7 +1115,7 @@ void HTMLInputElement::create_range_input_shadow_tree()
                     return JS::js_undefined();
                 },
                 0, "", &realm());
-            auto mouseup_callback = realm().heap().allocate<WebIDL::CallbackType>(*mouseup_callback_function, Bindings::principal_host_defined_environment_settings_object(realm()));
+            auto mouseup_callback = realm().heap().allocate<WebIDL::CallbackType>(*mouseup_callback_function, realm());
             DOM::AddEventListenerOptions mouseup_listener_options;
             mouseup_listener_options.once = true;
             window.add_event_listener(UIEvents::EventNames::mouseup, DOM::IDLEventListener::create(realm(), mouseup_callback), mouseup_listener_options);
@@ -1123,7 +1123,7 @@ void HTMLInputElement::create_range_input_shadow_tree()
             return JS::js_undefined();
         },
         0, "", &realm());
-    auto mousedown_callback = realm().heap().allocate<WebIDL::CallbackType>(*mousedown_callback_function, Bindings::principal_host_defined_environment_settings_object(realm()));
+    auto mousedown_callback = realm().heap().allocate<WebIDL::CallbackType>(*mousedown_callback_function, realm());
     add_event_listener_without_options(UIEvents::EventNames::mousedown, DOM::IDLEventListener::create(realm(), mousedown_callback));
 }
 

+ 4 - 2
Libraries/LibWeb/HTML/UniversalGlobalScope.cpp

@@ -124,7 +124,8 @@ GC::Ref<WebIDL::CallbackType> UniversalGlobalScopeMixin::count_queuing_strategy_
         auto function = JS::NativeFunction::create(realm, move(steps), 0, "size", &realm);
 
         // 3. Set globalObject’s count queuing strategy size function to a Function that represents a reference to F, with callback context equal to globalObject’s relevant settings object.
-        m_count_queuing_strategy_size_function = realm.create<WebIDL::CallbackType>(*function, relevant_settings_object(this_impl()));
+        // FIXME: Update spec comment to pass globalObject's relevant realm once Streams spec is updated for ShadowRealm spec
+        m_count_queuing_strategy_size_function = realm.create<WebIDL::CallbackType>(*function, realm);
     }
 
     return GC::Ref { *m_count_queuing_strategy_size_function };
@@ -148,7 +149,8 @@ GC::Ref<WebIDL::CallbackType> UniversalGlobalScopeMixin::byte_length_queuing_str
         auto function = JS::NativeFunction::create(realm, move(steps), 1, "size", &realm);
 
         // 3. Set globalObject’s byte length queuing strategy size function to a Function that represents a reference to F, with callback context equal to globalObject’s relevant settings object.
-        m_byte_length_queuing_strategy_size_function = realm.create<WebIDL::CallbackType>(*function, relevant_settings_object(this_impl()));
+        // FIXME: Update spec comment to pass globalObject's relevant realm once Streams spec is updated for ShadowRealm spec
+        m_byte_length_queuing_strategy_size_function = realm.create<WebIDL::CallbackType>(*function, realm);
     }
 
     return GC::Ref { *m_byte_length_queuing_strategy_size_function };

+ 1 - 1
Libraries/LibWeb/ResizeObserver/ResizeObserver.cpp

@@ -100,7 +100,7 @@ void ResizeObserver::disconnect()
 void ResizeObserver::invoke_callback(ReadonlySpan<GC::Ref<ResizeObserverEntry>> entries) const
 {
     auto& callback = *m_callback;
-    auto& realm = callback.callback_context->realm();
+    auto& realm = callback.callback_context;
 
     auto wrapped_records = MUST(JS::Array::create(realm, 0));
     for (size_t i = 0; i < entries.size(); ++i) {

+ 1 - 1
Libraries/LibWeb/Streams/AbstractOperations.cpp

@@ -5428,7 +5428,7 @@ JS::ThrowCompletionOr<GC::Root<WebIDL::CallbackType>> property_to_callback(JS::V
     if (!property.is_function())
         return vm.throw_completion<JS::TypeError>(JS::ErrorType::NotAFunction, property.to_string_without_side_effects());
 
-    return vm.heap().allocate<WebIDL::CallbackType>(property.as_object(), HTML::incumbent_settings_object(), operation_returns_promise);
+    return vm.heap().allocate<WebIDL::CallbackType>(property.as_object(), HTML::incumbent_realm(), operation_returns_promise);
 }
 
 // https://streams.spec.whatwg.org/#set-up-readable-byte-stream-controller-from-underlying-source

+ 3 - 6
Libraries/LibWeb/WebIDL/AbstractOperations.cpp

@@ -167,9 +167,8 @@ JS::Completion call_user_object_operation(WebIDL::CallbackType& callback, String
     // 4. Let relevant realm be O’s associated Realm.
     auto& relevant_realm = object->shape().realm();
 
-    // FIXME: We should get the realm directly from the callback context.
     // 5. Let stored realm be value’s callback context.
-    auto& stored_realm = callback.callback_context->realm();
+    auto& stored_realm = callback.callback_context;
 
     // 6. Prepare to run script with relevant realm.
     HTML::prepare_to_run_script(relevant_realm);
@@ -252,9 +251,8 @@ JS::Completion invoke_callback(WebIDL::CallbackType& callback, Optional<JS::Valu
     // 5. Let relevant realm be F’s associated Realm.
     auto& relevant_realm = function_object->shape().realm();
 
-    // FIXME: We should get the realm directly from the callback context.
     // 6. Let stored realm be value’s callback context.
-    auto& stored_realm = callback.callback_context->realm();
+    auto& stored_realm = callback.callback_context;
 
     // 8. Prepare to run script with relevant realm.
     HTML::prepare_to_run_script(relevant_realm);
@@ -297,9 +295,8 @@ JS::Completion construct(WebIDL::CallbackType& callback, GC::MarkedVector<JS::Va
     if (!JS::Value(function_object).is_constructor())
         return relevant_realm.vm().template throw_completion<JS::TypeError>(JS::ErrorType::NotAConstructor, JS::Value(function_object).to_string_without_side_effects());
 
-    // FIXME: We should get the realm directly from the callback context.
     // 4. Let stored realm be callable’s callback context.
-    auto& stored_realm = callback.callback_context->realm();
+    auto& stored_realm = callback.callback_context;
 
     // 5. Prepare to run script with relevant realm.
     HTML::prepare_to_run_script(relevant_realm);

+ 1 - 1
Libraries/LibWeb/WebIDL/CallbackType.cpp

@@ -12,7 +12,7 @@ namespace Web::WebIDL {
 
 GC_DEFINE_ALLOCATOR(CallbackType);
 
-CallbackType::CallbackType(JS::Object& callback, HTML::EnvironmentSettingsObject& callback_context, OperationReturnsPromise operation_returns_promise)
+CallbackType::CallbackType(JS::Object& callback, JS::Realm& callback_context, OperationReturnsPromise operation_returns_promise)
     : callback(callback)
     , callback_context(callback_context)
     , operation_returns_promise(operation_returns_promise)

+ 3 - 2
Libraries/LibWeb/WebIDL/CallbackType.h

@@ -24,12 +24,13 @@ class CallbackType final : public JS::Cell {
     GC_DECLARE_ALLOCATOR(CallbackType);
 
 public:
-    CallbackType(JS::Object& callback, HTML::EnvironmentSettingsObject& callback_context, OperationReturnsPromise = OperationReturnsPromise::No);
+    CallbackType(JS::Object& callback, JS::Realm& callback_context, OperationReturnsPromise = OperationReturnsPromise::No);
 
     GC::Ref<JS::Object> callback;
 
     // https://webidl.spec.whatwg.org/#dfn-callback-context
-    GC::Ref<HTML::EnvironmentSettingsObject> callback_context;
+    // NOTE: This is a Realm per ShadowRealm proposal https://github.com/whatwg/webidl/pull/1437
+    GC::Ref<JS::Realm> callback_context;
 
     // Non-standard property used to distinguish Promise-returning callbacks in callback-related AOs
     OperationReturnsPromise operation_returns_promise;

+ 6 - 6
Meta/Lagom/Tools/CodeGenerators/LibWeb/BindingsGenerator/IDLGenerators.cpp

@@ -554,7 +554,7 @@ static void generate_to_cpp(SourceGenerator& generator, ParameterType& parameter
         if (!@js_name@@js_suffix@.is_object())
             return vm.throw_completion<JS::TypeError>(JS::ErrorType::NotAnObject, @js_name@@js_suffix@.to_string_without_side_effects());
 
-        auto callback_type = vm.heap().allocate<WebIDL::CallbackType>(@js_name@@js_suffix@.as_object(), HTML::incumbent_settings_object());
+        auto callback_type = vm.heap().allocate<WebIDL::CallbackType>(@js_name@@js_suffix@.as_object(), HTML::incumbent_realm());
         @cpp_name@ = TRY(throw_dom_exception_if_needed(vm, [&] { return @cpp_type@::create(realm, *callback_type); }));
     }
 )~~~");
@@ -563,7 +563,7 @@ static void generate_to_cpp(SourceGenerator& generator, ParameterType& parameter
     if (!@js_name@@js_suffix@.is_object())
         return vm.throw_completion<JS::TypeError>(JS::ErrorType::NotAnObject, @js_name@@js_suffix@.to_string_without_side_effects());
 
-    auto callback_type = vm.heap().allocate<WebIDL::CallbackType>(@js_name@@js_suffix@.as_object(), HTML::incumbent_settings_object());
+    auto callback_type = vm.heap().allocate<WebIDL::CallbackType>(@js_name@@js_suffix@.as_object(), HTML::incumbent_realm());
     auto @cpp_name@ = adopt_ref(*new @cpp_type@(callback_type));
 )~~~");
         }
@@ -916,16 +916,16 @@ static void generate_to_cpp(SourceGenerator& generator, ParameterType& parameter
         return vm.throw_completion<JS::TypeError>(JS::ErrorType::NotAFunction, @js_name@@js_suffix@.to_string_without_side_effects());
 )~~~");
         }
-        // 2. Return the IDL callback function type value that represents a reference to the same object that V represents, with the incumbent settings object as the callback context.
+        // 2. Return the IDL callback function type value that represents a reference to the same object that V represents, with the incumbent realm as the callback context.
         if (parameter.type->is_nullable() || callback_function.is_legacy_treat_non_object_as_null) {
             callback_function_generator.append(R"~~~(
     GC::Ptr<WebIDL::CallbackType> @cpp_name@;
     if (@js_name@@js_suffix@.is_object())
-        @cpp_name@ = vm.heap().allocate<WebIDL::CallbackType>(@js_name@@js_suffix@.as_object(), HTML::incumbent_settings_object(), @operation_returns_promise@);
+        @cpp_name@ = vm.heap().allocate<WebIDL::CallbackType>(@js_name@@js_suffix@.as_object(), HTML::incumbent_realm(), @operation_returns_promise@);
 )~~~");
         } else {
             callback_function_generator.append(R"~~~(
-    auto @cpp_name@ = vm.heap().allocate<WebIDL::CallbackType>(@js_name@@js_suffix@.as_object(), HTML::incumbent_settings_object(), @operation_returns_promise@);
+    auto @cpp_name@ = vm.heap().allocate<WebIDL::CallbackType>(@js_name@@js_suffix@.as_object(), HTML::incumbent_realm(), @operation_returns_promise@);
 )~~~");
         }
     } else if (parameter.type->name() == "sequence") {
@@ -1282,7 +1282,7 @@ static void generate_to_cpp(SourceGenerator& generator, ParameterType& parameter
         if (includes_callable) {
             union_generator.append(R"~~~(
             if (@js_name@@js_suffix@_object.is_function())
-                return vm.heap().allocate<WebIDL::CallbackType>(@js_name@@js_suffix@.as_function(), HTML::incumbent_settings_object());
+                return vm.heap().allocate<WebIDL::CallbackType>(@js_name@@js_suffix@.as_function(), HTML::incumbent_realm());
 )~~~");
         }
 

+ 3 - 0
Tests/LibWeb/Text/expected/Streams/QueuingStrategy-same-instance.txt

@@ -1,2 +1,5 @@
 CountQueuingStrategy | size1 === size2 -> true
 ByteLengthQueuingStrategy | size1 === size2 -> true
+
+CountQueuingStrategy | size1 === size2 -> true
+ByteLengthQueuingStrategy | size1 === size2 -> true

+ 15 - 0
Tests/LibWeb/Text/input/Streams/QueuingStrategy-same-instance.html

@@ -1,6 +1,21 @@
 <script src="../include.js"></script>
 <script>
     test(() => {
+        const realm = new ShadowRealm();
+        const result = realm.evaluate(`
+            (() => {
+                let str = "";
+                for (const QueuingStrategy of [CountQueuingStrategy, ByteLengthQueuingStrategy]) {
+                    const size1 = (new QueuingStrategy({ highWaterMark: 5 })).size;
+                    const size2 = (new QueuingStrategy({ highWaterMark: 10 })).size;
+                    str += \`\${QueuingStrategy.name} | size1 === size2 -> \${size1 === size2}\n\`;
+                }
+                return str;
+            })()
+        `);
+
+        println(result);
+
         for (const QueuingStrategy of [CountQueuingStrategy, ByteLengthQueuingStrategy]) {
             const size1 = (new QueuingStrategy({ highWaterMark: 5 })).size;
             const size2 = (new QueuingStrategy({ highWaterMark: 10 })).size;