blob: a35ddf61a5a8b37d1559ed2f129049a7d8b76ed6 [file] [log] [blame]
// Copyright 2015 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/frame/local_frame_view.h"
#include <memory>
#include "testing/gmock/include/gmock/gmock.h"
#include "third_party/blink/renderer/core/html/html_element.h"
#include "third_party/blink/renderer/core/layout/layout_view.h"
#include "third_party/blink/renderer/core/paint/paint_layer.h"
#include "third_party/blink/renderer/core/paint/paint_property_tree_printer.h"
#include "third_party/blink/renderer/core/testing/core_unit_test_helper.h"
#include "third_party/blink/renderer/platform/geometry/int_size.h"
#include "third_party/blink/renderer/platform/graphics/paint/paint_artifact.h"
#include "third_party/blink/renderer/platform/runtime_enabled_features.h"
using testing::_;
using testing::AnyNumber;
namespace blink {
namespace {
class AnimationMockChromeClient : public EmptyChromeClient {
public:
AnimationMockChromeClient() : has_scheduled_animation_(false) {}
// ChromeClient
MOCK_METHOD2(AttachRootGraphicsLayer,
void(GraphicsLayer*, LocalFrame* localRoot));
MOCK_METHOD3(MockSetToolTip, void(LocalFrame*, const String&, TextDirection));
void SetToolTip(LocalFrame& frame,
const String& tooltip_text,
TextDirection dir) override {
MockSetToolTip(&frame, tooltip_text, dir);
}
void ScheduleAnimation(const LocalFrameView*) override {
has_scheduled_animation_ = true;
}
bool has_scheduled_animation_;
};
class LocalFrameViewTest : public RenderingTest {
protected:
LocalFrameViewTest()
: RenderingTest(SingleChildLocalFrameClient::Create()),
chrome_client_(new AnimationMockChromeClient) {
EXPECT_CALL(GetAnimationMockChromeClient(), AttachRootGraphicsLayer(_, _))
.Times(AnyNumber());
}
~LocalFrameViewTest() override {
testing::Mock::VerifyAndClearExpectations(&GetAnimationMockChromeClient());
}
ChromeClient& GetChromeClient() const override { return *chrome_client_; }
void SetUp() override {
RenderingTest::SetUp();
EnableCompositing();
}
AnimationMockChromeClient& GetAnimationMockChromeClient() const {
return *chrome_client_;
}
private:
Persistent<AnimationMockChromeClient> chrome_client_;
};
TEST_F(LocalFrameViewTest, SetPaintInvalidationDuringUpdateAllLifecyclePhases) {
SetBodyInnerHTML("<div id='a' style='color: blue'>A</div>");
GetDocument().getElementById("a")->setAttribute(HTMLNames::styleAttr,
"color: green");
GetAnimationMockChromeClient().has_scheduled_animation_ = false;
GetDocument().View()->UpdateAllLifecyclePhases();
EXPECT_FALSE(GetAnimationMockChromeClient().has_scheduled_animation_);
}
TEST_F(LocalFrameViewTest, SetPaintInvalidationOutOfUpdateAllLifecyclePhases) {
SetBodyInnerHTML("<div id='a' style='color: blue'>A</div>");
GetAnimationMockChromeClient().has_scheduled_animation_ = false;
GetDocument()
.getElementById("a")
->GetLayoutObject()
->SetShouldDoFullPaintInvalidation();
EXPECT_TRUE(GetAnimationMockChromeClient().has_scheduled_animation_);
GetAnimationMockChromeClient().has_scheduled_animation_ = false;
GetDocument()
.getElementById("a")
->GetLayoutObject()
->SetShouldDoFullPaintInvalidation();
EXPECT_TRUE(GetAnimationMockChromeClient().has_scheduled_animation_);
GetAnimationMockChromeClient().has_scheduled_animation_ = false;
GetDocument().View()->UpdateAllLifecyclePhases();
EXPECT_FALSE(GetAnimationMockChromeClient().has_scheduled_animation_);
}
// If we don't hide the tooltip on scroll, it can negatively impact scrolling
// performance. See crbug.com/586852 for details.
TEST_F(LocalFrameViewTest, HideTooltipWhenScrollPositionChanges) {
SetBodyInnerHTML("<div style='width:1000px;height:1000px'></div>");
EXPECT_CALL(GetAnimationMockChromeClient(),
MockSetToolTip(GetDocument().GetFrame(), String(), _));
GetDocument().View()->LayoutViewport()->SetScrollOffset(ScrollOffset(1, 1),
kUserScroll);
// Programmatic scrolling should not dismiss the tooltip, so setToolTip
// should not be called for this invocation.
EXPECT_CALL(GetAnimationMockChromeClient(),
MockSetToolTip(GetDocument().GetFrame(), String(), _))
.Times(0);
GetDocument().View()->LayoutViewport()->SetScrollOffset(ScrollOffset(2, 2),
kProgrammaticScroll);
}
// NoOverflowInIncrementVisuallyNonEmptyPixelCount tests fail if the number of
// pixels is calculated in 32-bit integer, because 65536 * 65536 would become 0
// if it was calculated in 32-bit and thus it would be considered as empty.
TEST_F(LocalFrameViewTest, NoOverflowInIncrementVisuallyNonEmptyPixelCount) {
EXPECT_FALSE(GetDocument().View()->IsVisuallyNonEmpty());
GetDocument().View()->IncrementVisuallyNonEmptyPixelCount(
IntSize(65536, 65536));
EXPECT_TRUE(GetDocument().View()->IsVisuallyNonEmpty());
}
// This test addresses http://crbug.com/696173, in which a call to
// LocalFrameView::UpdateLayersAndCompositingAfterScrollIfNeeded during layout
// caused a crash as the code was incorrectly assuming that the ancestor
// overflow layer would always be valid.
TEST_F(LocalFrameViewTest,
ViewportConstrainedObjectsHandledCorrectlyDuringLayout) {
SetBodyInnerHTML(R"HTML(
<style>.container { height: 200%; }
#sticky { position: sticky; top: 0; height: 50px; }</style>
<div class='container'><div id='sticky'></div></div>
)HTML");
LayoutBoxModelObject* sticky = ToLayoutBoxModelObject(
GetDocument().getElementById("sticky")->GetLayoutObject());
// Deliberately invalidate the ancestor overflow layer. This approximates
// http://crbug.com/696173, in which the ancestor overflow layer can be null
// during layout.
sticky->Layer()->UpdateAncestorOverflowLayer(nullptr);
// This call should not crash.
GetDocument().View()->LayoutViewport()->SetScrollOffset(ScrollOffset(0, 100),
kProgrammaticScroll);
}
TEST_F(LocalFrameViewTest, UpdateLifecyclePhasesForPrintingDetachedFrame) {
SetBodyInnerHTML("<iframe style='display: none'></iframe>");
SetChildFrameHTML("A");
ChildDocument().SetPrinting(Document::kPrinting);
ChildDocument().View()->UpdateLifecyclePhasesForPrinting();
// The following checks that the detached frame has been walked for PrePaint.
EXPECT_EQ(DocumentLifecycle::kPrePaintClean,
GetDocument().Lifecycle().GetState());
EXPECT_EQ(DocumentLifecycle::kPrePaintClean,
ChildDocument().Lifecycle().GetState());
auto* child_layout_view = ChildDocument().GetLayoutView();
EXPECT_TRUE(child_layout_view->FirstFragment().PaintProperties());
}
} // namespace
} // namespace blink