| OLD | NEW |
| 1 // Copyright 2015 The Chromium Authors. All rights reserved. | 1 // Copyright 2015 The Chromium Authors. All rights reserved. |
| 2 // Use of this source code is governed by a BSD-style license that can be | 2 // Use of this source code is governed by a BSD-style license that can be |
| 3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
| 4 | 4 |
| 5 #include "core/input/EventHandler.h" | 5 #include "core/input/EventHandler.h" |
| 6 | 6 |
| 7 #include "core/dom/Document.h" | 7 #include "core/dom/Document.h" |
| 8 #include "core/dom/Range.h" | 8 #include "core/dom/Range.h" |
| 9 #include "core/editing/Editor.h" | 9 #include "core/editing/Editor.h" |
| 10 #include "core/editing/FrameSelection.h" | 10 #include "core/editing/FrameSelection.h" |
| 11 #include "core/frame/FrameView.h" | 11 #include "core/frame/FrameView.h" |
| 12 #include "core/frame/LocalFrame.h" | 12 #include "core/frame/LocalFrame.h" |
| 13 #include "core/frame/Settings.h" | 13 #include "core/frame/Settings.h" |
| 14 #include "core/page/AutoscrollController.h" | 14 #include "core/page/AutoscrollController.h" |
| 15 #include "core/page/Page.h" | 15 #include "core/page/Page.h" |
| 16 #include "core/testing/DummyPageHolder.h" | 16 #include "core/testing/DummyPageHolder.h" |
| 17 #include "platform/PlatformMouseEvent.h" | 17 #include "platform/PlatformMouseEvent.h" |
| 18 #include "testing/gtest/include/gtest/gtest.h" | 18 #include "testing/gtest/include/gtest/gtest.h" |
| 19 #include <memory> | 19 #include <memory> |
| 20 | 20 |
| 21 namespace blink { | 21 namespace blink { |
| 22 | 22 |
| 23 class EventHandlerTest : public ::testing::Test { | 23 class EventHandlerTest : public ::testing::Test { |
| 24 protected: | 24 protected: |
| 25 void SetUp() override; | 25 void SetUp() override; |
| 26 | 26 |
| 27 Page& page() const { return m_dummyPageHolder->page(); } | 27 Page& page() const { return m_dummyPageHolder->page(); } |
| 28 Document& document() const { return m_dummyPageHolder->document(); } | 28 Document& document() const { return m_dummyPageHolder->document(); } |
| 29 FrameSelection& selection() const { return document().frame()->selection(); } |
| 29 | 30 |
| 30 void setHtmlInnerHTML(const char* htmlContent); | 31 void setHtmlInnerHTML(const char* htmlContent); |
| 31 | 32 |
| 32 private: | 33 private: |
| 33 std::unique_ptr<DummyPageHolder> m_dummyPageHolder; | 34 std::unique_ptr<DummyPageHolder> m_dummyPageHolder; |
| 34 }; | 35 }; |
| 35 | 36 |
| 36 class TapEventBuilder : public WebGestureEvent { | 37 class TapEventBuilder : public WebGestureEvent { |
| 37 public: | 38 public: |
| 38 TapEventBuilder(IntPoint position, int tapCount) | 39 TapEventBuilder(IntPoint position, int tapCount) |
| 39 : WebGestureEvent(WebInputEvent::GestureTap, | 40 : WebGestureEvent(WebInputEvent::GestureTap, |
| 40 WebInputEvent::NoModifiers, | 41 WebInputEvent::NoModifiers, |
| 41 TimeTicks::Now().InSeconds()) { | 42 TimeTicks::Now().InSeconds()) { |
| 42 x = globalX = position.x(); | 43 x = globalX = position.x(); |
| 43 y = globalY = position.y(); | 44 y = globalY = position.y(); |
| 44 sourceDevice = WebGestureDeviceTouchscreen; | 45 sourceDevice = WebGestureDeviceTouchscreen; |
| 45 data.tap.tapCount = tapCount; | 46 data.tap.tapCount = tapCount; |
| 46 data.tap.width = 5; | 47 data.tap.width = 5; |
| 47 data.tap.height = 5; | 48 data.tap.height = 5; |
| 48 m_frameScale = 1; | 49 m_frameScale = 1; |
| 49 } | 50 } |
| 50 }; | 51 }; |
| 51 | 52 |
| 53 class LongPressEventBuilder : public WebGestureEvent { |
| 54 public: |
| 55 LongPressEventBuilder(IntPoint position) : WebGestureEvent() { |
| 56 m_type = WebInputEvent::GestureLongPress; |
| 57 x = globalX = position.x(); |
| 58 y = globalY = position.y(); |
| 59 sourceDevice = WebGestureDeviceTouchscreen; |
| 60 data.longPress.width = 5; |
| 61 data.longPress.height = 5; |
| 62 m_frameScale = 1; |
| 63 } |
| 64 }; |
| 65 |
| 66 class MousePressEventBuilder : public PlatformMouseEvent { |
| 67 public: |
| 68 MousePressEventBuilder(IntPoint position, |
| 69 int clickCount, |
| 70 WebMouseEvent::Button button) |
| 71 : PlatformMouseEvent(position, |
| 72 position, |
| 73 button, |
| 74 PlatformEvent::MousePressed, |
| 75 clickCount, |
| 76 static_cast<PlatformEvent::Modifiers>(0), |
| 77 TimeTicks::Now()) {} |
| 78 }; |
| 79 |
| 52 void EventHandlerTest::SetUp() { | 80 void EventHandlerTest::SetUp() { |
| 53 m_dummyPageHolder = DummyPageHolder::create(IntSize(300, 400)); | 81 m_dummyPageHolder = DummyPageHolder::create(IntSize(300, 400)); |
| 54 } | 82 } |
| 55 | 83 |
| 56 void EventHandlerTest::setHtmlInnerHTML(const char* htmlContent) { | 84 void EventHandlerTest::setHtmlInnerHTML(const char* htmlContent) { |
| 57 document().documentElement()->setInnerHTML(String::fromUTF8(htmlContent)); | 85 document().documentElement()->setInnerHTML(String::fromUTF8(htmlContent)); |
| 58 document().view()->updateAllLifecyclePhases(); | 86 document().view()->updateAllLifecyclePhases(); |
| 59 } | 87 } |
| 60 | 88 |
| 61 TEST_F(EventHandlerTest, dragSelectionAfterScroll) { | 89 TEST_F(EventHandlerTest, dragSelectionAfterScroll) { |
| (...skipping 31 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 93 page().autoscrollController().animate(WTF::monotonicallyIncreasingTime()); | 121 page().autoscrollController().animate(WTF::monotonicallyIncreasingTime()); |
| 94 page().animator().serviceScriptedAnimations( | 122 page().animator().serviceScriptedAnimations( |
| 95 WTF::monotonicallyIncreasingTime()); | 123 WTF::monotonicallyIncreasingTime()); |
| 96 | 124 |
| 97 PlatformMouseEvent mouseUpEvent( | 125 PlatformMouseEvent mouseUpEvent( |
| 98 IntPoint(100, 50), IntPoint(200, 250), WebPointerProperties::Button::Left, | 126 IntPoint(100, 50), IntPoint(200, 250), WebPointerProperties::Button::Left, |
| 99 PlatformEvent::MouseReleased, 1, static_cast<PlatformEvent::Modifiers>(0), | 127 PlatformEvent::MouseReleased, 1, static_cast<PlatformEvent::Modifiers>(0), |
| 100 TimeTicks::Now()); | 128 TimeTicks::Now()); |
| 101 document().frame()->eventHandler().handleMouseReleaseEvent(mouseUpEvent); | 129 document().frame()->eventHandler().handleMouseReleaseEvent(mouseUpEvent); |
| 102 | 130 |
| 103 FrameSelection& selection = document().frame()->selection(); | 131 ASSERT_TRUE(selection().isRange()); |
| 104 ASSERT_TRUE(selection.isRange()); | |
| 105 Range* range = | 132 Range* range = |
| 106 createRange(selection.selection().toNormalizedEphemeralRange()); | 133 createRange(selection().selection().toNormalizedEphemeralRange()); |
| 107 ASSERT_TRUE(range); | 134 ASSERT_TRUE(range); |
| 108 EXPECT_EQ("Line 1\nLine 2", range->text()); | 135 EXPECT_EQ("Line 1\nLine 2", range->text()); |
| 109 } | 136 } |
| 110 | 137 |
| 111 TEST_F(EventHandlerTest, multiClickSelectionFromTap) { | 138 TEST_F(EventHandlerTest, multiClickSelectionFromTap) { |
| 112 setHtmlInnerHTML( | 139 setHtmlInnerHTML( |
| 113 "<style> body { margin: 0px; } .line { display: block; width: 300px; " | 140 "<style> body { margin: 0px; } .line { display: block; width: 300px; " |
| 114 "height: 30px; } </style>" | 141 "height: 30px; } </style>" |
| 115 "<body contenteditable='true'><span class='line' id='line'>One Two " | 142 "<body contenteditable='true'><span class='line' id='line'>One Two " |
| 116 "Three</span></body>"); | 143 "Three</span></body>"); |
| 117 | 144 |
| 118 FrameSelection& selection = document().frame()->selection(); | |
| 119 Node* line = document().getElementById("line")->firstChild(); | 145 Node* line = document().getElementById("line")->firstChild(); |
| 120 | 146 |
| 121 TapEventBuilder singleTapEvent(IntPoint(0, 0), 1); | 147 TapEventBuilder singleTapEvent(IntPoint(0, 0), 1); |
| 122 document().frame()->eventHandler().handleGestureEvent(singleTapEvent); | 148 document().frame()->eventHandler().handleGestureEvent(singleTapEvent); |
| 123 ASSERT_TRUE(selection.isCaret()); | 149 ASSERT_TRUE(selection().isCaret()); |
| 124 EXPECT_EQ(Position(line, 0), selection.start()); | 150 EXPECT_EQ(Position(line, 0), selection().start()); |
| 125 | 151 |
| 126 // Multi-tap events on editable elements should trigger selection, just | 152 // Multi-tap events on editable elements should trigger selection, just |
| 127 // like multi-click events. | 153 // like multi-click events. |
| 128 TapEventBuilder doubleTapEvent(IntPoint(0, 0), 2); | 154 TapEventBuilder doubleTapEvent(IntPoint(0, 0), 2); |
| 129 document().frame()->eventHandler().handleGestureEvent(doubleTapEvent); | 155 document().frame()->eventHandler().handleGestureEvent(doubleTapEvent); |
| 130 ASSERT_TRUE(selection.isRange()); | 156 ASSERT_TRUE(selection().isRange()); |
| 131 EXPECT_EQ(Position(line, 0), selection.start()); | 157 EXPECT_EQ(Position(line, 0), selection().start()); |
| 132 if (document().frame()->editor().isSelectTrailingWhitespaceEnabled()) { | 158 if (document().frame()->editor().isSelectTrailingWhitespaceEnabled()) { |
| 133 EXPECT_EQ(Position(line, 4), selection.end()); | 159 EXPECT_EQ(Position(line, 4), selection().end()); |
| 134 EXPECT_EQ("One ", WebString(selection.selectedText()).utf8()); | 160 EXPECT_EQ("One ", WebString(selection().selectedText()).utf8()); |
| 135 } else { | 161 } else { |
| 136 EXPECT_EQ(Position(line, 3), selection.end()); | 162 EXPECT_EQ(Position(line, 3), selection().end()); |
| 137 EXPECT_EQ("One", WebString(selection.selectedText()).utf8()); | 163 EXPECT_EQ("One", WebString(selection().selectedText()).utf8()); |
| 138 } | 164 } |
| 139 | 165 |
| 140 TapEventBuilder tripleTapEvent(IntPoint(0, 0), 3); | 166 TapEventBuilder tripleTapEvent(IntPoint(0, 0), 3); |
| 141 document().frame()->eventHandler().handleGestureEvent(tripleTapEvent); | 167 document().frame()->eventHandler().handleGestureEvent(tripleTapEvent); |
| 142 ASSERT_TRUE(selection.isRange()); | 168 ASSERT_TRUE(selection().isRange()); |
| 143 EXPECT_EQ(Position(line, 0), selection.start()); | 169 EXPECT_EQ(Position(line, 0), selection().start()); |
| 144 EXPECT_EQ(Position(line, 13), selection.end()); | 170 EXPECT_EQ(Position(line, 13), selection().end()); |
| 145 EXPECT_EQ("One Two Three", WebString(selection.selectedText()).utf8()); | 171 EXPECT_EQ("One Two Three", WebString(selection().selectedText()).utf8()); |
| 146 } | 172 } |
| 147 | 173 |
| 148 TEST_F(EventHandlerTest, multiClickSelectionFromTapDisabledIfNotEditable) { | 174 TEST_F(EventHandlerTest, multiClickSelectionFromTapDisabledIfNotEditable) { |
| 149 setHtmlInnerHTML( | 175 setHtmlInnerHTML( |
| 150 "<style> body { margin: 0px; } .line { display: block; width: 300px; " | 176 "<style> body { margin: 0px; } .line { display: block; width: 300px; " |
| 151 "height: 30px; } </style>" | 177 "height: 30px; } </style>" |
| 152 "<span class='line' id='line'>One Two Three</span>"); | 178 "<span class='line' id='line'>One Two Three</span>"); |
| 153 | 179 |
| 154 FrameSelection& selection = document().frame()->selection(); | |
| 155 Node* line = document().getElementById("line")->firstChild(); | 180 Node* line = document().getElementById("line")->firstChild(); |
| 156 | 181 |
| 157 TapEventBuilder singleTapEvent(IntPoint(0, 0), 1); | 182 TapEventBuilder singleTapEvent(IntPoint(0, 0), 1); |
| 158 document().frame()->eventHandler().handleGestureEvent(singleTapEvent); | 183 document().frame()->eventHandler().handleGestureEvent(singleTapEvent); |
| 159 ASSERT_TRUE(selection.isCaret()); | 184 ASSERT_TRUE(selection().isCaret()); |
| 160 EXPECT_EQ(Position(line, 0), selection.start()); | 185 EXPECT_EQ(Position(line, 0), selection().start()); |
| 161 | 186 |
| 162 // As the text is readonly, multi-tap events should not trigger selection. | 187 // As the text is readonly, multi-tap events should not trigger selection. |
| 163 TapEventBuilder doubleTapEvent(IntPoint(0, 0), 2); | 188 TapEventBuilder doubleTapEvent(IntPoint(0, 0), 2); |
| 164 document().frame()->eventHandler().handleGestureEvent(doubleTapEvent); | 189 document().frame()->eventHandler().handleGestureEvent(doubleTapEvent); |
| 165 ASSERT_TRUE(selection.isCaret()); | 190 ASSERT_TRUE(selection().isCaret()); |
| 166 EXPECT_EQ(Position(line, 0), selection.start()); | 191 EXPECT_EQ(Position(line, 0), selection().start()); |
| 167 | 192 |
| 168 TapEventBuilder tripleTapEvent(IntPoint(0, 0), 3); | 193 TapEventBuilder tripleTapEvent(IntPoint(0, 0), 3); |
| 169 document().frame()->eventHandler().handleGestureEvent(tripleTapEvent); | 194 document().frame()->eventHandler().handleGestureEvent(tripleTapEvent); |
| 170 ASSERT_TRUE(selection.isCaret()); | 195 ASSERT_TRUE(selection().isCaret()); |
| 171 EXPECT_EQ(Position(line, 0), selection.start()); | 196 EXPECT_EQ(Position(line, 0), selection().start()); |
| 172 } | 197 } |
| 173 | 198 |
| 174 TEST_F(EventHandlerTest, draggedInlinePositionTest) { | 199 TEST_F(EventHandlerTest, draggedInlinePositionTest) { |
| 175 setHtmlInnerHTML( | 200 setHtmlInnerHTML( |
| 176 "<style>" | 201 "<style>" |
| 177 "body { margin: 0px; }" | 202 "body { margin: 0px; }" |
| 178 ".line { font-family: sans-serif; background: blue; width: 300px; " | 203 ".line { font-family: sans-serif; background: blue; width: 300px; " |
| 179 "height: 30px; font-size: 40px; margin-left: 250px; }" | 204 "height: 30px; font-size: 40px; margin-left: 250px; }" |
| 180 "</style>" | 205 "</style>" |
| 181 "<div style='width: 300px; height: 100px;'>" | 206 "<div style='width: 300px; height: 100px;'>" |
| (...skipping 68 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 250 .build()); | 275 .build()); |
| 251 PlatformMouseEvent mouseDownEvent( | 276 PlatformMouseEvent mouseDownEvent( |
| 252 IntPoint(0, 0), IntPoint(100, 200), WebPointerProperties::Button::Right, | 277 IntPoint(0, 0), IntPoint(100, 200), WebPointerProperties::Button::Right, |
| 253 PlatformEvent::MousePressed, 1, PlatformEvent::Modifiers::RightButtonDown, | 278 PlatformEvent::MousePressed, 1, PlatformEvent::Modifiers::RightButtonDown, |
| 254 TimeTicks::Now()); | 279 TimeTicks::Now()); |
| 255 EXPECT_EQ( | 280 EXPECT_EQ( |
| 256 WebInputEventResult::HandledApplication, | 281 WebInputEventResult::HandledApplication, |
| 257 document().frame()->eventHandler().sendContextMenuEvent(mouseDownEvent)); | 282 document().frame()->eventHandler().sendContextMenuEvent(mouseDownEvent)); |
| 258 } | 283 } |
| 259 | 284 |
| 285 TEST_F(EventHandlerTest, EmptyTextfieldInsertionOnTap) { |
| 286 setHtmlInnerHTML("<textarea cols=50 rows=50></textarea>"); |
| 287 |
| 288 TapEventBuilder singleTapEvent(IntPoint(200, 200), 1); |
| 289 document().frame()->eventHandler().handleGestureEvent(singleTapEvent); |
| 290 |
| 291 ASSERT_TRUE(selection().isCaret()); |
| 292 ASSERT_FALSE(selection().isHandleVisible()); |
| 293 } |
| 294 |
| 295 TEST_F(EventHandlerTest, NonEmptyTextfieldInsertionOnTap) { |
| 296 setHtmlInnerHTML("<textarea cols=50 rows=50>Enter text</textarea>"); |
| 297 |
| 298 TapEventBuilder singleTapEvent(IntPoint(200, 200), 1); |
| 299 document().frame()->eventHandler().handleGestureEvent(singleTapEvent); |
| 300 |
| 301 ASSERT_TRUE(selection().isCaret()); |
| 302 ASSERT_TRUE(selection().isHandleVisible()); |
| 303 } |
| 304 |
| 305 TEST_F(EventHandlerTest, EmptyTextfieldInsertionOnLongPress) { |
| 306 setHtmlInnerHTML("<textarea cols=50 rows=50></textarea>"); |
| 307 |
| 308 LongPressEventBuilder longPressEvent(IntPoint(200, 200)); |
| 309 document().frame()->eventHandler().handleGestureEvent(longPressEvent); |
| 310 |
| 311 ASSERT_TRUE(selection().isCaret()); |
| 312 ASSERT_TRUE(selection().isHandleVisible()); |
| 313 |
| 314 // Single Tap on an empty edit field should clear insertion handle |
| 315 TapEventBuilder singleTapEvent(IntPoint(200, 200), 1); |
| 316 document().frame()->eventHandler().handleGestureEvent(singleTapEvent); |
| 317 |
| 318 ASSERT_TRUE(selection().isCaret()); |
| 319 ASSERT_FALSE(selection().isHandleVisible()); |
| 320 } |
| 321 |
| 322 TEST_F(EventHandlerTest, NonEmptyTextfieldInsertionOnLongPress) { |
| 323 setHtmlInnerHTML("<textarea cols=50 rows=50>Enter text</textarea>"); |
| 324 |
| 325 LongPressEventBuilder longPressEvent(IntPoint(200, 200)); |
| 326 document().frame()->eventHandler().handleGestureEvent(longPressEvent); |
| 327 |
| 328 ASSERT_TRUE(selection().isCaret()); |
| 329 ASSERT_TRUE(selection().isHandleVisible()); |
| 330 } |
| 331 |
| 332 TEST_F(EventHandlerTest, ClearHandleAfterTap) { |
| 333 setHtmlInnerHTML("<textarea cols=50 rows=50>Enter text</textarea>"); |
| 334 |
| 335 // Show handle |
| 336 LongPressEventBuilder longPressEvent(IntPoint(200, 200)); |
| 337 document().frame()->eventHandler().handleGestureEvent(longPressEvent); |
| 338 |
| 339 ASSERT_TRUE(selection().isCaret()); |
| 340 ASSERT_TRUE(selection().isHandleVisible()); |
| 341 |
| 342 // Tap away from text area should clear handle |
| 343 TapEventBuilder singleTapEvent(IntPoint(700, 700), 1); |
| 344 document().frame()->eventHandler().handleGestureEvent(singleTapEvent); |
| 345 |
| 346 ASSERT_TRUE(selection().isNone()); |
| 347 ASSERT_FALSE(selection().isHandleVisible()); |
| 348 } |
| 349 |
| 350 TEST_F(EventHandlerTest, HandleNotShownOnMouseEvents) { |
| 351 setHtmlInnerHTML("<textarea cols=50 rows=50>Enter text</textarea>"); |
| 352 |
| 353 MousePressEventBuilder leftMousePressEvent( |
| 354 IntPoint(200, 200), 1, WebPointerProperties::Button::Left); |
| 355 document().frame()->eventHandler().handleMousePressEvent(leftMousePressEvent); |
| 356 |
| 357 ASSERT_TRUE(selection().isCaret()); |
| 358 ASSERT_FALSE(selection().isHandleVisible()); |
| 359 |
| 360 MousePressEventBuilder rightMousePressEvent( |
| 361 IntPoint(200, 200), 1, WebPointerProperties::Button::Right); |
| 362 document().frame()->eventHandler().handleMousePressEvent( |
| 363 rightMousePressEvent); |
| 364 |
| 365 ASSERT_TRUE(selection().isCaret()); |
| 366 ASSERT_FALSE(selection().isHandleVisible()); |
| 367 |
| 368 MousePressEventBuilder doubleClickMousePressEvent( |
| 369 IntPoint(200, 200), 2, WebPointerProperties::Button::Left); |
| 370 document().frame()->eventHandler().handleMousePressEvent( |
| 371 doubleClickMousePressEvent); |
| 372 |
| 373 ASSERT_TRUE(selection().isRange()); |
| 374 ASSERT_FALSE(selection().isHandleVisible()); |
| 375 |
| 376 MousePressEventBuilder tripleClickMousePressEvent( |
| 377 IntPoint(200, 200), 3, WebPointerProperties::Button::Left); |
| 378 document().frame()->eventHandler().handleMousePressEvent( |
| 379 tripleClickMousePressEvent); |
| 380 |
| 381 ASSERT_TRUE(selection().isRange()); |
| 382 ASSERT_FALSE(selection().isHandleVisible()); |
| 383 } |
| 384 |
| 260 } // namespace blink | 385 } // namespace blink |
| OLD | NEW |