From 9968c9f7a6686a0e1909d3c6d9c2f5d51225e1b8 Mon Sep 17 00:00:00 2001 From: Aliaksandr Kalenik Date: Thu, 22 Feb 2024 04:41:12 +0100 Subject: [PATCH] LibWeb: Fix hit-testing by excluding CSS transform from clip rect check Transforms are applied to both clip rectangle and position, so we need to remove the transform from clip rectangle before checking if position falls within the clip rectangle. In this change, the removal of transform is moved into `Paintable::clip_rect()` that is shared between hit-testing and painting. This change fixes hit-testing in Discord's multifactor authentication form. --- ...flow-hidden-wrapped-into-css-translate.txt | 1 + ...low-hidden-wrapped-into-css-translate.html | 32 +++++++++++++++++++ .../LibWeb/Painting/PaintableBox.cpp | 20 +++++++----- 3 files changed, 45 insertions(+), 8 deletions(-) create mode 100644 Tests/LibWeb/Text/expected/hit_testing/overflow-hidden-wrapped-into-css-translate.txt create mode 100644 Tests/LibWeb/Text/input/hit_testing/overflow-hidden-wrapped-into-css-translate.html diff --git a/Tests/LibWeb/Text/expected/hit_testing/overflow-hidden-wrapped-into-css-translate.txt b/Tests/LibWeb/Text/expected/hit_testing/overflow-hidden-wrapped-into-css-translate.txt new file mode 100644 index 0000000000..2feeaf5bdb --- /dev/null +++ b/Tests/LibWeb/Text/expected/hit_testing/overflow-hidden-wrapped-into-css-translate.txt @@ -0,0 +1 @@ +
diff --git a/Tests/LibWeb/Text/input/hit_testing/overflow-hidden-wrapped-into-css-translate.html b/Tests/LibWeb/Text/input/hit_testing/overflow-hidden-wrapped-into-css-translate.html new file mode 100644 index 0000000000..f9ae5e9071 --- /dev/null +++ b/Tests/LibWeb/Text/input/hit_testing/overflow-hidden-wrapped-into-css-translate.html @@ -0,0 +1,32 @@ +
+ + diff --git a/Userland/Libraries/LibWeb/Painting/PaintableBox.cpp b/Userland/Libraries/LibWeb/Painting/PaintableBox.cpp index 140cd68d27..c21dfeed94 100644 --- a/Userland/Libraries/LibWeb/Painting/PaintableBox.cpp +++ b/Userland/Libraries/LibWeb/Painting/PaintableBox.cpp @@ -214,8 +214,17 @@ Optional PaintableBox::enclosing_scroll_frame_offset() const Optional PaintableBox::clip_rect() const { - if (m_enclosing_clip_frame) - return m_enclosing_clip_frame->rect(); + if (m_enclosing_clip_frame) { + auto rect = m_enclosing_clip_frame->rect(); + // NOTE: Since the painting command executor applies a CSS transform and the clip rect is calculated + // with this transform in account, we need to remove the transform from the clip rect. + // Otherwise, the transform will be applied twice to the clip rect. + // Similarly, for hit-testing, the transform must be removed from the clip rectangle since the position + // includes the transform. + auto combined_transform = compute_combined_css_transform(); + rect.translate_by(-combined_transform.translation().to_type()); + return rect; + } return {}; } @@ -435,17 +444,12 @@ void PaintableBox::apply_clip_overflow_rect(PaintContext& context, PaintPhase ph if (clip_rect().has_value()) { auto overflow_clip_rect = clip_rect().value(); - // NOTE: Since the painting command executor applies a CSS transform and the clip rect is calculated - // with this transform in account, we need to remove the transform from the clip rect. - // Otherwise, the transform will be applied twice to the clip rect. - auto combined_transform = compute_combined_css_transform(); - overflow_clip_rect.translate_by(-combined_transform.translation().to_type()); - m_clipping_overflow = true; context.recording_painter().save(); context.recording_painter().add_clip_rect(context.enclosing_device_rect(overflow_clip_rect).to_type()); auto const& border_radii_clips = this->border_radii_clips(); m_corner_clipper_ids.resize(border_radii_clips.size()); + auto combined_transform = compute_combined_css_transform(); for (size_t corner_clip_index = 0; corner_clip_index < border_radii_clips.size(); ++corner_clip_index) { auto const& corner_clip = border_radii_clips[corner_clip_index]; auto corners = corner_clip.radii.as_corners(context);