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