| // 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 "third_party/blink/renderer/core/page/focus_controller.h" |
| |
| #include <memory> |
| #include "testing/gtest/include/gtest/gtest.h" |
| #include "third_party/blink/renderer/bindings/core/v8/v8_binding_for_core.h" |
| #include "third_party/blink/renderer/core/dom/shadow_root.h" |
| #include "third_party/blink/renderer/core/html/html_element.h" |
| #include "third_party/blink/renderer/core/testing/page_test_base.h" |
| |
| namespace blink { |
| |
| class FocusControllerTest : public PageTestBase { |
| private: |
| void SetUp() override { PageTestBase::SetUp(IntSize()); } |
| }; |
| |
| TEST_F(FocusControllerTest, SetInitialFocus) { |
| GetDocument().body()->SetInnerHTMLFromString("<input><textarea>"); |
| Element* input = ToElement(GetDocument().body()->firstChild()); |
| // Set sequential focus navigation point before the initial focus. |
| input->focus(); |
| input->blur(); |
| GetFocusController().SetInitialFocus(kWebFocusTypeForward); |
| EXPECT_EQ(input, GetDocument().FocusedElement()) |
| << "We should ignore sequential focus navigation starting point in " |
| "setInitialFocus()."; |
| } |
| |
| TEST_F(FocusControllerTest, DoNotCrash1) { |
| GetDocument().body()->SetInnerHTMLFromString( |
| "<div id='host'></div>This test is for crbug.com/609012<p id='target' " |
| "tabindex='0'></p>"); |
| // <div> with shadow root |
| Element* host = ToElement(GetDocument().body()->firstChild()); |
| host->AttachShadowRootInternal(ShadowRootType::kOpen); |
| // "This test is for crbug.com/609012" |
| Node* text = host->nextSibling(); |
| // <p> |
| Element* target = ToElement(text->nextSibling()); |
| |
| // Set sequential focus navigation point at text node. |
| GetDocument().SetSequentialFocusNavigationStartingPoint(text); |
| |
| GetFocusController().AdvanceFocus(kWebFocusTypeForward); |
| EXPECT_EQ(target, GetDocument().FocusedElement()) |
| << "This should not hit assertion and finish properly."; |
| } |
| |
| TEST_F(FocusControllerTest, DoNotCrash2) { |
| GetDocument().body()->SetInnerHTMLFromString( |
| "<p id='target' tabindex='0'></p>This test is for crbug.com/609012<div " |
| "id='host'></div>"); |
| // <p> |
| Element* target = ToElement(GetDocument().body()->firstChild()); |
| // "This test is for crbug.com/609012" |
| Node* text = target->nextSibling(); |
| // <div> with shadow root |
| Element* host = ToElement(text->nextSibling()); |
| host->AttachShadowRootInternal(ShadowRootType::kOpen); |
| |
| // Set sequential focus navigation point at text node. |
| GetDocument().SetSequentialFocusNavigationStartingPoint(text); |
| |
| GetFocusController().AdvanceFocus(kWebFocusTypeBackward); |
| EXPECT_EQ(target, GetDocument().FocusedElement()) |
| << "This should not hit assertion and finish properly."; |
| } |
| |
| TEST_F(FocusControllerTest, SetActiveOnInactiveDocument) { |
| // Test for crbug.com/700334 |
| GetDocument().Shutdown(); |
| // Document::shutdown() detaches document from its frame, and thus |
| // document().page() becomes nullptr. |
| // Use DummyPageHolder's page to retrieve FocusController. |
| GetPage().GetFocusController().SetActive(true); |
| } |
| |
| // This test is for crbug.com/733218 |
| TEST_F(FocusControllerTest, SVGFocusableElementInForm) { |
| GetDocument().body()->SetInnerHTMLFromString( |
| "<form>" |
| "<input id='first'>" |
| "<svg width='100px' height='100px' tabindex='0'>" |
| "<circle cx='50' cy='50' r='30' />" |
| "</svg>" |
| "<input id='last'>" |
| "</form>"); |
| |
| Element* form = ToElement(GetDocument().body()->firstChild()); |
| Element* first = ToElement(form->firstChild()); |
| Element* last = ToElement(form->lastChild()); |
| |
| Element* next = GetFocusController().NextFocusableElementInForm( |
| first, kWebFocusTypeForward); |
| EXPECT_EQ(next, last) |
| << "SVG Element should be skipped even when focusable in form."; |
| |
| Element* prev = GetFocusController().NextFocusableElementInForm( |
| next, kWebFocusTypeBackward); |
| EXPECT_EQ(prev, first) |
| << "SVG Element should be skipped even when focusable in form."; |
| } |
| |
| TEST_F(FocusControllerTest, FindFocusableAfterElement) { |
| GetDocument().body()->SetInnerHTMLFromString( |
| "<input id='first'><div id='second'></div><input id='third'><div " |
| "id='fourth' tabindex='0'></div>"); |
| Element* first = GetElementById("first"); |
| Element* second = GetElementById("second"); |
| Element* third = GetElementById("third"); |
| Element* fourth = GetElementById("fourth"); |
| EXPECT_EQ(third, GetFocusController().FindFocusableElementAfter( |
| *first, kWebFocusTypeForward)); |
| EXPECT_EQ(third, GetFocusController().FindFocusableElementAfter( |
| *second, kWebFocusTypeForward)); |
| EXPECT_EQ(fourth, GetFocusController().FindFocusableElementAfter( |
| *third, kWebFocusTypeForward)); |
| EXPECT_EQ(nullptr, GetFocusController().FindFocusableElementAfter( |
| *fourth, kWebFocusTypeForward)); |
| |
| EXPECT_EQ(nullptr, GetFocusController().FindFocusableElementAfter( |
| *first, kWebFocusTypeBackward)); |
| EXPECT_EQ(first, GetFocusController().FindFocusableElementAfter( |
| *second, kWebFocusTypeBackward)); |
| EXPECT_EQ(first, GetFocusController().FindFocusableElementAfter( |
| *third, kWebFocusTypeBackward)); |
| EXPECT_EQ(third, GetFocusController().FindFocusableElementAfter( |
| *fourth, kWebFocusTypeBackward)); |
| |
| EXPECT_EQ(nullptr, GetFocusController().FindFocusableElementAfter( |
| *first, kWebFocusTypeNone)); |
| } |
| |
| } // namespace blink |