blob: abe86ef53f34e211f342181feacbbe3e94a0f340 [file] [log] [blame]
// Copyright 2018 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "third_party/blink/renderer/core/layout/jank_tracker.h"
#include "third_party/blink/public/platform/web_mouse_event.h"
#include "third_party/blink/renderer/core/testing/core_unit_test_helper.h"
namespace blink {
class JankTrackerTest : public RenderingTest {
protected:
void SetUp() override {
RenderingTest::SetUp();
EnableCompositing();
}
LocalFrameView& GetFrameView() { return *GetFrame().View(); }
JankTracker& GetJankTracker() { return GetFrameView().GetJankTracker(); }
void SimulateInput() {
GetJankTracker().NotifyInput(WebMouseEvent(
WebInputEvent::kMouseDown, WebFloatPoint(), WebFloatPoint(),
WebPointerProperties::Button::kLeft, 0,
WebInputEvent::Modifiers::kLeftButtonDown, CurrentTimeTicks()));
}
void UpdateAllLifecyclePhases() {
GetFrameView().UpdateAllLifecyclePhases(
DocumentLifecycle::LifecycleUpdateReason::kTest);
}
};
TEST_F(JankTrackerTest, SimpleBlockMovement) {
SetBodyInnerHTML(R"HTML(
<style>
#j { position: relative; width: 300px; height: 100px; }
</style>
<div id='j'></div>
)HTML");
EXPECT_EQ(0.0, GetJankTracker().Score());
EXPECT_EQ(0.0, GetJankTracker().MaxDistance());
GetDocument().getElementById("j")->setAttribute(html_names::kStyleAttr,
AtomicString("top: 60px"));
UpdateAllLifecyclePhases();
// 300 * (100 + 60) / (default viewport size 800 * 600)
EXPECT_FLOAT_EQ(0.1, GetJankTracker().Score());
EXPECT_FLOAT_EQ(60.0, GetJankTracker().MaxDistance());
}
TEST_F(JankTrackerTest, GranularitySnapping) {
SetBodyInnerHTML(R"HTML(
<style>
#j { position: relative; width: 304px; height: 104px; }
</style>
<div id='j'></div>
)HTML");
GetDocument().getElementById("j")->setAttribute(html_names::kStyleAttr,
AtomicString("top: 58px"));
UpdateAllLifecyclePhases();
// Rect locations and sizes should snap to multiples of 600 / 60 = 10.
EXPECT_FLOAT_EQ(0.1, GetJankTracker().Score());
}
TEST_F(JankTrackerTest, Transform) {
SetBodyInnerHTML(R"HTML(
<style>
body { margin: 0; }
#c { transform: translateX(-300px) translateY(-40px); }
#j { position: relative; width: 600px; height: 140px; }
</style>
<div id='c'>
<div id='j'></div>
</div>
)HTML");
GetDocument().getElementById("j")->setAttribute(html_names::kStyleAttr,
AtomicString("top: 60px"));
UpdateAllLifecyclePhases();
// (600 - 300) * (140 - 40 + 60) / (default viewport size 800 * 600)
EXPECT_FLOAT_EQ(0.1, GetJankTracker().Score());
}
TEST_F(JankTrackerTest, RtlDistance) {
SetBodyInnerHTML(R"HTML(
<style>
#j { position: relative; width: 100px; height: 100px; direction: rtl; }
</style>
<div id='j'></div>
)HTML");
GetDocument().getElementById("j")->setAttribute(
html_names::kStyleAttr, AtomicString("width: 70px; left: 10px"));
UpdateAllLifecyclePhases();
EXPECT_FLOAT_EQ(20.0, GetJankTracker().MaxDistance());
}
TEST_F(JankTrackerTest, SmallMovementIgnored) {
SetBodyInnerHTML(R"HTML(
<style>
#j { position: relative; width: 300px; height: 100px; }
</style>
<div id='j'></div>
)HTML");
GetDocument().getElementById("j")->setAttribute(html_names::kStyleAttr,
AtomicString("top: 2px"));
UpdateAllLifecyclePhases();
EXPECT_EQ(0.0, GetJankTracker().Score());
}
TEST_F(JankTrackerTest, SmallMovementIgnoredWithZoom) {
GetDocument().GetFrame()->SetPageZoomFactor(2);
SetBodyInnerHTML(R"HTML(
<style>
#j { position: relative; width: 300px; height: 100px; }
</style>
<div id='j'></div>
)HTML");
GetDocument().getElementById("j")->setAttribute(html_names::kStyleAttr,
AtomicString("top: 2px"));
UpdateAllLifecyclePhases();
EXPECT_EQ(0.0, GetJankTracker().Score());
}
TEST_F(JankTrackerTest, IgnoreAfterInput) {
SetBodyInnerHTML(R"HTML(
<style>
#j { position: relative; width: 300px; height: 100px; }
</style>
<div id='j'></div>
)HTML");
GetDocument().getElementById("j")->setAttribute(html_names::kStyleAttr,
AtomicString("top: 60px"));
SimulateInput();
UpdateAllLifecyclePhases();
EXPECT_EQ(0.0, GetJankTracker().Score());
}
TEST_F(JankTrackerTest, CompositedElementMovement) {
SetBodyInnerHTML(R"HTML(
<style>
#jank {
position: relative;
width: 500px;
height: 200px;
background: yellow;
}
#container {
position: absolute;
width: 400px;
height: 400px;
left: 400px;
top: 100px;
background: #ccc;
}
.tr { will-change: transform; }
</style>
<div id='container' class='tr'>
<div id='space'></div>
<div id='jank' class='tr'></div>
</div>
)HTML");
GetDocument().getElementById("space")->setAttribute(
html_names::kStyleAttr, AtomicString("height: 100px"));
UpdateAllLifecyclePhases();
// #jank is 400x200 after viewport intersection with correct application of
// composited #container offset, and 100px lower after janking, so jank score
// is (400 * 300) / (viewport size 800 * 600)
EXPECT_FLOAT_EQ(0.25, GetJankTracker().Score());
}
TEST_F(JankTrackerTest, CompositedJankBeforeFirstPaint) {
// Tests that we don't crash if a new layer janks during a second compositing
// update before prepaint sets up property tree state. See crbug.com/881735
// (which invokes UpdateLifecycleToCompositingCleanPlusScrolling through
// accessibilityController.accessibleElementById).
SetBodyInnerHTML(R"HTML(
<style>
.hide { display: none; }
.tr { will-change: transform; }
body { margin: 0; }
div { height: 100px; }
</style>
<div id="container">
<div id="A">A</div>
<div id="B" class="tr hide">B</div>
</div>
)HTML");
GetDocument().getElementById("B")->setAttribute(html_names::kClassAttr,
AtomicString("tr"));
GetFrameView().UpdateLifecycleToCompositingCleanPlusScrolling();
GetDocument().getElementById("A")->setAttribute(html_names::kClassAttr,
AtomicString("hide"));
UpdateAllLifecyclePhases();
}
} // namespace blink