Commit c0d690f1 authored by Xiaocheng Hu's avatar Xiaocheng Hu Committed by Commit Bot

[LayoutNG] Implement hit test on culled inline elements

This patch implements hit testing on culled inline elements by:

- After hit testing each inline fragment, fall back to the legacy
  LayoutObject tree to see if there is any culled inline ancestor
  element, and hit test each of them. This part replicates relevant
  logic in InlineFlowBox::NodeAtPoint.

- Adds a |parent_fragment| parameter to
  LayoutInline::HitTestCulledInline to ensure that only descendant
  rects of |parent_fragment| are hit tested. This ensure that, e.g.,
  for a multi-line SPAN, we hit test its rects line-by-line, to ensure
  correct hit test ordering.

Tests: 6 layout tests are fixed. One of them is rebaselined due to
whitespace-only difference in text dump.

Bug: 851075
Cq-Include-Trybots: luci.chromium.try:linux_layout_tests_layout_ng;luci.chromium.try:linux_layout_tests_slimming_paint_v2;master.tryserver.blink:linux_trusty_blink_rel
Change-Id: Ic303ce5f8a192f92ad326c304a9d84fa898e4c94
Reviewed-on: https://chromium-review.googlesource.com/1110737Reviewed-by: default avatarYoshifumi Inoue <yosin@chromium.org>
Reviewed-by: default avatarKoji Ishii <kojii@chromium.org>
Reviewed-by: default avatarEmil A Eklund <eae@chromium.org>
Commit-Queue: Xiaocheng Hu <xiaochengh@chromium.org>
Cr-Commit-Position: refs/heads/master@{#570335}
parent db4a30a3
......@@ -328,7 +328,6 @@ crbug.com/591099 external/wpt/requestidlecallback/callback-invoked.html [ Pass ]
crbug.com/591099 external/wpt/requestidlecallback/cancel-invoked.html [ Pass ]
crbug.com/591099 external/wpt/requestidlecallback/idlharness.html [ Pass ]
crbug.com/591099 external/wpt/resource-timing/resource_timing.worker.html [ Pass ]
crbug.com/591099 external/wpt/shadow-dom/DocumentOrShadowRoot-prototype-elementFromPoint.html [ Failure ]
crbug.com/591099 external/wpt/webaudio/the-audio-api/the-mediaelementaudiosourcenode-interface/mediaElementAudioSourceToScriptProcessorTest.html [ Pass ]
crbug.com/591099 external/wpt/webrtc/interfaces.html [ Pass Timeout ]
crbug.com/591099 external/wpt/websockets/opening-handshake/003-sets-origin.worker.html [ Pass ]
......@@ -438,12 +437,7 @@ crbug.com/591099 fast/dom/HTMLAreaElement/area-download.html [ Failure ]
crbug.com/714962 fast/dom/Range/getBoundingClientRect-linebreak-character.html [ Failure ]
crbug.com/591099 fast/dom/Window/window-lookup-precedence.html [ Failure ]
crbug.com/714962 fast/dom/elementFromPoint-relative-to-viewport.html [ Failure ]
crbug.com/714962 fast/dom/elementsFromPoint/elementsFromPoint-inline.html [ Failure ]
crbug.com/591099 fast/dom/nodesFromRect/nodesFromRect-basic.html [ Failure ]
crbug.com/714962 fast/dom/nodesFromRect/nodesFromRect-culled-inline-with-linebreak.html [ Failure ]
crbug.com/714962 fast/dom/nodesFromRect/nodesFromRect-culled-inlines.html [ Failure ]
crbug.com/591099 fast/dom/nodesFromRect/nodesFromRect-inner-documents.html [ Failure ]
crbug.com/714962 fast/dom/nodesFromRect/nodesFromRect-links-and-text.html [ Failure ]
crbug.com/591099 fast/dynamic/first-letter-after-list-marker.html [ Failure ]
crbug.com/591099 fast/dynamic/text-combine.html [ Failure ]
crbug.com/591099 fast/encoding/utf-16-big-endian.html [ Failure ]
......
A
PASS successfullyParsed is true
TEST COMPLETE
PASS All correct nodes found for rect
PASS All correct nodes found for rect
PASS All correct nodes found for rect
PASS All correct nodes found for rect
PASS All correct nodes found for rect
PASS All correct nodes found for rect
......@@ -957,8 +957,9 @@ class HitTestCulledInlinesGeneratorContext {
bool LayoutInline::HitTestCulledInline(
HitTestResult& result,
const HitTestLocation& location_in_container,
const LayoutPoint& accumulated_offset) {
DCHECK(!AlwaysCreateLineBoxes());
const LayoutPoint& accumulated_offset,
const NGPaintFragment* container_fragment) {
DCHECK(container_fragment || !AlwaysCreateLineBoxes());
if (!VisibleToHitTestRequest(result.GetHitTestRequest()))
return false;
......@@ -968,7 +969,26 @@ bool LayoutInline::HitTestCulledInline(
Region region_result;
HitTestCulledInlinesGeneratorContext context(region_result,
adjusted_location);
GenerateCulledLineBoxRects(context, this);
if (container_fragment) {
DCHECK(EnclosingNGBlockFlow());
DCHECK(container_fragment->IsDescendantOfNotSelf(
*EnclosingNGBlockFlow()->PaintFragment()));
const NGPhysicalContainerFragment& traversal_root =
ToNGPhysicalContainerFragment(container_fragment->PhysicalFragment());
DCHECK(traversal_root.IsInline() || traversal_root.IsLineBox());
const LayoutPoint root_offset =
container_fragment->InlineOffsetToContainerBox().ToLayoutPoint();
const auto& descendants =
NGInlineFragmentTraversal::SelfFragmentsOf(traversal_root, this);
for (const auto& descendant : descendants) {
LayoutRect rect = descendant.RectInContainerBox().ToLayoutRect();
rect.MoveBy(root_offset);
context(rect);
}
} else {
DCHECK(!EnclosingNGBlockFlow());
GenerateCulledLineBoxRects(context, this);
}
if (context.Intersected()) {
UpdateHitTestResult(result, adjusted_location.Point());
......
......@@ -34,6 +34,7 @@
namespace blink {
class LayoutBlockFlow;
class NGPaintFragment;
// LayoutInline is the LayoutObject associated with display: inline.
// This is called an "inline box" in CSS 2.1.
......@@ -205,9 +206,15 @@ class CORE_EXPORT LayoutInline : public LayoutBoxModelObject {
int,
LayoutUnit* extra_width_to_end_of_line) const final;
// When this LayoutInline doesn't generate line boxes of its own, regenerate
// the rects of the line boxes and hit test the rects.
// In LayoutNG, |parent_fragment| is non-null, and limits the regenerated
// rects to be from descendant fragments of |parent_fragment|.
// In legacy, |parent_fragment| is always null, and all rects are regenerated.
bool HitTestCulledInline(HitTestResult&,
const HitTestLocation& location_in_container,
const LayoutPoint& accumulated_offset);
const LayoutPoint& accumulated_offset,
const NGPaintFragment* parent_fragment = nullptr);
LayoutPoint FirstLineBoxTopLeft() const;
......
......@@ -7,6 +7,7 @@
#include "third_party/blink/renderer/core/layout/background_bleed_avoidance.h"
#include "third_party/blink/renderer/core/layout/hit_test_location.h"
#include "third_party/blink/renderer/core/layout/hit_test_result.h"
#include "third_party/blink/renderer/core/layout/layout_inline.h"
#include "third_party/blink/renderer/core/layout/layout_list_marker.h"
#include "third_party/blink/renderer/core/layout/layout_table.h"
#include "third_party/blink/renderer/core/layout/layout_table_cell.h"
......@@ -80,6 +81,54 @@ bool FragmentVisibleToHitTestRequest(const NGPaintFragment& fragment,
!(fragment.GetNode() && fragment.GetNode()->IsInert());
}
bool HitTestCulledInlineAncestors(HitTestResult& result,
const NGPaintFragment& fragment,
const NGPaintFragment* previous_sibling,
const HitTestLocation& location_in_container,
const LayoutPoint& accumulated_offset) {
DCHECK(fragment.Parent());
DCHECK(fragment.PhysicalFragment().IsInline());
const NGPaintFragment& parent = *fragment.Parent();
const LayoutPoint fallback_accumulated_offset =
FallbackAccumulatedOffset(fragment, accumulated_offset);
const LayoutObject* limit_layout_object =
parent.PhysicalFragment().IsLineBox() ? parent.Parent()->GetLayoutObject()
: parent.GetLayoutObject();
LayoutObject* current_layout_object = fragment.GetLayoutObject();
for (LayoutObject* culled_parent = current_layout_object->Parent();
culled_parent && culled_parent != limit_layout_object;
culled_parent = culled_parent->Parent()) {
// |culled_parent| is a culled inline element to be hit tested, since it's
// "between" |fragment| and |fragment->Parent()| but doesn't have its own
// box fragment.
// To ensure the correct hit test ordering, |culled_parent| must be hit
// tested only once after all of its descendants are hit tested:
// - Shortcut: when |current_layout_object| is the only child (of
// |culled_parent|), since it's just hit tested, we can safely hit test its
// parent;
// - General case: we hit test |culled_parent| only when it is not an
// ancestor of |previous_sibling|; otherwise, |previous_sibling| has to be
// hit tested first.
// TODO(crbug.com/849331): It's wrong for bidi inline fragmentation. Fix it.
const bool has_sibling = current_layout_object->PreviousSibling() ||
current_layout_object->NextSibling();
if (has_sibling && previous_sibling &&
previous_sibling->GetLayoutObject()->IsDescendantOf(culled_parent))
break;
if (culled_parent->IsLayoutInline() &&
ToLayoutInline(culled_parent)
->HitTestCulledInline(result, location_in_container,
fallback_accumulated_offset, &parent))
return true;
current_layout_object = culled_parent;
}
return false;
}
} // anonymous namespace
NGBoxFragmentPainter::NGBoxFragmentPainter(const NGPaintFragment& box)
......@@ -1058,6 +1107,16 @@ bool NGBoxFragmentPainter::HitTestChildren(
}
if (stop_hit_testing)
return true;
if (!fragment.IsInline())
continue;
// Hit test culled inline boxes between |fragment| and its parent fragment.
const NGPaintFragment* previous_sibling =
std::next(iter) == children.rend() ? nullptr : std::next(iter)->get();
if (HitTestCulledInlineAncestors(result, *child, previous_sibling,
location_in_container, accumulated_offset))
return true;
}
return false;
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment