blob: ba227494e79df436af2bd64618b19a71dde765ee [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/layout/LayoutInline.h"
#include "core/layout/LayoutTestHelper.h"
#include "core/layout/LayoutView.h"
#include "platform/geometry/TransformState.h"
#include "testing/gtest/include/gtest/gtest.h"
namespace blink {
class MapCoordinatesTest : public RenderingTest {
public:
MapCoordinatesTest()
: RenderingTest(SingleChildFrameLoaderClient::create()) {}
FloatPoint mapLocalToAncestor(const LayoutObject*,
const LayoutBoxModelObject* ancestor,
FloatPoint,
MapCoordinatesFlags = 0) const;
FloatQuad mapLocalToAncestor(const LayoutObject*,
const LayoutBoxModelObject* ancestor,
FloatQuad,
MapCoordinatesFlags = 0) const;
FloatPoint mapAncestorToLocal(const LayoutObject*,
const LayoutBoxModelObject* ancestor,
FloatPoint,
MapCoordinatesFlags = 0) const;
FloatQuad mapAncestorToLocal(const LayoutObject*,
const LayoutBoxModelObject* ancestor,
FloatQuad,
MapCoordinatesFlags = 0) const;
};
// One note about tests here that operate on LayoutInline and LayoutText
// objects: mapLocalToAncestor() expects such objects to pass their static
// location and size (relatively to the border edge of their container) to
// mapLocalToAncestor() via the TransformState argument. mapLocalToAncestor() is
// then only expected to make adjustments for relative-positioning,
// container-specific characteristics (such as writing mode roots, multicol),
// and so on. This in contrast to LayoutBox objects, where the TransformState
// passed is relative to the box itself, not the container.
FloatPoint MapCoordinatesTest::mapLocalToAncestor(
const LayoutObject* object,
const LayoutBoxModelObject* ancestor,
FloatPoint point,
MapCoordinatesFlags mode) const {
TransformState transformState(TransformState::ApplyTransformDirection, point);
object->mapLocalToAncestor(ancestor, transformState, mode);
transformState.flatten();
return transformState.lastPlanarPoint();
}
FloatQuad MapCoordinatesTest::mapLocalToAncestor(
const LayoutObject* object,
const LayoutBoxModelObject* ancestor,
FloatQuad quad,
MapCoordinatesFlags mode) const {
TransformState transformState(TransformState::ApplyTransformDirection,
quad.boundingBox().center(), quad);
object->mapLocalToAncestor(ancestor, transformState, mode);
transformState.flatten();
return transformState.lastPlanarQuad();
}
FloatPoint MapCoordinatesTest::mapAncestorToLocal(
const LayoutObject* object,
const LayoutBoxModelObject* ancestor,
FloatPoint point,
MapCoordinatesFlags mode) const {
TransformState transformState(
TransformState::UnapplyInverseTransformDirection, point);
object->mapAncestorToLocal(ancestor, transformState, mode);
transformState.flatten();
return transformState.lastPlanarPoint();
}
FloatQuad MapCoordinatesTest::mapAncestorToLocal(
const LayoutObject* object,
const LayoutBoxModelObject* ancestor,
FloatQuad quad,
MapCoordinatesFlags mode) const {
TransformState transformState(
TransformState::UnapplyInverseTransformDirection,
quad.boundingBox().center(), quad);
object->mapAncestorToLocal(ancestor, transformState, mode);
transformState.flatten();
return transformState.lastPlanarQuad();
}
TEST_F(MapCoordinatesTest, SimpleText) {
setBodyInnerHTML("<div id='container'><br>text</div>");
LayoutBox* container = toLayoutBox(getLayoutObjectByElementId("container"));
LayoutObject* text = toLayoutBlockFlow(container)->lastChild();
ASSERT_TRUE(text->isText());
FloatPoint mappedPoint =
mapLocalToAncestor(text, container, FloatPoint(10, 30));
EXPECT_EQ(FloatPoint(10, 30), mappedPoint);
mappedPoint = mapAncestorToLocal(text, container, mappedPoint);
EXPECT_EQ(FloatPoint(10, 30), mappedPoint);
}
TEST_F(MapCoordinatesTest, SimpleInline) {
setBodyInnerHTML("<div><span id='target'>text</span></div>");
LayoutObject* target = getLayoutObjectByElementId("target");
FloatPoint mappedPoint = mapLocalToAncestor(
target, toLayoutBoxModelObject(target->parent()), FloatPoint(10, 10));
EXPECT_EQ(FloatPoint(10, 10), mappedPoint);
mappedPoint = mapAncestorToLocal(
target, toLayoutBoxModelObject(target->parent()), mappedPoint);
EXPECT_EQ(FloatPoint(10, 10), mappedPoint);
}
TEST_F(MapCoordinatesTest, SimpleBlock) {
setBodyInnerHTML(
"<div style='margin:666px; border:8px solid; padding:7px;'>"
" <div id='target' style='margin:10px; border:666px; "
"padding:666px;'></div>"
"</div>");
LayoutObject* target = getLayoutObjectByElementId("target");
FloatPoint mappedPoint = mapLocalToAncestor(
target, toLayoutBoxModelObject(target->parent()), FloatPoint(100, 100));
EXPECT_EQ(FloatPoint(125, 125), mappedPoint);
mappedPoint = mapAncestorToLocal(
target, toLayoutBoxModelObject(target->parent()), mappedPoint);
EXPECT_EQ(FloatPoint(100, 100), mappedPoint);
}
TEST_F(MapCoordinatesTest, OverflowClip) {
setBodyInnerHTML(
"<div id='overflow' style='height: 100px; width: 100px; border:8px "
"solid; padding:7px; overflow:scroll'>"
" <div style='height:200px; width:200px'></div>"
" <div id='target' style='margin:10px; border:666px; "
"padding:666px;'></div>"
"</div>");
LayoutObject* target = getLayoutObjectByElementId("target");
LayoutObject* overflow = getLayoutObjectByElementId("overflow");
toLayoutBox(overflow)->scrollToPosition(FloatPoint(32, 54));
FloatPoint mappedPoint = mapLocalToAncestor(
target, toLayoutBoxModelObject(target->parent()), FloatPoint(100, 100));
EXPECT_EQ(FloatPoint(93, 271), mappedPoint);
mappedPoint = mapAncestorToLocal(
target, toLayoutBoxModelObject(target->parent()), mappedPoint);
EXPECT_EQ(FloatPoint(100, 100), mappedPoint);
}
TEST_F(MapCoordinatesTest, TextInRelPosInline) {
setBodyInnerHTML(
"<div><span style='position:relative; left:7px; top:4px;'><br "
"id='sibling'>text</span></div>");
LayoutObject* br = getLayoutObjectByElementId("sibling");
LayoutObject* text = br->nextSibling();
ASSERT_TRUE(text->isText());
FloatPoint mappedPoint =
mapLocalToAncestor(text, text->containingBlock(), FloatPoint(10, 30));
EXPECT_EQ(FloatPoint(17, 34), mappedPoint);
mappedPoint = mapAncestorToLocal(text, text->containingBlock(), mappedPoint);
EXPECT_EQ(FloatPoint(10, 30), mappedPoint);
}
TEST_F(MapCoordinatesTest, RelposInline) {
setBodyInnerHTML(
"<span id='target' style='position:relative; left:50px; "
"top:100px;'>text</span>");
LayoutObject* target = getLayoutObjectByElementId("target");
FloatPoint mappedPoint = mapLocalToAncestor(
target, toLayoutBoxModelObject(target->parent()), FloatPoint(10, 10));
EXPECT_EQ(FloatPoint(60, 110), mappedPoint);
mappedPoint = mapAncestorToLocal(
target, toLayoutBoxModelObject(target->parent()), mappedPoint);
EXPECT_EQ(FloatPoint(10, 10), mappedPoint);
}
TEST_F(MapCoordinatesTest, RelposInlineInRelposInline) {
setBodyInnerHTML(
"<div style='padding-left:10px;'>"
" <span style='position:relative; left:5px; top:6px;'>"
" <span id='target' style='position:relative; left:50px; "
"top:100px;'>text</span>"
" </span>"
"</div>");
LayoutObject* target = getLayoutObjectByElementId("target");
LayoutInline* parent = toLayoutInline(target->parent());
LayoutBlockFlow* containingBlock = toLayoutBlockFlow(parent->parent());
FloatPoint mappedPoint =
mapLocalToAncestor(target, containingBlock, FloatPoint(20, 10));
EXPECT_EQ(FloatPoint(75, 116), mappedPoint);
mappedPoint = mapAncestorToLocal(target, containingBlock, mappedPoint);
EXPECT_EQ(FloatPoint(20, 10), mappedPoint);
// Walk each ancestor in the chain separately, to verify each step on the way.
mappedPoint = mapLocalToAncestor(target, parent, FloatPoint(20, 10));
EXPECT_EQ(FloatPoint(70, 110), mappedPoint);
mappedPoint = mapLocalToAncestor(parent, containingBlock, mappedPoint);
EXPECT_EQ(FloatPoint(75, 116), mappedPoint);
mappedPoint = mapAncestorToLocal(parent, containingBlock, mappedPoint);
EXPECT_EQ(FloatPoint(70, 110), mappedPoint);
mappedPoint = mapAncestorToLocal(target, parent, mappedPoint);
EXPECT_EQ(FloatPoint(20, 10), mappedPoint);
}
TEST_F(MapCoordinatesTest, RelPosBlock) {
setBodyInnerHTML(
"<div id='container' style='margin:666px; border:8px solid; "
"padding:7px;'>"
" <div id='middle' style='margin:30px; border:1px solid;'>"
" <div id='target' style='position:relative; left:50px; top:50px; "
"margin:10px; border:666px; padding:666px;'></div>"
" </div>"
"</div>");
LayoutBox* target = toLayoutBox(getLayoutObjectByElementId("target"));
LayoutBox* container = toLayoutBox(getLayoutObjectByElementId("container"));
FloatPoint mappedPoint = mapLocalToAncestor(target, container, FloatPoint());
EXPECT_EQ(FloatPoint(106, 106), mappedPoint);
mappedPoint = mapAncestorToLocal(target, container, FloatPoint(110, 110));
EXPECT_EQ(FloatPoint(4, 4), mappedPoint);
// Walk each ancestor in the chain separately, to verify each step on the way.
LayoutBox* middle = toLayoutBox(getLayoutObjectByElementId("middle"));
mappedPoint = mapLocalToAncestor(target, middle, FloatPoint());
EXPECT_EQ(FloatPoint(61, 61), mappedPoint);
mappedPoint = mapLocalToAncestor(middle, container, mappedPoint);
EXPECT_EQ(FloatPoint(106, 106), mappedPoint);
mappedPoint = mapAncestorToLocal(middle, container, mappedPoint);
EXPECT_EQ(FloatPoint(61, 61), mappedPoint);
mappedPoint = mapAncestorToLocal(target, middle, mappedPoint);
EXPECT_EQ(FloatPoint(), mappedPoint);
}
TEST_F(MapCoordinatesTest, AbsPos) {
setBodyInnerHTML(
"<div id='container' style='position:relative; margin:666px; border:8px "
"solid; padding:7px;'>"
" <div id='staticChild' style='margin:30px; padding-top:666px;'>"
" <div style='padding-top:666px;'></div>"
" <div id='target' style='position:absolute; left:-1px; top:-1px; "
"margin:10px; border:666px; padding:666px;'></div>"
" </div>"
"</div>");
LayoutBox* target = toLayoutBox(getLayoutObjectByElementId("target"));
LayoutBox* container = toLayoutBox(getLayoutObjectByElementId("container"));
FloatPoint mappedPoint = mapLocalToAncestor(target, container, FloatPoint());
EXPECT_EQ(FloatPoint(17, 17), mappedPoint);
mappedPoint = mapAncestorToLocal(target, container, FloatPoint(18, 18));
EXPECT_EQ(FloatPoint(1, 1), mappedPoint);
// Walk each ancestor in the chain separately, to verify each step on the way.
LayoutBox* staticChild =
toLayoutBox(getLayoutObjectByElementId("staticChild"));
mappedPoint = mapLocalToAncestor(target, staticChild, FloatPoint());
EXPECT_EQ(FloatPoint(-28, -28), mappedPoint);
mappedPoint = mapLocalToAncestor(staticChild, container, mappedPoint);
EXPECT_EQ(FloatPoint(17, 17), mappedPoint);
mappedPoint = mapAncestorToLocal(staticChild, container, mappedPoint);
EXPECT_EQ(FloatPoint(-28, -28), mappedPoint);
mappedPoint = mapAncestorToLocal(target, staticChild, mappedPoint);
EXPECT_EQ(FloatPoint(), mappedPoint);
}
TEST_F(MapCoordinatesTest, AbsPosAuto) {
setBodyInnerHTML(
"<div id='container' style='position:absolute; margin:666px; border:8px "
"solid; padding:7px;'>"
" <div id='staticChild' style='margin:30px; padding-top:5px;'>"
" <div style='padding-top:20px;'></div>"
" <div id='target' style='position:absolute; margin:10px; "
"border:666px; padding:666px;'></div>"
" </div>"
"</div>");
LayoutBox* target = toLayoutBox(getLayoutObjectByElementId("target"));
LayoutBox* container = toLayoutBox(getLayoutObjectByElementId("container"));
FloatPoint mappedPoint = mapLocalToAncestor(target, container, FloatPoint());
EXPECT_EQ(FloatPoint(55, 80), mappedPoint);
mappedPoint = mapAncestorToLocal(target, container, FloatPoint(56, 82));
EXPECT_EQ(FloatPoint(1, 2), mappedPoint);
// Walk each ancestor in the chain separately, to verify each step on the way.
LayoutBox* staticChild =
toLayoutBox(getLayoutObjectByElementId("staticChild"));
mappedPoint = mapLocalToAncestor(target, staticChild, FloatPoint());
EXPECT_EQ(FloatPoint(10, 35), mappedPoint);
mappedPoint = mapLocalToAncestor(staticChild, container, mappedPoint);
EXPECT_EQ(FloatPoint(55, 80), mappedPoint);
mappedPoint = mapAncestorToLocal(staticChild, container, mappedPoint);
EXPECT_EQ(FloatPoint(10, 35), mappedPoint);
mappedPoint = mapAncestorToLocal(target, staticChild, mappedPoint);
EXPECT_EQ(FloatPoint(), mappedPoint);
}
TEST_F(MapCoordinatesTest, FixedPos) {
// Assuming BODY margin of 8px.
setBodyInnerHTML(
"<div id='container' style='position:absolute; margin:4px; border:5px "
"solid; padding:7px;'>"
" <div id='staticChild' style='padding-top:666px;'>"
" <div style='padding-top:666px;'></div>"
" <div id='target' style='position:fixed; left:-1px; top:-1px; "
"margin:10px; border:666px; padding:666px;'></div>"
" </div>"
"</div>");
LayoutBox* target = toLayoutBox(getLayoutObjectByElementId("target"));
LayoutBox* staticChild =
toLayoutBox(getLayoutObjectByElementId("staticChild"));
LayoutBox* container = toLayoutBox(getLayoutObjectByElementId("container"));
LayoutBox* body = container->parentBox();
LayoutBox* html = body->parentBox();
LayoutBox* view = html->parentBox();
ASSERT_TRUE(view->isLayoutView());
FloatPoint mappedPoint = mapLocalToAncestor(target, view, FloatPoint());
EXPECT_EQ(FloatPoint(9, 9), mappedPoint);
mappedPoint = mapAncestorToLocal(target, view, FloatPoint(10, 11));
EXPECT_EQ(FloatPoint(1, 2), mappedPoint);
// Walk each ancestor in the chain separately, to verify each step on the way.
mappedPoint = mapLocalToAncestor(target, staticChild, FloatPoint());
EXPECT_EQ(FloatPoint(-15, -15), mappedPoint);
mappedPoint = mapLocalToAncestor(staticChild, container, mappedPoint);
EXPECT_EQ(FloatPoint(-3, -3), mappedPoint);
mappedPoint = mapLocalToAncestor(container, body, mappedPoint);
EXPECT_EQ(FloatPoint(1, 1), mappedPoint);
mappedPoint = mapLocalToAncestor(body, html, mappedPoint);
EXPECT_EQ(FloatPoint(9, 9), mappedPoint);
mappedPoint = mapLocalToAncestor(html, view, mappedPoint);
EXPECT_EQ(FloatPoint(9, 9), mappedPoint);
mappedPoint = mapAncestorToLocal(html, view, mappedPoint);
EXPECT_EQ(FloatPoint(9, 9), mappedPoint);
mappedPoint = mapAncestorToLocal(body, html, mappedPoint);
EXPECT_EQ(FloatPoint(1, 1), mappedPoint);
mappedPoint = mapAncestorToLocal(container, body, mappedPoint);
EXPECT_EQ(FloatPoint(-3, -3), mappedPoint);
mappedPoint = mapAncestorToLocal(staticChild, container, mappedPoint);
EXPECT_EQ(FloatPoint(-15, -15), mappedPoint);
mappedPoint = mapAncestorToLocal(target, staticChild, mappedPoint);
EXPECT_EQ(FloatPoint(), mappedPoint);
}
TEST_F(MapCoordinatesTest, FixedPosAuto) {
// Assuming BODY margin of 8px.
setBodyInnerHTML(
"<div id='container' style='position:absolute; margin:3px; border:8px "
"solid; padding:7px;'>"
" <div id='staticChild' style='padding-top:5px;'>"
" <div style='padding-top:20px;'></div>"
" <div id='target' style='position:fixed; margin:10px; "
"border:666px; padding:666px;'></div>"
" </div>"
"</div>");
LayoutBox* target = toLayoutBox(getLayoutObjectByElementId("target"));
LayoutBox* staticChild =
toLayoutBox(getLayoutObjectByElementId("staticChild"));
LayoutBox* container = toLayoutBox(getLayoutObjectByElementId("container"));
LayoutBox* body = container->parentBox();
LayoutBox* html = body->parentBox();
LayoutBox* view = html->parentBox();
ASSERT_TRUE(view->isLayoutView());
FloatPoint mappedPoint =
mapLocalToAncestor(target, target->containingBlock(), FloatPoint());
EXPECT_EQ(FloatPoint(36, 61), mappedPoint);
mappedPoint =
mapAncestorToLocal(target, target->containingBlock(), FloatPoint(36, 61));
EXPECT_EQ(FloatPoint(), mappedPoint);
// Walk each ancestor in the chain separately, to verify each step on the way.
mappedPoint = mapLocalToAncestor(target, staticChild, FloatPoint());
EXPECT_EQ(FloatPoint(10, 35), mappedPoint);
mappedPoint = mapLocalToAncestor(staticChild, container, mappedPoint);
EXPECT_EQ(FloatPoint(25, 50), mappedPoint);
mappedPoint = mapLocalToAncestor(container, body, mappedPoint);
EXPECT_EQ(FloatPoint(28, 53), mappedPoint);
mappedPoint = mapLocalToAncestor(body, html, mappedPoint);
EXPECT_EQ(FloatPoint(36, 61), mappedPoint);
mappedPoint = mapLocalToAncestor(html, view, mappedPoint);
EXPECT_EQ(FloatPoint(36, 61), mappedPoint);
mappedPoint = mapAncestorToLocal(html, view, mappedPoint);
EXPECT_EQ(FloatPoint(36, 61), mappedPoint);
mappedPoint = mapAncestorToLocal(body, html, mappedPoint);
EXPECT_EQ(FloatPoint(28, 53), mappedPoint);
mappedPoint = mapAncestorToLocal(container, body, mappedPoint);
EXPECT_EQ(FloatPoint(25, 50), mappedPoint);
mappedPoint = mapAncestorToLocal(staticChild, container, mappedPoint);
EXPECT_EQ(FloatPoint(10, 35), mappedPoint);
mappedPoint = mapAncestorToLocal(target, staticChild, mappedPoint);
EXPECT_EQ(FloatPoint(), mappedPoint);
}
TEST_F(MapCoordinatesTest, FixedPosInFixedPos) {
// Assuming BODY margin of 8px.
setBodyInnerHTML(
"<div id='container' style='position:absolute; margin:4px; border:5px "
"solid; padding:7px;'>"
" <div id='staticChild' style='padding-top:666px;'>"
" <div style='padding-top:666px;'></div>"
" <div id='outerFixed' style='position:fixed; left:100px; "
"top:100px; margin:10px; border:666px; padding:666px;'>"
" <div id='target' style='position:fixed; left:-1px; "
"top:-1px; margin:10px; border:666px; padding:666px;'></div>"
" </div>"
" </div>"
"</div>");
LayoutBox* target = toLayoutBox(getLayoutObjectByElementId("target"));
LayoutBox* outerFixed = toLayoutBox(getLayoutObjectByElementId("outerFixed"));
LayoutBox* staticChild =
toLayoutBox(getLayoutObjectByElementId("staticChild"));
LayoutBox* container = toLayoutBox(getLayoutObjectByElementId("container"));
LayoutBox* body = container->parentBox();
LayoutBox* html = body->parentBox();
LayoutBox* view = html->parentBox();
ASSERT_TRUE(view->isLayoutView());
FloatPoint mappedPoint = mapLocalToAncestor(target, view, FloatPoint());
EXPECT_EQ(FloatPoint(9, 9), mappedPoint);
mappedPoint = mapAncestorToLocal(target, view, FloatPoint(9, 9));
EXPECT_EQ(FloatPoint(), mappedPoint);
// Walk each ancestor in the chain separately, to verify each step on the way.
mappedPoint = mapLocalToAncestor(target, outerFixed, FloatPoint());
EXPECT_EQ(FloatPoint(-101, -101), mappedPoint);
mappedPoint = mapLocalToAncestor(outerFixed, staticChild, mappedPoint);
EXPECT_EQ(FloatPoint(-15, -15), mappedPoint);
mappedPoint = mapLocalToAncestor(staticChild, container, mappedPoint);
EXPECT_EQ(FloatPoint(-3, -3), mappedPoint);
mappedPoint = mapLocalToAncestor(container, body, mappedPoint);
EXPECT_EQ(FloatPoint(1, 1), mappedPoint);
mappedPoint = mapLocalToAncestor(body, html, mappedPoint);
EXPECT_EQ(FloatPoint(9, 9), mappedPoint);
mappedPoint = mapLocalToAncestor(html, view, mappedPoint);
EXPECT_EQ(FloatPoint(9, 9), mappedPoint);
mappedPoint = mapAncestorToLocal(html, view, mappedPoint);
EXPECT_EQ(FloatPoint(9, 9), mappedPoint);
mappedPoint = mapAncestorToLocal(body, html, mappedPoint);
EXPECT_EQ(FloatPoint(1, 1), mappedPoint);
mappedPoint = mapAncestorToLocal(container, body, mappedPoint);
EXPECT_EQ(FloatPoint(-3, -3), mappedPoint);
mappedPoint = mapAncestorToLocal(staticChild, container, mappedPoint);
EXPECT_EQ(FloatPoint(-15, -15), mappedPoint);
mappedPoint = mapAncestorToLocal(outerFixed, staticChild, mappedPoint);
EXPECT_EQ(FloatPoint(-101, -101), mappedPoint);
mappedPoint = mapAncestorToLocal(target, outerFixed, mappedPoint);
EXPECT_EQ(FloatPoint(), mappedPoint);
}
TEST_F(MapCoordinatesTest, FixedPosInTransform) {
setBodyInnerHTML(
"<style>#container { transform: translateY(100px); position: absolute; "
"left: 0; top: 100px; }"
".fixed { position: fixed; top: 0; }"
".spacer { height: 2000px; } </style>"
"<div id='container'><div class='fixed' id='target'></div></div>"
"<div class='spacer'></div>");
document().view()->setScrollOffset(ScrollOffset(0.0, 50), ProgrammaticScroll);
document().view()->updateAllLifecyclePhases();
EXPECT_EQ(50, document().view()->scrollOffsetInt().height());
LayoutBox* target = toLayoutBox(getLayoutObjectByElementId("target"));
LayoutBox* container = toLayoutBox(getLayoutObjectByElementId("container"));
LayoutBox* body = container->parentBox();
LayoutBox* html = body->parentBox();
LayoutBox* view = html->parentBox();
ASSERT_TRUE(view->isLayoutView());
FloatPoint mappedPoint = mapLocalToAncestor(target, view, FloatPoint());
EXPECT_EQ(FloatPoint(0, 100), mappedPoint);
mappedPoint = mapAncestorToLocal(target, view, FloatPoint(0, 100));
EXPECT_EQ(FloatPoint(), mappedPoint);
mappedPoint = mapLocalToAncestor(target, container, FloatPoint());
EXPECT_EQ(FloatPoint(0, 0), mappedPoint);
mappedPoint = mapAncestorToLocal(target, container, FloatPoint(0, 0));
EXPECT_EQ(FloatPoint(), mappedPoint);
mappedPoint = mapLocalToAncestor(container, view, FloatPoint());
EXPECT_EQ(FloatPoint(0, 100), mappedPoint);
mappedPoint = mapAncestorToLocal(container, view, FloatPoint(0, 100));
EXPECT_EQ(FloatPoint(), mappedPoint);
}
TEST_F(MapCoordinatesTest, FixedPosInContainPaint) {
setBodyInnerHTML(
"<style>#container { contain: paint; position: absolute; left: 0; top: "
"100px; }"
".fixed { position: fixed; top: 0; }"
".spacer { height: 2000px; } </style>"
"<div id='container'><div class='fixed' id='target'></div></div>"
"<div class='spacer'></div>");
document().view()->setScrollOffset(ScrollOffset(0.0, 50), ProgrammaticScroll);
document().view()->updateAllLifecyclePhases();
EXPECT_EQ(50, document().view()->scrollOffsetInt().height());
LayoutBox* target = toLayoutBox(getLayoutObjectByElementId("target"));
LayoutBox* container = toLayoutBox(getLayoutObjectByElementId("container"));
LayoutBox* body = container->parentBox();
LayoutBox* html = body->parentBox();
LayoutBox* view = html->parentBox();
ASSERT_TRUE(view->isLayoutView());
FloatPoint mappedPoint = mapLocalToAncestor(target, view, FloatPoint());
EXPECT_EQ(FloatPoint(0, 100), mappedPoint);
mappedPoint = mapAncestorToLocal(target, view, FloatPoint(0, 100));
EXPECT_EQ(FloatPoint(), mappedPoint);
mappedPoint = mapLocalToAncestor(target, container, FloatPoint());
EXPECT_EQ(FloatPoint(0, 0), mappedPoint);
mappedPoint = mapAncestorToLocal(target, container, FloatPoint(0, 0));
EXPECT_EQ(FloatPoint(), mappedPoint);
mappedPoint = mapLocalToAncestor(container, view, FloatPoint());
EXPECT_EQ(FloatPoint(0, 100), mappedPoint);
mappedPoint = mapAncestorToLocal(container, view, FloatPoint(0, 100));
EXPECT_EQ(FloatPoint(), mappedPoint);
}
// TODO(chrishtr): add more multi-frame tests.
TEST_F(MapCoordinatesTest, FixedPosInIFrameWhenMainFrameScrolled) {
document().setBaseURLOverride(KURL(ParsedURLString, "http://test.com"));
setBodyInnerHTML(
"<style>body { margin: 0; }</style>"
"<div style='width: 200; height: 8000px'></div>"
"<iframe id=frame src='http://test.com' width='500' height='500' "
"frameBorder='0'>"
"</iframe>");
Document& frameDocument = setupChildIframe(
"frame",
"<style>body { margin: 0; } #target { width: 200px; height: 200px; "
"position:fixed}</style><div id=target></div>");
document().view()->setScrollOffset(ScrollOffset(0.0, 1000),
ProgrammaticScroll);
document().view()->updateAllLifecyclePhases();
Element* target = frameDocument.getElementById("target");
ASSERT_TRUE(target);
FloatPoint mappedPoint =
mapAncestorToLocal(target->layoutObject(), nullptr, FloatPoint(10, 70),
TraverseDocumentBoundaries);
// y = 70 - 8000, since the iframe is offset by 8000px from the main frame.
// The scroll is not taken into account because the element is not fixed to
// the root LayoutView, and the space of the root LayoutView does not include
// scroll.
EXPECT_EQ(FloatPoint(10, -7930), mappedPoint);
}
TEST_F(MapCoordinatesTest, IFrameTransformed) {
document().setBaseURLOverride(KURL(ParsedURLString, "http://test.com"));
setBodyInnerHTML(
"<style>body { margin: 0; }</style>"
"<iframe style='transform: scale(2)' id=frame src='http://test.com' "
"width='500' height='500' frameBorder='0'>"
"</iframe>");
Document& frameDocument =
setupChildIframe("frame",
"<style>body { margin: 0; } #target { width: 200px; "
"height: 8000px}</style><div id=target></div>");
document().view()->updateAllLifecyclePhases();
frameDocument.view()->setScrollOffset(ScrollOffset(0.0, 1000),
ProgrammaticScroll);
frameDocument.view()->updateAllLifecyclePhases();
Element* target = frameDocument.getElementById("target");
ASSERT_TRUE(target);
FloatPoint mappedPoint =
mapAncestorToLocal(target->layoutObject(), nullptr, FloatPoint(200, 200),
TraverseDocumentBoundaries | UseTransforms);
// Derivation:
// (200, 200) -> (-50, -50) (Adjust for transform origin of scale, which is
// at the center of the 500x500 iframe)
// (-50, -50) -> (-25, -25) (Divide by 2 to invert the scale)
// (-25, -25) -> (225, 225) (Add the origin back in)
// (225, 225) -> (225, 1225) (Adjust by scroll offset of y=1000)
EXPECT_EQ(FloatPoint(225, 1225), mappedPoint);
}
TEST_F(MapCoordinatesTest, FixedPosInScrolledIFrameWithTransform) {
document().setBaseURLOverride(KURL(ParsedURLString, "http://test.com"));
setBodyInnerHTML(
"<style>* { margin: 0; }</style>"
"<div style='position: absolute; left: 0px; top: 0px; width: 1024px; "
"height: 768px; transform-origin: 0 0; transform: scale(0.5, 0.5);'>"
" <iframe id='frame' frameborder=0 src='http://test.com' "
"class='frame' sandbox='allow-same-origin' width='1024' "
"height='768'></iframe>"
"</div>");
Document& frameDocument = setupChildIframe(
"frame",
"<style>* { margin: 0; } #target { width: 200px; height: 200px; "
"position:fixed}</style><div id=target></div>"
"<div style='width: 200; height: 8000px'></div>");
document().view()->updateAllLifecyclePhases();
frameDocument.view()->setScrollOffset(ScrollOffset(0.0, 1000),
ProgrammaticScroll);
Element* target = frameDocument.getElementById("target");
ASSERT_TRUE(target);
FloatPoint mappedPoint =
mapAncestorToLocal(target->layoutObject(), nullptr, FloatPoint(0, 0),
UseTransforms | TraverseDocumentBoundaries);
EXPECT_EQ(FloatPoint(0, 0), mappedPoint);
}
TEST_F(MapCoordinatesTest, MulticolWithText) {
setBodyInnerHTML(
"<div id='multicol' style='columns:2; column-gap:20px; width:400px; "
"line-height:50px; padding:5px; orphans:1; widows:1;'>"
" <br id='sibling'>"
" text"
"</div>");
LayoutObject* target = getLayoutObjectByElementId("sibling")->nextSibling();
ASSERT_TRUE(target->isText());
LayoutBox* flowThread = toLayoutBox(target->parent());
ASSERT_TRUE(flowThread->isLayoutFlowThread());
LayoutBox* multicol = toLayoutBox(getLayoutObjectByElementId("multicol"));
FloatPoint mappedPoint =
mapLocalToAncestor(target, flowThread, FloatPoint(10, 70));
EXPECT_EQ(FloatPoint(10, 70), mappedPoint);
mappedPoint = mapAncestorToLocal(target, flowThread, mappedPoint);
EXPECT_EQ(FloatPoint(10, 70), mappedPoint);
mappedPoint = mapLocalToAncestor(flowThread, multicol, FloatPoint(10, 70));
EXPECT_EQ(FloatPoint(225, 25), mappedPoint);
mappedPoint = mapAncestorToLocal(flowThread, multicol, mappedPoint);
EXPECT_EQ(FloatPoint(10, 70), mappedPoint);
}
TEST_F(MapCoordinatesTest, MulticolWithInline) {
setBodyInnerHTML(
"<div id='multicol' style='columns:2; column-gap:20px; width:400px; "
"line-height:50px; padding:5px; orphans:1; widows:1;'>"
" <span id='target'><br>text</span>"
"</div>");
LayoutObject* target = getLayoutObjectByElementId("target");
LayoutBox* flowThread = toLayoutBox(target->parent());
ASSERT_TRUE(flowThread->isLayoutFlowThread());
LayoutBox* multicol = toLayoutBox(getLayoutObjectByElementId("multicol"));
FloatPoint mappedPoint =
mapLocalToAncestor(target, flowThread, FloatPoint(10, 70));
EXPECT_EQ(FloatPoint(10, 70), mappedPoint);
mappedPoint = mapAncestorToLocal(target, flowThread, mappedPoint);
EXPECT_EQ(FloatPoint(10, 70), mappedPoint);
mappedPoint = mapLocalToAncestor(flowThread, multicol, FloatPoint(10, 70));
EXPECT_EQ(FloatPoint(225, 25), mappedPoint);
mappedPoint = mapAncestorToLocal(flowThread, multicol, mappedPoint);
EXPECT_EQ(FloatPoint(10, 70), mappedPoint);
}
TEST_F(MapCoordinatesTest, MulticolWithBlock) {
setBodyInnerHTML(
"<div id='container' style='-webkit-columns:3; -webkit-column-gap:0; "
"column-fill:auto; width:300px; height:100px; border:8px solid; "
"padding:7px;'>"
" <div style='height:110px;'></div>"
" <div id='target' style='margin:10px; border:13px; "
"padding:13px;'></div>"
"</div>");
LayoutBox* target = toLayoutBox(getLayoutObjectByElementId("target"));
LayoutBox* container = toLayoutBox(getLayoutObjectByElementId("container"));
FloatPoint mappedPoint = mapLocalToAncestor(target, container, FloatPoint());
EXPECT_EQ(FloatPoint(125, 35), mappedPoint);
mappedPoint = mapAncestorToLocal(target, container, mappedPoint);
EXPECT_EQ(FloatPoint(), mappedPoint);
// Walk each ancestor in the chain separately, to verify each step on the way.
LayoutBox* flowThread = target->parentBox();
ASSERT_TRUE(flowThread->isLayoutFlowThread());
mappedPoint = mapLocalToAncestor(target, flowThread, FloatPoint());
EXPECT_EQ(FloatPoint(10, 120), mappedPoint);
mappedPoint = mapAncestorToLocal(target, flowThread, mappedPoint);
EXPECT_EQ(FloatPoint(), mappedPoint);
mappedPoint = mapLocalToAncestor(flowThread, container, FloatPoint(10, 120));
EXPECT_EQ(FloatPoint(125, 35), mappedPoint);
mappedPoint = mapAncestorToLocal(flowThread, container, mappedPoint);
EXPECT_EQ(FloatPoint(10, 120), mappedPoint);
}
TEST_F(MapCoordinatesTest, NestedMulticolWithBlock) {
setBodyInnerHTML(
"<div id='outerMulticol' style='columns:2; column-gap:0; "
"column-fill:auto; width:560px; height:215px; border:8px solid; "
"padding:7px;'>"
" <div style='height:10px;'></div>"
" <div id='innerMulticol' style='columns:2; column-gap:0; border:8px "
"solid; padding:7px;'>"
" <div style='height:630px;'></div>"
" <div id='target' style='width:50px; height:50px;'></div>"
" </div>"
"</div>");
LayoutBox* target = toLayoutBox(getLayoutObjectByElementId("target"));
LayoutBox* outerMulticol =
toLayoutBox(getLayoutObjectByElementId("outerMulticol"));
LayoutBox* innerMulticol =
toLayoutBox(getLayoutObjectByElementId("innerMulticol"));
LayoutBox* innerFlowThread = target->parentBox();
ASSERT_TRUE(innerFlowThread->isLayoutFlowThread());
LayoutBox* outerFlowThread = innerMulticol->parentBox();
ASSERT_TRUE(outerFlowThread->isLayoutFlowThread());
FloatPoint mappedPoint =
mapLocalToAncestor(target, outerMulticol, FloatPoint());
EXPECT_EQ(FloatPoint(435, 115), mappedPoint);
mappedPoint = mapAncestorToLocal(target, outerMulticol, mappedPoint);
EXPECT_EQ(FloatPoint(), mappedPoint);
// Walk each ancestor in the chain separately, to verify each step on the way.
mappedPoint = mapLocalToAncestor(target, innerFlowThread, FloatPoint());
EXPECT_EQ(FloatPoint(0, 630), mappedPoint);
mappedPoint = mapAncestorToLocal(target, innerFlowThread, mappedPoint);
EXPECT_EQ(FloatPoint(), mappedPoint);
mappedPoint =
mapLocalToAncestor(innerFlowThread, innerMulticol, FloatPoint(0, 630));
EXPECT_EQ(FloatPoint(140, 305), mappedPoint);
mappedPoint = mapAncestorToLocal(innerFlowThread, innerMulticol, mappedPoint);
EXPECT_EQ(FloatPoint(0, 630), mappedPoint);
mappedPoint =
mapLocalToAncestor(innerMulticol, outerFlowThread, FloatPoint(140, 305));
EXPECT_EQ(FloatPoint(140, 315), mappedPoint);
mappedPoint = mapAncestorToLocal(innerMulticol, outerFlowThread, mappedPoint);
EXPECT_EQ(FloatPoint(140, 305), mappedPoint);
mappedPoint =
mapLocalToAncestor(outerFlowThread, outerMulticol, FloatPoint(140, 315));
EXPECT_EQ(FloatPoint(435, 115), mappedPoint);
mappedPoint = mapAncestorToLocal(outerFlowThread, outerMulticol, mappedPoint);
EXPECT_EQ(FloatPoint(140, 315), mappedPoint);
}
TEST_F(MapCoordinatesTest, MulticolWithAbsPosInRelPos) {
setBodyInnerHTML(
"<div id='multicol' style='-webkit-columns:3; -webkit-column-gap:0; "
"column-fill:auto; width:300px; height:100px; border:8px solid; "
"padding:7px;'>"
" <div style='height:110px;'></div>"
" <div id='relpos' style='position:relative; left:4px; top:4px;'>"
" <div id='target' style='position:absolute; left:15px; top:15px; "
"margin:10px; border:13px; padding:13px;'></div>"
" </div>"
"</div>");
LayoutBox* target = toLayoutBox(getLayoutObjectByElementId("target"));
LayoutBox* multicol = toLayoutBox(getLayoutObjectByElementId("multicol"));
FloatPoint mappedPoint = mapLocalToAncestor(target, multicol, FloatPoint());
EXPECT_EQ(FloatPoint(144, 54), mappedPoint);
mappedPoint = mapAncestorToLocal(target, multicol, mappedPoint);
EXPECT_EQ(FloatPoint(), mappedPoint);
// Walk each ancestor in the chain separately, to verify each step on the way.
LayoutBox* relpos = toLayoutBox(getLayoutObjectByElementId("relpos"));
LayoutBox* flowThread = relpos->parentBox();
ASSERT_TRUE(flowThread->isLayoutFlowThread());
mappedPoint = mapLocalToAncestor(target, relpos, FloatPoint());
EXPECT_EQ(FloatPoint(25, 25), mappedPoint);
mappedPoint = mapAncestorToLocal(target, relpos, mappedPoint);
EXPECT_EQ(FloatPoint(), mappedPoint);
mappedPoint = mapLocalToAncestor(relpos, flowThread, FloatPoint(25, 25));
EXPECT_EQ(FloatPoint(29, 139), mappedPoint);
mappedPoint = mapAncestorToLocal(relpos, flowThread, mappedPoint);
EXPECT_EQ(FloatPoint(25, 25), mappedPoint);
mappedPoint = mapLocalToAncestor(flowThread, multicol, FloatPoint(29, 139));
EXPECT_EQ(FloatPoint(144, 54), mappedPoint);
mappedPoint = mapAncestorToLocal(flowThread, multicol, mappedPoint);
EXPECT_EQ(FloatPoint(29, 139), mappedPoint);
}
TEST_F(MapCoordinatesTest, MulticolWithAbsPosNotContained) {
setBodyInnerHTML(
"<div id='container' style='position:relative; margin:666px; border:7px "
"solid; padding:3px;'>"
" <div id='multicol' style='-webkit-columns:3; -webkit-column-gap:0; "
"column-fill:auto; width:300px; height:100px; border:8px solid; "
"padding:7px;'>"
" <div style='height:110px;'></div>"
" <div id='target' style='position:absolute; left:-1px; top:-1px; "
"margin:10px; border:13px; padding:13px;'></div>"
" </div>"
"</div>");
LayoutBox* target = toLayoutBox(getLayoutObjectByElementId("target"));
LayoutBox* container = toLayoutBox(getLayoutObjectByElementId("container"));
// The multicol container isn't in the containing block chain of the abspos
// #target.
FloatPoint mappedPoint = mapLocalToAncestor(target, container, FloatPoint());
EXPECT_EQ(FloatPoint(16, 16), mappedPoint);
mappedPoint = mapAncestorToLocal(target, container, mappedPoint);
EXPECT_EQ(FloatPoint(), mappedPoint);
// Walk each ancestor in the chain separately, to verify each step on the way.
LayoutBox* multicol = toLayoutBox(getLayoutObjectByElementId("multicol"));
LayoutBox* flowThread = target->parentBox();
ASSERT_TRUE(flowThread->isLayoutFlowThread());
mappedPoint = mapLocalToAncestor(target, flowThread, FloatPoint());
EXPECT_EQ(FloatPoint(-9, -9), mappedPoint);
mappedPoint = mapLocalToAncestor(flowThread, multicol, mappedPoint);
EXPECT_EQ(FloatPoint(6, 6), mappedPoint);
mappedPoint = mapLocalToAncestor(multicol, container, mappedPoint);
EXPECT_EQ(FloatPoint(16, 16), mappedPoint);
mappedPoint = mapAncestorToLocal(multicol, container, mappedPoint);
EXPECT_EQ(FloatPoint(6, 6), mappedPoint);
mappedPoint = mapAncestorToLocal(flowThread, multicol, mappedPoint);
EXPECT_EQ(FloatPoint(-9, -9), mappedPoint);
mappedPoint = mapAncestorToLocal(target, flowThread, mappedPoint);
EXPECT_EQ(FloatPoint(), mappedPoint);
}
TEST_F(MapCoordinatesTest, FlippedBlocksWritingModeWithText) {
setBodyInnerHTML(
"<div style='-webkit-writing-mode:vertical-rl;'>"
" <div style='width:13px;'></div>"
" <div style='width:200px; height:400px; line-height:50px;'>"
" <br id='sibling'>text"
" </div>"
" <div style='width:5px;'></div>"
"</div>");
LayoutObject* br = getLayoutObjectByElementId("sibling");
LayoutObject* text = br->nextSibling();
ASSERT_TRUE(text->isText());
// Map to the nearest container. Flipping should occur.
FloatPoint mappedPoint = mapLocalToAncestor(
text, text->containingBlock(), FloatPoint(75, 10), ApplyContainerFlip);
EXPECT_EQ(FloatPoint(125, 10), mappedPoint);
mappedPoint = mapAncestorToLocal(text, text->containingBlock(), mappedPoint,
ApplyContainerFlip);
EXPECT_EQ(FloatPoint(75, 10), mappedPoint);
// Map to a container further up in the tree. Flipping should still occur on
// the nearest container. LayoutObject::mapLocalToAncestor() is called
// recursively until the ancestor is reached, and the ApplyContainerFlip flag
// is cleared after having processed the innermost object.
mappedPoint =
mapLocalToAncestor(text, text->containingBlock()->containingBlock(),
FloatPoint(75, 10), ApplyContainerFlip);
EXPECT_EQ(FloatPoint(130, 10), mappedPoint);
mappedPoint =
mapAncestorToLocal(text, text->containingBlock()->containingBlock(),
mappedPoint, ApplyContainerFlip);
EXPECT_EQ(FloatPoint(75, 10), mappedPoint);
// If the ApplyContainerFlip flag isn't specified, no flipping should take
// place.
mappedPoint = mapLocalToAncestor(
text, text->containingBlock()->containingBlock(), FloatPoint(75, 10));
EXPECT_EQ(FloatPoint(80, 10), mappedPoint);
mappedPoint = mapAncestorToLocal(
text, text->containingBlock()->containingBlock(), mappedPoint);
EXPECT_EQ(FloatPoint(75, 10), mappedPoint);
}
TEST_F(MapCoordinatesTest, FlippedBlocksWritingModeWithInline) {
setBodyInnerHTML(
"<div style='-webkit-writing-mode:vertical-rl;'>"
" <div style='width:13px;'></div>"
" <div style='width:200px; height:400px; line-height:50px;'>"
" <span>"
" <span id='target'><br>text</span>"
" </span>"
" </div>"
" <div style='width:7px;'></div>"
"</div>");
LayoutObject* target = getLayoutObjectByElementId("target");
ASSERT_TRUE(target);
// First map to the parent SPAN. Nothing special should happen, since flipping
// occurs at the nearest container.
FloatPoint mappedPoint =
mapLocalToAncestor(target, toLayoutBoxModelObject(target->parent()),
FloatPoint(75, 10), ApplyContainerFlip);
EXPECT_EQ(FloatPoint(75, 10), mappedPoint);
mappedPoint =
mapAncestorToLocal(target, toLayoutBoxModelObject(target->parent()),
mappedPoint, ApplyContainerFlip);
EXPECT_EQ(FloatPoint(75, 10), mappedPoint);
// Continue to the nearest container. Flipping should occur.
mappedPoint = mapLocalToAncestor(toLayoutBoxModelObject(target->parent()),
target->containingBlock(),
FloatPoint(75, 10), ApplyContainerFlip);
EXPECT_EQ(FloatPoint(125, 10), mappedPoint);
mappedPoint = mapAncestorToLocal(toLayoutBoxModelObject(target->parent()),
target->containingBlock(), mappedPoint,
ApplyContainerFlip);
EXPECT_EQ(FloatPoint(75, 10), mappedPoint);
// Now map from the innermost inline to the nearest container in one go.
mappedPoint = mapLocalToAncestor(target, target->containingBlock(),
FloatPoint(75, 10), ApplyContainerFlip);
EXPECT_EQ(FloatPoint(125, 10), mappedPoint);
mappedPoint = mapAncestorToLocal(target, target->containingBlock(),
mappedPoint, ApplyContainerFlip);
EXPECT_EQ(FloatPoint(75, 10), mappedPoint);
// Map to a container further up in the tree. Flipping should still only occur
// on the nearest container.
mappedPoint =
mapLocalToAncestor(target, target->containingBlock()->containingBlock(),
FloatPoint(75, 10), ApplyContainerFlip);
EXPECT_EQ(FloatPoint(132, 10), mappedPoint);
mappedPoint =
mapAncestorToLocal(target, target->containingBlock()->containingBlock(),
mappedPoint, ApplyContainerFlip);
EXPECT_EQ(FloatPoint(75, 10), mappedPoint);
// If the ApplyContainerFlip flag isn't specified, no flipping should take
// place.
mappedPoint = mapLocalToAncestor(
target, target->containingBlock()->containingBlock(), FloatPoint(75, 10));
EXPECT_EQ(FloatPoint(82, 10), mappedPoint);
mappedPoint = mapAncestorToLocal(
target, target->containingBlock()->containingBlock(), mappedPoint);
EXPECT_EQ(FloatPoint(75, 10), mappedPoint);
}
TEST_F(MapCoordinatesTest, FlippedBlocksWritingModeWithBlock) {
setBodyInnerHTML(
"<div id='container' style='-webkit-writing-mode:vertical-rl; border:8px "
"solid; padding:7px; width:200px; height:200px;'>"
" <div id='middle' style='border:1px solid;'>"
" <div style='width:30px;'></div>"
" <div id='target' style='margin:6px; width:25px;'></div>"
" </div>"
"</div>");
LayoutBox* target = toLayoutBox(getLayoutObjectByElementId("target"));
LayoutBox* container = toLayoutBox(getLayoutObjectByElementId("container"));
FloatPoint mappedPoint = mapLocalToAncestor(target, container, FloatPoint());
EXPECT_EQ(FloatPoint(153, 22), mappedPoint);
mappedPoint = mapAncestorToLocal(target, container, mappedPoint);
EXPECT_EQ(FloatPoint(), mappedPoint);
// Walk each ancestor in the chain separately, to verify each step on the way.
LayoutBox* middle = toLayoutBox(getLayoutObjectByElementId("middle"));
mappedPoint = mapLocalToAncestor(target, middle, FloatPoint());
EXPECT_EQ(FloatPoint(7, 7), mappedPoint);
mappedPoint = mapAncestorToLocal(target, middle, mappedPoint);
EXPECT_EQ(FloatPoint(), mappedPoint);
mappedPoint = mapLocalToAncestor(middle, container, FloatPoint(7, 7));
EXPECT_EQ(FloatPoint(153, 22), mappedPoint);
mappedPoint = mapAncestorToLocal(middle, container, mappedPoint);
EXPECT_EQ(FloatPoint(7, 7), mappedPoint);
}
TEST_F(MapCoordinatesTest, Table) {
setBodyInnerHTML(
"<style>td { padding: 2px; }</style>"
"<div id='container' style='border:3px solid;'>"
" <table style='margin:9px; border:5px solid; border-spacing:10px;'>"
" <thead>"
" <tr>"
" <td>"
" <div style='width:100px; height:100px;'></div>"
" </td>"
" </tr>"
" </thead>"
" <tbody>"
" <tr>"
" <td>"
" <div style='width:100px; height:100px;'></div>"
" </td>"
" </tr>"
" <tr>"
" <td>"
" <div style='width:100px; height:100px;'></div>"
" </td>"
" <td>"
" <div id='target' style='width:100px; "
"height:10px;'></div>"
" </td>"
" </tr>"
" </tbody>"
" </table>"
"</div>");
LayoutBox* target = toLayoutBox(getLayoutObjectByElementId("target"));
LayoutBox* container = toLayoutBox(getLayoutObjectByElementId("container"));
FloatPoint mappedPoint = mapLocalToAncestor(target, container, FloatPoint());
EXPECT_EQ(FloatPoint(143, 302), mappedPoint);
mappedPoint = mapAncestorToLocal(target, container, mappedPoint);
EXPECT_EQ(FloatPoint(), mappedPoint);
// Walk each ancestor in the chain separately, to verify each step on the way.
LayoutBox* td = target->parentBox();
ASSERT_TRUE(td->isTableCell());
mappedPoint = mapLocalToAncestor(target, td, FloatPoint());
// Cells are middle-aligned by default.
EXPECT_EQ(FloatPoint(2, 47), mappedPoint);
mappedPoint = mapAncestorToLocal(target, td, mappedPoint);
EXPECT_EQ(FloatPoint(), mappedPoint);
LayoutBox* tr = td->parentBox();
ASSERT_TRUE(tr->isTableRow());
mappedPoint = mapLocalToAncestor(td, tr, FloatPoint(2, 47));
EXPECT_EQ(FloatPoint(126, 47), mappedPoint);
mappedPoint = mapAncestorToLocal(td, tr, mappedPoint);
EXPECT_EQ(FloatPoint(2, 47), mappedPoint);
LayoutBox* tbody = tr->parentBox();
ASSERT_TRUE(tbody->isTableSection());
mappedPoint = mapLocalToAncestor(tr, tbody, FloatPoint(126, 47));
EXPECT_EQ(FloatPoint(126, 161), mappedPoint);
mappedPoint = mapAncestorToLocal(tr, tbody, mappedPoint);
EXPECT_EQ(FloatPoint(126, 47), mappedPoint);
LayoutBox* table = tbody->parentBox();
ASSERT_TRUE(table->isTable());
mappedPoint = mapLocalToAncestor(tbody, table, FloatPoint(126, 161));
EXPECT_EQ(FloatPoint(131, 290), mappedPoint);
mappedPoint = mapAncestorToLocal(tbody, table, mappedPoint);
EXPECT_EQ(FloatPoint(126, 161), mappedPoint);
mappedPoint = mapLocalToAncestor(table, container, FloatPoint(131, 290));
EXPECT_EQ(FloatPoint(143, 302), mappedPoint);
mappedPoint = mapAncestorToLocal(table, container, mappedPoint);
EXPECT_EQ(FloatPoint(131, 290), mappedPoint);
}
static bool floatValuesAlmostEqual(float expected, float actual) {
return fabs(expected - actual) < 0.01;
}
static bool floatQuadsAlmostEqual(const FloatQuad& expected,
const FloatQuad& actual) {
return floatValuesAlmostEqual(expected.p1().x(), actual.p1().x()) &&
floatValuesAlmostEqual(expected.p1().y(), actual.p1().y()) &&
floatValuesAlmostEqual(expected.p2().x(), actual.p2().x()) &&
floatValuesAlmostEqual(expected.p2().y(), actual.p2().y()) &&
floatValuesAlmostEqual(expected.p3().x(), actual.p3().x()) &&
floatValuesAlmostEqual(expected.p3().y(), actual.p3().y()) &&
floatValuesAlmostEqual(expected.p4().x(), actual.p4().x()) &&
floatValuesAlmostEqual(expected.p4().y(), actual.p4().y());
}
// If comparison fails, pretty-print the error using EXPECT_EQ()
#define EXPECT_FLOAT_QUAD_EQ(expected, actual) \
do { \
if (!floatQuadsAlmostEqual(expected, actual)) { \
EXPECT_EQ(expected, actual); \
} \
} while (false)
TEST_F(MapCoordinatesTest, Transforms) {
setBodyInnerHTML(
"<div id='container'>"
" <div id='outerTransform' style='transform:rotate(45deg); "
"width:200px; height:200px;'>"
" <div id='innerTransform' style='transform:rotate(45deg); "
"width:200px; height:200px;'>"
" <div id='target' style='width:200px; height:200px;'></div>"
" </div>"
" </div>"
"</div>");
LayoutBox* target = toLayoutBox(getLayoutObjectByElementId("target"));
LayoutBox* container = toLayoutBox(getLayoutObjectByElementId("container"));
FloatQuad initialQuad(FloatPoint(0, 0), FloatPoint(200, 0),
FloatPoint(200, 200), FloatPoint(0, 200));
FloatQuad mappedQuad =
mapLocalToAncestor(target, container, initialQuad, UseTransforms);
EXPECT_FLOAT_QUAD_EQ(FloatQuad(FloatPoint(200, 0), FloatPoint(200, 200),
FloatPoint(0, 200), FloatPoint(0, 0)),
mappedQuad);
mappedQuad = mapAncestorToLocal(target, container, mappedQuad, UseTransforms);
EXPECT_FLOAT_QUAD_EQ(initialQuad, mappedQuad);
// Walk each ancestor in the chain separately, to verify each step on the way.
LayoutBox* innerTransform =
toLayoutBox(getLayoutObjectByElementId("innerTransform"));
LayoutBox* outerTransform =
toLayoutBox(getLayoutObjectByElementId("outerTransform"));
mappedQuad =
mapLocalToAncestor(target, innerTransform, initialQuad, UseTransforms);
EXPECT_FLOAT_QUAD_EQ(FloatQuad(FloatPoint(0, 0), FloatPoint(200, 0),
FloatPoint(200, 200), FloatPoint(0, 200)),
mappedQuad);
mappedQuad =
mapAncestorToLocal(target, innerTransform, mappedQuad, UseTransforms);
EXPECT_FLOAT_QUAD_EQ(initialQuad, mappedQuad);
initialQuad = FloatQuad(FloatPoint(0, 0), FloatPoint(200, 0),
FloatPoint(200, 200), FloatPoint(0, 200));
mappedQuad = mapLocalToAncestor(innerTransform, outerTransform, initialQuad,
UseTransforms);
// Clockwise rotation by 45 degrees.
EXPECT_FLOAT_QUAD_EQ(
FloatQuad(FloatPoint(100, -41.42), FloatPoint(241.42, 100),
FloatPoint(100, 241.42), FloatPoint(-41.42, 100)),
mappedQuad);
mappedQuad = mapAncestorToLocal(innerTransform, outerTransform, mappedQuad,
UseTransforms);
EXPECT_FLOAT_QUAD_EQ(initialQuad, mappedQuad);
initialQuad = FloatQuad(FloatPoint(100, -41.42), FloatPoint(241.42, 100),
FloatPoint(100, 241.42), FloatPoint(-41.42, 100));
mappedQuad =
mapLocalToAncestor(outerTransform, container, initialQuad, UseTransforms);
// Another clockwise rotation by 45 degrees. So now 90 degrees in total.
EXPECT_FLOAT_QUAD_EQ(FloatQuad(FloatPoint(200, 0), FloatPoint(200, 200),
FloatPoint(0, 200), FloatPoint(0, 0)),
mappedQuad);
mappedQuad =
mapAncestorToLocal(outerTransform, container, mappedQuad, UseTransforms);
EXPECT_FLOAT_QUAD_EQ(initialQuad, mappedQuad);
}
TEST_F(MapCoordinatesTest, SVGShape) {
setBodyInnerHTML(
"<svg id='container'>"
" <g transform='translate(100 200)'>"
" <rect id='target' width='100' height='100'/>"
" </g>"
"</svg>");
LayoutObject* target = getLayoutObjectByElementId("target");
LayoutBox* container = toLayoutBox(getLayoutObjectByElementId("container"));
FloatPoint mappedPoint = mapLocalToAncestor(target, container, FloatPoint());
EXPECT_EQ(FloatPoint(100, 200), mappedPoint);
mappedPoint = mapAncestorToLocal(target, container, mappedPoint);
EXPECT_EQ(FloatPoint(), mappedPoint);
}
TEST_F(MapCoordinatesTest, SVGShapeScale) {
setBodyInnerHTML(
"<svg id='container'>"
" <g transform='scale(2) translate(50 40)'>"
" <rect id='target' transform='translate(50 80)' x='66' y='77' "
"width='100' height='100'/>"
" </g>"
"</svg>");
LayoutObject* target = getLayoutObjectByElementId("target");
LayoutBox* container = toLayoutBox(getLayoutObjectByElementId("container"));
FloatPoint mappedPoint = mapLocalToAncestor(target, container, FloatPoint());
EXPECT_EQ(FloatPoint(200, 240), mappedPoint);
mappedPoint = mapAncestorToLocal(target, container, mappedPoint);
EXPECT_EQ(FloatPoint(), mappedPoint);
}
TEST_F(MapCoordinatesTest, SVGShapeWithViewBoxWithoutScale) {
setBodyInnerHTML(
"<svg id='container' viewBox='0 0 200 200' width='400' height='200'>"
" <g transform='translate(100 50)'>"
" <rect id='target' width='100' height='100'/>"
" </g>"
"</svg>");
LayoutObject* target = getLayoutObjectByElementId("target");
LayoutBox* container = toLayoutBox(getLayoutObjectByElementId("container"));
FloatPoint mappedPoint = mapLocalToAncestor(target, container, FloatPoint());
EXPECT_EQ(FloatPoint(200, 50), mappedPoint);
mappedPoint = mapAncestorToLocal(target, container, mappedPoint);
EXPECT_EQ(FloatPoint(), mappedPoint);
}
TEST_F(MapCoordinatesTest, SVGShapeWithViewBoxWithScale) {
setBodyInnerHTML(
"<svg id='container' viewBox='0 0 100 100' width='400' height='200'>"
" <g transform='translate(50 50)'>"
" <rect id='target' width='100' height='100'/>"
" </g>"
"</svg>");
LayoutObject* target = getLayoutObjectByElementId("target");
LayoutBox* container = toLayoutBox(getLayoutObjectByElementId("container"));
FloatPoint mappedPoint = mapLocalToAncestor(target, container, FloatPoint());
EXPECT_EQ(FloatPoint(200, 100), mappedPoint);
mappedPoint = mapAncestorToLocal(target, container, mappedPoint);
EXPECT_EQ(FloatPoint(), mappedPoint);
}
TEST_F(MapCoordinatesTest, SVGShapeWithViewBoxWithNonZeroOffset) {
setBodyInnerHTML(
"<svg id='container' viewBox='100 100 200 200' width='400' height='200'>"
" <g transform='translate(100 50)'>"
" <rect id='target' transform='translate(100 100)' width='100' "
"height='100'/>"
" </g>"
"</svg>");
LayoutObject* target = getLayoutObjectByElementId("target");
LayoutBox* container = toLayoutBox(getLayoutObjectByElementId("container"));
FloatPoint mappedPoint = mapLocalToAncestor(target, container, FloatPoint());
EXPECT_EQ(FloatPoint(200, 50), mappedPoint);
mappedPoint = mapAncestorToLocal(target, container, mappedPoint);
EXPECT_EQ(FloatPoint(), mappedPoint);
}
TEST_F(MapCoordinatesTest, SVGShapeWithViewBoxWithNonZeroOffsetAndScale) {
setBodyInnerHTML(
"<svg id='container' viewBox='100 100 100 100' width='400' height='200'>"
" <g transform='translate(50 50)'>"
" <rect id='target' transform='translate(100 100)' width='100' "
"height='100'/>"
" </g>"
"</svg>");
LayoutObject* target = getLayoutObjectByElementId("target");
LayoutBox* container = toLayoutBox(getLayoutObjectByElementId("container"));
FloatPoint mappedPoint = mapLocalToAncestor(target, container, FloatPoint());
EXPECT_EQ(FloatPoint(200, 100), mappedPoint);
mappedPoint = mapAncestorToLocal(target, container, mappedPoint);
EXPECT_EQ(FloatPoint(), mappedPoint);
}
TEST_F(MapCoordinatesTest, SVGForeignObject) {
setBodyInnerHTML(
"<svg id='container' viewBox='0 0 100 100' width='400' height='200'>"
" <g transform='translate(50 50)'>"
" <foreignObject transform='translate(-25 -25)'>"
" <div xmlns='http://www.w3.org/1999/xhtml' id='target' "
"style='margin-left: 50px; border: 42px; padding: 84px; width: 50px; "
"height: 50px'>"
" </div>"
" </foreignObject>"
" </g>"
"</svg>");
LayoutObject* target = getLayoutObjectByElementId("target");
LayoutBox* container = toLayoutBox(getLayoutObjectByElementId("container"));
FloatPoint mappedPoint = mapLocalToAncestor(target, container, FloatPoint());
EXPECT_EQ(FloatPoint(250, 50), mappedPoint);
// <svg>
mappedPoint = mapAncestorToLocal(target->parent()->parent()->parent(),
container, FloatPoint(250, 50));
EXPECT_EQ(FloatPoint(250, 50), mappedPoint);
// <g>
mappedPoint = mapAncestorToLocal(target->parent()->parent(), container,
FloatPoint(250, 50));
EXPECT_EQ(FloatPoint(25, -25), mappedPoint);
// <foreignObject>
mappedPoint =
mapAncestorToLocal(target->parent(), container, FloatPoint(250, 50));
EXPECT_EQ(FloatPoint(50, 0), mappedPoint);
// <div>
mappedPoint = mapAncestorToLocal(target, container, FloatPoint(250, 50));
EXPECT_EQ(FloatPoint(), mappedPoint);
}
TEST_F(MapCoordinatesTest, LocalToAbsoluteTransform) {
setBodyInnerHTML(
"<div id='container' style='position: absolute; left: 0; top: 0;'>"
" <div id='scale' style='transform: scale(2.0); transform-origin: left "
"top;'>"
" <div id='child'></div>"
" </div>"
"</div>");
LayoutBoxModelObject* container =
toLayoutBoxModelObject(getLayoutObjectByElementId("container"));
TransformationMatrix containerMatrix = container->localToAbsoluteTransform();
EXPECT_TRUE(containerMatrix.isIdentity());
LayoutObject* child = getLayoutObjectByElementId("child");
TransformationMatrix childMatrix = child->localToAbsoluteTransform();
EXPECT_FALSE(childMatrix.isIdentityOrTranslation());
EXPECT_TRUE(childMatrix.isAffine());
EXPECT_EQ(0.0, childMatrix.projectPoint(FloatPoint(0.0, 0.0)).x());
EXPECT_EQ(0.0, childMatrix.projectPoint(FloatPoint(0.0, 0.0)).y());
EXPECT_EQ(20.0, childMatrix.projectPoint(FloatPoint(10.0, 20.0)).x());
EXPECT_EQ(40.0, childMatrix.projectPoint(FloatPoint(10.0, 20.0)).y());
}
TEST_F(MapCoordinatesTest, LocalToAncestorTransform) {
setBodyInnerHTML(
"<div id='container'>"
" <div id='rotate1' style='transform: rotate(45deg); transform-origin: "
"left top;'>"
" <div id='rotate2' style='transform: rotate(90deg); "
"transform-origin: left top;'>"
" <div id='child'></div>"
" </div>"
" </div>"
"</div>");
LayoutBoxModelObject* container =
toLayoutBoxModelObject(getLayoutObjectByElementId("container"));
LayoutBoxModelObject* rotate1 =
toLayoutBoxModelObject(getLayoutObjectByElementId("rotate1"));
LayoutBoxModelObject* rotate2 =
toLayoutBoxModelObject(getLayoutObjectByElementId("rotate2"));
LayoutObject* child = getLayoutObjectByElementId("child");
TransformationMatrix matrix;
matrix = child->localToAncestorTransform(rotate2);
EXPECT_TRUE(matrix.isIdentity());
// Rotate (100, 0) 90 degrees to (0, 100)
matrix = child->localToAncestorTransform(rotate1);
EXPECT_FALSE(matrix.isIdentity());
EXPECT_TRUE(matrix.isAffine());
EXPECT_NEAR(0.0, matrix.projectPoint(FloatPoint(100.0, 0.0)).x(),
LayoutUnit::epsilon());
EXPECT_NEAR(100.0, matrix.projectPoint(FloatPoint(100.0, 0.0)).y(),
LayoutUnit::epsilon());
// Rotate (100, 0) 135 degrees to (-70.7, 70.7)
matrix = child->localToAncestorTransform(container);
EXPECT_FALSE(matrix.isIdentity());
EXPECT_TRUE(matrix.isAffine());
EXPECT_NEAR(-100.0 * sqrt(2.0) / 2.0,
matrix.projectPoint(FloatPoint(100.0, 0.0)).x(),
LayoutUnit::epsilon());
EXPECT_NEAR(100.0 * sqrt(2.0) / 2.0,
matrix.projectPoint(FloatPoint(100.0, 0.0)).y(),
LayoutUnit::epsilon());
}
TEST_F(MapCoordinatesTest, LocalToAbsoluteTransformFlattens) {
document().frame()->settings()->setAcceleratedCompositingEnabled(true);
setBodyInnerHTML(
"<div style='position: absolute; left: 0; top: 0;'>"
" <div style='transform: rotateY(45deg); "
"-webkit-transform-style:preserve-3d;'>"
" <div style='transform: rotateY(-45deg); "
"-webkit-transform-style:preserve-3d;'>"
" <div id='child1'></div>"
" </div>"
" </div>"
" <div style='transform: rotateY(45deg);'>"
" <div style='transform: rotateY(-45deg);'>"
" <div id='child2'></div>"
" </div>"
" </div>"
"</div>");
LayoutObject* child1 = getLayoutObjectByElementId("child1");
LayoutObject* child2 = getLayoutObjectByElementId("child2");
TransformationMatrix matrix;
matrix = child1->localToAbsoluteTransform();
// With child1, the rotations cancel and points should map basically back to
// themselves.
EXPECT_NEAR(100.0, matrix.projectPoint(FloatPoint(100.0, 50.0)).x(),
LayoutUnit::epsilon());
EXPECT_NEAR(50.0, matrix.projectPoint(FloatPoint(100.0, 50.0)).y(),
LayoutUnit::epsilon());
EXPECT_NEAR(50.0, matrix.projectPoint(FloatPoint(50.0, 100.0)).x(),
LayoutUnit::epsilon());
EXPECT_NEAR(100.0, matrix.projectPoint(FloatPoint(50.0, 100.0)).y(),
LayoutUnit::epsilon());
// With child2, each rotation gets flattened and the end result is
// approximately a 90-degree rotation.
matrix = child2->localToAbsoluteTransform();
EXPECT_NEAR(50.0, matrix.projectPoint(FloatPoint(100.0, 50.0)).x(),
LayoutUnit::epsilon());
EXPECT_NEAR(50.0, matrix.projectPoint(FloatPoint(100.0, 50.0)).y(),
LayoutUnit::epsilon());
EXPECT_NEAR(25.0, matrix.projectPoint(FloatPoint(50.0, 100.0)).x(),
LayoutUnit::epsilon());
EXPECT_NEAR(100.0, matrix.projectPoint(FloatPoint(50.0, 100.0)).y(),
LayoutUnit::epsilon());
}
} // namespace blink