| // 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/page/spatial_navigation.h" |
| |
| #include "testing/gtest/include/gtest/gtest.h" |
| #include "third_party/blink/renderer/core/exported/web_remote_frame_impl.h" |
| #include "third_party/blink/renderer/core/frame/frame_test_helpers.h" |
| #include "third_party/blink/renderer/core/frame/visual_viewport.h" |
| #include "third_party/blink/renderer/core/frame/web_local_frame_impl.h" |
| #include "third_party/blink/renderer/core/testing/core_unit_test_helper.h" |
| #include "third_party/blink/renderer/platform/testing/url_test_helpers.h" |
| |
| namespace blink { |
| |
| class SpatialNavigationTest : public RenderingTest { |
| public: |
| SpatialNavigationTest() |
| : RenderingTest(SingleChildLocalFrameClient::Create()) {} |
| |
| LayoutRect TopOfVisualViewport() { |
| LayoutRect visual_viewport = RootViewport(&GetFrame()); |
| visual_viewport.SetHeight(LayoutUnit(0)); |
| return visual_viewport; |
| } |
| |
| LayoutRect BottomOfVisualViewport() { |
| LayoutRect visual_viewport = RootViewport(&GetFrame()); |
| visual_viewport.SetY(visual_viewport.MaxY()); |
| visual_viewport.SetHeight(LayoutUnit(0)); |
| return visual_viewport; |
| } |
| |
| LayoutRect LeftSideOfVisualViewport() { |
| LayoutRect visual_viewport = RootViewport(&GetFrame()); |
| visual_viewport.SetWidth(LayoutUnit(0)); |
| return visual_viewport; |
| } |
| |
| LayoutRect RightSideOfVisualViewport() { |
| LayoutRect visual_viewport = RootViewport(&GetFrame()); |
| visual_viewport.SetX(visual_viewport.MaxX()); |
| visual_viewport.SetWidth(LayoutUnit(0)); |
| return visual_viewport; |
| } |
| |
| void AssertUseSidesOfVisualViewport(Node* focus_node) { |
| EXPECT_EQ(SearchOrigin(RootViewport(&GetFrame()), focus_node, |
| SpatialNavigationDirection::kUp), |
| BottomOfVisualViewport()); |
| EXPECT_EQ(SearchOrigin(RootViewport(&GetFrame()), focus_node, |
| SpatialNavigationDirection::kDown), |
| TopOfVisualViewport()); |
| EXPECT_EQ(SearchOrigin(RootViewport(&GetFrame()), focus_node, |
| SpatialNavigationDirection::kLeft), |
| RightSideOfVisualViewport()); |
| EXPECT_EQ(SearchOrigin(RootViewport(&GetFrame()), focus_node, |
| SpatialNavigationDirection::kRight), |
| LeftSideOfVisualViewport()); |
| } |
| |
| void UpdateAllLifecyclePhases(LocalFrameView* frame_view) { |
| frame_view->UpdateAllLifecyclePhases( |
| DocumentLifecycle::LifecycleUpdateReason::kTest); |
| } |
| }; |
| |
| TEST_F(SpatialNavigationTest, RootFramesVisualViewport) { |
| // Test RootViewport with a pinched viewport. |
| VisualViewport& visual_viewport = GetFrame().GetPage()->GetVisualViewport(); |
| visual_viewport.SetScale(2); |
| visual_viewport.SetLocation(FloatPoint(200, 200)); |
| |
| LocalFrameView* root_frame_view = GetFrame().LocalFrameRoot().View(); |
| const LayoutRect roots_visible_doc_rect( |
| root_frame_view->GetScrollableArea()->VisibleContentRect()); |
| // Convert the root frame's visible rect from document space -> frame space. |
| // For the root frame, frame space == root frame space, obviously. |
| LayoutRect viewport_rect_of_root_frame = |
| root_frame_view->DocumentToFrame(roots_visible_doc_rect); |
| |
| EXPECT_EQ(viewport_rect_of_root_frame, RootViewport(&GetFrame())); |
| } |
| |
| TEST_F(SpatialNavigationTest, FindContainerWhenEnclosingContainerIsDocument) { |
| SetBodyInnerHTML( |
| "<!DOCTYPE html>" |
| "<a id='child'>link</a>"); |
| |
| Element* child_element = GetDocument().getElementById("child"); |
| Node* enclosing_container = ScrollableAreaOrDocumentOf(child_element); |
| |
| EXPECT_EQ(enclosing_container, GetDocument()); |
| EXPECT_TRUE(IsScrollableAreaOrDocument(enclosing_container)); |
| } |
| |
| TEST_F(SpatialNavigationTest, FindContainerWhenEnclosingContainerIsIframe) { |
| SetBodyInnerHTML( |
| "<!DOCTYPE html>" |
| "<style>" |
| " iframe {" |
| " width: 100px;" |
| " height: 100px;" |
| " }" |
| "</style>" |
| "<iframe id='iframe'></iframe>"); |
| |
| SetChildFrameHTML( |
| "<!DOCTYPE html>" |
| "<a>link</a>"); |
| |
| UpdateAllLifecyclePhases(ChildDocument().View()); |
| Element* iframe = GetDocument().QuerySelector("iframe"); |
| Element* link = ChildDocument().QuerySelector("a"); |
| Node* enclosing_container = ScrollableAreaOrDocumentOf(link); |
| |
| EXPECT_FALSE(IsOffscreen(iframe)); |
| EXPECT_FALSE(IsOffscreen(&ChildDocument())); |
| EXPECT_FALSE(IsOffscreen(link)); |
| |
| EXPECT_EQ(enclosing_container, ChildDocument()); |
| EXPECT_TRUE(IsScrollableAreaOrDocument(enclosing_container)); |
| } |
| |
| TEST_F(SpatialNavigationTest, |
| FindContainerWhenEnclosingContainerIsScrollableOverflowBox) { |
| GetDocument().SetCompatibilityMode(Document::kQuirksMode); |
| SetBodyInnerHTML( |
| "<!DOCTYPE html>" |
| "<style>" |
| " #content {" |
| " margin-top: 200px;" // Outside the div's viewport. |
| " }" |
| " #container {" |
| " height: 100px;" |
| " overflow: scroll;" |
| " }" |
| "</style>" |
| "<div id='container'>" |
| " <div id='content'>some text here</div>" |
| "</div>"); |
| |
| Element* content = GetDocument().getElementById("content"); |
| Element* container = GetDocument().getElementById("container"); |
| Node* enclosing_container = ScrollableAreaOrDocumentOf(content); |
| |
| // TODO(crbug.com/889840): |
| // VisibleBoundsInVisualViewport does not (yet) take div-clipping into |
| // account. The node is off screen, but nevertheless VBIVV returns a non- |
| // empty rect. If you fix VisibleBoundsInVisualViewport, change to |
| // EXPECT_TRUE here and stop using LayoutObject in IsOffscreen(). |
| EXPECT_FALSE( |
| content->VisibleBoundsInVisualViewport().IsEmpty()); // EXPECT_TRUE. |
| |
| EXPECT_TRUE(IsOffscreen(content)); |
| EXPECT_FALSE(IsOffscreen(container)); |
| |
| EXPECT_EQ(enclosing_container, container); |
| EXPECT_TRUE(IsScrollableAreaOrDocument(enclosing_container)); |
| } |
| |
| TEST_F(SpatialNavigationTest, ZooomPutsElementOffScreen) { |
| SetBodyInnerHTML( |
| "<!DOCTYPE html>" |
| "<button id='a'>hello</button><br>" |
| "<button id='b' style='margin-top: 70%'>bello</button>"); |
| |
| Element* a = GetDocument().getElementById("a"); |
| Element* b = GetDocument().getElementById("b"); |
| EXPECT_FALSE(IsOffscreen(a)); |
| EXPECT_FALSE(IsOffscreen(b)); |
| |
| // Now, test IsOffscreen with a pinched viewport. |
| VisualViewport& visual_viewport = GetFrame().GetPage()->GetVisualViewport(); |
| visual_viewport.SetScale(2); |
| // #b is no longer visible. |
| EXPECT_FALSE(IsOffscreen(a)); |
| EXPECT_TRUE(IsOffscreen(b)); |
| } |
| |
| TEST_F(SpatialNavigationTest, RootViewportRespectsVisibleSize) { |
| EXPECT_EQ(RootViewport(&GetFrame()), LayoutRect(0, 0, 800, 600)); |
| |
| VisualViewport& visual_viewport = GetFrame().GetPage()->GetVisualViewport(); |
| visual_viewport.SetSize({123, 123}); |
| EXPECT_EQ(RootViewport(&GetFrame()), LayoutRect(0, 0, 123, 123)); |
| } |
| |
| TEST_F(SpatialNavigationTest, StartAtVisibleFocusedElement) { |
| SetBodyInnerHTML("<button id='b'>hello</button>"); |
| Element* b = GetDocument().getElementById("b"); |
| |
| EXPECT_EQ(SearchOrigin(RootViewport(&GetFrame()), b, |
| SpatialNavigationDirection::kDown), |
| NodeRectInRootFrame(b, true)); |
| } |
| |
| TEST_F(SpatialNavigationTest, StartAtVisibleFocusedScroller) { |
| SetBodyInnerHTML( |
| "<!DOCTYPE html>" |
| "<style>" |
| " #content {" |
| " margin-top: 200px;" // Outside the div's viewport. |
| " }" |
| " #scroller {" |
| " height: 100px;" |
| " overflow: scroll;" |
| " }" |
| "</style>" |
| "<div id='scroller'>" |
| " <div id='content'>some text here</div>" |
| "</div>"); |
| |
| Element* scroller = GetDocument().getElementById("scroller"); |
| EXPECT_EQ(SearchOrigin(RootViewport(&GetFrame()), scroller, |
| SpatialNavigationDirection::kDown), |
| NodeRectInRootFrame(scroller, true)); |
| } |
| |
| TEST_F(SpatialNavigationTest, StartAtVisibleFocusedIframe) { |
| SetBodyInnerHTML( |
| "<!DOCTYPE html>" |
| "<style>" |
| " iframe {" |
| " width: 100px;" |
| " height: 100px;" |
| " }" |
| "</style>" |
| "<iframe id='iframe'></iframe>"); |
| |
| SetChildFrameHTML( |
| "<!DOCTYPE html>" |
| "<div>some text here</div>"); |
| |
| Element* iframe = GetDocument().getElementById("iframe"); |
| EXPECT_EQ(SearchOrigin(RootViewport(&GetFrame()), iframe, |
| SpatialNavigationDirection::kDown), |
| NodeRectInRootFrame(iframe, true)); |
| } |
| |
| TEST_F(SpatialNavigationTest, StartAtTopWhenGoingDownwardsWithoutFocus) { |
| EXPECT_EQ(LayoutRect(0, 0, 111, 0), |
| SearchOrigin({0, 0, 111, 222}, nullptr, |
| SpatialNavigationDirection::kDown)); |
| |
| EXPECT_EQ(SearchOrigin(RootViewport(&GetFrame()), nullptr, |
| SpatialNavigationDirection::kDown), |
| TopOfVisualViewport()); |
| } |
| |
| TEST_F(SpatialNavigationTest, StartAtBottomWhenGoingUpwardsWithoutFocus) { |
| EXPECT_EQ( |
| LayoutRect(0, 222, 111, 0), |
| SearchOrigin({0, 0, 111, 222}, nullptr, SpatialNavigationDirection::kUp)); |
| |
| EXPECT_EQ(SearchOrigin(RootViewport(&GetFrame()), nullptr, |
| SpatialNavigationDirection::kUp), |
| BottomOfVisualViewport()); |
| } |
| |
| TEST_F(SpatialNavigationTest, StartAtLeftSideWhenGoingEastWithoutFocus) { |
| EXPECT_EQ(LayoutRect(0, 0, 0, 222), |
| SearchOrigin({0, 0, 111, 222}, nullptr, |
| SpatialNavigationDirection::kRight)); |
| |
| EXPECT_EQ(SearchOrigin(RootViewport(&GetFrame()), nullptr, |
| SpatialNavigationDirection::kRight), |
| LeftSideOfVisualViewport()); |
| } |
| |
| TEST_F(SpatialNavigationTest, StartAtRightSideWhenGoingWestWithoutFocus) { |
| EXPECT_EQ(LayoutRect(111, 0, 0, 222), |
| SearchOrigin({0, 0, 111, 222}, nullptr, |
| SpatialNavigationDirection::kLeft)); |
| |
| EXPECT_EQ(SearchOrigin(RootViewport(&GetFrame()), nullptr, |
| SpatialNavigationDirection::kLeft), |
| RightSideOfVisualViewport()); |
| } |
| |
| TEST_F(SpatialNavigationTest, |
| StartAtBottomWhenGoingUpwardsAndFocusIsOffscreen) { |
| SetBodyInnerHTML( |
| "<button id='b' style='margin-top: 120%;'>B</button>"); // Outside the |
| // visual |
| // viewport. |
| Element* b = GetDocument().getElementById("b"); |
| EXPECT_TRUE(IsOffscreen(b)); |
| |
| EXPECT_EQ(SearchOrigin(RootViewport(&GetFrame()), b, |
| SpatialNavigationDirection::kUp), |
| BottomOfVisualViewport()); |
| } |
| |
| TEST_F(SpatialNavigationTest, StartAtContainersEdge) { |
| SetBodyInnerHTML( |
| "<!DOCTYPE html>" |
| "<style>" |
| " div {" |
| " height: 100px;" |
| " width: 100px;" |
| " overflow: scroll;" |
| " }" |
| " button {" |
| " margin-top: 200px;" // Outside the div's viewport. |
| " }" |
| "</style>" |
| "<div id='container'>" |
| " <button id='b'>B</button>" |
| "</div>"); |
| |
| Element* b = GetDocument().getElementById("b"); |
| const Element* container = GetDocument().getElementById("container"); |
| const LayoutRect container_box = NodeRectInRootFrame(container, true); |
| |
| // TODO(crbug.com/889840): |
| // VisibleBoundsInVisualViewport does not (yet) take div-clipping into |
| // account. The node is off screen, but nevertheless VBIVV returns a non- |
| // empty rect. If you fix VisibleBoundsInVisualViewport, change to |
| // EXPECT_TRUE here and stop using LayoutObject in IsOffscreen(). |
| EXPECT_FALSE(b->VisibleBoundsInVisualViewport().IsEmpty()); // EXPECT_TRUE. |
| EXPECT_TRUE(IsOffscreen(b)); |
| |
| // Go down. |
| LayoutRect container_top_edge = container_box; |
| container_top_edge.SetHeight(LayoutUnit(0)); |
| EXPECT_EQ(SearchOrigin(RootViewport(&GetFrame()), b, |
| SpatialNavigationDirection::kDown), |
| container_top_edge); |
| |
| // Go up. |
| LayoutRect container_bottom_edge = container_box; |
| container_bottom_edge.SetY(container_bottom_edge.MaxX()); |
| container_bottom_edge.SetHeight(LayoutUnit(0)); |
| EXPECT_EQ(SearchOrigin(RootViewport(&GetFrame()), b, |
| SpatialNavigationDirection::kUp), |
| container_bottom_edge); |
| |
| // Go right. |
| LayoutRect container_leftmost_edge = container_box; |
| container_leftmost_edge.SetWidth(LayoutUnit(0)); |
| EXPECT_EQ(SearchOrigin(RootViewport(&GetFrame()), b, |
| SpatialNavigationDirection::kRight), |
| container_leftmost_edge); |
| |
| // Go left. |
| LayoutRect container_rightmost_edge = container_box; |
| container_rightmost_edge.SetX(container_bottom_edge.MaxX()); |
| container_rightmost_edge.SetWidth(LayoutUnit(0)); |
| EXPECT_EQ(SearchOrigin(RootViewport(&GetFrame()), b, |
| SpatialNavigationDirection::kLeft), |
| container_rightmost_edge); |
| } |
| |
| TEST_F(SpatialNavigationTest, |
| StartFromDocEdgeWhenFocusIsClippedInOffscreenScroller) { |
| SetBodyInnerHTML( |
| "<!DOCTYPE html>" |
| "<style>" |
| " div {" |
| " margin-top: 120%;" // Outside the visual viewport. |
| " height: 100px;" |
| " width: 100px;" |
| " overflow: scroll;" |
| " }" |
| " button {" |
| " margin-top: 300px;" // Outside the div's scrollport. |
| " }" |
| "</style>" |
| "<div id='scroller'>" |
| " <button id='b'>B</button>" |
| "</div>"); |
| |
| Element* scroller = GetDocument().getElementById("scroller"); |
| Element* b = GetDocument().getElementById("b"); |
| |
| EXPECT_TRUE(IsOffscreen(scroller)); |
| EXPECT_TRUE(IsOffscreen(b)); |
| |
| EXPECT_EQ(SearchOrigin(RootViewport(&GetFrame()), b, |
| SpatialNavigationDirection::kUp), |
| BottomOfVisualViewport()); |
| EXPECT_EQ(SearchOrigin(RootViewport(&GetFrame()), b, |
| SpatialNavigationDirection::kDown), |
| TopOfVisualViewport()); |
| } |
| |
| TEST_F(SpatialNavigationTest, |
| StartFromDocEdgeWhenFocusIsClippedInNestedOffscreenScroller) { |
| SetBodyInnerHTML( |
| "<!DOCTYPE html>" |
| "<style>" |
| " div {" |
| " margin-top: 120%;" // Outside the visual viewport. |
| " height: 100px;" |
| " width: 100px;" |
| " overflow: scroll;" |
| "}" |
| "a {" |
| " display: block;" |
| " margin-top: 300px;" |
| "}" |
| "</style>" |
| "<div id='scroller1'>" |
| " <div id='scroller2'>" |
| " <a id='link'>link</a>" |
| " </div>" |
| "</div>"); |
| |
| Element* scroller1 = GetDocument().getElementById("scroller1"); |
| Element* scroller2 = GetDocument().getElementById("scroller2"); |
| Element* link = GetDocument().getElementById("link"); |
| |
| EXPECT_TRUE(IsScrollableAreaOrDocument(scroller1)); |
| EXPECT_TRUE(IsScrollableAreaOrDocument(scroller2)); |
| EXPECT_TRUE(IsOffscreen(scroller1)); |
| EXPECT_TRUE(IsOffscreen(scroller1)); |
| EXPECT_TRUE(IsOffscreen(link)); |
| |
| AssertUseSidesOfVisualViewport(link); |
| } |
| |
| TEST_F(SpatialNavigationTest, PartiallyVisible) { |
| // <button>'s bottom is clipped. |
| SetBodyInnerHTML("<button id='b' style='height: 900px;'>B</button>"); |
| Element* b = GetDocument().getElementById("b"); |
| |
| EXPECT_FALSE(IsOffscreen(b)); // <button> is not completely offscreen. |
| |
| LayoutRect button_in_root_frame = NodeRectInRootFrame(b, true); |
| |
| EXPECT_EQ(SearchOrigin(RootViewport(&GetFrame()), b, |
| SpatialNavigationDirection::kUp), |
| Intersection(button_in_root_frame, RootViewport(&GetFrame()))); |
| |
| // Do some scrolling. |
| ScrollableArea* root_scroller = GetDocument().View()->GetScrollableArea(); |
| root_scroller->SetScrollOffset(ScrollOffset(0, 600), kProgrammaticScroll); |
| LayoutRect button_after_scroll = NodeRectInRootFrame(b, true); |
| ASSERT_NE(button_in_root_frame, |
| button_after_scroll); // As we scrolled, the |
| // <button>'s position in |
| // the root frame changed. |
| |
| // <button>'s top is clipped. |
| EXPECT_FALSE(IsOffscreen(b)); // <button> is not completely offscreen. |
| EXPECT_EQ(SearchOrigin(RootViewport(&GetFrame()), b, |
| SpatialNavigationDirection::kUp), |
| Intersection(button_after_scroll, RootViewport(&GetFrame()))); |
| } |
| |
| TEST_F(SpatialNavigationTest, |
| StartFromDocEdgeWhenOffscreenIframeDisplaysFocus) { |
| SetBodyInnerHTML( |
| "<!DOCTYPE html>" |
| "<style>" |
| " iframe {" |
| " margin-top: 120%;" // Outside the visual viewport. |
| " height: 100px;" |
| " width: 100px;" |
| " }" |
| "</style>" |
| "<iframe id='iframe'></iframe>"); |
| |
| SetChildFrameHTML( |
| "<!DOCTYPE html>" |
| "<a id='link'>link</a>"); |
| |
| UpdateAllLifecyclePhases(ChildDocument().View()); |
| Element* link = ChildDocument().QuerySelector("a"); |
| Element* iframe = GetDocument().QuerySelector("iframe"); |
| |
| // The <iframe> is not displayed in the visual viewport. In other words, it is |
| // being offscreen. And so is also its content, the <a>. |
| EXPECT_TRUE(IsOffscreen(iframe)); |
| EXPECT_TRUE(IsOffscreen(&ChildDocument())); |
| EXPECT_TRUE(IsOffscreen(link)); |
| |
| AssertUseSidesOfVisualViewport(link); |
| } |
| |
| TEST_F(SpatialNavigationTest, DivsCanClipIframes) { |
| SetBodyInnerHTML( |
| "<!DOCTYPE html>" |
| "<style>" |
| " div {" |
| " height: 100px;" |
| " width: 100px;" |
| " overflow: scroll;" |
| " }" |
| " iframe {" |
| " margin-top: 200px;" // Outside the div's viewport. |
| " height: 50px;" |
| " width: 50px;" |
| " }" |
| "</style>" |
| "<div>" |
| " <iframe id='iframe'></iframe>" |
| "</div>"); |
| |
| SetChildFrameHTML( |
| "<!DOCTYPE html>" |
| "<a>link</a>"); |
| |
| UpdateAllLifecyclePhases(ChildDocument().View()); |
| Element* div = GetDocument().QuerySelector("div"); |
| Element* iframe = GetDocument().QuerySelector("iframe"); |
| Element* link = ChildDocument().QuerySelector("a"); |
| EXPECT_FALSE(IsOffscreen(div)); |
| |
| // TODO(crbug.com/889840): |
| // VisibleBoundsInVisualViewport does not (yet) take div-clipping into |
| // account. The node is off screen, but nevertheless VBIVV returns a non- |
| // empty rect. If you fix VisibleBoundsInVisualViewport, change to |
| // EXPECT_TRUE here and stop using LayoutObject in IsOffscreen(). |
| EXPECT_FALSE( |
| iframe->VisibleBoundsInVisualViewport().IsEmpty()); // EXPECT_TRUE. |
| |
| // The <iframe> is not displayed in the visual viewport because it is clipped |
| // by the div. In other words, it is being offscreen. And so is also its |
| // content, the <a>. |
| EXPECT_TRUE(IsOffscreen(iframe)); |
| EXPECT_TRUE(IsOffscreen(&ChildDocument())); |
| EXPECT_TRUE(IsOffscreen(link)); |
| } |
| |
| TEST_F(SpatialNavigationTest, PartiallyVisibleIFrame) { |
| // <a> is off screen. The <iframe> is visible, but partially off screen. |
| SetBodyInnerHTML( |
| "<!DOCTYPE html>" |
| "<style>" |
| " iframe {" |
| " width: 200%;" |
| " height: 100px;" |
| " }" |
| "</style>" |
| "<iframe id='iframe'></iframe>"); |
| |
| SetChildFrameHTML( |
| "<!DOCTYPE html>" |
| "<style>" |
| " #child {" |
| " margin-left: 120%;" |
| " }" |
| "</style>" |
| "<a id='child'>link</a>"); |
| |
| UpdateAllLifecyclePhases(ChildDocument().View()); |
| Element* child_element = ChildDocument().getElementById("child"); |
| Node* enclosing_container = ScrollableAreaOrDocumentOf(child_element); |
| EXPECT_EQ(enclosing_container, ChildDocument()); |
| |
| EXPECT_TRUE(IsOffscreen(child_element)); // Completely offscreen. |
| EXPECT_FALSE(IsOffscreen(enclosing_container)); // Partially visible. |
| |
| LayoutRect iframe = NodeRectInRootFrame(enclosing_container, true); |
| |
| // When searching downwards we start at activeElement's |
| // container's (here: the iframe's) topmost visible edge. |
| EXPECT_EQ(SearchOrigin(RootViewport(&GetFrame()), child_element, |
| SpatialNavigationDirection::kDown), |
| OppositeEdge(SpatialNavigationDirection::kDown, |
| Intersection(iframe, RootViewport(&GetFrame())))); |
| |
| // When searching upwards we start at activeElement's |
| // container's (here: the iframe's) bottommost visible edge. |
| EXPECT_EQ(SearchOrigin(RootViewport(&GetFrame()), child_element, |
| SpatialNavigationDirection::kUp), |
| OppositeEdge(SpatialNavigationDirection::kUp, |
| Intersection(iframe, RootViewport(&GetFrame())))); |
| |
| // When searching eastwards, "to the right", we start at activeElement's |
| // container's (here: the iframe's) leftmost visible edge. |
| EXPECT_EQ(SearchOrigin(RootViewport(&GetFrame()), child_element, |
| SpatialNavigationDirection::kRight), |
| OppositeEdge(SpatialNavigationDirection::kRight, |
| Intersection(iframe, RootViewport(&GetFrame())))); |
| |
| // When searching westwards, "to the left", we start at activeElement's |
| // container's (here: the iframe's) rightmost visible edge. |
| EXPECT_EQ(SearchOrigin(RootViewport(&GetFrame()), child_element, |
| SpatialNavigationDirection::kLeft), |
| OppositeEdge(SpatialNavigationDirection::kLeft, |
| Intersection(iframe, RootViewport(&GetFrame())))); |
| } |
| |
| TEST_F(SpatialNavigationTest, BottomOfPinchedViewport) { |
| LayoutRect origin = SearchOrigin(RootViewport(&GetFrame()), nullptr, |
| SpatialNavigationDirection::kUp); |
| EXPECT_EQ(origin.Height(), 0); |
| EXPECT_EQ(origin.Width(), GetFrame().View()->Width()); |
| EXPECT_EQ(origin.X(), 0); |
| EXPECT_EQ(origin.Y(), GetFrame().View()->Height()); |
| EXPECT_EQ(origin, BottomOfVisualViewport()); |
| |
| // Now, test SearchOrigin with a pinched viewport. |
| VisualViewport& visual_viewport = GetFrame().GetPage()->GetVisualViewport(); |
| visual_viewport.SetScale(2); |
| visual_viewport.SetLocation(FloatPoint(200, 200)); |
| origin = SearchOrigin(RootViewport(&GetFrame()), nullptr, |
| SpatialNavigationDirection::kUp); |
| EXPECT_EQ(origin.Height(), 0); |
| EXPECT_LT(origin.Width(), GetFrame().View()->Width()); |
| EXPECT_GT(origin.X(), 0); |
| EXPECT_LT(origin.Y(), GetFrame().View()->Height()); |
| EXPECT_EQ(origin, BottomOfVisualViewport()); |
| } |
| |
| TEST_F(SpatialNavigationTest, TopOfPinchedViewport) { |
| LayoutRect origin = SearchOrigin(RootViewport(&GetFrame()), nullptr, |
| SpatialNavigationDirection::kDown); |
| EXPECT_EQ(origin.Height(), 0); |
| EXPECT_EQ(origin.Width(), GetFrame().View()->Width()); |
| EXPECT_EQ(origin.X(), 0); |
| EXPECT_EQ(origin.Y(), 0); |
| EXPECT_EQ(origin, TopOfVisualViewport()); |
| |
| // Now, test SearchOrigin with a pinched viewport. |
| VisualViewport& visual_viewport = GetFrame().GetPage()->GetVisualViewport(); |
| visual_viewport.SetScale(2); |
| visual_viewport.SetLocation(FloatPoint(200, 200)); |
| origin = SearchOrigin(RootViewport(&GetFrame()), nullptr, |
| SpatialNavigationDirection::kDown); |
| EXPECT_EQ(origin.Height(), 0); |
| EXPECT_LT(origin.Width(), GetFrame().View()->Width()); |
| EXPECT_GT(origin.X(), 0); |
| EXPECT_GT(origin.Y(), 0); |
| EXPECT_EQ(origin, TopOfVisualViewport()); |
| } |
| |
| TEST_F(SpatialNavigationTest, HasRemoteFrame) { |
| frame_test_helpers::WebViewHelper helper; |
| helper.InitializeAndLoad("about:blank"); |
| |
| WebViewImpl* webview = helper.GetWebView(); |
| WebURL base_url = url_test_helpers::ToKURL("http://www.test.com/"); |
| frame_test_helpers::LoadHTMLString(webview->MainFrameImpl(), |
| "<!DOCTYPE html>" |
| "<iframe id='iframe'></iframe>", |
| base_url); |
| |
| webview->ResizeWithBrowserControls(IntSize(400, 400), 50, 0, false); |
| UpdateAllLifecyclePhases(webview->MainFrameImpl()->GetFrame()->View()); |
| |
| Element* iframe = |
| webview->MainFrameImpl()->GetFrame()->GetDocument()->getElementById( |
| "iframe"); |
| EXPECT_FALSE(HasRemoteFrame(iframe)); |
| |
| webview->MainFrameImpl()->FirstChild()->Swap( |
| frame_test_helpers::CreateRemote()); |
| EXPECT_TRUE(HasRemoteFrame(iframe)); |
| } |
| |
| } // namespace blink |