blob: eb43df902022b371afe9bd5a7a5b24d643d5e285 [file] [log] [blame]
// Copyright 2016 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 "core/clipboard/DataTransfer.h"
#include "core/dom/Element.h"
#include "core/frame/LocalFrame.h"
#include "core/frame/LocalFrameView.h"
#include "core/frame/Settings.h"
#include "core/layout/LayoutObject.h"
#include "core/layout/LayoutTestHelper.h"
#include "platform/DragImage.h"
#include "platform/testing/RuntimeEnabledFeaturesTestHelpers.h"
#include "testing/gtest/include/gtest/gtest.h"
namespace blink {
typedef bool TestParamRootLayerScrolling;
class DataTransferTest
: public RenderingTest,
public ::testing::WithParamInterface<TestParamRootLayerScrolling>,
private ScopedRootLayerScrollingForTest {
public:
DataTransferTest() : ScopedRootLayerScrollingForTest(GetParam()) {}
protected:
Page& GetPage() const { return *GetDocument().GetPage(); }
LocalFrame& GetFrame() const { return *GetDocument().GetFrame(); }
void UpdateAllLifecyclePhases() {
GetDocument().View()->UpdateAllLifecyclePhases();
}
};
INSTANTIATE_TEST_CASE_P(All, DataTransferTest, ::testing::Bool());
TEST_P(DataTransferTest, NodeImage) {
SetBodyInnerHTML(R"HTML(
<style>
#sample { width: 100px; height: 100px; }
</style>
<div id=sample></div>
)HTML");
Element* sample = GetDocument().getElementById("sample");
const std::unique_ptr<DragImage> image =
DataTransfer::NodeImage(GetFrame(), *sample);
EXPECT_EQ(IntSize(100, 100), image->Size());
}
TEST_P(DataTransferTest, NodeImageWithNestedElement) {
SetBodyInnerHTML(R"HTML(
<style>
div { -webkit-user-drag: element }
span:-webkit-drag { color: #0F0 }
</style>
<div id=sample><span>Green when dragged</span></div>
)HTML");
Element* sample = GetDocument().getElementById("sample");
const std::unique_ptr<DragImage> image =
DataTransfer::NodeImage(GetFrame(), *sample);
EXPECT_EQ(Color(0, 255, 0),
sample->firstChild()->GetLayoutObject()->ResolveColor(
GetCSSPropertyColor()))
<< "Descendants node should have :-webkit-drag.";
}
TEST_P(DataTransferTest, NodeImageWithPsuedoClassWebKitDrag) {
SetBodyInnerHTML(R"HTML(
<style>
#sample { width: 100px; height: 100px; }
#sample:-webkit-drag { width: 200px; height: 200px; }
</style>
<div id=sample></div>
)HTML");
Element* sample = GetDocument().getElementById("sample");
const std::unique_ptr<DragImage> image =
DataTransfer::NodeImage(GetFrame(), *sample);
EXPECT_EQ(IntSize(200, 200), image->Size())
<< ":-webkit-drag should affect dragged image.";
}
TEST_P(DataTransferTest, NodeImageWithoutDraggedLayoutObject) {
SetBodyInnerHTML(R"HTML(
<style>
#sample { width: 100px; height: 100px; }
#sample:-webkit-drag { display:none }
</style>
<div id=sample></div>
)HTML");
Element* sample = GetDocument().getElementById("sample");
const std::unique_ptr<DragImage> image =
DataTransfer::NodeImage(GetFrame(), *sample);
EXPECT_EQ(nullptr, image.get()) << ":-webkit-drag blows away layout object";
}
TEST_P(DataTransferTest, NodeImageWithChangingLayoutObject) {
SetBodyInnerHTML(R"HTML(
<style>
#sample { color: blue; }
#sample:-webkit-drag { display: inline-block; color: red; }
</style>
<span id=sample>foo</span>
)HTML");
Element* sample = GetDocument().getElementById("sample");
UpdateAllLifecyclePhases();
LayoutObject* before_layout_object = sample->GetLayoutObject();
const std::unique_ptr<DragImage> image =
DataTransfer::NodeImage(GetFrame(), *sample);
EXPECT_TRUE(sample->GetLayoutObject() != before_layout_object)
<< ":-webkit-drag causes sample to have different layout object.";
EXPECT_EQ(Color(255, 0, 0),
sample->GetLayoutObject()->ResolveColor(GetCSSPropertyColor()))
<< "#sample has :-webkit-drag.";
// Layout w/o :-webkit-drag
UpdateAllLifecyclePhases();
EXPECT_EQ(Color(0, 0, 255),
sample->GetLayoutObject()->ResolveColor(GetCSSPropertyColor()))
<< "#sample doesn't have :-webkit-drag.";
}
TEST_P(DataTransferTest, NodeImageExceedsViewportBounds) {
SetBodyInnerHTML(R"HTML(
<style>
* { margin: 0; }
#node { width: 2000px; height: 2000px; }
</style>
<div id='node'></div>
)HTML");
Element& node = *GetDocument().getElementById("node");
const auto image = DataTransfer::NodeImage(GetFrame(), node);
EXPECT_EQ(IntSize(800, 600), image->Size());
}
TEST_P(DataTransferTest, NodeImageUnderScrollOffset) {
SetBodyInnerHTML(R"HTML(
<style>
* { margin: 0; }
#first { width: 500px; height: 500px; }
#second { width: 800px; height: 900px; }
</style>
<div id='first'></div>
<div id='second'></div>
)HTML");
const int scroll_amount = 10;
LocalFrameView* frame_view = GetDocument().View();
frame_view->LayoutViewportScrollableArea()->SetScrollOffset(
ScrollOffset(0, scroll_amount), kProgrammaticScroll);
// The first div should be offset by the scroll offset.
Element& first = *GetDocument().getElementById("first");
const auto first_image = DataTransfer::NodeImage(GetFrame(), first);
const int first_height = 500;
EXPECT_EQ(IntSize(500, first_height), first_image->Size());
// The second div should also be offset by the scroll offset. In addition,
// the second div should be clipped by the viewport.
Element& second = *GetDocument().getElementById("second");
const auto second_image = DataTransfer::NodeImage(GetFrame(), second);
const int viewport_height = 600;
EXPECT_EQ(IntSize(800, viewport_height - (first_height - scroll_amount)),
second_image->Size());
}
TEST_P(DataTransferTest, NodeImageSizeWithPageScaleFactor) {
SetBodyInnerHTML(R"HTML(
<style>
* { margin: 0; }
html, body { height: 2000px; }
#node { width: 200px; height: 141px; }
</style>
<div id='node'></div>
)HTML");
const int page_scale_factor = 2;
GetPage().SetPageScaleFactor(page_scale_factor);
Element& node = *GetDocument().getElementById("node");
const auto image = DataTransfer::NodeImage(GetFrame(), node);
const int node_width = 200;
const int node_height = 141;
EXPECT_EQ(
IntSize(node_width * page_scale_factor, node_height * page_scale_factor),
image->Size());
// Check that a scroll offset is scaled to device coordinates which includes
// page scale factor.
const int scroll_amount = 10;
LocalFrameView* frame_view = GetDocument().View();
frame_view->LayoutViewportScrollableArea()->SetScrollOffset(
ScrollOffset(0, scroll_amount), kProgrammaticScroll);
const auto image_with_offset = DataTransfer::NodeImage(GetFrame(), node);
EXPECT_EQ(
IntSize(node_width * page_scale_factor, node_height * page_scale_factor),
image_with_offset->Size());
}
TEST_P(DataTransferTest, NodeImageSizeWithPageScaleFactorTooLarge) {
SetBodyInnerHTML(R"HTML(
<style>
* { margin: 0; }
html, body { height: 2000px; }
#node { width: 800px; height: 601px; }
</style>
<div id='node'></div>
)HTML");
const int page_scale_factor = 2;
GetPage().SetPageScaleFactor(page_scale_factor);
Element& node = *GetDocument().getElementById("node");
const auto image = DataTransfer::NodeImage(GetFrame(), node);
const int node_width = 800;
const int node_height = 601;
EXPECT_EQ(IntSize(node_width * page_scale_factor,
(node_height - 1) * page_scale_factor),
image->Size());
// Check that a scroll offset is scaled to device coordinates which includes
// page scale factor.
const int scroll_amount = 10;
LocalFrameView* frame_view = GetDocument().View();
frame_view->LayoutViewportScrollableArea()->SetScrollOffset(
ScrollOffset(0, scroll_amount), kProgrammaticScroll);
const auto image_with_offset = DataTransfer::NodeImage(GetFrame(), node);
EXPECT_EQ(IntSize(node_width * page_scale_factor,
(node_height - scroll_amount) * page_scale_factor),
image_with_offset->Size());
}
TEST_P(DataTransferTest, NodeImageWithPageScaleFactor) {
// #bluegreen is a 2x1 rectangle where the left pixel is blue and the right
// pixel is green. The element is offset by a margin of 1px.
SetBodyInnerHTML(R"HTML(
<style>
* { margin: 0; }
#bluegreen {
width: 1px;
height: 1px;
background: #0f0;
border-left: 1px solid #00f;
margin: 1px;
}
</style>
<div id='bluegreen'></div>
)HTML");
const int page_scale_factor = 2;
GetPage().SetPageScaleFactor(page_scale_factor);
Element& blue_green = *GetDocument().getElementById("bluegreen");
const auto image = DataTransfer::NodeImage(GetFrame(), blue_green);
const int blue_green_width = 2;
const int blue_green_height = 1;
EXPECT_EQ(IntSize(blue_green_width * page_scale_factor,
blue_green_height * page_scale_factor),
image->Size());
// Even though #bluegreen is offset by a margin of 1px (which is 2px in device
// coordinates), we expect it to be painted at 0x0 and completely fill the 4x2
// bitmap.
SkBitmap expected_bitmap;
expected_bitmap.allocN32Pixels(4, 2);
expected_bitmap.eraseArea(SkIRect::MakeXYWH(0, 0, 2, 2), 0xFF0000FF);
expected_bitmap.eraseArea(SkIRect::MakeXYWH(2, 0, 2, 2), 0xFF00FF00);
const SkBitmap& bitmap = image->Bitmap();
for (int x = 0; x < bitmap.width(); ++x)
for (int y = 0; y < bitmap.height(); ++y)
EXPECT_EQ(expected_bitmap.getColor(x, y), bitmap.getColor(x, y));
}
TEST_P(DataTransferTest, NodeImageFullyOffscreen) {
SetBodyInnerHTML(R"HTML(
<style>
#target {
position: absolute;
top: 800px;
left: 0;
height: 100px;
width: 200px;
background: lightblue;
isolation: isolate;
}
</style>
<div id="target" draggable="true" ondragstart="drag(event)"></div>
)HTML");
const int scroll_amount = 800;
LocalFrameView* frame_view = GetDocument().View();
frame_view->LayoutViewportScrollableArea()->SetScrollOffset(
ScrollOffset(0, scroll_amount), kProgrammaticScroll);
Element& target = *GetDocument().getElementById("target");
const auto image = DataTransfer::NodeImage(GetFrame(), target);
EXPECT_EQ(IntSize(200, 100), image->Size());
}
TEST_P(DataTransferTest, NodeImageWithScrolling) {
SetBodyInnerHTML(R"HTML(
<style>
#target {
position: absolute;
top: 800px;
left: 0;
height: 100px;
width: 200px;
background: lightblue;
isolation: isolate;
}
</style>
<div id="target" draggable="true" ondragstart="drag(event)"></div>
)HTML");
Element& target = *GetDocument().getElementById("target");
const auto image = DataTransfer::NodeImage(GetFrame(), target);
EXPECT_EQ(IntSize(200, 100), image->Size());
}
} // namespace blink