diff --git a/Userland/Libraries/LibWeb/DOM/EventDispatcher.cpp b/Userland/Libraries/LibWeb/DOM/EventDispatcher.cpp index 72b05dc8e4e..f93223062f6 100644 --- a/Userland/Libraries/LibWeb/DOM/EventDispatcher.cpp +++ b/Userland/Libraries/LibWeb/DOM/EventDispatcher.cpp @@ -19,6 +19,7 @@ #include #include #include +#include #include #include #include @@ -28,33 +29,6 @@ namespace Web::DOM { -// FIXME: This shouldn't be here, as retargeting is not only used by the event dispatcher. -// When moving this function, it needs to be generalized. https://dom.spec.whatwg.org/#retarget -static EventTarget* retarget(EventTarget* left, EventTarget* right) -{ - // To retarget an object A against an object B, repeat these steps until they return an object: - for (;;) { - // 1. If one of the following is true then return A. - // - A is not a node - if (!is(left)) - return left; - - // - A’s root is not a shadow root - auto* left_node = verify_cast(left); - auto& left_root = left_node->root(); - if (!is(left_root)) - return left; - - // - B is a node and A’s root is a shadow-including inclusive ancestor of B - if (is(right) && left_root.is_shadow_including_inclusive_ancestor_of(verify_cast(*right))) - return left; - - // 2. Set A to A’s root’s host. - auto& left_shadow_root = verify_cast(left_root); - left = left_shadow_root.host(); - } -} - // https://dom.spec.whatwg.org/#concept-event-listener-inner-invoke bool EventDispatcher::inner_invoke(Event& event, Vector>& listeners, Event::Phase phase, bool invocation_target_in_shadow_tree) { @@ -222,7 +196,7 @@ bool EventDispatcher::dispatch(JS::NonnullGCPtr target, Event& even JS::GCPtr activation_target; // 4. Let relatedTarget be the result of retargeting event’s relatedTarget against target. - JS::GCPtr related_target = retarget(event.related_target(), target); + JS::GCPtr related_target = retarget(event.related_target(), target); bool clear_targets = false; // 5. If target is not relatedTarget or target is event’s relatedTarget, then: @@ -232,7 +206,7 @@ bool EventDispatcher::dispatch(JS::NonnullGCPtr target, Event& even // 2. For each touchTarget of event’s touch target list, append the result of retargeting touchTarget against target to touchTargets. for (auto& touch_target : event.touch_target_list()) { - touch_targets.append(retarget(touch_target, target)); + touch_targets.append(retarget(touch_target, target)); } // 3. Append to an event path with event, target, targetOverride, relatedTarget, touchTargets, and false. @@ -279,14 +253,14 @@ bool EventDispatcher::dispatch(JS::NonnullGCPtr target, Event& even slottable = parent; // 3. Let relatedTarget be the result of retargeting event’s relatedTarget against parent. - related_target = retarget(event.related_target(), parent); + related_target = retarget(event.related_target(), parent); // 4. Let touchTargets be a new list. touch_targets.clear(); // 5. For each touchTarget of event’s touch target list, append the result of retargeting touchTarget against parent to touchTargets. for (auto& touch_target : event.touch_target_list()) { - touch_targets.append(retarget(touch_target, parent)); + touch_targets.append(retarget(touch_target, parent)); } // 6. If parent is a Window object, or parent is a node and target’s root is a shadow-including inclusive ancestor of parent, then: diff --git a/Userland/Libraries/LibWeb/DOM/Utils.h b/Userland/Libraries/LibWeb/DOM/Utils.h new file mode 100644 index 00000000000..3324187e723 --- /dev/null +++ b/Userland/Libraries/LibWeb/DOM/Utils.h @@ -0,0 +1,42 @@ +/* + * Copyright (c) 2020, Luke Wilde + * Copyright (c) 2024, circl + * + * SPDX-License-Identifier: BSD-2-Clause + */ + +#pragma once + +#include +#include + +namespace Web::DOM { + +// https://dom.spec.whatwg.org/#retarget +template +T* retarget(T* a, T* b) +{ + // To retarget an object A against an object B, repeat these steps until they return an object: + for (;;) { + // 1. If one of the following is true then return A. + // - A is not a node + if (!is(a)) + return a; + + // - A’s root is not a shadow root + auto* a_node = verify_cast(a); + auto& a_root = a_node->root(); + if (!is(a_root)) + return a; + + // - B is a node and A’s root is a shadow-including inclusive ancestor of B + if (is(b) && a_root.is_shadow_including_inclusive_ancestor_of(verify_cast(*b))) + return a; + + // 2. Set A to A’s root’s host. + auto& a_shadow_root = verify_cast(a_root); + a = a_shadow_root.host(); + } +} + +}