Commit afe3da07 authored by Stefan Zager's avatar Stefan Zager Committed by Commit Bot

[IntersectionObserver] Fix visual rect hit testing for blur filters

Blur filters do not contribute to visual overflow, so hit testing
doesn't account for them, even when the kHitTestVisualOverflow flag
is used. This change expands the rect used for occlusion hit testing
to include the radius of blur filters.

BUG=827639
R=chrishtr@chromium.org

Change-Id: I01eb61eacbaac2005a84fa181daa8e6f8765d18d
Reviewed-on: https://chromium-review.googlesource.com/c/1364152Reviewed-by: default avatarChris Harrelson <chrishtr@chromium.org>
Commit-Queue: Stefan Zager <szager@chromium.org>
Cr-Commit-Position: refs/heads/master@{#619584}
parent d611fb50
......@@ -1566,11 +1566,16 @@ bool LayoutBox::HitTestAllPhases(HitTestResult& result,
// If we have clipping, then we can't have any spillout.
// TODO(pdr): Why is this optimization not valid for the effective root?
if (!IsEffectiveRootScroller()) {
LayoutRect overflow_box =
(HasOverflowClip() || ShouldApplyPaintContainment())
? BorderBoxRect()
: VisualOverflowRect();
FlipForWritingMode(overflow_box);
LayoutRect overflow_box;
if (result.GetHitTestRequest().GetType() &
HitTestRequest::kHitTestVisualOverflow) {
overflow_box = VisualOverflowRectIncludingFilters();
} else {
overflow_box = (HasOverflowClip() || ShouldApplyPaintContainment())
? BorderBoxRect()
: VisualOverflowRect();
FlipForWritingMode(overflow_box);
}
LayoutPoint adjusted_location = accumulated_offset + Location();
overflow_box.MoveBy(adjusted_location);
if (!location_in_container.Intersects(overflow_box))
......@@ -1624,7 +1629,8 @@ bool LayoutBox::NodeAtPoint(HitTestResult& result,
LayoutRect bounds_rect;
if (result.GetHitTestRequest().GetType() &
HitTestRequest::kHitTestVisualOverflow) {
bounds_rect = VisualOverflowRect();
bounds_rect = VisualOverflowRectIncludingFilters();
FlipForWritingMode(bounds_rect);
} else {
bounds_rect = BorderBoxRect();
}
......@@ -5294,6 +5300,17 @@ LayoutUnit LayoutBox::LayoutClientAfterEdge() const {
return overflow_ ? overflow_->LayoutClientAfterEdge() : ClientLogicalBottom();
}
LayoutRect LayoutBox::VisualOverflowRectIncludingFilters() const {
LayoutRect bounds_rect = VisualOverflowRect();
FlipForWritingMode(bounds_rect);
if (!StyleRef().HasFilter())
return bounds_rect;
FloatRect float_rect = Layer()->MapRectForFilter(FloatRect(bounds_rect));
float_rect.UniteIfNonZero(Layer()->FilterReferenceBox());
bounds_rect = EnclosingLayoutRect(float_rect);
return bounds_rect;
}
bool LayoutBox::HasTopOverflow() const {
return !StyleRef().IsLeftToRightDirection() && !IsHorizontalWritingMode();
}
......
......@@ -524,6 +524,10 @@ class CORE_EXPORT LayoutBox : public LayoutBoxModelObject {
return overflow_ ? overflow_->ContentsVisualOverflowRect() : LayoutRect();
}
// Returns the visual overflow rect, expanded to the area affected by any
// filters that paint outside of the box, in physical coordinates.
LayoutRect VisualOverflowRectIncludingFilters() const;
// These methods don't mean the box *actually* has top/left overflow. They
// mean that *if* the box overflows, it will overflow to the top/left rather
// than the bottom/right. This happens when child content is laid out
......
<!DOCTYPE html>
<script src="../../resources/testharness.js"></script>
<script src="../../resources/testharnessreport.js"></script>
<script src="../resources/intersection-observer-test-utils.js"></script>
<style>
body, html {
margin: 0;
}
pre, #log {
position: absolute;
top: 0;
left: 200px;
}
#target {
width: 100px;
height: 100px;
background-color: green;
}
#occluder {
margin-top: 10px;
width: 100px;
height: 100px;
background-color: blue;
filter: blur(50px);
}
</style>
<div id="target"></div>
<div id="occluder"></div>
<script>
var entries = [];
var target;
var occluder;
if (window.internals) {
internals.DisableIntersectionObserverThrottleDelay();
}
runTestCycle(function() {
target = document.getElementById("target");
occluder = document.getElementById("occluder");
assert_true(!!target, "target exists");
assert_true(!!occluder, "occluder exists");
var observer = new IntersectionObserver(function(changes) {
entries = entries.concat(changes)
}, {trackVisibility: true, delay: 100});
observer.observe(target);
entries = entries.concat(observer.takeRecords());
assert_equals(entries.length, 0, "No initial notifications.");
runTestCycle(step0, "First rAF.");
}, "IntersectionObserverV2 in a single document using the implicit root, with an occluding element.");
function step0() {
// Occluding elements with opacity=0 should not affect target visibility.
occluder.style.opacity = "0";
runTestCycle(step2, "occluder.style.opacity = 0");
// First notification should report occlusion due to blur filter.
checkLastEntry(entries, 0, [0, 100, 0, 100, 0, 100, 0, 100, 0, 800, 0, 600, true, false]);
}
function step2() {
checkLastEntry(entries, 1, [0, 100, 0, 100, 0, 100, 0, 100, 0, 800, 0, 600, true, true]);
}
</script>
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