Commit bc1941b4 authored by Vladimir Levin's avatar Vladimir Levin Committed by Chromium LUCI CQ

Reland "IO: Use pre-margin target rect for an empty check for intersection threshold"

This includes a fix to the original patch and a new test

Original change's description:
> IO: Use pre-margin target rect for an empty check for intersection threshold
>
> This patch ensures that we use a pre-margin version of the target rect
> if we are checking whether it was empty. This is to make sure that we
> compute a "1" intersection ratio in cases where an ancestor of a
> margin-padded target causes it to be empty.
>
> In this case, we should report 1 intersection, because we are intersecting
> and target is an empty rect. However, because we padded the target
> with a margin, we ended up doing the wrong check and ultimately compute
> 0 intersection in a different conditional branch (before this patch).
>
> This patch restores correct behavior.
>

R=szager@chromium.org, chrishtr@chromium.org

Fixed: 1156815
Change-Id: Icdc333dc60e6892783f59260d59eca682a3fc7e5
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2583083
Auto-Submit: vmpstr <vmpstr@chromium.org>
Reviewed-by: default avatarStefan Zager <szager@chromium.org>
Commit-Queue: vmpstr <vmpstr@chromium.org>
Cr-Commit-Position: refs/heads/master@{#835658}
parent c90ad9e0
......@@ -97,24 +97,27 @@ PhysicalRect InitializeRootRect(const LayoutObject* root,
return result;
}
// Return the bounding box of target in target's own coordinate system
PhysicalRect InitializeTargetRect(const LayoutObject* target,
unsigned flags,
const Vector<Length>& margin,
const LayoutObject* root) {
PhysicalRect result;
// Return the bounding box of target in target's own coordinate system, also
// return a bool indicating whether the target rect before margin application
// was empty.
std::pair<PhysicalRect, bool> InitializeTargetRect(const LayoutObject* target,
unsigned flags,
const Vector<Length>& margin,
const LayoutObject* root) {
std::pair<PhysicalRect, bool> result;
if ((flags & IntersectionGeometry::kShouldUseReplacedContentRect) &&
target->IsLayoutEmbeddedContent()) {
result = To<LayoutEmbeddedContent>(target)->ReplacedContentRect();
result.first = To<LayoutEmbeddedContent>(target)->ReplacedContentRect();
} else if (target->IsBox()) {
result = PhysicalRect(To<LayoutBox>(target)->BorderBoundingBox());
result.first = PhysicalRect(To<LayoutBox>(target)->BorderBoundingBox());
} else if (target->IsLayoutInline()) {
result = target->AbsoluteToLocalRect(
result.first = target->AbsoluteToLocalRect(
PhysicalRect::EnclosingRect(target->AbsoluteBoundingBoxFloatRect()));
} else {
result = To<LayoutText>(target)->PhysicalLinesBoundingBox();
result.first = To<LayoutText>(target)->PhysicalLinesBoundingBox();
}
ApplyMargin(result, margin, root->StyleRef().EffectiveZoom(),
result.second = result.first.IsEmpty();
ApplyMargin(result.first, margin, root->StyleRef().EffectiveZoom(),
InitializeRootRect(root, {} /* margin */));
return result;
}
......@@ -293,22 +296,30 @@ void IntersectionGeometry::ComputeGeometry(const RootGeometry& root_geometry,
// root_rect_ is in root's coordinate system
// The coordinate system for unclipped_intersection_rect_ depends on whether
// or not we can use previously cached geometry...
bool pre_margin_target_rect_is_empty;
if (ShouldUseCachedRects()) {
target_rect_ = cached_rects->local_target_rect;
pre_margin_target_rect_is_empty =
cached_rects->pre_margin_target_rect_is_empty;
// The cached intersection rect has already been mapped/clipped up to the
// root, except that the root's scroll offset and overflow clip have not
// been applied.
unclipped_intersection_rect_ =
cached_rects->unscrolled_unclipped_intersection_rect;
} else {
target_rect_ = InitializeTargetRect(target, flags_, target_margin, root);
std::tie(target_rect_, pre_margin_target_rect_is_empty) =
InitializeTargetRect(target, flags_, target_margin, root);
// We have to map/clip target_rect_ up to the root, so we begin with the
// intersection rect in target's coordinate system. After ClipToRoot, it
// will be in root's coordinate system.
unclipped_intersection_rect_ = target_rect_;
}
if (cached_rects)
if (cached_rects) {
cached_rects->local_target_rect = target_rect_;
cached_rects->pre_margin_target_rect_is_empty =
pre_margin_target_rect_is_empty;
}
root_rect_ = root_geometry.local_root_rect;
bool does_intersect =
......@@ -398,7 +409,18 @@ void IntersectionGeometry::ComputeGeometry(const RootGeometry& root_geometry,
if (does_intersect) {
const PhysicalRect& comparison_rect =
ShouldTrackFractionOfRoot() ? root_rect_ : target_rect_;
if (comparison_rect.IsEmpty()) {
// Note that if we are checking whether target is empty, we have to consider
// the fact that we might have padded the rect with a target margin. If we
// did, `pre_margin_target_rect_is_empty` would be true. Use this
// information to force the rect to be empty for the purposes of this
// computation. Note that it could also be the case that the rect started as
// non-empty and was transformed to be empty. In this case, we rely on
// target_rect_.IsEmpty() to be true, so we need to check the rect itself as
// well.
// In the fraction of root case, we can just check the comparison rect.
bool empty_override =
!ShouldTrackFractionOfRoot() && pre_margin_target_rect_is_empty;
if (comparison_rect.IsEmpty() || empty_override) {
intersection_ratio_ = 1;
} else {
const PhysicalSize& intersection_size = intersection_rect_.size;
......
......@@ -63,6 +63,8 @@ class CORE_EXPORT IntersectionGeometry {
// True iff unscrolled_unclipped_intersection_rect actually intersects the
// root, as defined by edge-inclusive intersection rules.
bool does_intersect;
// True iff the target rect before any margins were applied was empty
bool pre_margin_target_rect_is_empty;
// Invalidation flag
bool valid;
};
......
......@@ -272,6 +272,55 @@ TEST_F(IntersectionObserverTest, ReportsFractionOfTargetOrRoot) {
root_observer_delegate->LastEntry()->intersectionRatio(), 1e-6);
}
TEST_F(IntersectionObserverTest, TargetRectIsEmptyAfterMapping) {
// Place a 100x100 target element in the middle of a 200x200 main frame.
WebView().MainFrameViewWidget()->Resize(gfx::Size(200, 200));
SimRequest main_resource("https://example.com/", "text/html");
LoadURL("https://example.com/");
main_resource.Complete(R"HTML(
<style>
.clipper {
transform: rotatey(90deg);
}
.container {
overflow: hidden;
}
#target {
width: 10px;
height: 10px;
}
</style>
<div class=clipper>
<div class=container>
<div id=target></div>
</div>
</div>
)HTML");
Element* target = GetDocument().getElementById("target");
ASSERT_TRUE(target);
TestIntersectionObserverDelegate* target_observer_delegate =
MakeGarbageCollected<TestIntersectionObserverDelegate>(GetDocument());
IntersectionObserver* target_observer =
MakeGarbageCollected<IntersectionObserver>(
*target_observer_delegate, nullptr, Vector<Length>(),
Vector<float>{std::numeric_limits<float>::min()},
IntersectionObserver::kFractionOfTarget, 0, false, false,
IntersectionObserver::kApplyMarginToRoot);
DummyExceptionStateForTesting exception_state;
target_observer->observe(target, exception_state);
ASSERT_FALSE(exception_state.HadException());
Compositor().BeginFrame();
test::RunPendingTasks();
ASSERT_FALSE(Compositor().NeedsBeginFrame());
EXPECT_EQ(target_observer_delegate->CallCount(), 1);
EXPECT_EQ(target_observer_delegate->EntryCount(), 1);
EXPECT_TRUE(target_observer_delegate->LastEntry()->isIntersecting());
}
TEST_F(IntersectionObserverTest, ResumePostsTask) {
WebView().MainFrameViewWidget()->Resize(gfx::Size(800, 600));
SimRequest main_resource("https://example.com/", "text/html");
......
<!doctype HTML>
<html>
<meta charset="utf8">
<title>CSS Content Visibility: auto in overflow hidden paints (reference)</title>
<link rel="author" title="Vladimir Levin" href="mailto:vmpstr@chromium.org">
<link rel="help" href="https://drafts.csswg.org/css-contain/#content-visibility">
<div>content</div>
<!doctype HTML>
<meta charset="utf8">
<title>CSS Content Visibility: auto in overflow hidden paints</title>
<link rel="author" title="Vladimir Levin" href="mailto:vmpstr@chromium.org">
<link rel="help" href="https://drafts.csswg.org/css-contain/#content-visibility">
<link rel="match" href="content-visibility-079-ref.html">
<meta name="assert" content="content-visibility auto element paints in an overflow hidden element that is not sized">
<style>
.auto { content-visibility: auto; }
.overflow { overflow: hidden; }
</style>
<div class=overflow>
<div class=auto>content</div>
</div>
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