mirror of
https://github.com/LadybirdBrowser/ladybird.git
synced 2026-04-18 18:00:31 +00:00
LibWeb: Skip event dispatching work if there are no relevant listeners
We were going through path building, retargeting, capturing/bubbling phases etc. for each event that we were dispatching. We now optimize for the case where there are no listeners for the event type on the target node nor its ancestors by skipping all that work. This decimates methods that depend on event dispatching like `Document::set_hovered_node()` when there are no relevant listeners, which reduced its runtime by 99% (42.5ms -> 0.5ms) on my machine for sites like https://wordsalad.online/.
This commit is contained in:
parent
948bb4f45a
commit
57c9bb5caa
Notes:
github-actions[bot]
2026-03-10 15:59:09 +00:00
Author: https://github.com/gmta
Commit: 57c9bb5caa
Pull-request: https://github.com/LadybirdBrowser/ladybird/pull/8346
Reviewed-by: https://github.com/kalenikaliaksandr ✅
3 changed files with 27 additions and 3 deletions
|
|
@ -214,8 +214,18 @@ bool EventDispatcher::dispatch(GC::Ref<EventTarget> target, Event& event, bool l
|
|||
// 5. Let clearTargets be false.
|
||||
bool clear_targets = false;
|
||||
|
||||
// OPTIMIZATION: Only dispatch events if there is at least one listener on the node or its ancestors. Activation
|
||||
// events are always dispatched. This saves us from going through the path building and dispatch
|
||||
// phases for events that will be dropped on the floor anyway.
|
||||
bool is_activation_event = is<UIEvents::MouseEvent>(event) && event.type() == HTML::EventNames::click;
|
||||
bool should_dispatch = is_activation_event;
|
||||
if (!should_dispatch) {
|
||||
auto const* node = as_if<Node>(*target);
|
||||
should_dispatch = !node || node->has_inclusive_ancestor_with_event_listener(event.type());
|
||||
}
|
||||
|
||||
// 6. If target is not relatedTarget or target is event’s relatedTarget, then:
|
||||
if (related_target != target || event.related_target() == target) {
|
||||
if (should_dispatch && (related_target != target || event.related_target() == target)) {
|
||||
// 1. Let touchTargets be a new list.
|
||||
Event::TouchTargetList touch_targets;
|
||||
|
||||
|
|
@ -227,8 +237,9 @@ bool EventDispatcher::dispatch(GC::Ref<EventTarget> target, Event& event, bool l
|
|||
// 3. Append to an event path with event, target, targetOverride, relatedTarget, touchTargets, and false.
|
||||
event.append_to_path(*target, target_override, related_target, touch_targets, false);
|
||||
|
||||
// 4. Let isActivationEvent be true, if event is a MouseEvent object and event’s type attribute is "click"; otherwise false.
|
||||
bool is_activation_event = is<UIEvents::MouseEvent>(event) && event.type() == HTML::EventNames::click;
|
||||
// 4. Let isActivationEvent be true, if event is a MouseEvent object and event’s type attribute is "click";
|
||||
// otherwise false.
|
||||
// NB: Step 4 is executed above as part of an optimization.
|
||||
|
||||
// 5. If isActivationEvent is true and target has activation behavior, then set activationTarget to target.
|
||||
if (is_activation_event && target->has_activation_behavior())
|
||||
|
|
|
|||
|
|
@ -60,6 +60,7 @@
|
|||
#include <LibWeb/HTML/Parser/HTMLParser.h>
|
||||
#include <LibWeb/HTML/Scripting/SimilarOriginWindowAgent.h>
|
||||
#include <LibWeb/HTML/Scripting/TemporaryExecutionContext.h>
|
||||
#include <LibWeb/HTML/Window.h>
|
||||
#include <LibWeb/HTML/XMLSerializer.h>
|
||||
#include <LibWeb/Infra/CharacterTypes.h>
|
||||
#include <LibWeb/Layout/Node.h>
|
||||
|
|
@ -3406,6 +3407,17 @@ bool Node::has_inclusive_ancestor_with_display_none()
|
|||
return false;
|
||||
}
|
||||
|
||||
bool Node::has_inclusive_ancestor_with_event_listener(FlyString const& type) const
|
||||
{
|
||||
for (auto const* ancestor = this; ancestor; ancestor = ancestor->parent()) {
|
||||
if (ancestor->has_event_listener(type))
|
||||
return true;
|
||||
}
|
||||
if (auto window = document().window())
|
||||
return window->has_event_listener(type);
|
||||
return false;
|
||||
}
|
||||
|
||||
GC::Ptr<ShadowRoot> Node::containing_shadow_root()
|
||||
{
|
||||
if (auto* shadow_root = as_if<ShadowRoot>(*this))
|
||||
|
|
|
|||
|
|
@ -454,6 +454,7 @@ public:
|
|||
bool is_inert() const;
|
||||
|
||||
bool has_inclusive_ancestor_with_display_none();
|
||||
bool has_inclusive_ancestor_with_event_listener(FlyString const& type) const;
|
||||
|
||||
GC::Ptr<ShadowRoot> containing_shadow_root();
|
||||
GC::Ptr<ShadowRoot const> containing_shadow_root() const
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue