// Copyright 2017 The Chromium Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. #include "third_party/blink/renderer/core/layout/layout_geometry_map.h" #include "third_party/blink/renderer/core/testing/core_unit_test_helper.h" namespace blink { class LayoutSVGForeignObjectTest : public RenderingTest { public: LayoutSVGForeignObjectTest() : RenderingTest(SingleChildLocalFrameClient::Create()) {} const Node* HitTest(int x, int y) { HitTestResult result( HitTestRequest(HitTestRequest::kReadOnly | HitTestRequest::kActive | HitTestRequest::kAllowChildFrameContent), IntPoint(x, y)); GetLayoutView().HitTest(result); return result.InnerNode(); } }; TEST_F(LayoutSVGForeignObjectTest, DivInForeignObject) { SetBodyInnerHTML(R"HTML(
)HTML"); const auto& svg = *GetDocument().getElementById("svg"); const auto& foreign = *GetDocument().getElementById("foreign"); const auto& foreign_object = *GetLayoutObjectByElementId("foreign"); const auto& div = *GetLayoutObjectByElementId("div"); EXPECT_EQ(FloatRect(100, 100, 300, 200), foreign_object.ObjectBoundingBox()); EXPECT_EQ(AffineTransform(), foreign_object.LocalSVGTransform()); EXPECT_EQ(AffineTransform(), foreign_object.LocalToSVGParentTransform()); // mapToVisualRectInAncestorSpace LayoutRect div_rect(0, 0, 100, 50); EXPECT_TRUE(div.MapToVisualRectInAncestorSpace(&GetLayoutView(), div_rect)); EXPECT_EQ(LayoutRect(150, 150, 100, 50), div_rect); // mapLocalToAncestor TransformState transform_state(TransformState::kApplyTransformDirection, FloatPoint()); div.MapLocalToAncestor(&GetLayoutView(), transform_state, kTraverseDocumentBoundaries); transform_state.Flatten(); EXPECT_EQ(FloatPoint(150, 150), transform_state.LastPlanarPoint()); // mapAncestorToLocal TransformState transform_state1( TransformState::kUnapplyInverseTransformDirection, FloatPoint()); div.MapAncestorToLocal(&GetLayoutView(), transform_state1, kTraverseDocumentBoundaries); transform_state1.Flatten(); EXPECT_EQ(FloatPoint(-150, -150), transform_state1.LastPlanarPoint()); // pushMappingToContainer LayoutGeometryMap rgm(kTraverseDocumentBoundaries); rgm.PushMappingsToAncestor(&div, nullptr); EXPECT_EQ(FloatQuad(FloatRect(150, 150, 1, 2)), rgm.MapToAncestor(FloatRect(0, 0, 1, 2), nullptr)); // Hit testing EXPECT_EQ(svg, HitTest(1, 1)); EXPECT_EQ(foreign, HitTest(149, 149)); EXPECT_EQ(div.GetNode(), HitTest(150, 150)); EXPECT_EQ(div.GetNode(), HitTest(349, 249)); EXPECT_EQ(foreign, HitTest(350, 250)); EXPECT_EQ(svg, HitTest(450, 350)); } TEST_F(LayoutSVGForeignObjectTest, IframeInForeignObject) { SetBodyInnerHTML(R"HTML( )HTML"); SetChildFrameHTML(R"HTML(
)HTML"); GetDocument().View()->UpdateAllLifecyclePhases(); const auto& svg = *GetDocument().getElementById("svg"); const auto& foreign = *GetDocument().getElementById("foreign"); const auto& foreign_object = *GetLayoutObjectByElementId("foreign"); const auto& div = *ChildDocument().getElementById("div")->GetLayoutObject(); EXPECT_EQ(FloatRect(100, 100, 300, 250), foreign_object.ObjectBoundingBox()); EXPECT_EQ(AffineTransform(), foreign_object.LocalSVGTransform()); EXPECT_EQ(AffineTransform(), foreign_object.LocalToSVGParentTransform()); // mapToVisualRectInAncestorSpace LayoutRect div_rect(0, 0, 100, 50); EXPECT_TRUE(div.MapToVisualRectInAncestorSpace(&GetLayoutView(), div_rect)); EXPECT_EQ(LayoutRect(200, 200, 100, 50), div_rect); // mapLocalToAncestor TransformState transform_state(TransformState::kApplyTransformDirection, FloatPoint()); div.MapLocalToAncestor(&GetLayoutView(), transform_state, kTraverseDocumentBoundaries); transform_state.Flatten(); EXPECT_EQ(FloatPoint(200, 200), transform_state.LastPlanarPoint()); // mapAncestorToLocal TransformState transform_state1( TransformState::kUnapplyInverseTransformDirection, FloatPoint()); div.MapAncestorToLocal(&GetLayoutView(), transform_state1, kTraverseDocumentBoundaries); transform_state1.Flatten(); EXPECT_EQ(FloatPoint(-200, -200), transform_state1.LastPlanarPoint()); // pushMappingToContainer LayoutGeometryMap rgm(kTraverseDocumentBoundaries); rgm.PushMappingsToAncestor(&div, nullptr); EXPECT_EQ(FloatQuad(FloatRect(200, 200, 1, 2)), rgm.MapToAncestor(FloatRect(0, 0, 1, 2), nullptr)); // Hit testing EXPECT_EQ(svg, HitTest(90, 90)); EXPECT_EQ(foreign, HitTest(129, 129)); EXPECT_EQ(ChildDocument().documentElement(), HitTest(130, 130)); EXPECT_EQ(ChildDocument().documentElement(), HitTest(199, 199)); EXPECT_EQ(div.GetNode(), HitTest(200, 200)); EXPECT_EQ(div.GetNode(), HitTest(299, 249)); EXPECT_EQ(ChildDocument().documentElement(), HitTest(300, 250)); EXPECT_EQ(ChildDocument().documentElement(), HitTest(369, 319)); EXPECT_EQ(foreign, HitTest(370, 320)); EXPECT_EQ(svg, HitTest(450, 400)); } TEST_F(LayoutSVGForeignObjectTest, HitTestZoomedForeignObject) { SetBodyInnerHTML(R"HTML(
)HTML"); const auto& svg = *GetDocument().getElementById("svg"); const auto& foreign = *GetDocument().getElementById("foreign"); const auto& foreign_object = *GetLayoutObjectByElementId("foreign"); const auto& div = *GetDocument().getElementById("div"); EXPECT_EQ(FloatRect(10, 10, 100, 150), foreign_object.ObjectBoundingBox()); EXPECT_EQ(AffineTransform(), foreign_object.LocalSVGTransform()); EXPECT_EQ(AffineTransform(), foreign_object.LocalToSVGParentTransform()); // mapToVisualRectInAncestorSpace LayoutRect div_rect(0, 0, 100, 50); EXPECT_TRUE(div.GetLayoutObject()->MapToVisualRectInAncestorSpace( &GetLayoutView(), div_rect)); EXPECT_EQ(LayoutRect(286, 286, 339, 170), div_rect); // mapLocalToAncestor TransformState transform_state(TransformState::kApplyTransformDirection, FloatPoint()); div.GetLayoutObject()->MapLocalToAncestor(&GetLayoutView(), transform_state, kTraverseDocumentBoundaries); transform_state.Flatten(); EXPECT_EQ(FloatPoint(286.875, 286.875), transform_state.LastPlanarPoint()); // mapAncestorToLocal TransformState transform_state1( TransformState::kUnapplyInverseTransformDirection, FloatPoint(286.875, 286.875)); div.GetLayoutObject()->MapAncestorToLocal(&GetLayoutView(), transform_state1, kTraverseDocumentBoundaries); transform_state1.Flatten(); EXPECT_EQ(FloatPoint(), transform_state1.LastPlanarPoint()); EXPECT_EQ(svg, HitTest(20, 20)); EXPECT_EQ(foreign, HitTest(280, 280)); EXPECT_EQ(div, HitTest(290, 290)); } TEST_F(LayoutSVGForeignObjectTest, HitTestViewBoxForeignObject) { SetBodyInnerHTML(R"HTML(
)HTML"); const auto& svg = *GetDocument().getElementById("svg"); const auto& foreign = *GetDocument().getElementById("foreign"); const auto& div = *GetDocument().getElementById("div"); // mapLocalToAncestor TransformState transform_state(TransformState::kApplyTransformDirection, FloatPoint()); div.GetLayoutObject()->MapLocalToAncestor(&GetLayoutView(), transform_state, kTraverseDocumentBoundaries); transform_state.Flatten(); EXPECT_EQ(FloatPoint(128, 128), transform_state.LastPlanarPoint()); // mapAncestorToLocal TransformState transform_state1( TransformState::kUnapplyInverseTransformDirection, FloatPoint(128, 128)); div.GetLayoutObject()->MapAncestorToLocal(&GetLayoutView(), transform_state1, kTraverseDocumentBoundaries); transform_state1.Flatten(); EXPECT_EQ(FloatPoint(), transform_state1.LastPlanarPoint()); EXPECT_EQ(svg, HitTest(20, 20)); EXPECT_EQ(foreign, HitTest(120, 110)); EXPECT_EQ(div, HitTest(160, 160)); } TEST_F(LayoutSVGForeignObjectTest, HitTestUnderClipPath) { SetBodyInnerHTML(R"HTML( )HTML"); const auto& svg = *GetDocument().getElementById("svg"); const auto& foreignObject = *GetDocument().getElementById("foreignObject"); // The fist and the third return |svg| because the circle clip-path // clips out the foreignObject. EXPECT_EQ(svg, GetDocument().ElementFromPoint(20, 20)); EXPECT_EQ(foreignObject, GetDocument().ElementFromPoint(250, 250)); EXPECT_EQ(svg, GetDocument().ElementFromPoint(400, 400)); } } // namespace blink