Chromium Code Reviews| 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..96ca953edf2e42e680b3b1f980c75887598e3302 |
| --- /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); |
| + |
| + std::string getSelectionText(); |
|
tkent
2016/03/28 07:59:21
Please don't use std::string in Blink. Use WTF::S
kotenkov
2016/03/28 08:26:30
FIY: there are 251 more mentions of std::string in
|
| + |
| + 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); |
| +} |
| + |
| +std::string LinkSelectionTestBase::getSelectionText() |
| +{ |
| + return m_mainFrame->selectionAsText().utf8(); |
| +} |
| + |
| +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_EQ(std::string(), getSelectionText()); |
| +} |
| + |
| +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().empty()); |
| + |
| + 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().empty()); |
| + } |
| + } |
| +}; |
| + |
| +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 |