| Index: third_party/WebKit/Source/web/tests/LinkSelectionTest.cpp | 
| diff --git a/third_party/WebKit/Source/web/tests/LinkSelectionTest.cpp b/third_party/WebKit/Source/web/tests/LinkSelectionTest.cpp | 
| new file mode 100644 | 
| index 0000000000000000000000000000000000000000..28a8b4bc96b18cbe894883de24b931342e59cd29 | 
| --- /dev/null | 
| +++ b/third_party/WebKit/Source/web/tests/LinkSelectionTest.cpp | 
| @@ -0,0 +1,314 @@ | 
| +// Copyright (c) 2016 The Chromium Authors. All rights reserved. | 
| +// Use of this source code is governed by a BSD-style license that can be | 
| +// found in the LICENSE file. | 
| + | 
| +#include "core/dom/Range.h" | 
| +#include "core/frame/FrameView.h" | 
| +#include "core/input/EventHandler.h" | 
| +#include "core/page/ChromeClient.h" | 
| +#include "core/page/ContextMenuController.h" | 
| +#include "core/page/FocusController.h" | 
| +#include "core/page/Page.h" | 
| +#include "platform/Cursor.h" | 
| +#include "platform/testing/URLTestHelpers.h" | 
| +#include "platform/testing/UnitTestHelpers.h" | 
| +#include "public/web/WebSettings.h" | 
| +#include "testing/gmock/include/gmock/gmock.h" | 
| +#include "testing/gtest/include/gtest/gtest.h" | 
| +#include "web/WebLocalFrameImpl.h" | 
| +#include "web/tests/FrameTestHelpers.h" | 
| + | 
| +using ::testing::_; | 
| + | 
| +namespace blink { | 
| + | 
| +IntSize scaled(IntSize p, float scale) | 
| +{ | 
| +    p.scale(scale, scale); | 
| +    return p; | 
| +} | 
| + | 
| +class LinkSelectionTestBase : public ::testing::Test { | 
| +protected: | 
| +    enum DragFlag { | 
| +        SendDownEvent = 1, | 
| +        SendUpEvent = 1 << 1 | 
| +    }; | 
| +    using DragFlags = unsigned; | 
| + | 
| +    void emulateMouseDrag(const IntPoint& downPoint, const IntPoint& upPoint, int modifiers, | 
| +        DragFlags = SendDownEvent | SendUpEvent); | 
| + | 
| +    void emulateMouseClick(const IntPoint& clickPoint, WebMouseEvent::Button, int modifiers, int count = 1); | 
| +    void emulateMouseDown(const IntPoint& clickPoint, WebMouseEvent::Button, int modifiers, int count = 1); | 
| + | 
| +    String getSelectionText(); | 
| + | 
| +    FrameTestHelpers::WebViewHelper m_helper; | 
| +    WebViewImpl* m_webView = nullptr; | 
| +    RawPtrWillBePersistent<WebLocalFrameImpl> m_mainFrame = nullptr; | 
| +}; | 
| + | 
| +void LinkSelectionTestBase::emulateMouseDrag(const IntPoint& downPoint, const IntPoint& upPoint, int modifiers, DragFlags dragFlags) | 
| +{ | 
| +    if (dragFlags & SendDownEvent) { | 
| +        const auto& downEvent = FrameTestHelpers::createMouseEvent(WebMouseEvent::MouseDown, WebMouseEvent::ButtonLeft, downPoint, modifiers); | 
| +        m_webView->handleInputEvent(downEvent); | 
| +    } | 
| + | 
| +    const int kMoveEventsNumber = 10; | 
| +    const float kMoveIncrementFraction = 1. / kMoveEventsNumber; | 
| +    const auto& upDownVector = upPoint - downPoint; | 
| +    for (int i = 0; i < kMoveEventsNumber; ++i) { | 
| +        const auto& movePoint = downPoint + scaled(upDownVector, i * kMoveIncrementFraction); | 
| +        const auto& moveEvent = FrameTestHelpers::createMouseEvent(WebMouseEvent::MouseMove, WebMouseEvent::ButtonLeft, movePoint, modifiers); | 
| +        m_webView->handleInputEvent(moveEvent); | 
| +    } | 
| + | 
| +    if (dragFlags & SendUpEvent) { | 
| +        const auto& upEvent = FrameTestHelpers::createMouseEvent(WebMouseEvent::MouseUp, WebMouseEvent::ButtonLeft, upPoint, modifiers); | 
| +        m_webView->handleInputEvent(upEvent); | 
| +    } | 
| +} | 
| + | 
| +void LinkSelectionTestBase::emulateMouseClick(const IntPoint& clickPoint, WebMouseEvent::Button button, int modifiers, int count) | 
| +{ | 
| +    auto event = FrameTestHelpers::createMouseEvent(WebMouseEvent::MouseDown, button, clickPoint, modifiers); | 
| +    event.clickCount = count; | 
| +    m_webView->handleInputEvent(event); | 
| +    event.type = WebMouseEvent::MouseUp; | 
| +    m_webView->handleInputEvent(event); | 
| +} | 
| + | 
| +void LinkSelectionTestBase::emulateMouseDown(const IntPoint& clickPoint, WebMouseEvent::Button button, int modifiers, int count) | 
| +{ | 
| +    auto event = FrameTestHelpers::createMouseEvent(WebMouseEvent::MouseDown, button, clickPoint, modifiers); | 
| +    event.clickCount = count; | 
| +    m_webView->handleInputEvent(event); | 
| +} | 
| + | 
| +String LinkSelectionTestBase::getSelectionText() | 
| +{ | 
| +    return m_mainFrame->selectionAsText(); | 
| +} | 
| + | 
| +class TestFrameClient : public FrameTestHelpers::TestWebFrameClient { | 
| +public: | 
| +    MOCK_METHOD4(loadURLExternally, | 
| +        void(const WebURLRequest&, WebNavigationPolicy, const WebString& downloadName, bool shouldReplaceCurrentEntry)); | 
| +}; | 
| + | 
| +class LinkSelectionTest : public LinkSelectionTestBase { | 
| +protected: | 
| +    void SetUp() override | 
| +    { | 
| +        const char* const kHTMLString = | 
| +            "<a id='link' href='foo.com' style='font-size:20pt'>Text to select foobar</a>" | 
| +            "<div id='page_text'>Lorem ipsum dolor sit amet</div>"; | 
| + | 
| +        // We need to set deviceSupportsMouse setting to true and page's focus controller to active | 
| +        // so that FrameView can set the mouse cursor. | 
| +        m_webView = m_helper.initialize(false, &m_testFrameClient, nullptr, | 
| +            [](WebSettings* settings) { settings->setDeviceSupportsMouse(true); }); | 
| +        m_mainFrame = m_webView->mainFrameImpl(); | 
| +        FrameTestHelpers::loadHTMLString(m_mainFrame, kHTMLString, URLTestHelpers::toKURL("http://foobar.com")); | 
| +        m_webView->resize(WebSize(800, 600)); | 
| +        m_webView->page()->focusController().setActive(true); | 
| + | 
| +        auto* document = m_mainFrame->frame()->document(); | 
| +        ASSERT_NE(nullptr, document); | 
| +        auto* linkToSelect = document->getElementById("link")->firstChild(); | 
| +        ASSERT_NE(nullptr, linkToSelect); | 
| +        // We get larger range that we actually want to select, because we need a slightly larger | 
| +        // rect to include the last character to the selection. | 
| +        const auto rangeToSelect = Range::create(*document, linkToSelect, 5, linkToSelect, 16); | 
| + | 
| +        const auto& selectionRect = rangeToSelect->boundingBox(); | 
| +        const auto& selectionRectCenterY = selectionRect.center().y(); | 
| +        m_leftPointInLink = selectionRect.minXMinYCorner(); | 
| +        m_leftPointInLink.setY(selectionRectCenterY); | 
| + | 
| +        m_rightPointInLink = selectionRect.maxXMinYCorner(); | 
| +        m_rightPointInLink.setY(selectionRectCenterY); | 
| +        m_rightPointInLink.move(-2, 0); | 
| +    } | 
| + | 
| +    TestFrameClient m_testFrameClient; | 
| +    IntPoint m_leftPointInLink; | 
| +    IntPoint m_rightPointInLink; | 
| +}; | 
| + | 
| +TEST_F(LinkSelectionTest, MouseDragWithoutAltAllowNoLinkSelection) | 
| +{ | 
| +    emulateMouseDrag(m_leftPointInLink, m_rightPointInLink, 0); | 
| +    EXPECT_TRUE(getSelectionText().isEmpty()); | 
| +} | 
| + | 
| +TEST_F(LinkSelectionTest, MouseDragWithAltAllowSelection) | 
| +{ | 
| +    emulateMouseDrag(m_leftPointInLink, m_rightPointInLink, WebInputEvent::AltKey); | 
| +    EXPECT_EQ("to select", getSelectionText()); | 
| +} | 
| + | 
| +TEST_F(LinkSelectionTest, HandCursorDuringLinkDrag) | 
| +{ | 
| +    emulateMouseDrag(m_rightPointInLink, m_leftPointInLink, 0, SendDownEvent); | 
| +    m_mainFrame->frame()->localFrameRoot()->eventHandler().scheduleCursorUpdate(); | 
| +    testing::runDelayedTasks(50); | 
| +    const auto& cursor = m_mainFrame->frame()->chromeClient().lastSetCursorForTesting(); | 
| +    EXPECT_EQ(Cursor::Hand, cursor.getType()); | 
| +} | 
| + | 
| +TEST_F(LinkSelectionTest, CaretCursorOverLinkDuringSelection) | 
| +{ | 
| +    emulateMouseDrag(m_rightPointInLink, m_leftPointInLink, WebInputEvent::AltKey, SendDownEvent); | 
| +    m_mainFrame->frame()->localFrameRoot()->eventHandler().scheduleCursorUpdate(); | 
| +    testing::runDelayedTasks(50); | 
| +    const auto& cursor = m_mainFrame->frame()->chromeClient().lastSetCursorForTesting(); | 
| +    EXPECT_EQ(Cursor::IBeam, cursor.getType()); | 
| +} | 
| + | 
| +TEST_F(LinkSelectionTest, HandCursorOverLinkAfterContextMenu) | 
| +{ | 
| +    // Move mouse. | 
| +    emulateMouseDrag(m_rightPointInLink, m_leftPointInLink, 0, 0); | 
| + | 
| +    // Show context menu. We don't send mouseup event here since in browser it doesn't reach | 
| +    // blink because of shown context menu. | 
| +    emulateMouseDown(m_leftPointInLink, WebMouseEvent::ButtonRight, 0, 1); | 
| + | 
| +    LocalFrame* frame = m_mainFrame->frame(); | 
| +    // Hide context menu. | 
| +    frame->page()->contextMenuController().clearContextMenu(); | 
| + | 
| +    frame->localFrameRoot()->eventHandler().scheduleCursorUpdate(); | 
| +    testing::runDelayedTasks(50); | 
| +    const auto& cursor = m_mainFrame->frame()->chromeClient().lastSetCursorForTesting(); | 
| +    EXPECT_EQ(Cursor::Hand, cursor.getType()); | 
| +} | 
| + | 
| +TEST_F(LinkSelectionTest, SingleClickWithAltStartsDownload) | 
| +{ | 
| +    EXPECT_CALL(m_testFrameClient, loadURLExternally(_, WebNavigationPolicy::WebNavigationPolicyDownload, WebString(), _)); | 
| +    emulateMouseClick(m_leftPointInLink, WebMouseEvent::ButtonLeft, WebInputEvent::AltKey); | 
| +} | 
| + | 
| +TEST_F(LinkSelectionTest, SingleClickWithAltStartsDownloadWhenTextSelected) | 
| +{ | 
| +    auto* document = m_mainFrame->frame()->document(); | 
| +    auto* textToSelect = document->getElementById("page_text")->firstChild(); | 
| +    ASSERT_NE(nullptr, textToSelect); | 
| + | 
| +    // Select some page text outside the link element. | 
| +    const RefPtrWillBeRawPtr<Range> rangeToSelect = Range::create(*document, textToSelect, 1, textToSelect, 20); | 
| +    const auto& selectionRect = rangeToSelect->boundingBox(); | 
| +    m_mainFrame->moveRangeSelection(selectionRect.minXMinYCorner(), selectionRect.maxXMaxYCorner()); | 
| +    EXPECT_FALSE(getSelectionText().isEmpty()); | 
| + | 
| +    EXPECT_CALL(m_testFrameClient, loadURLExternally(_, WebNavigationPolicy::WebNavigationPolicyDownload, WebString(), _)); | 
| +    emulateMouseClick(m_leftPointInLink, WebMouseEvent::ButtonLeft, WebInputEvent::AltKey); | 
| +} | 
| + | 
| +class LinkSelectionClickEventsTest : public LinkSelectionTestBase { | 
| +protected: | 
| +    class MockEventListener final : public EventListener { | 
| +    public: | 
| +        static PassRefPtrWillBeRawPtr<MockEventListener> create() | 
| +        { | 
| +            return adoptRefWillBeNoop(new MockEventListener()); | 
| +        } | 
| + | 
| +        bool operator==(const EventListener& other) const final | 
| +        { | 
| +            return this == &other; | 
| +        } | 
| + | 
| +        MOCK_METHOD2(handleEvent, void(ExecutionContext* executionContext, Event*)); | 
| + | 
| +    private: | 
| +        MockEventListener() : EventListener(CPPEventListenerType) | 
| +        { | 
| +        } | 
| +    }; | 
| + | 
| +    void SetUp() override | 
| +    { | 
| +        const char* const kHTMLString = | 
| +            "<div id='empty_div' style='width: 100px; height: 100px;'></div>" | 
| +            "<span id='text_div'>Sometexttoshow</span>"; | 
| + | 
| +        m_webView = m_helper.initialize(false); | 
| +        m_mainFrame = m_webView->mainFrameImpl(); | 
| +        FrameTestHelpers::loadHTMLString(m_mainFrame, kHTMLString, URLTestHelpers::toKURL("http://foobar.com")); | 
| +        m_webView->resize(WebSize(800, 600)); | 
| +        m_webView->page()->focusController().setActive(true); | 
| + | 
| +        auto* document = m_mainFrame->frame()->document(); | 
| +        ASSERT_NE(nullptr, document); | 
| + | 
| +        auto* emptyDiv = document->getElementById("empty_div"); | 
| +        auto* textDiv = document->getElementById("text_div"); | 
| +        ASSERT_NE(nullptr, emptyDiv); | 
| +        ASSERT_NE(nullptr, textDiv); | 
| +    } | 
| + | 
| +    void checkMouseClicks(Element& element, bool doubleClickEvent) | 
| +    { | 
| +        struct ScopedListenersCleaner { | 
| +            ScopedListenersCleaner(Element* element) : m_element(element) {} | 
| + | 
| +            ~ScopedListenersCleaner() | 
| +            { | 
| +                m_element->removeAllEventListeners(); | 
| +            } | 
| + | 
| +            RawPtrWillBePersistent<Element> m_element; | 
| +        } const listenersCleaner(&element); | 
| + | 
| +        RefPtrWillBeRawPtr<MockEventListener> eventHandler = MockEventListener::create(); | 
| +        element.addEventListener( | 
| +            doubleClickEvent ? EventTypeNames::dblclick : EventTypeNames::click, | 
| +            eventHandler); | 
| + | 
| +        ::testing::InSequence s; | 
| +        EXPECT_CALL(*eventHandler, handleEvent(_, _)).Times(1); | 
| + | 
| +        const auto& elemBounds = element.boundsInViewport(); | 
| +        const int clickCount = doubleClickEvent ? 2 : 1; | 
| +        emulateMouseClick(elemBounds.center(), WebMouseEvent::ButtonLeft, 0, clickCount); | 
| + | 
| +        if (doubleClickEvent) { | 
| +            EXPECT_EQ(element.innerText().isEmpty(), getSelectionText().isEmpty()); | 
| +        } | 
| +    } | 
| +}; | 
| + | 
| +TEST_F(LinkSelectionClickEventsTest, SingleAndDoubleClickWillBeHandled) | 
| +{ | 
| +    auto* document = m_mainFrame->frame()->document(); | 
| +    auto* element = document->getElementById("empty_div"); | 
| + | 
| +    { | 
| +        SCOPED_TRACE("Empty div, single click"); | 
| +        checkMouseClicks(*element, false); | 
| +    } | 
| + | 
| +    { | 
| +        SCOPED_TRACE("Empty div, double click"); | 
| +        checkMouseClicks(*element, true); | 
| +    } | 
| + | 
| +    element = document->getElementById("text_div"); | 
| + | 
| +    { | 
| +        SCOPED_TRACE("Text div, single click"); | 
| +        checkMouseClicks(*element, false); | 
| +    } | 
| + | 
| +    { | 
| +        SCOPED_TRACE("Text div, double click"); | 
| +        checkMouseClicks(*element, true); | 
| +    } | 
| +} | 
| + | 
| +} // namespace blink | 
|  |