// 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("

text
"); 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("
text
"); 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( "
" "
" "
"); 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( "
" "
" "
" "
"); LayoutObject* target = getLayoutObjectByElementId("target"); LayoutObject* overflow = getLayoutObjectByElementId("overflow"); toLayoutBox(overflow)->scrollToOffset(DoubleSize(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("

text
"); 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("text"); 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( "
" " " " text" " " "
"); 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( "
" "
" "
" "
" "
"); 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( "
" "
" "
" "
" "
" "
"); 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( "
" "
" "
" "
" "
" "
"); 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( "
" "
" "
" "
" "
" "
"); 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( "
" "
" "
" "
" "
" "
"); 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( "
" "
" "
" "
" "
" "
" "
" "
"); 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); } // TODO(chrishtr): add more multi-frame tests. TEST_F(MapCoordinatesTest, FixedPosInScrolledIFrame) { document().setBaseURLOverride(KURL(ParsedURLString, "http://test.com")); setBodyInnerHTML( "" "
" ""); Document& frameDocument = setupChildIframe("frame", "
"); document().view()->setScrollPosition(DoublePoint(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, MulticolWithText) { setBodyInnerHTML( "
" "
" " text" "
"); 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 = mapLocalToAncestor(flowThread, multicol, mappedPoint); EXPECT_EQ(FloatPoint(225, 25), mappedPoint); mappedPoint = mapAncestorToLocal(flowThread, multicol, mappedPoint); EXPECT_EQ(FloatPoint(220, 20), mappedPoint); mappedPoint = mapAncestorToLocal(target, flowThread, mappedPoint); EXPECT_EQ(FloatPoint(10, 70), mappedPoint); } TEST_F(MapCoordinatesTest, MulticolWithInline) { setBodyInnerHTML( "
" "
text
" "
"); 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 = mapLocalToAncestor(flowThread, multicol, mappedPoint); EXPECT_EQ(FloatPoint(225, 25), mappedPoint); mappedPoint = mapAncestorToLocal(flowThread, multicol, mappedPoint); EXPECT_EQ(FloatPoint(220, 20), mappedPoint); mappedPoint = mapAncestorToLocal(target, flowThread, mappedPoint); EXPECT_EQ(FloatPoint(10, 70), mappedPoint); } TEST_F(MapCoordinatesTest, MulticolWithBlock) { setBodyInnerHTML( "
" "
" "
" "
"); 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 = mapLocalToAncestor(flowThread, container, mappedPoint); EXPECT_EQ(FloatPoint(125, 35), mappedPoint); mappedPoint = mapAncestorToLocal(flowThread, container, mappedPoint); EXPECT_EQ(FloatPoint(110, 20), mappedPoint); mappedPoint = mapAncestorToLocal(target, flowThread, mappedPoint); EXPECT_EQ(FloatPoint(), mappedPoint); } TEST_F(MapCoordinatesTest, MulticolWithAbsPosInRelPos) { setBodyInnerHTML( "
" "
" "
" "
" "
" "
"); 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 = mapLocalToAncestor(relpos, flowThread, mappedPoint); EXPECT_EQ(FloatPoint(29, 139), mappedPoint); mappedPoint = mapLocalToAncestor(flowThread, multicol, mappedPoint); EXPECT_EQ(FloatPoint(144, 54), mappedPoint); mappedPoint = mapAncestorToLocal(flowThread, multicol, mappedPoint); EXPECT_EQ(FloatPoint(129, 39), mappedPoint); mappedPoint = mapAncestorToLocal(relpos, flowThread, mappedPoint); EXPECT_EQ(FloatPoint(25, 25), mappedPoint); mappedPoint = mapAncestorToLocal(target, relpos, mappedPoint); EXPECT_EQ(FloatPoint(), mappedPoint); } TEST_F(MapCoordinatesTest, MulticolWithAbsPosNotContained) { setBodyInnerHTML( "
" "
" "
" "
" "
" "
"); 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( "
" "
" "
" "
text" "
" "
" "
"); 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( "
" "
" "
" " " "
text
" "
" "
" "
" "
"); 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( "
" "
" "
" "
" "
" "
"); 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( "" "
" " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " "
" "
" "
" "
" "
" "
" "
" "
" "
" "
"); 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( "
" "
" "
" "
" "
" "
" "
"); 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( "" " " " " " " ""); 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( "" " " " " " " ""); 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( "" " " " " " " ""); 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( "" " " " " " " ""); 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( "" " " " " " " ""); 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( "" " " " " " " ""); 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( "" " " " " "
" "
" "
" "
" "
"); LayoutObject* target = getLayoutObjectByElementId("target"); LayoutBox* container = toLayoutBox(getLayoutObjectByElementId("container")); FloatPoint mappedPoint = mapLocalToAncestor(target, container, FloatPoint()); EXPECT_EQ(FloatPoint(250, 50), mappedPoint); // mappedPoint = mapAncestorToLocal(target->parent()->parent()->parent(), container, FloatPoint(250, 50)); EXPECT_EQ(FloatPoint(250, 50), mappedPoint); // mappedPoint = mapAncestorToLocal(target->parent()->parent(), container, FloatPoint(250, 50)); EXPECT_EQ(FloatPoint(25, -25), mappedPoint); // mappedPoint = mapAncestorToLocal(target->parent(), container, FloatPoint(250, 50)); EXPECT_EQ(FloatPoint(50, 0), mappedPoint); //
mappedPoint = mapAncestorToLocal(target, container, FloatPoint(250, 50)); EXPECT_EQ(FloatPoint(), mappedPoint); } TEST_F(MapCoordinatesTest, LocalToAbsoluteTransform) { setBodyInnerHTML( "
" "
" "
" "
" "
"); 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( "
" "
" "
" "
" "
" "
" "
"); 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( "
" "
" "
" "
" "
" "
" "
" "
" "
" "
" "
" "
"); 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