Commit 22b784de authored by Bryan McQuade's avatar Bryan McQuade Committed by Commit Bot

Apply ancestor clipping when computing layout shift rect sizes.

Change-Id: I7714480b1369877a8164f57c22ab7bd43c9feb3a
Bug: 945963
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/1639742
Commit-Queue: Bryan McQuade <bmcquade@chromium.org>
Reviewed-by: default avatarXianzhu Wang <wangxianzhu@chromium.org>
Reviewed-by: default avatarSteve Kobes <skobes@chromium.org>
Cr-Commit-Position: refs/heads/master@{#666086}
parent 3234fdd1
...@@ -71,9 +71,8 @@ static bool SmallerThanRegionGranularity(const FloatRect& rect, ...@@ -71,9 +71,8 @@ static bool SmallerThanRegionGranularity(const FloatRect& rect,
rect.Height() * granularity_scale < 0.5; rect.Height() * granularity_scale < 0.5;
} }
static const TransformPaintPropertyNode& TransformNodeFor( static const PropertyTreeState PropertyTreeStateFor(LayoutObject& object) {
LayoutObject& object) { return object.FirstFragment().LocalBorderBoxProperties();
return object.FirstFragment().LocalBorderBoxProperties().Transform();
} }
static void RegionToTracedValue(const Region& region, static void RegionToTracedValue(const Region& region,
...@@ -150,34 +149,54 @@ void JankTracker::AccumulateJank(const LayoutObject& source, ...@@ -150,34 +149,54 @@ void JankTracker::AccumulateJank(const LayoutObject& source,
if (source.IsSVG()) if (source.IsSVG())
return; return;
const auto& local_xform = TransformNodeFor(painting_layer.GetLayoutObject()); const auto local_state =
const auto& root_xform = TransformNodeFor(*source.View()); PropertyTreeStateFor(painting_layer.GetLayoutObject());
const auto root_state = PropertyTreeStateFor(*source.View());
GeometryMapper::SourceToDestinationRect(local_xform, root_xform, old_rect); FloatClipRect clip_rect =
GeometryMapper::SourceToDestinationRect(local_xform, root_xform, new_rect); GeometryMapper::LocalToAncestorClipRect(local_state, root_state);
if (!old_rect.Intersects(viewport) && !new_rect.Intersects(viewport)) // If the clip region is empty, then the resulting layout shift isn't visible
// in the viewport so ignore it.
if (!clip_rect.IsInfinite() && clip_rect.Rect().IsEmpty())
return; return;
GeometryMapper::SourceToDestinationRect(local_state.Transform(),
root_state.Transform(), old_rect);
GeometryMapper::SourceToDestinationRect(local_state.Transform(),
root_state.Transform(), new_rect);
FloatRect clipped_old_rect(old_rect), clipped_new_rect(new_rect);
if (!clip_rect.IsInfinite()) {
clipped_old_rect.Intersect(clip_rect.Rect());
clipped_new_rect.Intersect(clip_rect.Rect());
}
IntRect visible_old_rect = RoundedIntRect(clipped_old_rect);
visible_old_rect.Intersect(viewport);
IntRect visible_new_rect = RoundedIntRect(clipped_new_rect);
visible_new_rect.Intersect(viewport);
if (visible_old_rect.IsEmpty() && visible_new_rect.IsEmpty())
return;
// Compute move distance based on unclipped rects, to accurately determine how
// much the element moved.
float move_distance = GetMoveDistance(old_rect, new_rect, source);
frame_max_distance_ = std::max(frame_max_distance_, move_distance);
#if DCHECK_IS_ON() #if DCHECK_IS_ON()
LocalFrame& frame = frame_view_->GetFrame(); LocalFrame& frame = frame_view_->GetFrame();
if (ShouldLog(frame)) { if (ShouldLog(frame)) {
DVLOG(2) << "in " << (frame.IsMainFrame() ? "" : "subframe ") DVLOG(2) << "in " << (frame.IsMainFrame() ? "" : "subframe ")
<< frame.GetDocument()->Url().GetString() << ", " << frame.GetDocument()->Url().GetString() << ", "
<< source.DebugName() << " moved from " << old_rect.ToString() << source.DebugName() << " moved from " << old_rect.ToString()
<< " to " << new_rect.ToString(); << " to " << new_rect.ToString() << " (visible from "
<< visible_old_rect.ToString() << " to "
<< visible_new_rect.ToString() << ")";
} }
#endif #endif
frame_max_distance_ = std::max(frame_max_distance_,
GetMoveDistance(old_rect, new_rect, source));
IntRect visible_old_rect = RoundedIntRect(old_rect);
visible_old_rect.Intersect(viewport);
IntRect visible_new_rect = RoundedIntRect(new_rect);
visible_new_rect.Intersect(viewport);
visible_old_rect.Scale(scale); visible_old_rect.Scale(scale);
visible_new_rect.Scale(scale); visible_new_rect.Scale(scale);
......
...@@ -276,6 +276,96 @@ TEST_F(JankTrackerTest, JankWhileScrolled) { ...@@ -276,6 +276,96 @@ TEST_F(JankTrackerTest, JankWhileScrolled) {
EXPECT_FLOAT_EQ(0.1, GetJankTracker().Score()); EXPECT_FLOAT_EQ(0.1, GetJankTracker().Score());
} }
TEST_F(JankTrackerTest, FullyClippedVisualRect) {
SetBodyInnerHTML(R"HTML(
<style>
body { margin: 0; }
#clip { width: 0px; height: 600px; overflow: hidden; }
#j { position: relative; width: 300px; height: 200px; }
</style>
<div id='clip'><div id='j'></div></div>
)HTML");
GetDocument().getElementById("j")->setAttribute(html_names::kStyleAttr,
AtomicString("top: 200px"));
UpdateAllLifecyclePhases();
EXPECT_FLOAT_EQ(0.0, GetJankTracker().Score());
}
TEST_F(JankTrackerTest, PartiallyClippedVisualRect) {
SetBodyInnerHTML(R"HTML(
<style>
body { margin: 0; }
#clip { width: 150px; height: 600px; overflow: hidden; }
#j { position: relative; width: 300px; height: 200px; }
</style>
<div id='clip'><div id='j'></div></div>
)HTML");
GetDocument().getElementById("j")->setAttribute(html_names::kStyleAttr,
AtomicString("top: 200px"));
UpdateAllLifecyclePhases();
// (clipped width 150px) * (height 200 + movement 200) / (800 * 600 viewport)
EXPECT_FLOAT_EQ(0.125, GetJankTracker().Score());
}
TEST_F(JankTrackerTest, MultiClipVisualRect) {
SetBodyInnerHTML(R"HTML(
<style>
body { margin: 0; }
#outer { width: 200px; height: 600px; overflow: hidden; }
#inner { width: 300px; height: 150px; overflow: hidden; }
#j { position: relative; width: 300px; height: 600px; }
</style>
<div id='outer'><div id='inner'><div id='j'></div></div></div>
)HTML");
GetDocument().getElementById("j")->setAttribute(html_names::kStyleAttr,
AtomicString("top: -200px"));
UpdateAllLifecyclePhases();
// Note that, while the element moves up 200px, its visibility is
// clipped at 0px,150px height, so the additional 200px of vertical
// move distance is not included in the score.
// (clip width 200) * (clip height 150) / (800 * 600 viewport)
EXPECT_FLOAT_EQ(0.0625, GetJankTracker().Score());
EXPECT_FLOAT_EQ(200.0, GetJankTracker().OverallMaxDistance());
}
TEST_F(JankTrackerTest, ShiftOutsideViewport) {
SetBodyInnerHTML(R"HTML(
<style>
body { margin: 0; }
#j { position: relative; width: 600px; height: 200px; top: 600px; }
</style>
<div id='j'></div>
)HTML");
GetDocument().getElementById("j")->setAttribute(html_names::kStyleAttr,
AtomicString("top: 800px"));
UpdateAllLifecyclePhases();
// Since the element moves entirely outside of the viewport, it shouldn't
// generate a score.
EXPECT_FLOAT_EQ(0.0, GetJankTracker().Score());
}
TEST_F(JankTrackerTest, ShiftInToViewport) {
SetBodyInnerHTML(R"HTML(
<style>
body { margin: 0; }
#j { position: relative; width: 600px; height: 200px; top: 600px; }
</style>
<div id='j'></div>
)HTML");
GetDocument().getElementById("j")->setAttribute(html_names::kStyleAttr,
AtomicString("top: 400px"));
UpdateAllLifecyclePhases();
// The element moves from outside the viewport to within the viewport, which
// should generate jank.
// (width 600) * (height 0 + move 200) / (800 * 600 viewport)
EXPECT_FLOAT_EQ(0.25, GetJankTracker().Score());
}
class JankTrackerSimTest : public SimTest {}; class JankTrackerSimTest : public SimTest {};
TEST_F(JankTrackerSimTest, SubframeWeighting) { TEST_F(JankTrackerSimTest, SubframeWeighting) {
......
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