Commit 9ccf3418 authored by Scott Violet's avatar Scott Violet Committed by Chromium LUCI CQ

blink: makes display lock use overflow-clip-rect when appropriate

Specifically, if the box has non visible overflow and
an overflow-clip-margin. Without this the overflow from
contain:paint doesn't take into the account the overflow-clip-margin,
and we may end up not painting when we should.

BUG=1157843
TEST=third_party/blink/web_tests/external/wpt/css/css-overflow/overflow-clip-margin-007.html

Change-Id: Iba11a683f8cc1eb595ef26984c0dce38133fc760
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2626027Reviewed-by: default avatarStefan Zager <szager@chromium.org>
Reviewed-by: default avatarXianzhu Wang <wangxianzhu@chromium.org>
Reviewed-by: default avatarvmpstr <vmpstr@chromium.org>
Commit-Queue: Scott Violet <sky@chromium.org>
Cr-Commit-Position: refs/heads/master@{#844846}
parent 89e4a67b
......@@ -91,6 +91,9 @@ IntersectionObserver& DisplayLockDocumentState::EnsureIntersectionObserver() {
//
// Note that we use 150% margin (on the viewport) so that we get the
// observation before the element enters the viewport.
//
// Paint containment requires using the overflow clip edge. To do otherwise
// results in overflow-clip-margin not being painted in certain scenarios.
intersection_observer_ = IntersectionObserver::Create(
{Length::Percent(150.f)}, {std::numeric_limits<float>::min()},
document_,
......@@ -101,7 +104,8 @@ IntersectionObserver& DisplayLockDocumentState::EnsureIntersectionObserver() {
IntersectionObserver::kDeliverDuringPostLayoutSteps,
IntersectionObserver::kFractionOfTarget, 0 /* delay */,
false /* track_visibility */, false /* always report_root_bounds */,
IntersectionObserver::kApplyMarginToTarget);
IntersectionObserver::kApplyMarginToTarget,
true /* use_overflow_clip_edge */);
}
return *intersection_observer_;
}
......
......@@ -97,6 +97,17 @@ PhysicalRect InitializeRootRect(const LayoutObject* root,
return result;
}
PhysicalRect GetBoxBounds(const LayoutBox* box, bool use_overflow_clip_edge) {
PhysicalRect bounds = PhysicalRect(box->BorderBoundingBox());
// OverflowClipMargin() should only apply if clipping occurs on both axis.
if (use_overflow_clip_edge && box->ShouldClipOverflowAlongBothAxis() &&
box->StyleRef().OverflowClipMargin() != LayoutUnit()) {
// OverflowClipRect() may be smaller than BorderBoundingBox().
bounds.Unite(box->OverflowClipRect(PhysicalOffset()));
}
return bounds;
}
// 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.
......@@ -109,7 +120,10 @@ std::pair<PhysicalRect, bool> InitializeTargetRect(const LayoutObject* target,
target->IsLayoutEmbeddedContent()) {
result.first = To<LayoutEmbeddedContent>(target)->ReplacedContentRect();
} else if (target->IsBox()) {
result.first = PhysicalRect(To<LayoutBox>(target)->BorderBoundingBox());
result.first =
GetBoxBounds(To<LayoutBox>(target),
(flags & IntersectionGeometry::kUseOverflowClipEdge) ==
IntersectionGeometry::kUseOverflowClipEdge);
} else if (target->IsLayoutInline()) {
result.first = target->AbsoluteToLocalRect(
PhysicalRect::EnclosingRect(target->AbsoluteBoundingBoxFloatRect()));
......@@ -186,7 +200,8 @@ static const unsigned kConstructorFlagsMask =
IntersectionGeometry::kShouldTrackFractionOfRoot |
IntersectionGeometry::kShouldUseReplacedContentRect |
IntersectionGeometry::kShouldConvertToCSSPixels |
IntersectionGeometry::kShouldUseCachedRects;
IntersectionGeometry::kShouldUseCachedRects |
IntersectionGeometry::kUseOverflowClipEdge;
} // namespace
......
......@@ -36,10 +36,13 @@ class CORE_EXPORT IntersectionGeometry {
kShouldUseReplacedContentRect = 1 << 3,
kShouldConvertToCSSPixels = 1 << 4,
kShouldUseCachedRects = 1 << 5,
// Applies to boxes. If true, OverflowClipRect() is used if necessary
// instead of BorderBoundingBox().
kUseOverflowClipEdge = 1 << 6,
// These flags will be computed
kRootIsImplicit = 1 << 6,
kIsVisible = 1 << 7
kRootIsImplicit = 1 << 7,
kIsVisible = 1 << 8
};
struct RootGeometry {
......
......@@ -200,6 +200,8 @@ unsigned IntersectionObservation::GetIntersectionGeometryFlags(
geometry_flags |= IntersectionGeometry::kShouldTrackFractionOfRoot;
if (CanUseCachedRects())
geometry_flags |= IntersectionGeometry::kShouldUseCachedRects;
if (Observer()->UseOverflowClipEdge())
geometry_flags |= IntersectionGeometry::kUseOverflowClipEdge;
return geometry_flags;
}
......
......@@ -46,6 +46,8 @@ class CORE_EXPORT IntersectionObservation final
// If this bit is set, we only process intersection observations that
// require post-layout delivery.
kPostLayoutDeliveryOnly = 1 << 5,
// If this is set, the overflow clip edge is used.
kUseOverflowClipEdge = 1 << 6,
};
IntersectionObservation(IntersectionObserver&, Element&);
......@@ -87,7 +89,7 @@ class CORE_EXPORT IntersectionObservation final
unsigned last_is_visible_ : 1;
unsigned needs_update_ : 1;
unsigned last_threshold_index_ : 30;
static const unsigned kMaxThresholdIndex = (unsigned)0x40000000;
static const unsigned kMaxThresholdIndex = static_cast<unsigned>(0x40000000);
};
} // namespace blink
......
......@@ -208,7 +208,7 @@ IntersectionObserver* IntersectionObserver::Create(
return MakeGarbageCollected<IntersectionObserver>(
delegate, root, margin, thresholds, kFractionOfTarget, delay,
track_visibility, false, kApplyMarginToRoot);
track_visibility, false, kApplyMarginToRoot, false);
}
IntersectionObserver* IntersectionObserver::Create(
......@@ -238,6 +238,7 @@ IntersectionObserver* IntersectionObserver::Create(
bool track_visibility,
bool always_report_root_bounds,
MarginTarget margin_target,
bool use_overflow_clip_edge,
ExceptionState& exception_state) {
IntersectionObserverDelegateImpl* intersection_observer_delegate =
MakeGarbageCollected<IntersectionObserverDelegateImpl>(
......@@ -245,7 +246,8 @@ IntersectionObserver* IntersectionObserver::Create(
behavior);
return MakeGarbageCollected<IntersectionObserver>(
*intersection_observer_delegate, nullptr, margin, thresholds, semantics,
delay, track_visibility, always_report_root_bounds, margin_target);
delay, track_visibility, always_report_root_bounds, margin_target,
use_overflow_clip_edge);
}
IntersectionObserver::IntersectionObserver(
......@@ -257,7 +259,8 @@ IntersectionObserver::IntersectionObserver(
DOMHighResTimeStamp delay,
bool track_visibility,
bool always_report_root_bounds,
MarginTarget margin_target)
MarginTarget margin_target,
bool use_overflow_clip_edge)
: ExecutionContextClient(delegate.GetExecutionContext()),
delegate_(&delegate),
root_(root),
......@@ -270,7 +273,8 @@ IntersectionObserver::IntersectionObserver(
track_fraction_of_root_(semantics == kFractionOfRoot),
always_report_root_bounds_(always_report_root_bounds),
needs_delivery_(0),
can_use_cached_rects_(0) {
can_use_cached_rects_(0),
use_overflow_clip_edge_(use_overflow_clip_edge) {
switch (margin.size()) {
case 0:
break;
......@@ -358,7 +362,9 @@ void IntersectionObserver::observe(Element* target,
observation->ComputeIntersection(
IntersectionObservation::kImplicitRootObserversNeedUpdate |
IntersectionObservation::kExplicitRootObserversNeedUpdate |
IntersectionObservation::kIgnoreDelay);
IntersectionObservation::kIgnoreDelay |
(use_overflow_clip_edge_ ? IntersectionObservation::kUseOverflowClipEdge
: 0));
}
}
......@@ -456,6 +462,9 @@ bool IntersectionObserver::ComputeIntersections(unsigned flags) {
if (post_layout_delivery_only != is_post_layout_delivery_observer)
return false;
if (use_overflow_clip_edge_)
flags |= IntersectionObservation::kUseOverflowClipEdge;
IntersectionGeometry::RootGeometry root_geometry(
IntersectionGeometry::GetRootLayoutObjectForTarget(root(), nullptr,
false),
......
......@@ -98,6 +98,8 @@ class CORE_EXPORT IntersectionObserver final
// the given |callback|. |thresholds| should be in the range [0,1], and are
// interpreted according to the given |semantics|. |delay| specifies the
// minimum period between change notifications.
// `use_overflow_clip_edge` indicates whether the overflow clip edge
// should be used instead of the bounding box if appropriate.
static IntersectionObserver* Create(
const Vector<Length>& margin,
const Vector<float>& thresholds,
......@@ -110,6 +112,7 @@ class CORE_EXPORT IntersectionObserver final
bool track_visbility = false,
bool always_report_root_bounds = false,
MarginTarget margin_target = kApplyMarginToRoot,
bool use_overflow_clip_edge = false,
ExceptionState& = ASSERT_NO_EXCEPTION);
static void ResumeSuspendedObservers();
......@@ -122,7 +125,8 @@ class CORE_EXPORT IntersectionObserver final
DOMHighResTimeStamp delay,
bool track_visibility,
bool always_report_root_bounds,
MarginTarget margin_target);
MarginTarget margin_target,
bool use_overflow_clip_edge);
// API methods.
void observe(Element*, ExceptionState& = ASSERT_NO_EXCEPTION);
......@@ -175,6 +179,8 @@ class CORE_EXPORT IntersectionObserver final
bool CanUseCachedRects() const { return can_use_cached_rects_; }
void InvalidateCachedRects() { can_use_cached_rects_ = 0; }
bool UseOverflowClipEdge() const { return use_overflow_clip_edge_ == 1; }
// ScriptWrappable override:
bool HasPendingActivity() const override;
......@@ -203,6 +209,7 @@ class CORE_EXPORT IntersectionObserver final
unsigned always_report_root_bounds_ : 1;
unsigned needs_delivery_ : 1;
unsigned can_use_cached_rects_ : 1;
unsigned use_overflow_clip_edge_ : 1;
};
} // namespace blink
......
......@@ -189,7 +189,8 @@ TEST_F(IntersectionObserverTest, ReportsFractionOfTargetOrRoot) {
*target_observer_delegate, nullptr, Vector<Length>(),
Vector<float>{kExpectedFractionOfTarget / 2},
IntersectionObserver::kFractionOfTarget, 0, false, false,
IntersectionObserver::kApplyMarginToRoot);
IntersectionObserver::kApplyMarginToRoot,
/* use_overflow_clip_edge */ false);
DummyExceptionStateForTesting exception_state;
target_observer->observe(target, exception_state);
ASSERT_FALSE(exception_state.HadException());
......@@ -201,7 +202,8 @@ TEST_F(IntersectionObserverTest, ReportsFractionOfTargetOrRoot) {
*root_observer_delegate, nullptr, Vector<Length>(),
Vector<float>{kExpectedFractionOfRoot / 2},
IntersectionObserver::kFractionOfRoot, 0, false, false,
IntersectionObserver::kApplyMarginToRoot);
IntersectionObserver::kApplyMarginToRoot,
/* use_overflow_clip_edge */ false);
root_observer->observe(target, exception_state);
ASSERT_FALSE(exception_state.HadException());
......@@ -257,7 +259,8 @@ TEST_F(IntersectionObserverTest, TargetRectIsEmptyAfterMapping) {
*target_observer_delegate, nullptr, Vector<Length>(),
Vector<float>{std::numeric_limits<float>::min()},
IntersectionObserver::kFractionOfTarget, 0, false, false,
IntersectionObserver::kApplyMarginToRoot);
IntersectionObserver::kApplyMarginToRoot,
/* use_overflow_clip_edge */ false);
DummyExceptionStateForTesting exception_state;
target_observer->observe(target, exception_state);
ASSERT_FALSE(exception_state.HadException());
......@@ -979,7 +982,8 @@ TEST_F(IntersectionObserverTest, ApplyMarginToTarget) {
*root_margin_delegate, nullptr, Vector<Length>{Length::Fixed(10)},
Vector<float>{std::numeric_limits<float>::min()},
IntersectionObserver::kFractionOfTarget, 0, false, false,
IntersectionObserver::kApplyMarginToRoot);
IntersectionObserver::kApplyMarginToRoot,
/* use_overflow_clip_edge */ false);
DummyExceptionStateForTesting exception_state;
root_margin_observer->observe(target, exception_state);
......@@ -993,7 +997,8 @@ TEST_F(IntersectionObserverTest, ApplyMarginToTarget) {
*target_margin_delegate, nullptr, Vector<Length>{Length::Fixed(10)},
Vector<float>{std::numeric_limits<float>::min()},
IntersectionObserver::kFractionOfTarget, 0, false, false,
IntersectionObserver::kApplyMarginToTarget);
IntersectionObserver::kApplyMarginToTarget,
/* use_overflow_clip_edge */ false);
target_margin_observer->observe(target, exception_state);
ASSERT_FALSE(exception_state.HadException());
......@@ -1046,7 +1051,8 @@ TEST_F(IntersectionObserverTest, TargetMarginPercentResolvesAgainstRoot) {
*target_margin_delegate, nullptr, Vector<Length>{Length::Percent(10)},
Vector<float>{std::numeric_limits<float>::min()},
IntersectionObserver::kFractionOfTarget, 0, false, false,
IntersectionObserver::kApplyMarginToTarget);
IntersectionObserver::kApplyMarginToTarget,
/* use_overflow_clip_edge */ false);
DummyExceptionStateForTesting exception_state;
target_margin_observer->observe(target, exception_state);
......
<!doctype html>
<html class="reftest">
<meta charset="utf-8">
<title>Overflow-clip-margin: is shown with content-visibility: auto</title>
<link rel="help" href="https://www.w3.org/TR/css-overflow-3/#propdef-overflow-clip-margin">
<link rel="author" title="Scott Violet" href="mailto:sky@chromium.org">
<style>
body {
height: 15000px;
}
.big {
width: 10px;
height: 20000px;
position: relative;
top: -10000px;
background: green;
}
</style>
<div class=big></div>
<script>
onload = function() {
document.documentElement.scrollTop = window.innerHeight * 3;
}
</script>
<!doctype html>
<html class="reftest">
<meta charset="utf-8">
<title>Overflow-clip-margin: is shown with content-visibility: auto</title>
<link rel="help" href="https://www.w3.org/TR/css-overflow-3/#propdef-overflow-clip-margin">
<link rel="author" title="Scott Violet" href="mailto:sky@chromium.org">
<link rel="match" href="overflow-clip-margin-007-ref.html">
<style>
body {
height: 15000px;
}
.auto {
content-visibility: auto;
width: 100px;
height: 100px;
overflow-clip-margin: 10000px;
background: lightblue;
}
.big {
width: 10px;
height: 20000px;
position: relative;
top: -10000px;
background: green;
}
</style>
<div class=auto>
<div class=big></div>
</div>
<script>
onload = function() {
document.documentElement.scrollTop = window.innerHeight * 3;
}
</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