1// Copyright 2014 The Chromium Authors. All rights reserved. 2// Use of this source code is governed by a BSD-style license that can be 3// found in the LICENSE file. 4 5#include <vector> 6 7#include "base/basictypes.h" 8#include "base/memory/scoped_ptr.h" 9#include "base/message_loop/message_loop.h" 10#include "base/time/time.h" 11#include "content/browser/renderer_host/input/touch_emulator.h" 12#include "content/browser/renderer_host/input/touch_emulator_client.h" 13#include "content/common/input/web_input_event_traits.h" 14#include "testing/gtest/include/gtest/gtest.h" 15#include "ui/events/gesture_detection/gesture_config_helper.h" 16 17#if defined(USE_AURA) 18#include "ui/aura/env.h" 19#include "ui/aura/test/test_screen.h" 20#endif 21 22using blink::WebGestureEvent; 23using blink::WebInputEvent; 24using blink::WebKeyboardEvent; 25using blink::WebMouseEvent; 26using blink::WebMouseWheelEvent; 27using blink::WebTouchEvent; 28using blink::WebTouchPoint; 29 30namespace content { 31 32class TouchEmulatorTest : public testing::Test, 33 public TouchEmulatorClient { 34 public: 35 TouchEmulatorTest() 36 : shift_pressed_(false), 37 mouse_pressed_(false), 38 ack_touches_synchronously_(true), 39 last_mouse_x_(-1), 40 last_mouse_y_(-1) { 41 last_event_time_seconds_ = 42 (base::TimeTicks::Now() - base::TimeTicks()).InSecondsF(); 43 event_time_delta_seconds_ = 0.1; 44 } 45 46 virtual ~TouchEmulatorTest() {} 47 48 // testing::Test 49 virtual void SetUp() OVERRIDE { 50#if defined(USE_AURA) 51 aura::Env::CreateInstance(true); 52 screen_.reset(aura::TestScreen::Create(gfx::Size())); 53 gfx::Screen::SetScreenInstance(gfx::SCREEN_TYPE_NATIVE, screen_.get()); 54#endif 55 56 emulator_.reset(new TouchEmulator(this)); 57 emulator_->Enable(); 58 } 59 60 virtual void TearDown() OVERRIDE { 61 emulator_->Disable(); 62 EXPECT_EQ("", ExpectedEvents()); 63 64#if defined(USE_AURA) 65 aura::Env::DeleteInstance(); 66 screen_.reset(); 67#endif 68 } 69 70 virtual void ForwardGestureEvent( 71 const blink::WebGestureEvent& event) OVERRIDE { 72 forwarded_events_.push_back(event.type); 73 } 74 75 virtual void ForwardEmulatedTouchEvent( 76 const blink::WebTouchEvent& event) OVERRIDE { 77 forwarded_events_.push_back(event.type); 78 EXPECT_EQ(1U, event.touchesLength); 79 EXPECT_EQ(last_mouse_x_, event.touches[0].position.x); 80 EXPECT_EQ(last_mouse_y_, event.touches[0].position.y); 81 int expectedCancelable = event.type != WebInputEvent::TouchCancel; 82 EXPECT_EQ(expectedCancelable, event.cancelable); 83 if (ack_touches_synchronously_) { 84 emulator()->HandleTouchEventAck( 85 event, INPUT_EVENT_ACK_STATE_NO_CONSUMER_EXISTS); 86 } 87 } 88 89 virtual void SetCursor(const WebCursor& cursor) OVERRIDE {} 90 91 virtual void ShowContextMenuAtPoint(const gfx::Point& point) OVERRIDE {} 92 93 protected: 94 TouchEmulator* emulator() const { 95 return emulator_.get(); 96 } 97 98 int modifiers() const { 99 return shift_pressed_ ? WebInputEvent::ShiftKey : 0; 100 } 101 102 std::string ExpectedEvents() { 103 std::string result; 104 for (size_t i = 0; i < forwarded_events_.size(); ++i) { 105 if (i != 0) 106 result += " "; 107 result += WebInputEventTraits::GetName(forwarded_events_[i]); 108 } 109 forwarded_events_.clear(); 110 return result; 111 } 112 113 double GetNextEventTimeSeconds() { 114 last_event_time_seconds_ += event_time_delta_seconds_; 115 return last_event_time_seconds_; 116 } 117 118 void set_event_time_delta_seconds_(double delta) { 119 event_time_delta_seconds_ = delta; 120 } 121 122 void SendKeyboardEvent(WebInputEvent::Type type) { 123 WebKeyboardEvent event; 124 event.timeStampSeconds = GetNextEventTimeSeconds(); 125 event.type = type; 126 event.modifiers = modifiers(); 127 emulator()->HandleKeyboardEvent(event); 128 } 129 130 void PressShift() { 131 DCHECK(!shift_pressed_); 132 shift_pressed_ = true; 133 SendKeyboardEvent(WebInputEvent::KeyDown); 134 } 135 136 void ReleaseShift() { 137 DCHECK(shift_pressed_); 138 shift_pressed_ = false; 139 SendKeyboardEvent(WebInputEvent::KeyUp); 140 } 141 142 void SendMouseEvent(WebInputEvent::Type type, int x, int y) { 143 WebMouseEvent event; 144 event.timeStampSeconds = GetNextEventTimeSeconds(); 145 event.type = type; 146 event.button = mouse_pressed_ ? WebMouseEvent::ButtonLeft : 147 WebMouseEvent::ButtonNone; 148 event.modifiers = modifiers(); 149 last_mouse_x_ = x; 150 last_mouse_y_ = y; 151 event.x = event.windowX = event.globalX = x; 152 event.y = event.windowY = event.globalY = y; 153 emulator()->HandleMouseEvent(event); 154 } 155 156 bool SendMouseWheelEvent() { 157 WebMouseWheelEvent event; 158 event.type = WebInputEvent::MouseWheel; 159 event.timeStampSeconds = GetNextEventTimeSeconds(); 160 // Return whether mouse wheel is forwarded. 161 return !emulator()->HandleMouseWheelEvent(event); 162 } 163 164 void MouseDown(int x, int y) { 165 DCHECK(!mouse_pressed_); 166 if (x != last_mouse_x_ || y != last_mouse_y_) 167 SendMouseEvent(WebInputEvent::MouseMove, x, y); 168 mouse_pressed_ = true; 169 SendMouseEvent(WebInputEvent::MouseDown, x, y); 170 } 171 172 void MouseDrag(int x, int y) { 173 DCHECK(mouse_pressed_); 174 SendMouseEvent(WebInputEvent::MouseMove, x, y); 175 } 176 177 void MouseMove(int x, int y) { 178 DCHECK(!mouse_pressed_); 179 SendMouseEvent(WebInputEvent::MouseMove, x, y); 180 } 181 182 void MouseUp(int x, int y) { 183 DCHECK(mouse_pressed_); 184 if (x != last_mouse_x_ || y != last_mouse_y_) 185 SendMouseEvent(WebInputEvent::MouseMove, x, y); 186 SendMouseEvent(WebInputEvent::MouseUp, x, y); 187 mouse_pressed_ = false; 188 } 189 190 bool TouchStart(int x, int y, bool ack) { 191 return SendTouchEvent( 192 WebInputEvent::TouchStart, WebTouchPoint::StatePressed, x, y, ack); 193 } 194 195 bool TouchMove(int x, int y, bool ack) { 196 return SendTouchEvent( 197 WebInputEvent::TouchMove, WebTouchPoint::StateMoved, x, y, ack); 198 } 199 200 bool TouchEnd(int x, int y, bool ack) { 201 return SendTouchEvent( 202 WebInputEvent::TouchEnd, WebTouchPoint::StateReleased, x, y, ack); 203 } 204 205 WebTouchEvent MakeTouchEvent(WebInputEvent::Type type, 206 WebTouchPoint::State state, int x, int y) { 207 WebTouchEvent event; 208 event.type = type; 209 event.timeStampSeconds = GetNextEventTimeSeconds(); 210 event.touchesLength = 1; 211 event.touches[0].id = 0; 212 event.touches[0].state = state; 213 event.touches[0].position.x = x; 214 event.touches[0].position.y = y; 215 event.touches[0].screenPosition.x = x; 216 event.touches[0].screenPosition.y = y; 217 return event; 218 } 219 220 bool SendTouchEvent(WebInputEvent::Type type, WebTouchPoint::State state, 221 int x, int y, bool ack) { 222 WebTouchEvent event = MakeTouchEvent(type, state, x, y); 223 if (emulator()->HandleTouchEvent(event)) { 224 // Touch event is not forwarded. 225 return false; 226 } 227 228 if (ack) { 229 // Can't send ack if there are some pending acks. 230 DCHECK(!touch_events_to_ack_.size()); 231 232 // Touch event is forwarded, ack should not be handled by emulator. 233 EXPECT_FALSE(emulator()->HandleTouchEventAck( 234 event, INPUT_EVENT_ACK_STATE_CONSUMED)); 235 } else { 236 touch_events_to_ack_.push_back(event); 237 } 238 return true; 239 } 240 241 void AckOldestTouchEvent() { 242 DCHECK(touch_events_to_ack_.size()); 243 WebTouchEvent event = touch_events_to_ack_[0]; 244 touch_events_to_ack_.erase(touch_events_to_ack_.begin()); 245 // Emulator should not handle ack from native stream. 246 EXPECT_FALSE(emulator()->HandleTouchEventAck( 247 event, INPUT_EVENT_ACK_STATE_NO_CONSUMER_EXISTS)); 248 } 249 250 void DisableSynchronousTouchAck() { ack_touches_synchronously_ = false; } 251 252 private: 253 scoped_ptr<TouchEmulator> emulator_; 254 std::vector<WebInputEvent::Type> forwarded_events_; 255#if defined(USE_AURA) 256 scoped_ptr<gfx::Screen> screen_; 257#endif 258 double last_event_time_seconds_; 259 double event_time_delta_seconds_; 260 bool shift_pressed_; 261 bool mouse_pressed_; 262 bool ack_touches_synchronously_; 263 int last_mouse_x_; 264 int last_mouse_y_; 265 std::vector<WebTouchEvent> touch_events_to_ack_; 266 base::MessageLoopForUI message_loop_; 267}; 268 269 270TEST_F(TouchEmulatorTest, NoTouches) { 271 MouseMove(100, 200); 272 MouseMove(300, 300); 273 EXPECT_EQ("", ExpectedEvents()); 274} 275 276TEST_F(TouchEmulatorTest, Touch) { 277 MouseMove(100, 200); 278 EXPECT_EQ("", ExpectedEvents()); 279 MouseDown(100, 200); 280 EXPECT_EQ("TouchStart GestureTapDown", ExpectedEvents()); 281 MouseUp(200, 200); 282 EXPECT_EQ( 283 "TouchMove GestureTapCancel GestureScrollBegin GestureScrollUpdate" 284 " TouchEnd GestureScrollEnd", 285 ExpectedEvents()); 286} 287 288TEST_F(TouchEmulatorTest, MultipleTouches) { 289 MouseMove(100, 200); 290 EXPECT_EQ("", ExpectedEvents()); 291 MouseDown(100, 200); 292 EXPECT_EQ("TouchStart GestureTapDown", ExpectedEvents()); 293 MouseUp(200, 200); 294 EXPECT_EQ( 295 "TouchMove GestureTapCancel GestureScrollBegin GestureScrollUpdate" 296 " TouchEnd GestureScrollEnd", 297 ExpectedEvents()); 298 MouseMove(300, 200); 299 MouseMove(200, 200); 300 EXPECT_EQ("", ExpectedEvents()); 301 MouseDown(300, 200); 302 EXPECT_EQ("TouchStart GestureTapDown", ExpectedEvents()); 303 MouseDrag(300, 300); 304 EXPECT_EQ( 305 "TouchMove GestureTapCancel GestureScrollBegin GestureScrollUpdate", 306 ExpectedEvents()); 307 MouseDrag(300, 400); 308 EXPECT_EQ("TouchMove GestureScrollUpdate", ExpectedEvents()); 309 MouseUp(300, 500); 310 EXPECT_EQ( 311 "TouchMove GestureScrollUpdate TouchEnd GestureScrollEnd", 312 ExpectedEvents()); 313} 314 315TEST_F(TouchEmulatorTest, Pinch) { 316 MouseDown(100, 200); 317 EXPECT_EQ("TouchStart GestureTapDown", ExpectedEvents()); 318 MouseDrag(200, 200); 319 EXPECT_EQ( 320 "TouchMove GestureTapCancel GestureScrollBegin GestureScrollUpdate", 321 ExpectedEvents()); 322 PressShift(); 323 EXPECT_EQ("", ExpectedEvents()); 324 MouseDrag(300, 200); 325 EXPECT_EQ("TouchMove GesturePinchBegin", ExpectedEvents()); 326 ReleaseShift(); 327 EXPECT_EQ("", ExpectedEvents()); 328 MouseDrag(400, 200); 329 EXPECT_EQ( 330 "TouchMove GesturePinchEnd GestureScrollUpdate", 331 ExpectedEvents()); 332 MouseUp(400, 200); 333 EXPECT_EQ("TouchEnd GestureScrollEnd", ExpectedEvents()); 334} 335 336TEST_F(TouchEmulatorTest, CancelWithDelayedAck) { 337 DisableSynchronousTouchAck(); 338 339 // Simulate a sequence that is interrupted by |CancelTouch()|. 340 MouseDown(100, 200); 341 EXPECT_EQ("TouchStart", ExpectedEvents()); 342 MouseDrag(200, 200); 343 EXPECT_EQ("TouchMove", ExpectedEvents()); 344 emulator()->CancelTouch(); 345 EXPECT_EQ("TouchCancel", ExpectedEvents()); 346 // The mouse up should have no effect as the sequence was already cancelled. 347 MouseUp(400, 200); 348 EXPECT_EQ("", ExpectedEvents()); 349 350 // Simulate a sequence that fully completes before |CancelTouch()|. 351 MouseDown(100, 200); 352 EXPECT_EQ("TouchStart", ExpectedEvents()); 353 MouseUp(100, 200); 354 EXPECT_EQ("TouchEnd", ExpectedEvents()); 355 // |CancelTouch| should have no effect as the sequence was already terminated. 356 emulator()->CancelTouch(); 357 EXPECT_EQ("", ExpectedEvents()); 358} 359 360TEST_F(TouchEmulatorTest, DisableAndReenable) { 361 MouseDown(100, 200); 362 EXPECT_EQ("TouchStart GestureTapDown", ExpectedEvents()); 363 MouseDrag(200, 200); 364 EXPECT_EQ( 365 "TouchMove GestureTapCancel GestureScrollBegin GestureScrollUpdate", 366 ExpectedEvents()); 367 PressShift(); 368 MouseDrag(300, 200); 369 EXPECT_EQ("TouchMove GesturePinchBegin", ExpectedEvents()); 370 371 // Disable while pinch is in progress. 372 emulator()->Disable(); 373 EXPECT_EQ("TouchCancel GesturePinchEnd GestureScrollEnd", ExpectedEvents()); 374 MouseUp(300, 200); 375 ReleaseShift(); 376 MouseMove(300, 300); 377 EXPECT_EQ("", ExpectedEvents()); 378 379 emulator()->Enable(); 380 MouseDown(300, 300); 381 EXPECT_EQ("TouchStart GestureTapDown", ExpectedEvents()); 382 MouseDrag(300, 400); 383 EXPECT_EQ( 384 "TouchMove GestureTapCancel GestureScrollBegin GestureScrollUpdate", 385 ExpectedEvents()); 386 387 // Disable while scroll is in progress. 388 emulator()->Disable(); 389 EXPECT_EQ("TouchCancel GestureScrollEnd", ExpectedEvents()); 390} 391 392TEST_F(TouchEmulatorTest, MouseMovesDropped) { 393 MouseMove(100, 200); 394 EXPECT_EQ("", ExpectedEvents()); 395 MouseDown(100, 200); 396 EXPECT_EQ("TouchStart GestureTapDown", ExpectedEvents()); 397 398 // Mouse move after mouse down is never dropped. 399 set_event_time_delta_seconds_(0.001); 400 MouseDrag(200, 200); 401 EXPECT_EQ( 402 "TouchMove GestureTapCancel GestureScrollBegin GestureScrollUpdate", 403 ExpectedEvents()); 404 405 // The following mouse moves are dropped. 406 MouseDrag(300, 200); 407 EXPECT_EQ("", ExpectedEvents()); 408 MouseDrag(350, 200); 409 EXPECT_EQ("", ExpectedEvents()); 410 411 // Dispatching again. 412 set_event_time_delta_seconds_(0.1); 413 MouseDrag(400, 200); 414 EXPECT_EQ( 415 "TouchMove GestureScrollUpdate", 416 ExpectedEvents()); 417 MouseUp(400, 200); 418 EXPECT_EQ( 419 "TouchEnd GestureScrollEnd", 420 ExpectedEvents()); 421} 422 423TEST_F(TouchEmulatorTest, MouseWheel) { 424 MouseMove(100, 200); 425 EXPECT_EQ("", ExpectedEvents()); 426 EXPECT_TRUE(SendMouseWheelEvent()); 427 MouseDown(100, 200); 428 EXPECT_EQ("TouchStart GestureTapDown", ExpectedEvents()); 429 EXPECT_FALSE(SendMouseWheelEvent()); 430 MouseUp(100, 200); 431 EXPECT_EQ("TouchEnd GestureShowPress GestureTap", ExpectedEvents()); 432 EXPECT_TRUE(SendMouseWheelEvent()); 433 MouseDown(300, 200); 434 EXPECT_EQ("TouchStart GestureTapDown", ExpectedEvents()); 435 EXPECT_FALSE(SendMouseWheelEvent()); 436 emulator()->Disable(); 437 EXPECT_EQ("TouchCancel GestureTapCancel", ExpectedEvents()); 438 EXPECT_TRUE(SendMouseWheelEvent()); 439 emulator()->Enable(); 440 EXPECT_TRUE(SendMouseWheelEvent()); 441} 442 443TEST_F(TouchEmulatorTest, MultipleTouchStreams) { 444 // Native stream should be blocked while emulated is active. 445 MouseMove(100, 200); 446 EXPECT_EQ("", ExpectedEvents()); 447 MouseDown(100, 200); 448 EXPECT_EQ("TouchStart GestureTapDown", ExpectedEvents()); 449 EXPECT_FALSE(TouchStart(10, 10, true)); 450 EXPECT_FALSE(TouchMove(20, 20, true)); 451 MouseUp(200, 200); 452 EXPECT_EQ( 453 "TouchMove GestureTapCancel GestureScrollBegin GestureScrollUpdate" 454 " TouchEnd GestureScrollEnd", 455 ExpectedEvents()); 456 EXPECT_FALSE(TouchEnd(20, 20, true)); 457 458 // Emulated stream should be blocked while native is active. 459 EXPECT_TRUE(TouchStart(10, 10, true)); 460 EXPECT_TRUE(TouchMove(20, 20, true)); 461 MouseDown(300, 200); 462 EXPECT_EQ("", ExpectedEvents()); 463 // Re-enabling in the middle of a touch sequence should not affect this. 464 emulator()->Disable(); 465 emulator()->Enable(); 466 MouseDrag(300, 300); 467 EXPECT_EQ("", ExpectedEvents()); 468 MouseUp(300, 300); 469 EXPECT_EQ("", ExpectedEvents()); 470 EXPECT_TRUE(TouchEnd(20, 20, true)); 471 EXPECT_EQ("", ExpectedEvents()); 472 473 // Late ack for TouchEnd should not mess things up. 474 EXPECT_TRUE(TouchStart(10, 10, false)); 475 EXPECT_TRUE(TouchMove(20, 20, false)); 476 emulator()->Disable(); 477 EXPECT_TRUE(TouchEnd(20, 20, false)); 478 EXPECT_TRUE(TouchStart(30, 30, false)); 479 AckOldestTouchEvent(); // TouchStart. 480 emulator()->Enable(); 481 AckOldestTouchEvent(); // TouchMove. 482 AckOldestTouchEvent(); // TouchEnd. 483 MouseDown(300, 200); 484 EXPECT_EQ("", ExpectedEvents()); 485 MouseDrag(300, 300); 486 EXPECT_EQ("", ExpectedEvents()); 487 MouseUp(300, 300); 488 EXPECT_EQ("", ExpectedEvents()); 489 AckOldestTouchEvent(); // TouchStart. 490 MouseDown(300, 200); 491 EXPECT_EQ("", ExpectedEvents()); 492 EXPECT_TRUE(TouchMove(30, 40, true)); 493 EXPECT_TRUE(TouchEnd(30, 40, true)); 494 MouseUp(300, 200); 495 EXPECT_EQ("", ExpectedEvents()); 496 497 // Emulation should be back to normal. 498 MouseDown(100, 200); 499 EXPECT_EQ("TouchStart GestureTapDown", ExpectedEvents()); 500 MouseUp(200, 200); 501 EXPECT_EQ( 502 "TouchMove GestureTapCancel GestureScrollBegin GestureScrollUpdate" 503 " TouchEnd GestureScrollEnd", 504 ExpectedEvents()); 505} 506 507TEST_F(TouchEmulatorTest, MultipleTouchStreamsLateEnable) { 508 // Enabling in the middle of native touch sequence should be handled. 509 // Send artificial late TouchEnd ack, like it is the first thing emulator 510 // does see. 511 WebTouchEvent event = MakeTouchEvent( 512 WebInputEvent::TouchEnd, WebTouchPoint::StateReleased, 10, 10); 513 EXPECT_FALSE(emulator()->HandleTouchEventAck( 514 event, INPUT_EVENT_ACK_STATE_CONSUMED)); 515 516 MouseDown(100, 200); 517 EXPECT_EQ("TouchStart GestureTapDown", ExpectedEvents()); 518 MouseUp(200, 200); 519 EXPECT_EQ( 520 "TouchMove GestureTapCancel GestureScrollBegin GestureScrollUpdate" 521 " TouchEnd GestureScrollEnd", 522 ExpectedEvents()); 523} 524 525} // namespace content 526