diff --git a/Libraries/LibWeb/Bindings/MainThreadVM.cpp b/Libraries/LibWeb/Bindings/MainThreadVM.cpp index 643fd6ad196..de25e42642f 100644 --- a/Libraries/LibWeb/Bindings/MainThreadVM.cpp +++ b/Libraries/LibWeb/Bindings/MainThreadVM.cpp @@ -161,7 +161,7 @@ ErrorOr initialize_main_thread_vm(HTML::EventLoop::Type type) auto& realm = script ? script->realm() : *vm.current_realm(); // 5. Let global be realm's global object. - auto* global_mixin = dynamic_cast(&realm.global_object()); + auto* global_mixin = dynamic_cast(&realm.global_object()); VERIFY(global_mixin); auto& global = global_mixin->this_impl(); diff --git a/Libraries/LibWeb/HTML/EventLoop/EventLoop.cpp b/Libraries/LibWeb/HTML/EventLoop/EventLoop.cpp index 32f550a567c..af711d4c831 100644 --- a/Libraries/LibWeb/HTML/EventLoop/EventLoop.cpp +++ b/Libraries/LibWeb/HTML/EventLoop/EventLoop.cpp @@ -497,7 +497,7 @@ void EventLoop::perform_a_microtask_checkpoint() // 4. For each environment settings object settingsObject whose responsible event loop is this event loop, notify about rejected promises given settingsObject's global object. for (auto& environment_settings_object : m_related_environment_settings_objects) { - auto* global = dynamic_cast(&environment_settings_object->global_object()); + auto* global = dynamic_cast(&environment_settings_object->global_object()); VERIFY(global); global->notify_about_rejected_promises({}); } diff --git a/Libraries/LibWeb/HTML/Scripting/Environments.cpp b/Libraries/LibWeb/HTML/Scripting/Environments.cpp index 19118339826..bedb963b21d 100644 --- a/Libraries/LibWeb/HTML/Scripting/Environments.cpp +++ b/Libraries/LibWeb/HTML/Scripting/Environments.cpp @@ -12,7 +12,6 @@ #include #include #include -#include #include #include #include diff --git a/Libraries/LibWeb/HTML/UniversalGlobalScope.cpp b/Libraries/LibWeb/HTML/UniversalGlobalScope.cpp index 49bbbbe86b3..5e1bc92172a 100644 --- a/Libraries/LibWeb/HTML/UniversalGlobalScope.cpp +++ b/Libraries/LibWeb/HTML/UniversalGlobalScope.cpp @@ -13,6 +13,7 @@ #include #include #include +#include #include #include #include @@ -32,6 +33,7 @@ void UniversalGlobalScopeMixin::visit_edges(GC::Cell::Visitor& visitor) { visitor.visit(m_count_queuing_strategy_size_function); visitor.visit(m_byte_length_queuing_strategy_size_function); + visitor.ignore(m_outstanding_rejected_promises_weak_set); } // https://html.spec.whatwg.org/multipage/webappapis.html#dom-btoa @@ -156,4 +158,89 @@ GC::Ref UniversalGlobalScopeMixin::byte_length_queuing_str return GC::Ref { *m_byte_length_queuing_strategy_size_function }; } +void UniversalGlobalScopeMixin::push_onto_outstanding_rejected_promises_weak_set(JS::Promise* promise) +{ + m_outstanding_rejected_promises_weak_set.append(promise); +} + +bool UniversalGlobalScopeMixin::remove_from_outstanding_rejected_promises_weak_set(JS::Promise* promise) +{ + return m_outstanding_rejected_promises_weak_set.remove_first_matching([&](JS::Promise* promise_in_set) { + return promise == promise_in_set; + }); +} + +void UniversalGlobalScopeMixin::push_onto_about_to_be_notified_rejected_promises_list(GC::Ref promise) +{ + m_about_to_be_notified_rejected_promises_list.append(GC::make_root(promise)); +} + +bool UniversalGlobalScopeMixin::remove_from_about_to_be_notified_rejected_promises_list(GC::Ref promise) +{ + return m_about_to_be_notified_rejected_promises_list.remove_first_matching([&](auto& promise_in_list) { + return promise == promise_in_list; + }); +} + +// https://html.spec.whatwg.org/multipage/webappapis.html#notify-about-rejected-promises +void UniversalGlobalScopeMixin::notify_about_rejected_promises(Badge) +{ + auto& realm = this_impl().realm(); + + // 1. Let list be a copy of settings object's about-to-be-notified rejected promises list. + auto list = m_about_to_be_notified_rejected_promises_list; + + // 2. If list is empty, return. + if (list.is_empty()) + return; + + // 3. Clear settings object's about-to-be-notified rejected promises list. + m_about_to_be_notified_rejected_promises_list.clear(); + + // 4. Let global be settings object's global object. + // We need this as an event target for the unhandledrejection event below + auto& global = verify_cast(this_impl()); + + // 5. Queue a global task on the DOM manipulation task source given global to run the following substep: + queue_global_task(Task::Source::DOMManipulation, global, GC::create_function(realm.heap(), [this, &global, list = move(list)] { + auto& realm = global.realm(); + + // 1. For each promise p in list: + for (auto const& promise : list) { + + // 1. If p's [[PromiseIsHandled]] internal slot is true, continue to the next iteration of the loop. + if (promise->is_handled()) + continue; + + // 2. Let notHandled be the result of firing an event named unhandledrejection at global, using PromiseRejectionEvent, with the cancelable attribute initialized to true, + // the promise attribute initialized to p, and the reason attribute initialized to the value of p's [[PromiseResult]] internal slot. + PromiseRejectionEventInit event_init { + { + .bubbles = false, + .cancelable = true, + .composed = false, + }, + // Sadly we can't use .promise and .reason here, as we can't use the designator on the initialization of DOM::EventInit above. + /* .promise = */ *promise, + /* .reason = */ promise->result(), + }; + + auto promise_rejection_event = PromiseRejectionEvent::create(realm, HTML::EventNames::unhandledrejection, event_init); + + bool not_handled = global.dispatch_event(*promise_rejection_event); + + // 3. If notHandled is false, then the promise rejection is handled. Otherwise, the promise rejection is not handled. + + // 4. If p's [[PromiseIsHandled]] internal slot is false, add p to settings object's outstanding rejected promises weak set. + if (!promise->is_handled()) + m_outstanding_rejected_promises_weak_set.append(*promise); + + // This algorithm results in promise rejections being marked as handled or not handled. These concepts parallel handled and not handled script errors. + // If a rejection is still not handled after this, then the rejection may be reported to a developer console. + if (not_handled) + HTML::report_exception_to_console(promise->result(), realm, ErrorInPromise::Yes); + } + })); +} + } diff --git a/Libraries/LibWeb/HTML/UniversalGlobalScope.h b/Libraries/LibWeb/HTML/UniversalGlobalScope.h index 0d40d3cbd4a..a6df15afb6d 100644 --- a/Libraries/LibWeb/HTML/UniversalGlobalScope.h +++ b/Libraries/LibWeb/HTML/UniversalGlobalScope.h @@ -32,6 +32,18 @@ public: GC::Ref count_queuing_strategy_size_function(); GC::Ref byte_length_queuing_strategy_size_function(); + void push_onto_outstanding_rejected_promises_weak_set(JS::Promise*); + + // Returns true if removed, false otherwise. + bool remove_from_outstanding_rejected_promises_weak_set(JS::Promise*); + + void push_onto_about_to_be_notified_rejected_promises_list(GC::Ref); + + // Returns true if removed, false otherwise. + bool remove_from_about_to_be_notified_rejected_promises_list(GC::Ref); + + void notify_about_rejected_promises(Badge); + protected: void visit_edges(GC::Cell::Visitor&); @@ -41,6 +53,13 @@ private: // https://streams.spec.whatwg.org/#byte-length-queuing-strategy-size-function GC::Ptr m_byte_length_queuing_strategy_size_function; + + // https://html.spec.whatwg.org/multipage/webappapis.html#about-to-be-notified-rejected-promises-list + Vector> m_about_to_be_notified_rejected_promises_list; + + // https://html.spec.whatwg.org/multipage/webappapis.html#outstanding-rejected-promises-weak-set + // The outstanding rejected promises weak set must not create strong references to any of its members, and implementations are free to limit its size, e.g. by removing old entries from it when new ones are added. + Vector> m_outstanding_rejected_promises_weak_set; }; } diff --git a/Libraries/LibWeb/HTML/WindowOrWorkerGlobalScope.cpp b/Libraries/LibWeb/HTML/WindowOrWorkerGlobalScope.cpp index 29d2abe1578..137cf034441 100644 --- a/Libraries/LibWeb/HTML/WindowOrWorkerGlobalScope.cpp +++ b/Libraries/LibWeb/HTML/WindowOrWorkerGlobalScope.cpp @@ -20,7 +20,6 @@ #include #include #include -#include #include #include #include @@ -74,7 +73,6 @@ void WindowOrWorkerGlobalScopeMixin::visit_edges(JS::Cell::Visitor& visitor) entry.value.visit_edges(visitor); visitor.visit(m_registered_event_sources); visitor.visit(m_crypto); - visitor.ignore(m_outstanding_rejected_promises_weak_set); } void WindowOrWorkerGlobalScopeMixin::finalize() @@ -791,89 +789,4 @@ GC::Ref WindowOrWorkerGlobalScopeMixin::crypto() return GC::Ref { *m_crypto }; } -void WindowOrWorkerGlobalScopeMixin::push_onto_outstanding_rejected_promises_weak_set(JS::Promise* promise) -{ - m_outstanding_rejected_promises_weak_set.append(promise); -} - -bool WindowOrWorkerGlobalScopeMixin::remove_from_outstanding_rejected_promises_weak_set(JS::Promise* promise) -{ - return m_outstanding_rejected_promises_weak_set.remove_first_matching([&](JS::Promise* promise_in_set) { - return promise == promise_in_set; - }); -} - -void WindowOrWorkerGlobalScopeMixin::push_onto_about_to_be_notified_rejected_promises_list(GC::Ref promise) -{ - m_about_to_be_notified_rejected_promises_list.append(GC::make_root(promise)); -} - -bool WindowOrWorkerGlobalScopeMixin::remove_from_about_to_be_notified_rejected_promises_list(GC::Ref promise) -{ - return m_about_to_be_notified_rejected_promises_list.remove_first_matching([&](auto& promise_in_list) { - return promise == promise_in_list; - }); -} - -// https://html.spec.whatwg.org/multipage/webappapis.html#notify-about-rejected-promises -void WindowOrWorkerGlobalScopeMixin::notify_about_rejected_promises(Badge) -{ - auto& realm = this_impl().realm(); - - // 1. Let list be a copy of settings object's about-to-be-notified rejected promises list. - auto list = m_about_to_be_notified_rejected_promises_list; - - // 2. If list is empty, return. - if (list.is_empty()) - return; - - // 3. Clear settings object's about-to-be-notified rejected promises list. - m_about_to_be_notified_rejected_promises_list.clear(); - - // 4. Let global be settings object's global object. - // We need this as an event target for the unhandledrejection event below - auto& global = verify_cast(this_impl()); - - // 5. Queue a global task on the DOM manipulation task source given global to run the following substep: - queue_global_task(Task::Source::DOMManipulation, global, GC::create_function(realm.heap(), [this, &global, list = move(list)] { - auto& realm = global.realm(); - - // 1. For each promise p in list: - for (auto const& promise : list) { - - // 1. If p's [[PromiseIsHandled]] internal slot is true, continue to the next iteration of the loop. - if (promise->is_handled()) - continue; - - // 2. Let notHandled be the result of firing an event named unhandledrejection at global, using PromiseRejectionEvent, with the cancelable attribute initialized to true, - // the promise attribute initialized to p, and the reason attribute initialized to the value of p's [[PromiseResult]] internal slot. - PromiseRejectionEventInit event_init { - { - .bubbles = false, - .cancelable = true, - .composed = false, - }, - // Sadly we can't use .promise and .reason here, as we can't use the designator on the initialization of DOM::EventInit above. - /* .promise = */ *promise, - /* .reason = */ promise->result(), - }; - - auto promise_rejection_event = PromiseRejectionEvent::create(realm, HTML::EventNames::unhandledrejection, event_init); - - bool not_handled = global.dispatch_event(*promise_rejection_event); - - // 3. If notHandled is false, then the promise rejection is handled. Otherwise, the promise rejection is not handled. - - // 4. If p's [[PromiseIsHandled]] internal slot is false, add p to settings object's outstanding rejected promises weak set. - if (!promise->is_handled()) - m_outstanding_rejected_promises_weak_set.append(*promise); - - // This algorithm results in promise rejections being marked as handled or not handled. These concepts parallel handled and not handled script errors. - // If a rejection is still not handled after this, then the rejection may be reported to a developer console. - if (not_handled) - HTML::report_exception_to_console(promise->result(), realm, ErrorInPromise::Yes); - } - })); -} - } diff --git a/Libraries/LibWeb/HTML/WindowOrWorkerGlobalScope.h b/Libraries/LibWeb/HTML/WindowOrWorkerGlobalScope.h index 4a651e96320..ed186ff2a45 100644 --- a/Libraries/LibWeb/HTML/WindowOrWorkerGlobalScope.h +++ b/Libraries/LibWeb/HTML/WindowOrWorkerGlobalScope.h @@ -76,18 +76,6 @@ public: [[nodiscard]] GC::Ref crypto(); - void push_onto_outstanding_rejected_promises_weak_set(JS::Promise*); - - // Returns true if removed, false otherwise. - bool remove_from_outstanding_rejected_promises_weak_set(JS::Promise*); - - void push_onto_about_to_be_notified_rejected_promises_list(GC::Ref); - - // Returns true if removed, false otherwise. - bool remove_from_about_to_be_notified_rejected_promises_list(GC::Ref); - - void notify_about_rejected_promises(Badge); - protected: void initialize(JS::Realm&); void visit_edges(JS::Cell::Visitor&); @@ -130,13 +118,6 @@ private: GC::Ptr m_crypto; bool m_error_reporting_mode { false }; - - // https://html.spec.whatwg.org/multipage/webappapis.html#about-to-be-notified-rejected-promises-list - Vector> m_about_to_be_notified_rejected_promises_list; - - // https://html.spec.whatwg.org/multipage/webappapis.html#outstanding-rejected-promises-weak-set - // The outstanding rejected promises weak set must not create strong references to any of its members, and implementations are free to limit its size, e.g. by removing old entries from it when new ones are added. - Vector> m_outstanding_rejected_promises_weak_set; }; } diff --git a/Tests/LibWeb/Text/expected/HTML/ShadowRealm-rejection.txt b/Tests/LibWeb/Text/expected/HTML/ShadowRealm-rejection.txt new file mode 100644 index 00000000000..8a0af950e73 --- /dev/null +++ b/Tests/LibWeb/Text/expected/HTML/ShadowRealm-rejection.txt @@ -0,0 +1 @@ +Error: An error! diff --git a/Tests/LibWeb/Text/input/HTML/ShadowRealm-rejection.html b/Tests/LibWeb/Text/input/HTML/ShadowRealm-rejection.html new file mode 100644 index 00000000000..9f16c3decf2 --- /dev/null +++ b/Tests/LibWeb/Text/input/HTML/ShadowRealm-rejection.html @@ -0,0 +1,20 @@ + +