| // 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 "testing/gtest/include/gtest/gtest.h" |
| #include "third_party/blink/renderer/core/layout/geometry/transform_state.h" |
| #include "third_party/blink/renderer/core/layout/layout_inline.h" |
| #include "third_party/blink/renderer/core/layout/layout_view.h" |
| #include "third_party/blink/renderer/core/paint/paint_layer_scrollable_area.h" |
| #include "third_party/blink/renderer/core/testing/core_unit_test_helper.h" |
| |
| namespace blink { |
| |
| class MapCoordinatesTest : public RenderingTest { |
| public: |
| MapCoordinatesTest() |
| : RenderingTest(MakeGarbageCollected<SingleChildLocalFrameClient>()) {} |
| |
| void SetUp() override { |
| // This is required to test 3d transforms. |
| EnableCompositing(); |
| RenderingTest::SetUp(); |
| } |
| |
| PhysicalOffset MapLocalToAncestor(const LayoutObject*, |
| const LayoutBoxModelObject* ancestor, |
| PhysicalOffset, |
| MapCoordinatesFlags = 0) const; |
| FloatQuad MapLocalToAncestor(const LayoutObject*, |
| const LayoutBoxModelObject* ancestor, |
| FloatQuad, |
| MapCoordinatesFlags = 0) const; |
| PhysicalOffset MapAncestorToLocal(const LayoutObject*, |
| const LayoutBoxModelObject* ancestor, |
| PhysicalOffset, |
| MapCoordinatesFlags = 0) const; |
| FloatQuad MapAncestorToLocal(const LayoutObject*, |
| const LayoutBoxModelObject* ancestor, |
| FloatQuad, |
| MapCoordinatesFlags = 0) const; |
| |
| // Adjust point by the scroll offset of the LayoutView. This only has an |
| // effect if root layer scrolling is enabled. The only reason for doing |
| // this here is so the test expected values can be the same whether or not |
| // root layer scrolling is enabled. This is analogous to what |
| // LayoutGeometryMapTest does; for more context, see: |
| // https://codereview.chromium.org/2417103002/#msg11 |
| PhysicalOffset AdjustForFrameScroll(const PhysicalOffset&) 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. |
| |
| PhysicalOffset MapCoordinatesTest::AdjustForFrameScroll( |
| const PhysicalOffset& point) const { |
| PhysicalOffset result(point); |
| LayoutView* layout_view = GetDocument().GetLayoutView(); |
| if (layout_view->HasNonVisibleOverflow()) |
| result -= PhysicalOffset(layout_view->ScrolledContentOffset()); |
| return result; |
| } |
| |
| PhysicalOffset MapCoordinatesTest::MapLocalToAncestor( |
| const LayoutObject* object, |
| const LayoutBoxModelObject* ancestor, |
| PhysicalOffset point, |
| MapCoordinatesFlags mode) const { |
| return object->LocalToAncestorPoint(point, ancestor, mode); |
| } |
| |
| FloatQuad MapCoordinatesTest::MapLocalToAncestor( |
| const LayoutObject* object, |
| const LayoutBoxModelObject* ancestor, |
| FloatQuad quad, |
| MapCoordinatesFlags mode) const { |
| return object->LocalToAncestorQuad(quad, ancestor, mode); |
| } |
| |
| PhysicalOffset MapCoordinatesTest::MapAncestorToLocal( |
| const LayoutObject* object, |
| const LayoutBoxModelObject* ancestor, |
| PhysicalOffset point, |
| MapCoordinatesFlags mode) const { |
| return object->AncestorToLocalPoint(ancestor, point, mode); |
| } |
| |
| FloatQuad MapCoordinatesTest::MapAncestorToLocal( |
| const LayoutObject* object, |
| const LayoutBoxModelObject* ancestor, |
| FloatQuad quad, |
| MapCoordinatesFlags mode) const { |
| return object->AncestorToLocalQuad(ancestor, quad, mode); |
| } |
| |
| TEST_F(MapCoordinatesTest, SimpleText) { |
| SetBodyInnerHTML("<div id='container'><br>text</div>"); |
| |
| LayoutBox* container = ToLayoutBox(GetLayoutObjectByElementId("container")); |
| LayoutObject* text = To<LayoutBlockFlow>(container)->LastChild(); |
| ASSERT_TRUE(text->IsText()); |
| PhysicalOffset mapped_point = |
| MapLocalToAncestor(text, container, PhysicalOffset(10, 30)); |
| EXPECT_EQ(PhysicalOffset(10, 30), mapped_point); |
| mapped_point = MapAncestorToLocal(text, container, mapped_point); |
| EXPECT_EQ(PhysicalOffset(10, 30), mapped_point); |
| } |
| |
| TEST_F(MapCoordinatesTest, SimpleInline) { |
| SetBodyInnerHTML("<div><span id='target'>text</span></div>"); |
| |
| LayoutObject* target = GetLayoutObjectByElementId("target"); |
| PhysicalOffset mapped_point = MapLocalToAncestor( |
| target, ToLayoutBoxModelObject(target->Parent()), PhysicalOffset(10, 10)); |
| EXPECT_EQ(PhysicalOffset(10, 10), mapped_point); |
| mapped_point = MapAncestorToLocal( |
| target, ToLayoutBoxModelObject(target->Parent()), mapped_point); |
| EXPECT_EQ(PhysicalOffset(10, 10), mapped_point); |
| } |
| |
| TEST_F(MapCoordinatesTest, SimpleBlock) { |
| SetBodyInnerHTML(R"HTML( |
| <div style='margin:666px; border:8px solid; padding:7px;'> |
| <div id='target' style='margin:10px; border:666px; |
| padding:666px;'></div> |
| </div> |
| )HTML"); |
| |
| LayoutObject* target = GetLayoutObjectByElementId("target"); |
| PhysicalOffset mapped_point = |
| MapLocalToAncestor(target, ToLayoutBoxModelObject(target->Parent()), |
| PhysicalOffset(100, 100)); |
| EXPECT_EQ(PhysicalOffset(125, 125), mapped_point); |
| mapped_point = MapAncestorToLocal( |
| target, ToLayoutBoxModelObject(target->Parent()), mapped_point); |
| EXPECT_EQ(PhysicalOffset(100, 100), mapped_point); |
| } |
| |
| TEST_F(MapCoordinatesTest, OverflowClip) { |
| SetBodyInnerHTML(R"HTML( |
| <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> |
| )HTML"); |
| |
| LayoutObject* target = GetLayoutObjectByElementId("target"); |
| LayoutObject* overflow = GetLayoutObjectByElementId("overflow"); |
| To<Element>(overflow->GetNode()) |
| ->GetLayoutBoxForScrolling() |
| ->GetScrollableArea() |
| ->ScrollToAbsolutePosition(FloatPoint(32, 54)); |
| |
| PhysicalOffset mapped_point = |
| MapLocalToAncestor(target, ToLayoutBoxModelObject(target->Parent()), |
| PhysicalOffset(100, 100)); |
| EXPECT_EQ(PhysicalOffset(93, 271), mapped_point); |
| mapped_point = MapAncestorToLocal( |
| target, ToLayoutBoxModelObject(target->Parent()), mapped_point); |
| EXPECT_EQ(PhysicalOffset(100, 100), mapped_point); |
| } |
| |
| 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()); |
| PhysicalOffset mapped_point = |
| MapLocalToAncestor(text, text->ContainingBlock(), PhysicalOffset(10, 30)); |
| if (RuntimeEnabledFeatures::LayoutNGEnabled()) |
| EXPECT_EQ(PhysicalOffset(10, 30), mapped_point); |
| else |
| EXPECT_EQ(PhysicalOffset(17, 34), mapped_point); |
| mapped_point = |
| MapAncestorToLocal(text, text->ContainingBlock(), mapped_point); |
| EXPECT_EQ(PhysicalOffset(10, 30), mapped_point); |
| } |
| |
| TEST_F(MapCoordinatesTest, RelposInline) { |
| SetBodyInnerHTML( |
| "<span id='target' style='position:relative; left:50px; " |
| "top:100px;'>text</span>"); |
| |
| LayoutObject* target = GetLayoutObjectByElementId("target"); |
| PhysicalOffset mapped_point = MapLocalToAncestor( |
| target, ToLayoutBoxModelObject(target->Parent()), PhysicalOffset(10, 10)); |
| if (RuntimeEnabledFeatures::LayoutNGEnabled()) |
| EXPECT_EQ(PhysicalOffset(10, 10), mapped_point); |
| else |
| EXPECT_EQ(PhysicalOffset(60, 110), mapped_point); |
| mapped_point = MapAncestorToLocal( |
| target, ToLayoutBoxModelObject(target->Parent()), mapped_point); |
| EXPECT_EQ(PhysicalOffset(10, 10), mapped_point); |
| } |
| |
| TEST_F(MapCoordinatesTest, RelposInlineInRelposInline) { |
| SetBodyInnerHTML(R"HTML( |
| <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> |
| )HTML"); |
| |
| LayoutObject* target = GetLayoutObjectByElementId("target"); |
| LayoutInline* parent = ToLayoutInline(target->Parent()); |
| auto* containing_block = To<LayoutBlockFlow>(parent->Parent()); |
| |
| PhysicalOffset mapped_point = |
| MapLocalToAncestor(target, containing_block, PhysicalOffset(20, 10)); |
| if (RuntimeEnabledFeatures::LayoutNGEnabled()) |
| EXPECT_EQ(PhysicalOffset(20, 10), mapped_point); |
| else |
| EXPECT_EQ(PhysicalOffset(75, 116), mapped_point); |
| mapped_point = MapAncestorToLocal(target, containing_block, mapped_point); |
| EXPECT_EQ(PhysicalOffset(20, 10), mapped_point); |
| |
| // Walk each ancestor in the chain separately, to verify each step on the way. |
| mapped_point = MapLocalToAncestor(target, parent, PhysicalOffset(20, 10)); |
| if (RuntimeEnabledFeatures::LayoutNGEnabled()) |
| EXPECT_EQ(PhysicalOffset(20, 10), mapped_point); |
| else |
| EXPECT_EQ(PhysicalOffset(70, 110), mapped_point); |
| |
| mapped_point = MapLocalToAncestor(parent, containing_block, mapped_point); |
| if (RuntimeEnabledFeatures::LayoutNGEnabled()) |
| EXPECT_EQ(PhysicalOffset(20, 10), mapped_point); |
| else |
| EXPECT_EQ(PhysicalOffset(75, 116), mapped_point); |
| |
| mapped_point = MapAncestorToLocal(parent, containing_block, mapped_point); |
| if (RuntimeEnabledFeatures::LayoutNGEnabled()) |
| EXPECT_EQ(PhysicalOffset(20, 10), mapped_point); |
| else |
| EXPECT_EQ(PhysicalOffset(70, 110), mapped_point); |
| |
| mapped_point = MapAncestorToLocal(target, parent, mapped_point); |
| if (RuntimeEnabledFeatures::LayoutNGEnabled()) |
| EXPECT_EQ(PhysicalOffset(20, 10), mapped_point); |
| else |
| EXPECT_EQ(PhysicalOffset(20, 10), mapped_point); |
| } |
| |
| TEST_F(MapCoordinatesTest, RelPosBlock) { |
| SetBodyInnerHTML(R"HTML( |
| <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> |
| )HTML"); |
| |
| LayoutBox* target = ToLayoutBox(GetLayoutObjectByElementId("target")); |
| LayoutBox* container = ToLayoutBox(GetLayoutObjectByElementId("container")); |
| |
| PhysicalOffset mapped_point = |
| MapLocalToAncestor(target, container, PhysicalOffset()); |
| EXPECT_EQ(PhysicalOffset(106, 106), mapped_point); |
| mapped_point = |
| MapAncestorToLocal(target, container, PhysicalOffset(110, 110)); |
| EXPECT_EQ(PhysicalOffset(4, 4), mapped_point); |
| |
| // Walk each ancestor in the chain separately, to verify each step on the way. |
| LayoutBox* middle = ToLayoutBox(GetLayoutObjectByElementId("middle")); |
| |
| mapped_point = MapLocalToAncestor(target, middle, PhysicalOffset()); |
| EXPECT_EQ(PhysicalOffset(61, 61), mapped_point); |
| |
| mapped_point = MapLocalToAncestor(middle, container, mapped_point); |
| EXPECT_EQ(PhysicalOffset(106, 106), mapped_point); |
| |
| mapped_point = MapAncestorToLocal(middle, container, mapped_point); |
| EXPECT_EQ(PhysicalOffset(61, 61), mapped_point); |
| |
| mapped_point = MapAncestorToLocal(target, middle, mapped_point); |
| EXPECT_EQ(PhysicalOffset(), mapped_point); |
| } |
| |
| TEST_F(MapCoordinatesTest, AbsPos) { |
| SetBodyInnerHTML(R"HTML( |
| <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> |
| )HTML"); |
| |
| LayoutBox* target = ToLayoutBox(GetLayoutObjectByElementId("target")); |
| LayoutBox* container = ToLayoutBox(GetLayoutObjectByElementId("container")); |
| |
| PhysicalOffset mapped_point = |
| MapLocalToAncestor(target, container, PhysicalOffset()); |
| EXPECT_EQ(PhysicalOffset(17, 17), mapped_point); |
| mapped_point = MapAncestorToLocal(target, container, PhysicalOffset(18, 18)); |
| EXPECT_EQ(PhysicalOffset(1, 1), mapped_point); |
| |
| // Walk each ancestor in the chain separately, to verify each step on the way. |
| LayoutBox* static_child = |
| ToLayoutBox(GetLayoutObjectByElementId("staticChild")); |
| |
| mapped_point = MapLocalToAncestor(target, static_child, PhysicalOffset()); |
| EXPECT_EQ(PhysicalOffset(-28, -28), mapped_point); |
| |
| mapped_point = MapLocalToAncestor(static_child, container, mapped_point); |
| EXPECT_EQ(PhysicalOffset(17, 17), mapped_point); |
| |
| mapped_point = MapAncestorToLocal(static_child, container, mapped_point); |
| EXPECT_EQ(PhysicalOffset(-28, -28), mapped_point); |
| |
| mapped_point = MapAncestorToLocal(target, static_child, mapped_point); |
| EXPECT_EQ(PhysicalOffset(), mapped_point); |
| } |
| |
| TEST_F(MapCoordinatesTest, AbsPosAuto) { |
| SetBodyInnerHTML(R"HTML( |
| <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> |
| )HTML"); |
| |
| LayoutBox* target = ToLayoutBox(GetLayoutObjectByElementId("target")); |
| LayoutBox* container = ToLayoutBox(GetLayoutObjectByElementId("container")); |
| |
| PhysicalOffset mapped_point = |
| MapLocalToAncestor(target, container, PhysicalOffset()); |
| EXPECT_EQ(PhysicalOffset(55, 80), mapped_point); |
| mapped_point = MapAncestorToLocal(target, container, PhysicalOffset(56, 82)); |
| EXPECT_EQ(PhysicalOffset(1, 2), mapped_point); |
| |
| // Walk each ancestor in the chain separately, to verify each step on the way. |
| LayoutBox* static_child = |
| ToLayoutBox(GetLayoutObjectByElementId("staticChild")); |
| |
| mapped_point = MapLocalToAncestor(target, static_child, PhysicalOffset()); |
| EXPECT_EQ(PhysicalOffset(10, 35), mapped_point); |
| |
| mapped_point = MapLocalToAncestor(static_child, container, mapped_point); |
| EXPECT_EQ(PhysicalOffset(55, 80), mapped_point); |
| |
| mapped_point = MapAncestorToLocal(static_child, container, mapped_point); |
| EXPECT_EQ(PhysicalOffset(10, 35), mapped_point); |
| |
| mapped_point = MapAncestorToLocal(target, static_child, mapped_point); |
| EXPECT_EQ(PhysicalOffset(), mapped_point); |
| } |
| |
| TEST_F(MapCoordinatesTest, FixedPos) { |
| // Assuming BODY margin of 8px. |
| SetBodyInnerHTML(R"HTML( |
| <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> |
| )HTML"); |
| |
| LayoutBox* target = ToLayoutBox(GetLayoutObjectByElementId("target")); |
| LayoutBox* static_child = |
| ToLayoutBox(GetLayoutObjectByElementId("staticChild")); |
| LayoutBox* container = ToLayoutBox(GetLayoutObjectByElementId("container")); |
| LayoutBox* body = container->ParentBox(); |
| LayoutBox* html = body->ParentBox(); |
| LayoutBox* view = html->ParentBox(); |
| ASSERT_TRUE(IsA<LayoutView>(view)); |
| |
| PhysicalOffset mapped_point = |
| MapLocalToAncestor(target, view, PhysicalOffset()); |
| EXPECT_EQ(PhysicalOffset(9, 9), mapped_point); |
| mapped_point = MapAncestorToLocal(target, view, PhysicalOffset(10, 11)); |
| EXPECT_EQ(PhysicalOffset(1, 2), mapped_point); |
| |
| // Walk each ancestor in the chain separately, to verify each step on the way. |
| mapped_point = MapLocalToAncestor(target, static_child, PhysicalOffset()); |
| EXPECT_EQ(PhysicalOffset(-15, -15), mapped_point); |
| |
| mapped_point = MapLocalToAncestor(static_child, container, mapped_point); |
| EXPECT_EQ(PhysicalOffset(-3, -3), mapped_point); |
| |
| mapped_point = MapLocalToAncestor(container, body, mapped_point); |
| EXPECT_EQ(PhysicalOffset(1, 1), mapped_point); |
| |
| mapped_point = MapLocalToAncestor(body, html, mapped_point); |
| EXPECT_EQ(PhysicalOffset(9, 9), mapped_point); |
| |
| mapped_point = MapLocalToAncestor(html, view, mapped_point); |
| EXPECT_EQ(PhysicalOffset(9, 9), mapped_point); |
| |
| mapped_point = MapAncestorToLocal(html, view, mapped_point); |
| EXPECT_EQ(PhysicalOffset(9, 9), mapped_point); |
| |
| mapped_point = MapAncestorToLocal(body, html, mapped_point); |
| EXPECT_EQ(PhysicalOffset(1, 1), mapped_point); |
| |
| mapped_point = MapAncestorToLocal(container, body, mapped_point); |
| EXPECT_EQ(PhysicalOffset(-3, -3), mapped_point); |
| |
| mapped_point = MapAncestorToLocal(static_child, container, mapped_point); |
| EXPECT_EQ(PhysicalOffset(-15, -15), mapped_point); |
| |
| mapped_point = MapAncestorToLocal(target, static_child, mapped_point); |
| EXPECT_EQ(PhysicalOffset(), mapped_point); |
| } |
| |
| TEST_F(MapCoordinatesTest, FixedPosAuto) { |
| // Assuming BODY margin of 8px. |
| SetBodyInnerHTML(R"HTML( |
| <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> |
| )HTML"); |
| |
| LayoutBox* target = ToLayoutBox(GetLayoutObjectByElementId("target")); |
| LayoutBox* static_child = |
| ToLayoutBox(GetLayoutObjectByElementId("staticChild")); |
| LayoutBox* container = ToLayoutBox(GetLayoutObjectByElementId("container")); |
| LayoutBox* body = container->ParentBox(); |
| LayoutBox* html = body->ParentBox(); |
| LayoutBox* view = html->ParentBox(); |
| ASSERT_TRUE(IsA<LayoutView>(view)); |
| |
| PhysicalOffset mapped_point = |
| MapLocalToAncestor(target, target->ContainingBlock(), PhysicalOffset()); |
| EXPECT_EQ(PhysicalOffset(36, 61), mapped_point); |
| mapped_point = MapAncestorToLocal(target, target->ContainingBlock(), |
| PhysicalOffset(36, 61)); |
| EXPECT_EQ(PhysicalOffset(), mapped_point); |
| |
| // Walk each ancestor in the chain separately, to verify each step on the way. |
| mapped_point = MapLocalToAncestor(target, static_child, PhysicalOffset()); |
| EXPECT_EQ(PhysicalOffset(10, 35), mapped_point); |
| |
| mapped_point = MapLocalToAncestor(static_child, container, mapped_point); |
| EXPECT_EQ(PhysicalOffset(25, 50), mapped_point); |
| |
| mapped_point = MapLocalToAncestor(container, body, mapped_point); |
| EXPECT_EQ(PhysicalOffset(28, 53), mapped_point); |
| |
| mapped_point = MapLocalToAncestor(body, html, mapped_point); |
| EXPECT_EQ(PhysicalOffset(36, 61), mapped_point); |
| |
| mapped_point = MapLocalToAncestor(html, view, mapped_point); |
| EXPECT_EQ(PhysicalOffset(36, 61), mapped_point); |
| |
| mapped_point = MapAncestorToLocal(html, view, mapped_point); |
| EXPECT_EQ(PhysicalOffset(36, 61), mapped_point); |
| |
| mapped_point = MapAncestorToLocal(body, html, mapped_point); |
| EXPECT_EQ(PhysicalOffset(28, 53), mapped_point); |
| |
| mapped_point = MapAncestorToLocal(container, body, mapped_point); |
| EXPECT_EQ(PhysicalOffset(25, 50), mapped_point); |
| |
| mapped_point = MapAncestorToLocal(static_child, container, mapped_point); |
| EXPECT_EQ(PhysicalOffset(10, 35), mapped_point); |
| |
| mapped_point = MapAncestorToLocal(target, static_child, mapped_point); |
| EXPECT_EQ(PhysicalOffset(), mapped_point); |
| } |
| |
| TEST_F(MapCoordinatesTest, FixedPosInFixedPos) { |
| // Assuming BODY margin of 8px. |
| SetBodyInnerHTML(R"HTML( |
| <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> |
| )HTML"); |
| |
| LayoutBox* target = ToLayoutBox(GetLayoutObjectByElementId("target")); |
| LayoutBox* outer_fixed = |
| ToLayoutBox(GetLayoutObjectByElementId("outerFixed")); |
| LayoutBox* static_child = |
| ToLayoutBox(GetLayoutObjectByElementId("staticChild")); |
| LayoutBox* container = ToLayoutBox(GetLayoutObjectByElementId("container")); |
| LayoutBox* body = container->ParentBox(); |
| LayoutBox* html = body->ParentBox(); |
| LayoutBox* view = html->ParentBox(); |
| ASSERT_TRUE(IsA<LayoutView>(view)); |
| |
| PhysicalOffset mapped_point = |
| MapLocalToAncestor(target, view, PhysicalOffset()); |
| EXPECT_EQ(PhysicalOffset(9, 9), mapped_point); |
| mapped_point = MapAncestorToLocal(target, view, PhysicalOffset(9, 9)); |
| EXPECT_EQ(PhysicalOffset(), mapped_point); |
| |
| // Walk each ancestor in the chain separately, to verify each step on the way. |
| mapped_point = MapLocalToAncestor(target, outer_fixed, PhysicalOffset()); |
| EXPECT_EQ(PhysicalOffset(-101, -101), mapped_point); |
| |
| mapped_point = MapLocalToAncestor(outer_fixed, static_child, mapped_point); |
| EXPECT_EQ(PhysicalOffset(-15, -15), mapped_point); |
| |
| mapped_point = MapLocalToAncestor(static_child, container, mapped_point); |
| EXPECT_EQ(PhysicalOffset(-3, -3), mapped_point); |
| |
| mapped_point = MapLocalToAncestor(container, body, mapped_point); |
| EXPECT_EQ(PhysicalOffset(1, 1), mapped_point); |
| |
| mapped_point = MapLocalToAncestor(body, html, mapped_point); |
| EXPECT_EQ(PhysicalOffset(9, 9), mapped_point); |
| |
| mapped_point = MapLocalToAncestor(html, view, mapped_point); |
| EXPECT_EQ(PhysicalOffset(9, 9), mapped_point); |
| |
| mapped_point = MapAncestorToLocal(html, view, mapped_point); |
| EXPECT_EQ(PhysicalOffset(9, 9), mapped_point); |
| |
| mapped_point = MapAncestorToLocal(body, html, mapped_point); |
| EXPECT_EQ(PhysicalOffset(1, 1), mapped_point); |
| |
| mapped_point = MapAncestorToLocal(container, body, mapped_point); |
| EXPECT_EQ(PhysicalOffset(-3, -3), mapped_point); |
| |
| mapped_point = MapAncestorToLocal(static_child, container, mapped_point); |
| EXPECT_EQ(PhysicalOffset(-15, -15), mapped_point); |
| |
| mapped_point = MapAncestorToLocal(outer_fixed, static_child, mapped_point); |
| EXPECT_EQ(PhysicalOffset(-101, -101), mapped_point); |
| |
| mapped_point = MapAncestorToLocal(target, outer_fixed, mapped_point); |
| EXPECT_EQ(PhysicalOffset(), mapped_point); |
| } |
| |
| TEST_F(MapCoordinatesTest, FixedPosInFixedPosScrollView) { |
| SetBodyInnerHTML(R"HTML( |
| <div style='height: 4000px'></div> |
| <div id='container' style='position:fixed; top: 100px; left: 100px'> |
| <div id='target' style='position:fixed; top: 200px; left: 200px'> |
| </div> |
| </div> |
| )HTML"); |
| |
| LayoutBox* target = ToLayoutBox(GetLayoutObjectByElementId("target")); |
| LayoutBox* container = ToLayoutBox(GetLayoutObjectByElementId("container")); |
| LayoutBox* body = container->ParentBox(); |
| LayoutBox* html = body->ParentBox(); |
| LayoutBox* view = html->ParentBox(); |
| ASSERT_TRUE(IsA<LayoutView>(view)); |
| |
| GetDocument().View()->LayoutViewport()->SetScrollOffset( |
| ScrollOffset(0.0, 50), mojom::blink::ScrollType::kProgrammatic); |
| UpdateAllLifecyclePhasesForTest(); |
| EXPECT_EQ(50, |
| GetDocument().View()->LayoutViewport()->ScrollOffsetInt().Height()); |
| |
| PhysicalOffset mapped_point = |
| MapLocalToAncestor(target, view, PhysicalOffset()); |
| EXPECT_EQ(AdjustForFrameScroll(PhysicalOffset(200, 250)), mapped_point); |
| mapped_point = MapAncestorToLocal(target, view, mapped_point); |
| EXPECT_EQ(PhysicalOffset(), mapped_point); |
| |
| mapped_point = MapLocalToAncestor(target, container, PhysicalOffset()); |
| EXPECT_EQ(PhysicalOffset(100, 100), mapped_point); |
| mapped_point = |
| MapAncestorToLocal(target, container, PhysicalOffset(100, 100)); |
| EXPECT_EQ(PhysicalOffset(), mapped_point); |
| } |
| |
| TEST_F(MapCoordinatesTest, FixedPosInAbsolutePosScrollView) { |
| SetBodyInnerHTML(R"HTML( |
| <div style='height: 4000px'></div> |
| <div id='container' style='position:absolute; top: 100px; left: 100px'> |
| <div id='target' style='position:fixed; top: 200px; left: 200px'> |
| </div> |
| </div> |
| )HTML"); |
| |
| LayoutBox* target = ToLayoutBox(GetLayoutObjectByElementId("target")); |
| LayoutBox* container = ToLayoutBox(GetLayoutObjectByElementId("container")); |
| LayoutBox* body = container->ParentBox(); |
| LayoutBox* html = body->ParentBox(); |
| LayoutBox* view = html->ParentBox(); |
| ASSERT_TRUE(IsA<LayoutView>(view)); |
| |
| GetDocument().View()->LayoutViewport()->SetScrollOffset( |
| ScrollOffset(0.0, 50), mojom::blink::ScrollType::kProgrammatic); |
| UpdateAllLifecyclePhasesForTest(); |
| EXPECT_EQ(50, |
| GetDocument().View()->LayoutViewport()->ScrollOffsetInt().Height()); |
| |
| PhysicalOffset mapped_point = |
| MapLocalToAncestor(target, view, PhysicalOffset()); |
| EXPECT_EQ(AdjustForFrameScroll(PhysicalOffset(200, 250)), mapped_point); |
| mapped_point = MapAncestorToLocal(target, view, mapped_point); |
| EXPECT_EQ(PhysicalOffset(), mapped_point); |
| |
| mapped_point = MapLocalToAncestor(target, container, PhysicalOffset()); |
| EXPECT_EQ(PhysicalOffset(100, 150), mapped_point); |
| mapped_point = |
| MapAncestorToLocal(target, container, PhysicalOffset(100, 150)); |
| EXPECT_EQ(PhysicalOffset(), mapped_point); |
| } |
| |
| TEST_F(MapCoordinatesTest, FixedPosInTransform) { |
| SetBodyInnerHTML(R"HTML( |
| <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> |
| )HTML"); |
| |
| GetDocument().View()->LayoutViewport()->SetScrollOffset( |
| ScrollOffset(0.0, 50), mojom::blink::ScrollType::kProgrammatic); |
| UpdateAllLifecyclePhasesForTest(); |
| EXPECT_EQ(50, |
| GetDocument().View()->LayoutViewport()->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(IsA<LayoutView>(view)); |
| |
| PhysicalOffset mapped_point = |
| MapLocalToAncestor(target, view, PhysicalOffset()); |
| EXPECT_EQ(AdjustForFrameScroll(PhysicalOffset(0, 200)), mapped_point); |
| mapped_point = MapAncestorToLocal(target, view, mapped_point); |
| EXPECT_EQ(PhysicalOffset(), mapped_point); |
| |
| mapped_point = MapLocalToAncestor(target, container, PhysicalOffset()); |
| EXPECT_EQ(PhysicalOffset(), mapped_point); |
| mapped_point = MapAncestorToLocal(target, container, PhysicalOffset(0, 0)); |
| EXPECT_EQ(PhysicalOffset(), mapped_point); |
| |
| mapped_point = MapLocalToAncestor(container, view, PhysicalOffset()); |
| EXPECT_EQ(AdjustForFrameScroll(PhysicalOffset(0, 200)), mapped_point); |
| mapped_point = MapAncestorToLocal(container, view, mapped_point); |
| EXPECT_EQ(PhysicalOffset(), mapped_point); |
| } |
| |
| TEST_F(MapCoordinatesTest, FixedPosInContainPaint) { |
| SetBodyInnerHTML(R"HTML( |
| <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> |
| )HTML"); |
| |
| GetDocument().View()->LayoutViewport()->SetScrollOffset( |
| ScrollOffset(0.0, 50), mojom::blink::ScrollType::kProgrammatic); |
| UpdateAllLifecyclePhasesForTest(); |
| EXPECT_EQ(50, |
| GetDocument().View()->LayoutViewport()->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(IsA<LayoutView>(view)); |
| |
| PhysicalOffset mapped_point = |
| MapLocalToAncestor(target, view, PhysicalOffset()); |
| EXPECT_EQ(AdjustForFrameScroll(PhysicalOffset(0, 100)), mapped_point); |
| mapped_point = MapAncestorToLocal(target, view, mapped_point); |
| EXPECT_EQ(PhysicalOffset(), mapped_point); |
| |
| mapped_point = MapLocalToAncestor(target, container, PhysicalOffset()); |
| EXPECT_EQ(PhysicalOffset(0, 0), mapped_point); |
| mapped_point = MapAncestorToLocal(target, container, PhysicalOffset(0, 0)); |
| EXPECT_EQ(PhysicalOffset(), mapped_point); |
| |
| mapped_point = MapLocalToAncestor(container, view, PhysicalOffset()); |
| EXPECT_EQ(AdjustForFrameScroll(PhysicalOffset(0, 100)), mapped_point); |
| mapped_point = MapAncestorToLocal(container, view, mapped_point); |
| EXPECT_EQ(PhysicalOffset(), mapped_point); |
| } |
| |
| // TODO(chrishtr): add more multi-frame tests. |
| TEST_F(MapCoordinatesTest, FixedPosInIFrameWhenMainFrameScrolled) { |
| GetDocument().SetBaseURLOverride(KURL("http://test.com")); |
| SetBodyInnerHTML(R"HTML( |
| <style>body { margin: 0; }</style> |
| <div style='width: 200; height: 8000px'></div> |
| <iframe src='http://test.com' width='500' height='500' |
| frameBorder='0'> |
| </iframe> |
| )HTML"); |
| SetChildFrameHTML( |
| "<style>body { margin: 0; } #target { width: 200px; height: 200px; " |
| "position:fixed}</style><div id=target></div>"); |
| |
| GetDocument().View()->LayoutViewport()->SetScrollOffset( |
| ScrollOffset(0.0, 1000), mojom::blink::ScrollType::kProgrammatic); |
| UpdateAllLifecyclePhasesForTest(); |
| |
| Element* target = ChildDocument().getElementById("target"); |
| ASSERT_TRUE(target); |
| PhysicalOffset mapped_point = |
| MapAncestorToLocal(target->GetLayoutObject(), nullptr, |
| PhysicalOffset(10, 70), kTraverseDocumentBoundaries); |
| |
| // 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(PhysicalOffset(10, -7930), AdjustForFrameScroll(mapped_point)); |
| } |
| |
| TEST_F(MapCoordinatesTest, IFrameTransformed) { |
| GetDocument().SetBaseURLOverride(KURL("http://test.com")); |
| SetBodyInnerHTML(R"HTML( |
| <style>body { margin: 0; }</style> |
| <iframe style='transform: scale(2)' src='http://test.com' |
| width='500' height='500' frameBorder='0'> |
| </iframe> |
| )HTML"); |
| SetChildFrameHTML( |
| "<style>body { margin: 0; } #target { width: 200px; " |
| "height: 8000px}</style><div id=target></div>"); |
| |
| UpdateAllLifecyclePhasesForTest(); |
| |
| ChildDocument().View()->LayoutViewport()->SetScrollOffset( |
| ScrollOffset(0.0, 1000), mojom::blink::ScrollType::kProgrammatic); |
| ChildDocument().View()->UpdateAllLifecyclePhases(DocumentUpdateReason::kTest); |
| |
| Element* target = ChildDocument().getElementById("target"); |
| ASSERT_TRUE(target); |
| PhysicalOffset mapped_point = |
| MapAncestorToLocal(target->GetLayoutObject(), nullptr, |
| PhysicalOffset(200, 200), kTraverseDocumentBoundaries); |
| |
| // 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(PhysicalOffset(225, 1225), mapped_point); |
| } |
| |
| TEST_F(MapCoordinatesTest, FixedPosInScrolledIFrameWithTransform) { |
| GetDocument().SetBaseURLOverride(KURL("http://test.com")); |
| SetBodyInnerHTML(R"HTML( |
| <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 frameborder=0 src='http://test.com' |
| sandbox='allow-same-origin' width='1024' height='768'></iframe> |
| </div> |
| )HTML"); |
| SetChildFrameHTML(R"HTML( |
| <style>* { margin: 0; } #target { width: 200px; height: 200px; |
| position:fixed}</style><div id=target></div> |
| <div style='width: 200; height: 8000px'></div> |
| )HTML"); |
| |
| UpdateAllLifecyclePhasesForTest(); |
| ChildDocument().View()->LayoutViewport()->SetScrollOffset( |
| ScrollOffset(0.0, 1000), mojom::blink::ScrollType::kProgrammatic); |
| UpdateAllLifecyclePhasesForTest(); |
| |
| Element* target = ChildDocument().getElementById("target"); |
| ASSERT_TRUE(target); |
| PhysicalOffset mapped_point = |
| MapAncestorToLocal(target->GetLayoutObject(), nullptr, |
| PhysicalOffset(0, 0), kTraverseDocumentBoundaries); |
| |
| EXPECT_EQ(PhysicalOffset(0, 0), mapped_point); |
| } |
| |
| TEST_F(MapCoordinatesTest, MulticolWithText) { |
| SetBodyInnerHTML(R"HTML( |
| <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> |
| )HTML"); |
| |
| LayoutObject* target = GetLayoutObjectByElementId("sibling")->NextSibling(); |
| ASSERT_TRUE(target->IsText()); |
| LayoutBox* flow_thread = ToLayoutBox(target->Parent()); |
| ASSERT_TRUE(flow_thread->IsLayoutFlowThread()); |
| LayoutBox* multicol = ToLayoutBox(GetLayoutObjectByElementId("multicol")); |
| |
| PhysicalOffset mapped_point = |
| MapLocalToAncestor(target, flow_thread, PhysicalOffset(10, 70)); |
| EXPECT_EQ(PhysicalOffset(10, 70), mapped_point); |
| mapped_point = MapAncestorToLocal(target, flow_thread, mapped_point); |
| EXPECT_EQ(PhysicalOffset(10, 70), mapped_point); |
| |
| mapped_point = |
| MapLocalToAncestor(flow_thread, multicol, PhysicalOffset(10, 70)); |
| EXPECT_EQ(PhysicalOffset(225, 25), mapped_point); |
| mapped_point = MapAncestorToLocal(flow_thread, multicol, mapped_point); |
| EXPECT_EQ(PhysicalOffset(10, 70), mapped_point); |
| } |
| |
| TEST_F(MapCoordinatesTest, MulticolWithInline) { |
| SetBodyInnerHTML(R"HTML( |
| <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> |
| )HTML"); |
| |
| LayoutObject* target = GetLayoutObjectByElementId("target"); |
| LayoutBox* flow_thread = ToLayoutBox(target->Parent()); |
| ASSERT_TRUE(flow_thread->IsLayoutFlowThread()); |
| LayoutBox* multicol = ToLayoutBox(GetLayoutObjectByElementId("multicol")); |
| |
| PhysicalOffset mapped_point = |
| MapLocalToAncestor(target, flow_thread, PhysicalOffset(10, 70)); |
| EXPECT_EQ(PhysicalOffset(10, 70), mapped_point); |
| mapped_point = MapAncestorToLocal(target, flow_thread, mapped_point); |
| EXPECT_EQ(PhysicalOffset(10, 70), mapped_point); |
| |
| mapped_point = |
| MapLocalToAncestor(flow_thread, multicol, PhysicalOffset(10, 70)); |
| EXPECT_EQ(PhysicalOffset(225, 25), mapped_point); |
| mapped_point = MapAncestorToLocal(flow_thread, multicol, mapped_point); |
| EXPECT_EQ(PhysicalOffset(10, 70), mapped_point); |
| } |
| |
| TEST_F(MapCoordinatesTest, MulticolWithBlock) { |
| SetBodyInnerHTML(R"HTML( |
| <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> |
| )HTML"); |
| |
| LayoutBox* target = ToLayoutBox(GetLayoutObjectByElementId("target")); |
| LayoutBox* container = ToLayoutBox(GetLayoutObjectByElementId("container")); |
| |
| PhysicalOffset mapped_point = |
| MapLocalToAncestor(target, container, PhysicalOffset()); |
| EXPECT_EQ(PhysicalOffset(125, 35), mapped_point); |
| mapped_point = MapAncestorToLocal(target, container, mapped_point); |
| EXPECT_EQ(PhysicalOffset(), mapped_point); |
| |
| // Walk each ancestor in the chain separately, to verify each step on the way. |
| LayoutBox* flow_thread = target->ParentBox(); |
| ASSERT_TRUE(flow_thread->IsLayoutFlowThread()); |
| |
| mapped_point = MapLocalToAncestor(target, flow_thread, PhysicalOffset()); |
| EXPECT_EQ(PhysicalOffset(10, 120), mapped_point); |
| mapped_point = MapAncestorToLocal(target, flow_thread, mapped_point); |
| EXPECT_EQ(PhysicalOffset(), mapped_point); |
| |
| mapped_point = |
| MapLocalToAncestor(flow_thread, container, PhysicalOffset(10, 120)); |
| EXPECT_EQ(PhysicalOffset(125, 35), mapped_point); |
| mapped_point = MapAncestorToLocal(flow_thread, container, mapped_point); |
| EXPECT_EQ(PhysicalOffset(10, 120), mapped_point); |
| } |
| |
| TEST_F(MapCoordinatesTest, MulticolWithBlockAbove) { |
| SetBodyInnerHTML(R"HTML( |
| <div id='container' style='columns:3; column-gap:0; |
| column-fill:auto; width:300px; height:200px;'> |
| <div id='target' style='margin-top:-50px; height:100px;'></div> |
| </div> |
| )HTML"); |
| |
| LayoutBox* target = ToLayoutBox(GetLayoutObjectByElementId("target")); |
| LayoutBox* container = ToLayoutBox(GetLayoutObjectByElementId("container")); |
| |
| PhysicalOffset mapped_point = |
| MapLocalToAncestor(target, container, PhysicalOffset()); |
| EXPECT_EQ(PhysicalOffset(0, -50), mapped_point); |
| mapped_point = MapAncestorToLocal(target, container, mapped_point); |
| EXPECT_EQ(PhysicalOffset(), mapped_point); |
| |
| // Walk each ancestor in the chain separately, to verify each step on the way. |
| LayoutBox* flow_thread = target->ParentBox(); |
| ASSERT_TRUE(flow_thread->IsLayoutFlowThread()); |
| |
| mapped_point = MapLocalToAncestor(target, flow_thread, PhysicalOffset()); |
| EXPECT_EQ(PhysicalOffset(0, -50), mapped_point); |
| mapped_point = MapAncestorToLocal(target, flow_thread, mapped_point); |
| EXPECT_EQ(PhysicalOffset(), mapped_point); |
| |
| mapped_point = |
| MapLocalToAncestor(flow_thread, container, PhysicalOffset(0, -50)); |
| EXPECT_EQ(PhysicalOffset(0, -50), mapped_point); |
| mapped_point = MapAncestorToLocal(flow_thread, container, mapped_point); |
| EXPECT_EQ(PhysicalOffset(0, -50), mapped_point); |
| } |
| |
| TEST_F(MapCoordinatesTest, NestedMulticolWithBlock) { |
| SetBodyInnerHTML(R"HTML( |
| <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> |
| )HTML"); |
| |
| LayoutBox* target = ToLayoutBox(GetLayoutObjectByElementId("target")); |
| LayoutBox* outer_multicol = |
| ToLayoutBox(GetLayoutObjectByElementId("outerMulticol")); |
| LayoutBox* inner_multicol = |
| ToLayoutBox(GetLayoutObjectByElementId("innerMulticol")); |
| LayoutBox* inner_flow_thread = target->ParentBox(); |
| ASSERT_TRUE(inner_flow_thread->IsLayoutFlowThread()); |
| LayoutBox* outer_flow_thread = inner_multicol->ParentBox(); |
| ASSERT_TRUE(outer_flow_thread->IsLayoutFlowThread()); |
| |
| PhysicalOffset mapped_point = |
| MapLocalToAncestor(target, outer_multicol, PhysicalOffset()); |
| EXPECT_EQ(PhysicalOffset(435, 115), mapped_point); |
| mapped_point = MapAncestorToLocal(target, outer_multicol, mapped_point); |
| EXPECT_EQ(PhysicalOffset(), mapped_point); |
| |
| // Walk each ancestor in the chain separately, to verify each step on the way. |
| mapped_point = |
| MapLocalToAncestor(target, inner_flow_thread, PhysicalOffset()); |
| EXPECT_EQ(PhysicalOffset(0, 630), mapped_point); |
| mapped_point = MapAncestorToLocal(target, inner_flow_thread, mapped_point); |
| EXPECT_EQ(PhysicalOffset(), mapped_point); |
| |
| mapped_point = MapLocalToAncestor(inner_flow_thread, inner_multicol, |
| PhysicalOffset(0, 630)); |
| EXPECT_EQ(PhysicalOffset(140, 305), mapped_point); |
| mapped_point = |
| MapAncestorToLocal(inner_flow_thread, inner_multicol, mapped_point); |
| EXPECT_EQ(PhysicalOffset(0, 630), mapped_point); |
| |
| mapped_point = MapLocalToAncestor(inner_multicol, outer_flow_thread, |
| PhysicalOffset(140, 305)); |
| EXPECT_EQ(PhysicalOffset(140, 315), mapped_point); |
| mapped_point = |
| MapAncestorToLocal(inner_multicol, outer_flow_thread, mapped_point); |
| EXPECT_EQ(PhysicalOffset(140, 305), mapped_point); |
| |
| mapped_point = MapLocalToAncestor(outer_flow_thread, outer_multicol, |
| PhysicalOffset(140, 315)); |
| EXPECT_EQ(PhysicalOffset(435, 115), mapped_point); |
| mapped_point = |
| MapAncestorToLocal(outer_flow_thread, outer_multicol, mapped_point); |
| EXPECT_EQ(PhysicalOffset(140, 315), mapped_point); |
| } |
| |
| TEST_F(MapCoordinatesTest, MulticolWithAbsPosInRelPos) { |
| SetBodyInnerHTML(R"HTML( |
| <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> |
| )HTML"); |
| |
| LayoutBox* target = ToLayoutBox(GetLayoutObjectByElementId("target")); |
| LayoutBox* multicol = ToLayoutBox(GetLayoutObjectByElementId("multicol")); |
| |
| PhysicalOffset mapped_point = |
| MapLocalToAncestor(target, multicol, PhysicalOffset()); |
| EXPECT_EQ(PhysicalOffset(144, 54), mapped_point); |
| mapped_point = MapAncestorToLocal(target, multicol, mapped_point); |
| EXPECT_EQ(PhysicalOffset(), mapped_point); |
| |
| // Walk each ancestor in the chain separately, to verify each step on the way. |
| LayoutBox* relpos = ToLayoutBox(GetLayoutObjectByElementId("relpos")); |
| LayoutBox* flow_thread = relpos->ParentBox(); |
| ASSERT_TRUE(flow_thread->IsLayoutFlowThread()); |
| |
| mapped_point = MapLocalToAncestor(target, relpos, PhysicalOffset()); |
| EXPECT_EQ(PhysicalOffset(25, 25), mapped_point); |
| mapped_point = MapAncestorToLocal(target, relpos, mapped_point); |
| EXPECT_EQ(PhysicalOffset(), mapped_point); |
| |
| mapped_point = |
| MapLocalToAncestor(relpos, flow_thread, PhysicalOffset(25, 25)); |
| EXPECT_EQ(PhysicalOffset(29, 139), mapped_point); |
| mapped_point = MapAncestorToLocal(relpos, flow_thread, mapped_point); |
| EXPECT_EQ(PhysicalOffset(25, 25), mapped_point); |
| |
| mapped_point = |
| MapLocalToAncestor(flow_thread, multicol, PhysicalOffset(29, 139)); |
| EXPECT_EQ(PhysicalOffset(144, 54), mapped_point); |
| mapped_point = MapAncestorToLocal(flow_thread, multicol, mapped_point); |
| EXPECT_EQ(PhysicalOffset(29, 139), mapped_point); |
| } |
| |
| TEST_F(MapCoordinatesTest, MulticolWithAbsPosNotContained) { |
| SetBodyInnerHTML(R"HTML( |
| <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> |
| )HTML"); |
| |
| LayoutBox* target = ToLayoutBox(GetLayoutObjectByElementId("target")); |
| LayoutBox* container = ToLayoutBox(GetLayoutObjectByElementId("container")); |
| |
| // The multicol container isn't in the containing block chain of the abspos |
| // #target. |
| PhysicalOffset mapped_point = |
| MapLocalToAncestor(target, container, PhysicalOffset()); |
| EXPECT_EQ(PhysicalOffset(16, 16), mapped_point); |
| mapped_point = MapAncestorToLocal(target, container, mapped_point); |
| EXPECT_EQ(PhysicalOffset(), mapped_point); |
| |
| // Walk each ancestor in the chain separately, to verify each step on the way. |
| LayoutBox* multicol = ToLayoutBox(GetLayoutObjectByElementId("multicol")); |
| LayoutBox* flow_thread = target->ParentBox(); |
| ASSERT_TRUE(flow_thread->IsLayoutFlowThread()); |
| |
| mapped_point = MapLocalToAncestor(target, flow_thread, PhysicalOffset()); |
| EXPECT_EQ(PhysicalOffset(-9, -9), mapped_point); |
| |
| mapped_point = MapLocalToAncestor(flow_thread, multicol, mapped_point); |
| EXPECT_EQ(PhysicalOffset(6, 6), mapped_point); |
| |
| mapped_point = MapLocalToAncestor(multicol, container, mapped_point); |
| EXPECT_EQ(PhysicalOffset(16, 16), mapped_point); |
| |
| mapped_point = MapAncestorToLocal(multicol, container, mapped_point); |
| EXPECT_EQ(PhysicalOffset(6, 6), mapped_point); |
| |
| mapped_point = MapAncestorToLocal(flow_thread, multicol, mapped_point); |
| EXPECT_EQ(PhysicalOffset(-9, -9), mapped_point); |
| |
| mapped_point = MapAncestorToLocal(target, flow_thread, mapped_point); |
| EXPECT_EQ(PhysicalOffset(), mapped_point); |
| } |
| |
| TEST_F(MapCoordinatesTest, MulticolRtl) { |
| SetBodyInnerHTML(R"HTML( |
| <div id='container' style='columns:3; column-gap:0; column-fill:auto; |
| width:300px; height:200px; direction:rtl;'> |
| <div style='height:200px;'></div> |
| <div id='target' style='height:50px;'></div> |
| </div> |
| )HTML"); |
| |
| LayoutBox* target = ToLayoutBox(GetLayoutObjectByElementId("target")); |
| LayoutBox* container = ToLayoutBox(GetLayoutObjectByElementId("container")); |
| |
| PhysicalOffset mapped_point = |
| MapLocalToAncestor(target, container, PhysicalOffset()); |
| EXPECT_EQ(PhysicalOffset(100, 0), mapped_point); |
| mapped_point = MapAncestorToLocal(target, container, mapped_point); |
| EXPECT_EQ(PhysicalOffset(), mapped_point); |
| |
| // Walk each ancestor in the chain separately, to verify each step on the way. |
| LayoutBox* flow_thread = target->ParentBox(); |
| ASSERT_TRUE(flow_thread->IsLayoutFlowThread()); |
| |
| mapped_point = MapLocalToAncestor(target, flow_thread, PhysicalOffset()); |
| EXPECT_EQ(PhysicalOffset(0, 200), mapped_point); |
| mapped_point = MapAncestorToLocal(target, flow_thread, mapped_point); |
| EXPECT_EQ(PhysicalOffset(), mapped_point); |
| |
| mapped_point = |
| MapLocalToAncestor(flow_thread, container, PhysicalOffset(0, 200)); |
| EXPECT_EQ(PhysicalOffset(100, 0), mapped_point); |
| mapped_point = MapAncestorToLocal(flow_thread, container, mapped_point); |
| EXPECT_EQ(PhysicalOffset(0, 200), mapped_point); |
| } |
| |
| TEST_F(MapCoordinatesTest, MulticolWithLargeBorder) { |
| SetBodyInnerHTML(R"HTML( |
| <div id='container' style='columns:3; column-gap:0; column-fill:auto; |
| width:300px; height:200px; border:200px solid;'> |
| <div style='height:200px;'></div> |
| <div id='target' style='height:50px;'></div> |
| <div style='height:200px;'></div> |
| </div> |
| )HTML"); |
| |
| LayoutBox* target = ToLayoutBox(GetLayoutObjectByElementId("target")); |
| LayoutBox* container = ToLayoutBox(GetLayoutObjectByElementId("container")); |
| |
| PhysicalOffset mapped_point = |
| MapLocalToAncestor(target, container, PhysicalOffset()); |
| EXPECT_EQ(PhysicalOffset(300, 200), mapped_point); |
| mapped_point = MapAncestorToLocal(target, container, mapped_point); |
| EXPECT_EQ(PhysicalOffset(), mapped_point); |
| |
| // Walk each ancestor in the chain separately, to verify each step on the way. |
| LayoutBox* flow_thread = target->ParentBox(); |
| ASSERT_TRUE(flow_thread->IsLayoutFlowThread()); |
| |
| mapped_point = MapLocalToAncestor(target, flow_thread, PhysicalOffset()); |
| EXPECT_EQ(PhysicalOffset(0, 200), mapped_point); |
| mapped_point = MapAncestorToLocal(target, flow_thread, mapped_point); |
| EXPECT_EQ(PhysicalOffset(), mapped_point); |
| |
| mapped_point = |
| MapLocalToAncestor(flow_thread, container, PhysicalOffset(0, 200)); |
| EXPECT_EQ(PhysicalOffset(300, 200), mapped_point); |
| mapped_point = MapAncestorToLocal(flow_thread, container, mapped_point); |
| EXPECT_EQ(PhysicalOffset(0, 200), mapped_point); |
| } |
| |
| TEST_F(MapCoordinatesTest, FlippedBlocksWritingModeWithText) { |
| SetBodyInnerHTML(R"HTML( |
| <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> |
| )HTML"); |
| |
| LayoutObject* br = GetLayoutObjectByElementId("sibling"); |
| LayoutObject* text = br->NextSibling(); |
| ASSERT_TRUE(text->IsText()); |
| |
| // Map to the nearest container. Nothing special should happen because |
| // everything is in physical coordinates. |
| PhysicalOffset mapped_point = |
| MapLocalToAncestor(text, text->ContainingBlock(), PhysicalOffset(75, 10)); |
| EXPECT_EQ(PhysicalOffset(75, 10), mapped_point); |
| mapped_point = |
| MapAncestorToLocal(text, text->ContainingBlock(), mapped_point); |
| EXPECT_EQ(PhysicalOffset(75, 10), mapped_point); |
| |
| // Map to a container further up in the tree. |
| mapped_point = MapLocalToAncestor( |
| text, text->ContainingBlock()->ContainingBlock(), PhysicalOffset(75, 10)); |
| EXPECT_EQ(PhysicalOffset(80, 10), mapped_point); |
| mapped_point = MapAncestorToLocal( |
| text, text->ContainingBlock()->ContainingBlock(), mapped_point); |
| EXPECT_EQ(PhysicalOffset(75, 10), mapped_point); |
| } |
| |
| TEST_F(MapCoordinatesTest, FlippedBlocksWritingModeWithInline) { |
| SetBodyInnerHTML(R"HTML( |
| <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> |
| )HTML"); |
| |
| LayoutObject* target = GetLayoutObjectByElementId("target"); |
| ASSERT_TRUE(target); |
| |
| // First map to the parent SPAN. Nothing special should happen. |
| PhysicalOffset mapped_point = MapLocalToAncestor( |
| target, ToLayoutBoxModelObject(target->Parent()), PhysicalOffset(75, 10)); |
| EXPECT_EQ(PhysicalOffset(75, 10), mapped_point); |
| mapped_point = MapAncestorToLocal( |
| target, ToLayoutBoxModelObject(target->Parent()), mapped_point); |
| EXPECT_EQ(PhysicalOffset(75, 10), mapped_point); |
| |
| // Continue to the nearest container. Nothing special should happen because |
| // everything is in physical coordinates. |
| mapped_point = |
| MapLocalToAncestor(ToLayoutBoxModelObject(target->Parent()), |
| target->ContainingBlock(), PhysicalOffset(75, 10)); |
| EXPECT_EQ(PhysicalOffset(75, 10), mapped_point); |
| mapped_point = MapAncestorToLocal(ToLayoutBoxModelObject(target->Parent()), |
| target->ContainingBlock(), mapped_point); |
| EXPECT_EQ(PhysicalOffset(75, 10), mapped_point); |
| |
| // Now map from the innermost inline to the nearest container in one go. |
| mapped_point = MapLocalToAncestor(target, target->ContainingBlock(), |
| PhysicalOffset(75, 10)); |
| EXPECT_EQ(PhysicalOffset(75, 10), mapped_point); |
| mapped_point = |
| MapAncestorToLocal(target, target->ContainingBlock(), mapped_point); |
| EXPECT_EQ(PhysicalOffset(75, 10), mapped_point); |
| |
| // Map to a container further up in the tree. |
| mapped_point = |
| MapLocalToAncestor(target, target->ContainingBlock()->ContainingBlock(), |
| PhysicalOffset(75, 10)); |
| EXPECT_EQ(PhysicalOffset(82, 10), mapped_point); |
| mapped_point = MapAncestorToLocal( |
| target, target->ContainingBlock()->ContainingBlock(), mapped_point); |
| EXPECT_EQ(PhysicalOffset(75, 10), mapped_point); |
| } |
| |
| TEST_F(MapCoordinatesTest, FlippedBlocksWritingModeWithBlock) { |
| SetBodyInnerHTML(R"HTML( |
| <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> |
| )HTML"); |
| |
| LayoutBox* target = ToLayoutBox(GetLayoutObjectByElementId("target")); |
| LayoutBox* container = ToLayoutBox(GetLayoutObjectByElementId("container")); |
| |
| PhysicalOffset mapped_point = |
| MapLocalToAncestor(target, container, PhysicalOffset()); |
| EXPECT_EQ(PhysicalOffset(153, 22), mapped_point); |
| mapped_point = MapAncestorToLocal(target, container, mapped_point); |
| EXPECT_EQ(PhysicalOffset(), mapped_point); |
| |
| // Walk each ancestor in the chain separately, to verify each step on the way. |
| LayoutBox* middle = ToLayoutBox(GetLayoutObjectByElementId("middle")); |
| |
| mapped_point = MapLocalToAncestor(target, middle, PhysicalOffset()); |
| EXPECT_EQ(PhysicalOffset(7, 7), mapped_point); |
| mapped_point = MapAncestorToLocal(target, middle, mapped_point); |
| EXPECT_EQ(PhysicalOffset(), mapped_point); |
| |
| mapped_point = MapLocalToAncestor(middle, container, PhysicalOffset(7, 7)); |
| EXPECT_EQ(PhysicalOffset(153, 22), mapped_point); |
| mapped_point = MapAncestorToLocal(middle, container, mapped_point); |
| EXPECT_EQ(PhysicalOffset(7, 7), mapped_point); |
| } |
| |
| TEST_F(MapCoordinatesTest, Table) { |
| SetBodyInnerHTML(R"HTML( |
| <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> |
| )HTML"); |
| |
| LayoutBox* target = ToLayoutBox(GetLayoutObjectByElementId("target")); |
| LayoutBox* container = ToLayoutBox(GetLayoutObjectByElementId("container")); |
| |
| PhysicalOffset mapped_point = |
| MapLocalToAncestor(target, container, PhysicalOffset()); |
| EXPECT_EQ(PhysicalOffset(143, 302), mapped_point); |
| mapped_point = MapAncestorToLocal(target, container, mapped_point); |
| EXPECT_EQ(PhysicalOffset(), mapped_point); |
| |
| // Walk each ancestor in the chain separately, to verify each step on the way. |
| LayoutBox* td = target->ParentBox(); |
| ASSERT_TRUE(td->IsTableCell()); |
| mapped_point = MapLocalToAncestor(target, td, PhysicalOffset()); |
| // Cells are middle-aligned by default. |
| EXPECT_EQ(PhysicalOffset(2, 47), mapped_point); |
| mapped_point = MapAncestorToLocal(target, td, mapped_point); |
| EXPECT_EQ(PhysicalOffset(), mapped_point); |
| |
| LayoutBox* tr = td->ParentBox(); |
| ASSERT_TRUE(tr->IsTableRow()); |
| mapped_point = MapLocalToAncestor(td, tr, PhysicalOffset(2, 47)); |
| EXPECT_EQ(PhysicalOffset(126, 47), mapped_point); |
| mapped_point = MapAncestorToLocal(td, tr, mapped_point); |
| EXPECT_EQ(PhysicalOffset(2, 47), mapped_point); |
| |
| LayoutBox* tbody = tr->ParentBox(); |
| ASSERT_TRUE(tbody->IsTableSection()); |
| mapped_point = MapLocalToAncestor(tr, tbody, PhysicalOffset(126, 47)); |
| EXPECT_EQ(PhysicalOffset(126, 161), mapped_point); |
| mapped_point = MapAncestorToLocal(tr, tbody, mapped_point); |
| EXPECT_EQ(PhysicalOffset(126, 47), mapped_point); |
| |
| LayoutBox* table = tbody->ParentBox(); |
| ASSERT_TRUE(table->IsTable()); |
| mapped_point = MapLocalToAncestor(tbody, table, PhysicalOffset(126, 161)); |
| EXPECT_EQ(PhysicalOffset(131, 290), mapped_point); |
| mapped_point = MapAncestorToLocal(tbody, table, mapped_point); |
| EXPECT_EQ(PhysicalOffset(126, 161), mapped_point); |
| |
| mapped_point = MapLocalToAncestor(table, container, PhysicalOffset(131, 290)); |
| EXPECT_EQ(PhysicalOffset(143, 302), mapped_point); |
| mapped_point = MapAncestorToLocal(table, container, mapped_point); |
| EXPECT_EQ(PhysicalOffset(131, 290), mapped_point); |
| } |
| |
| 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(R"HTML( |
| <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> |
| )HTML"); |
| |
| LayoutBox* target = ToLayoutBox(GetLayoutObjectByElementId("target")); |
| LayoutBox* container = ToLayoutBox(GetLayoutObjectByElementId("container")); |
| |
| FloatQuad initial_quad(FloatPoint(0, 0), FloatPoint(200, 0), |
| FloatPoint(200, 200), FloatPoint(0, 200)); |
| FloatQuad mapped_quad = MapLocalToAncestor(target, container, initial_quad); |
| EXPECT_FLOAT_QUAD_EQ(FloatQuad(FloatPoint(200, 0), FloatPoint(200, 200), |
| FloatPoint(0, 200), FloatPoint(0, 0)), |
| mapped_quad); |
| mapped_quad = MapAncestorToLocal(target, container, mapped_quad); |
| EXPECT_FLOAT_QUAD_EQ(initial_quad, mapped_quad); |
| |
| // Walk each ancestor in the chain separately, to verify each step on the way. |
| LayoutBox* inner_transform = |
| ToLayoutBox(GetLayoutObjectByElementId("innerTransform")); |
| LayoutBox* outer_transform = |
| ToLayoutBox(GetLayoutObjectByElementId("outerTransform")); |
| |
| mapped_quad = MapLocalToAncestor(target, inner_transform, initial_quad); |
| EXPECT_FLOAT_QUAD_EQ(FloatQuad(FloatPoint(0, 0), FloatPoint(200, 0), |
| FloatPoint(200, 200), FloatPoint(0, 200)), |
| mapped_quad); |
| mapped_quad = MapAncestorToLocal(target, inner_transform, mapped_quad); |
| EXPECT_FLOAT_QUAD_EQ(initial_quad, mapped_quad); |
| |
| initial_quad = FloatQuad(FloatPoint(0, 0), FloatPoint(200, 0), |
| FloatPoint(200, 200), FloatPoint(0, 200)); |
| mapped_quad = |
| MapLocalToAncestor(inner_transform, outer_transform, initial_quad); |
| // 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)), |
| mapped_quad); |
| mapped_quad = |
| MapAncestorToLocal(inner_transform, outer_transform, mapped_quad); |
| EXPECT_FLOAT_QUAD_EQ(initial_quad, mapped_quad); |
| |
| initial_quad = FloatQuad(FloatPoint(100, -41.42), FloatPoint(241.42, 100), |
| FloatPoint(100, 241.42), FloatPoint(-41.42, 100)); |
| mapped_quad = MapLocalToAncestor(outer_transform, container, initial_quad); |
| // 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)), |
| mapped_quad); |
| mapped_quad = MapAncestorToLocal(outer_transform, container, mapped_quad); |
| EXPECT_FLOAT_QUAD_EQ(initial_quad, mapped_quad); |
| } |
| |
| TEST_F(MapCoordinatesTest, SVGShape) { |
| SetBodyInnerHTML(R"HTML( |
| <svg id='container'> |
| <g transform='translate(100 200)'> |
| <rect id='target' width='100' height='100'/> |
| </g> |
| </svg> |
| )HTML"); |
| |
| LayoutObject* target = GetLayoutObjectByElementId("target"); |
| LayoutBox* container = ToLayoutBox(GetLayoutObjectByElementId("container")); |
| |
| PhysicalOffset mapped_point = |
| MapLocalToAncestor(target, container, PhysicalOffset()); |
| EXPECT_EQ(PhysicalOffset(100, 200), mapped_point); |
| mapped_point = MapAncestorToLocal(target, container, mapped_point); |
| EXPECT_EQ(PhysicalOffset(), mapped_point); |
| } |
| |
| TEST_F(MapCoordinatesTest, SVGShapeScale) { |
| SetBodyInnerHTML(R"HTML( |
| <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> |
| )HTML"); |
| |
| LayoutObject* target = GetLayoutObjectByElementId("target"); |
| LayoutBox* container = ToLayoutBox(GetLayoutObjectByElementId("container")); |
| |
| PhysicalOffset mapped_point = |
| MapLocalToAncestor(target, container, PhysicalOffset()); |
| EXPECT_EQ(PhysicalOffset(200, 240), mapped_point); |
| mapped_point = MapAncestorToLocal(target, container, mapped_point); |
| EXPECT_EQ(PhysicalOffset(), mapped_point); |
| } |
| |
| TEST_F(MapCoordinatesTest, SVGShapeWithViewBoxWithoutScale) { |
| SetBodyInnerHTML(R"HTML( |
| <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> |
| )HTML"); |
| |
| LayoutObject* target = GetLayoutObjectByElementId("target"); |
| LayoutBox* container = ToLayoutBox(GetLayoutObjectByElementId("container")); |
| |
| PhysicalOffset mapped_point = |
| MapLocalToAncestor(target, container, PhysicalOffset()); |
| EXPECT_EQ(PhysicalOffset(200, 50), mapped_point); |
| mapped_point = MapAncestorToLocal(target, container, mapped_point); |
| EXPECT_EQ(PhysicalOffset(), mapped_point); |
| } |
| |
| TEST_F(MapCoordinatesTest, SVGShapeWithViewBoxWithScale) { |
| SetBodyInnerHTML(R"HTML( |
| <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> |
| )HTML"); |
| |
| LayoutObject* target = GetLayoutObjectByElementId("target"); |
| LayoutBox* container = ToLayoutBox(GetLayoutObjectByElementId("container")); |
| |
| PhysicalOffset mapped_point = |
| MapLocalToAncestor(target, container, PhysicalOffset()); |
| EXPECT_EQ(PhysicalOffset(200, 100), mapped_point); |
| mapped_point = MapAncestorToLocal(target, container, mapped_point); |
| EXPECT_EQ(PhysicalOffset(), mapped_point); |
| } |
| |
| TEST_F(MapCoordinatesTest, SVGShapeWithViewBoxWithNonZeroOffset) { |
| SetBodyInnerHTML(R"HTML( |
| <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> |
| )HTML"); |
| |
| LayoutObject* target = GetLayoutObjectByElementId("target"); |
| LayoutBox* container = ToLayoutBox(GetLayoutObjectByElementId("container")); |
| |
| PhysicalOffset mapped_point = |
| MapLocalToAncestor(target, container, PhysicalOffset()); |
| EXPECT_EQ(PhysicalOffset(200, 50), mapped_point); |
| mapped_point = MapAncestorToLocal(target, container, mapped_point); |
| EXPECT_EQ(PhysicalOffset(), mapped_point); |
| } |
| |
| TEST_F(MapCoordinatesTest, SVGShapeWithViewBoxWithNonZeroOffsetAndScale) { |
| SetBodyInnerHTML(R"HTML( |
| <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> |
| )HTML"); |
| |
| LayoutObject* target = GetLayoutObjectByElementId("target"); |
| LayoutBox* container = ToLayoutBox(GetLayoutObjectByElementId("container")); |
| |
| PhysicalOffset mapped_point = |
| MapLocalToAncestor(target, container, PhysicalOffset()); |
| EXPECT_EQ(PhysicalOffset(200, 100), mapped_point); |
| mapped_point = MapAncestorToLocal(target, container, mapped_point); |
| EXPECT_EQ(PhysicalOffset(), mapped_point); |
| } |
| |
| TEST_F(MapCoordinatesTest, SVGForeignObject) { |
| SetBodyInnerHTML(R"HTML( |
| <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> |
| )HTML"); |
| |
| LayoutObject* target = GetLayoutObjectByElementId("target"); |
| LayoutBox* container = ToLayoutBox(GetLayoutObjectByElementId("container")); |
| |
| PhysicalOffset mapped_point = |
| MapLocalToAncestor(target, container, PhysicalOffset()); |
| EXPECT_EQ(PhysicalOffset(250, 50), mapped_point); |
| // <svg> |
| mapped_point = MapAncestorToLocal(target->Parent()->Parent()->Parent(), |
| container, PhysicalOffset(250, 50)); |
| EXPECT_EQ(PhysicalOffset(250, 50), mapped_point); |
| // <g> |
| mapped_point = MapAncestorToLocal(target->Parent()->Parent(), container, |
| PhysicalOffset(250, 50)); |
| EXPECT_EQ(PhysicalOffset(25, -25), mapped_point); |
| // <foreignObject> |
| mapped_point = |
| MapAncestorToLocal(target->Parent(), container, PhysicalOffset(250, 50)); |
| EXPECT_EQ(PhysicalOffset(50, 0), mapped_point); |
| // <div> |
| mapped_point = MapAncestorToLocal(target, container, PhysicalOffset(250, 50)); |
| EXPECT_EQ(PhysicalOffset(), mapped_point); |
| } |
| |
| TEST_F(MapCoordinatesTest, LocalToAbsoluteTransform) { |
| SetBodyInnerHTML(R"HTML( |
| <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> |
| )HTML"); |
| LayoutBoxModelObject* container = |
| ToLayoutBoxModelObject(GetLayoutObjectByElementId("container")); |
| TransformationMatrix container_matrix = container->LocalToAbsoluteTransform(); |
| EXPECT_TRUE(container_matrix.IsIdentity()); |
| |
| LayoutObject* child = GetLayoutObjectByElementId("child"); |
| TransformationMatrix child_matrix = child->LocalToAbsoluteTransform(); |
| EXPECT_FALSE(child_matrix.IsIdentityOrTranslation()); |
| EXPECT_TRUE(child_matrix.IsAffine()); |
| EXPECT_EQ(FloatPoint(), child_matrix.ProjectPoint(FloatPoint())); |
| EXPECT_EQ(FloatPoint(20.0f, 40.0f), |
| child_matrix.ProjectPoint(FloatPoint(10.0f, 20.0f))); |
| } |
| |
| TEST_F(MapCoordinatesTest, LocalToAncestorTransform) { |
| SetBodyInnerHTML(R"HTML( |
| <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> |
| )HTML"); |
| 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) { |
| SetBodyInnerHTML(R"HTML( |
| <div style='position: absolute; left: 0; top: 0;'> |
| <div style='transform: rotateY(45deg); transform-style: preserve-3d;'> |
| <div style='transform: rotateY(-45deg); 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> |
| )HTML"); |
| 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()); |
| } |
| |
| TEST_F(MapCoordinatesTest, Transform3DWithOffset) { |
| SetBodyInnerHTML(R"HTML( |
| <style> |
| body { margin: 0; } |
| </style> |
| <div style="perspective: 400px; width: 0; height: 0"> |
| <div> |
| <div style="height: 100px"></div> |
| <div style="transform-style: preserve-3d; transform: rotateY(0deg)"> |
| <div id="target" style="width: 100px; height: 100px; |
| transform: translateZ(200px)"> |
| </div> |
| </div> |
| </div> |
| </div> |
| )HTML"); |
| |
| auto* target = GetLayoutObjectByElementId("target"); |
| EXPECT_EQ(FloatRect(0, 200, 200, 200), |
| MapLocalToAncestor(target, nullptr, FloatRect(0, 0, 100, 100))); |
| } |
| |
| // This test verifies that the mapped location of a div within a scroller |
| // remains the same after scroll when ignoring scroll offset. |
| TEST_F(MapCoordinatesTest, IgnoreScrollOffset) { |
| SetBodyInnerHTML(R"HTML( |
| <style> |
| body { margin: 0; } |
| .scroller { overflow: scroll; height: 100px; width: 100px; |
| top: 100px; position: absolute; } |
| .box { width: 10px; height: 10px; top: 10px; position: absolute; } |
| .spacer { height: 2000px; } |
| </style> |
| <div class='scroller' id='scroller'> |
| <div class='box' id='box'></div> |
| <div class='spacer'></div> |
| </div> |
| )HTML"); |
| |
| LayoutBox* scroller = ToLayoutBox(GetLayoutObjectByElementId("scroller")); |
| LayoutBox* box = ToLayoutBox(GetLayoutObjectByElementId("box")); |
| |
| EXPECT_EQ(PhysicalOffset(0, 10), |
| MapLocalToAncestor(box, scroller, PhysicalOffset())); |
| EXPECT_EQ( |
| PhysicalOffset(0, 10), |
| MapLocalToAncestor(box, scroller, PhysicalOffset(), kIgnoreScrollOffset)); |
| |
| To<Element>(scroller->GetNode()) |
| ->GetLayoutBoxForScrolling() |
| ->GetScrollableArea() |
| ->ScrollToAbsolutePosition(FloatPoint(0, 50)); |
| |
| EXPECT_EQ(PhysicalOffset(0, -40), |
| MapLocalToAncestor(box, scroller, PhysicalOffset())); |
| EXPECT_EQ( |
| PhysicalOffset(0, 10), |
| MapLocalToAncestor(box, scroller, PhysicalOffset(), kIgnoreScrollOffset)); |
| } |
| |
| // This test verifies that the mapped location of an inline div within a |
| // scroller remains the same after scroll when ignoring scroll offset. |
| TEST_F(MapCoordinatesTest, IgnoreScrollOffsetForInline) { |
| SetBodyInnerHTML(R"HTML( |
| <style> |
| body { margin: 0; } |
| .scroller { overflow: scroll; width: 100px; height: 100px; top: 100px; |
| position: absolute; } |
| .box { width: 10px; height: 10px; top: 10px; position: sticky; } |
| .inline { display: inline; } |
| .spacer { height: 2000px; } |
| </style> |
| <div class='scroller' id='scroller'> |
| <div class='inline box' id='box'></div> |
| <div class='spacer'></div> |
| </div> |
| )HTML"); |
| |
| LayoutBox* scroller = ToLayoutBox(GetLayoutObjectByElementId("scroller")); |
| LayoutInline* box = ToLayoutInline(GetLayoutObjectByElementId("box")); |
| |
| EXPECT_EQ(PhysicalOffset(0, 10), |
| MapLocalToAncestor(box, scroller, PhysicalOffset())); |
| EXPECT_EQ( |
| PhysicalOffset(0, 10), |
| MapLocalToAncestor(box, scroller, PhysicalOffset(), kIgnoreScrollOffset)); |
| |
| To<Element>(scroller->GetNode()) |
| ->GetLayoutBoxForScrolling() |
| ->GetScrollableArea() |
| ->ScrollToAbsolutePosition(FloatPoint(0, 50)); |
| |
| EXPECT_EQ(PhysicalOffset(0, 10), |
| MapLocalToAncestor(box, scroller, PhysicalOffset())); |
| EXPECT_EQ( |
| PhysicalOffset(0, 60), |
| MapLocalToAncestor(box, scroller, PhysicalOffset(), kIgnoreScrollOffset)); |
| } |
| |
| // This test verifies that ignoring scroll offset works with writing modes. |
| TEST_F(MapCoordinatesTest, IgnoreScrollOffsetWithWritingModes) { |
| SetBodyInnerHTML(R"HTML( |
| <style> |
| body { margin: 0; } |
| .scroller { writing-mode: vertical-rl; overflow: scroll; height: 100px; |
| width: 100px; top: 100px; position: absolute; } |
| .box { width: 10px; height: 10px; top: 10px; position: absolute; } |
| .spacer { width: 2000px; height: 2000px; } |
| </style> |
| <div class='scroller' id='scroller'> |
| <div class='box' id='box'></div> |
| <div class='spacer'></div> |
| </div> |
| )HTML"); |
| |
| LayoutBox* scroller = ToLayoutBox(GetLayoutObjectByElementId("scroller")); |
| LayoutBox* box = ToLayoutBox(GetLayoutObjectByElementId("box")); |
| auto* scroll_element = To<Element>(scroller->GetNode()); |
| |
| EXPECT_EQ(PhysicalOffset(90, 10), |
| MapLocalToAncestor(box, scroller, PhysicalOffset())); |
| EXPECT_EQ( |
| PhysicalOffset(1990, 10), |
| MapLocalToAncestor(box, scroller, PhysicalOffset(), kIgnoreScrollOffset)); |
| |
| scroll_element->GetLayoutBoxForScrolling() |
| ->GetScrollableArea() |
| ->ScrollToAbsolutePosition(FloatPoint(0, 50)); |
| |
| EXPECT_EQ(PhysicalOffset(1990, -40), |
| MapLocalToAncestor(box, scroller, PhysicalOffset())); |
| EXPECT_EQ( |
| PhysicalOffset(1990, 10), |
| MapLocalToAncestor(box, scroller, PhysicalOffset(), kIgnoreScrollOffset)); |
| |
| scroll_element->GetLayoutBoxForScrolling() |
| ->GetScrollableArea() |
| ->ScrollToAbsolutePosition(FloatPoint(1900, 50)); |
| |
| EXPECT_EQ(PhysicalOffset(90, -40), |
| MapLocalToAncestor(box, scroller, PhysicalOffset())); |
| EXPECT_EQ( |
| PhysicalOffset(1990, 10), |
| MapLocalToAncestor(box, scroller, PhysicalOffset(), kIgnoreScrollOffset)); |
| } |
| |
| // This test verifies that ignoring scroll offset works with writing modes and |
| // non-overlay scrollbar. |
| TEST_F(MapCoordinatesTest, |
| IgnoreScrollOffsetWithWritingModesAndNonOverlayScrollbar) { |
| USE_NON_OVERLAY_SCROLLBARS(); |
| |
| SetBodyInnerHTML(R"HTML( |
| <style> |
| body { margin: 0; } |
| .scroller { writing-mode: vertical-rl; overflow: scroll; height: 100px; |
| width: 100px; top: 100px; position: absolute; } |
| .box { width: 10px; height: 10px; top: 10px; position: absolute; } |
| .spacer { width: 2000px; height: 2000px; } |
| </style> |
| <div class='scroller' id='scroller'> |
| <div class='box' id='box'></div> |
| <div class='spacer'></div> |
| </div> |
| )HTML"); |
| |
| LayoutBox* scroller = ToLayoutBox(GetLayoutObjectByElementId("scroller")); |
| LayoutBox* box = ToLayoutBox(GetLayoutObjectByElementId("box")); |
| |
| // The box is on the left of the scrollbar so the width of the scrollbar |
| // affects the location of the box. |
| EXPECT_EQ(PhysicalOffset(75, 10), |
| MapLocalToAncestor(box, scroller, PhysicalOffset())); |
| EXPECT_EQ( |
| PhysicalOffset(1990, 10), |
| MapLocalToAncestor(box, scroller, PhysicalOffset(), kIgnoreScrollOffset)); |
| |
| To<Element>(scroller->GetNode()) |
| ->GetLayoutBoxForScrolling() |
| ->GetScrollableArea() |
| ->ScrollToAbsolutePosition(FloatPoint(0, 0)); |
| |
| // The box is now on the right of the scrollbar therefore there is nothing |
| // between the box and the right border of the content. |
| EXPECT_EQ(PhysicalOffset(1990, 10), |
| MapLocalToAncestor(box, scroller, PhysicalOffset())); |
| EXPECT_EQ( |
| PhysicalOffset(1990, 10), |
| MapLocalToAncestor(box, scroller, PhysicalOffset(), kIgnoreScrollOffset)); |
| } |
| |
| } // namespace blink |