Home | History | Annotate | Download | only in test
      1 // Copyright (c) 2012 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 "ui/aura/test/event_generator.h"
      6 
      7 #include "base/bind.h"
      8 #include "base/memory/scoped_ptr.h"
      9 #include "base/message_loop/message_loop_proxy.h"
     10 #include "ui/aura/client/screen_position_client.h"
     11 #include "ui/aura/root_window.h"
     12 #include "ui/base/events/event.h"
     13 #include "ui/base/events/event_utils.h"
     14 #include "ui/gfx/vector2d_conversions.h"
     15 
     16 #if defined(USE_X11)
     17 #include <X11/Xlib.h>
     18 #include "ui/base/x/x11_util.h"
     19 #endif
     20 
     21 #if defined(OS_WIN)
     22 #include "ui/base/keycodes/keyboard_code_conversion.h"
     23 #endif
     24 
     25 namespace aura {
     26 namespace test {
     27 namespace {
     28 
     29 void DummyCallback(ui::EventType, const gfx::Vector2dF&) {
     30 }
     31 
     32 class DefaultEventGeneratorDelegate : public EventGeneratorDelegate {
     33  public:
     34   explicit DefaultEventGeneratorDelegate(RootWindow* root_window)
     35       : root_window_(root_window) {}
     36   virtual ~DefaultEventGeneratorDelegate() {}
     37 
     38   // EventGeneratorDelegate overrides:
     39   virtual RootWindow* GetRootWindowAt(const gfx::Point& point) const OVERRIDE {
     40     return root_window_;
     41   }
     42 
     43   virtual client::ScreenPositionClient* GetScreenPositionClient(
     44       const aura::Window* window) const OVERRIDE {
     45     return NULL;
     46   }
     47 
     48  private:
     49   RootWindow* root_window_;
     50 
     51   DISALLOW_COPY_AND_ASSIGN(DefaultEventGeneratorDelegate);
     52 };
     53 
     54 class TestKeyEvent : public ui::KeyEvent {
     55  public:
     56   TestKeyEvent(const base::NativeEvent& native_event, int flags, bool is_char)
     57       : KeyEvent(native_event, is_char) {
     58     set_flags(flags);
     59   }
     60 };
     61 
     62 class TestTouchEvent : public ui::TouchEvent {
     63  public:
     64   TestTouchEvent(ui::EventType type,
     65                  const gfx::Point& root_location,
     66                  int flags)
     67       : TouchEvent(type, root_location, flags, 0, ui::EventTimeForNow(),
     68                    1.0f, 1.0f, 1.0f, 1.0f) {
     69   }
     70 
     71  private:
     72   DISALLOW_COPY_AND_ASSIGN(TestTouchEvent);
     73 };
     74 
     75 const int kAllButtonMask = ui::EF_LEFT_MOUSE_BUTTON | ui::EF_RIGHT_MOUSE_BUTTON;
     76 
     77 }  // namespace
     78 
     79 EventGenerator::EventGenerator(RootWindow* root_window)
     80     : delegate_(new DefaultEventGeneratorDelegate(root_window)),
     81       current_root_window_(delegate_->GetRootWindowAt(current_location_)),
     82       flags_(0),
     83       grab_(false),
     84       async_(false) {
     85 }
     86 
     87 EventGenerator::EventGenerator(RootWindow* root_window, const gfx::Point& point)
     88     : delegate_(new DefaultEventGeneratorDelegate(root_window)),
     89       current_location_(point),
     90       current_root_window_(delegate_->GetRootWindowAt(current_location_)),
     91       flags_(0),
     92       grab_(false),
     93       async_(false) {
     94 }
     95 
     96 EventGenerator::EventGenerator(RootWindow* root_window, Window* window)
     97     : delegate_(new DefaultEventGeneratorDelegate(root_window)),
     98       current_location_(CenterOfWindow(window)),
     99       current_root_window_(delegate_->GetRootWindowAt(current_location_)),
    100       flags_(0),
    101       grab_(false),
    102       async_(false) {
    103 }
    104 
    105 EventGenerator::EventGenerator(EventGeneratorDelegate* delegate)
    106     : delegate_(delegate),
    107       current_root_window_(delegate_->GetRootWindowAt(current_location_)),
    108       flags_(0),
    109       grab_(false),
    110       async_(false) {
    111 }
    112 
    113 EventGenerator::~EventGenerator() {
    114   for (std::list<ui::Event*>::iterator i = pending_events_.begin();
    115       i != pending_events_.end(); ++i)
    116     delete *i;
    117   pending_events_.clear();
    118 }
    119 
    120 void EventGenerator::PressLeftButton() {
    121   PressButton(ui::EF_LEFT_MOUSE_BUTTON);
    122 }
    123 
    124 void EventGenerator::ReleaseLeftButton() {
    125   ReleaseButton(ui::EF_LEFT_MOUSE_BUTTON);
    126 }
    127 
    128 void EventGenerator::ClickLeftButton() {
    129   PressLeftButton();
    130   ReleaseLeftButton();
    131 }
    132 
    133 void EventGenerator::DoubleClickLeftButton() {
    134   flags_ |= ui::EF_IS_DOUBLE_CLICK;
    135   PressLeftButton();
    136   flags_ ^= ui::EF_IS_DOUBLE_CLICK;
    137   ReleaseLeftButton();
    138 }
    139 
    140 void EventGenerator::PressRightButton() {
    141   PressButton(ui::EF_RIGHT_MOUSE_BUTTON);
    142 }
    143 
    144 void EventGenerator::ReleaseRightButton() {
    145   ReleaseButton(ui::EF_RIGHT_MOUSE_BUTTON);
    146 }
    147 
    148 void EventGenerator::MoveMouseToInHost(const gfx::Point& point_in_host) {
    149   const ui::EventType event_type = (flags_ & ui::EF_LEFT_MOUSE_BUTTON) ?
    150       ui::ET_MOUSE_DRAGGED : ui::ET_MOUSE_MOVED;
    151   ui::MouseEvent mouseev(event_type, point_in_host, point_in_host, flags_);
    152   Dispatch(&mouseev);
    153 
    154   current_location_ = point_in_host;
    155   current_root_window_->ConvertPointFromHost(&current_location_);
    156 }
    157 
    158 void EventGenerator::MoveMouseTo(const gfx::Point& point_in_screen,
    159                                  int count) {
    160   DCHECK_GT(count, 0);
    161   const ui::EventType event_type = (flags_ & ui::EF_LEFT_MOUSE_BUTTON) ?
    162       ui::ET_MOUSE_DRAGGED : ui::ET_MOUSE_MOVED;
    163 
    164   gfx::Vector2dF diff(point_in_screen - current_location_);
    165   for (float i = 1; i <= count; i++) {
    166     gfx::Vector2dF step(diff);
    167     step.Scale(i / count);
    168     gfx::Point move_point = current_location_ + gfx::ToRoundedVector2d(step);
    169     if (!grab_)
    170       UpdateCurrentRootWindow(move_point);
    171     ConvertPointToTarget(current_root_window_, &move_point);
    172     ui::MouseEvent mouseev(event_type, move_point, move_point, flags_);
    173     Dispatch(&mouseev);
    174   }
    175   current_location_ = point_in_screen;
    176 }
    177 
    178 void EventGenerator::MoveMouseRelativeTo(const Window* window,
    179                                          const gfx::Point& point_in_parent) {
    180   gfx::Point point(point_in_parent);
    181   ConvertPointFromTarget(window, &point);
    182   MoveMouseTo(point);
    183 }
    184 
    185 void EventGenerator::DragMouseTo(const gfx::Point& point) {
    186   PressLeftButton();
    187   MoveMouseTo(point);
    188   ReleaseLeftButton();
    189 }
    190 
    191 void EventGenerator::MoveMouseToCenterOf(Window* window) {
    192   MoveMouseTo(CenterOfWindow(window));
    193 }
    194 
    195 void EventGenerator::PressTouch() {
    196   TestTouchEvent touchev(
    197       ui::ET_TOUCH_PRESSED, GetLocationInCurrentRoot(), flags_);
    198   Dispatch(&touchev);
    199 }
    200 
    201 void EventGenerator::MoveTouch(const gfx::Point& point) {
    202   current_location_ = point;
    203   TestTouchEvent touchev(
    204       ui::ET_TOUCH_MOVED, GetLocationInCurrentRoot(), flags_);
    205   Dispatch(&touchev);
    206 
    207   if (!grab_)
    208     UpdateCurrentRootWindow(point);
    209 }
    210 
    211 void EventGenerator::ReleaseTouch() {
    212   TestTouchEvent touchev(
    213       ui::ET_TOUCH_RELEASED, GetLocationInCurrentRoot(), flags_);
    214   Dispatch(&touchev);
    215 }
    216 
    217 void EventGenerator::PressMoveAndReleaseTouchTo(const gfx::Point& point) {
    218   PressTouch();
    219   MoveTouch(point);
    220   ReleaseTouch();
    221 }
    222 
    223 void EventGenerator::PressMoveAndReleaseTouchToCenterOf(Window* window) {
    224   PressMoveAndReleaseTouchTo(CenterOfWindow(window));
    225 }
    226 
    227 void EventGenerator::GestureTapAt(const gfx::Point& location) {
    228   const int kTouchId = 2;
    229   ui::TouchEvent press(ui::ET_TOUCH_PRESSED,
    230                        location,
    231                        kTouchId,
    232                        ui::EventTimeForNow());
    233   Dispatch(&press);
    234 
    235   ui::TouchEvent release(
    236       ui::ET_TOUCH_RELEASED, location, kTouchId,
    237       press.time_stamp() + base::TimeDelta::FromMilliseconds(50));
    238   Dispatch(&release);
    239 }
    240 
    241 void EventGenerator::GestureTapDownAndUp(const gfx::Point& location) {
    242   const int kTouchId = 3;
    243   ui::TouchEvent press(ui::ET_TOUCH_PRESSED,
    244                        location,
    245                        kTouchId,
    246                        ui::EventTimeForNow());
    247   Dispatch(&press);
    248 
    249   ui::TouchEvent release(
    250       ui::ET_TOUCH_RELEASED, location, kTouchId,
    251       press.time_stamp() + base::TimeDelta::FromMilliseconds(1000));
    252   Dispatch(&release);
    253 }
    254 
    255 void EventGenerator::GestureScrollSequence(const gfx::Point& start,
    256                                            const gfx::Point& end,
    257                                            const base::TimeDelta& step_delay,
    258                                            int steps) {
    259   GestureScrollSequenceWithCallback(start, end, step_delay, steps,
    260                                     base::Bind(&DummyCallback));
    261 }
    262 
    263 void EventGenerator::GestureScrollSequenceWithCallback(
    264     const gfx::Point& start,
    265     const gfx::Point& end,
    266     const base::TimeDelta& step_delay,
    267     int steps,
    268     const ScrollStepCallback& callback) {
    269   const int kTouchId = 5;
    270   base::TimeDelta timestamp = ui::EventTimeForNow();
    271   ui::TouchEvent press(ui::ET_TOUCH_PRESSED, start, kTouchId, timestamp);
    272   Dispatch(&press);
    273 
    274   callback.Run(ui::ET_GESTURE_SCROLL_BEGIN, gfx::Vector2dF());
    275 
    276   int dx = (end.x() - start.x()) / steps;
    277   int dy = (end.y() - start.y()) / steps;
    278   gfx::Point location = start;
    279   for (int i = 0; i < steps; ++i) {
    280     location.Offset(dx, dy);
    281     timestamp += step_delay;
    282     ui::TouchEvent move(ui::ET_TOUCH_MOVED, location, kTouchId, timestamp);
    283     Dispatch(&move);
    284     callback.Run(ui::ET_GESTURE_SCROLL_UPDATE, gfx::Vector2dF(dx, dy));
    285   }
    286 
    287   ui::TouchEvent release(ui::ET_TOUCH_RELEASED, end, kTouchId, timestamp);
    288   Dispatch(&release);
    289 
    290   callback.Run(ui::ET_GESTURE_SCROLL_END, gfx::Vector2dF());
    291 }
    292 
    293 void EventGenerator::GestureMultiFingerScroll(int count,
    294                                               const gfx::Point* start,
    295                                               int event_separation_time_ms,
    296                                               int steps,
    297                                               int move_x,
    298                                               int move_y) {
    299   const int kMaxTouchPoints = 10;
    300   gfx::Point points[kMaxTouchPoints];
    301   CHECK_LE(count, kMaxTouchPoints);
    302   CHECK_GT(steps, 0);
    303 
    304   int delta_x = move_x / steps;
    305   int delta_y = move_y / steps;
    306 
    307   base::TimeDelta press_time = ui::EventTimeForNow();
    308   for (int i = 0; i < count; ++i) {
    309     points[i] = start[i];
    310     ui::TouchEvent press(ui::ET_TOUCH_PRESSED, points[i], i, press_time);
    311     Dispatch(&press);
    312   }
    313 
    314   for (int step = 0; step < steps; ++step) {
    315     base::TimeDelta move_time = press_time +
    316         base::TimeDelta::FromMilliseconds(event_separation_time_ms * step);
    317     for (int i = 0; i < count; ++i) {
    318       points[i].Offset(delta_x, delta_y);
    319       ui::TouchEvent move(ui::ET_TOUCH_MOVED, points[i], i, move_time);
    320       Dispatch(&move);
    321     }
    322   }
    323 
    324   base::TimeDelta release_time = press_time +
    325       base::TimeDelta::FromMilliseconds(event_separation_time_ms * steps);
    326   for (int i = 0; i < count; ++i) {
    327     ui::TouchEvent release(
    328         ui::ET_TOUCH_RELEASED, points[i], i, release_time);
    329     Dispatch(&release);
    330   }
    331 }
    332 
    333 void EventGenerator::ScrollSequence(const gfx::Point& start,
    334                                     const base::TimeDelta& step_delay,
    335                                     float x_offset,
    336                                     float y_offset,
    337                                     int steps,
    338                                     int num_fingers) {
    339   base::TimeDelta timestamp = base::TimeDelta::FromInternalValue(
    340       base::TimeTicks::Now().ToInternalValue());
    341   ui::ScrollEvent fling_cancel(ui::ET_SCROLL_FLING_CANCEL,
    342                                start,
    343                                timestamp,
    344                                0,
    345                                0, 0,
    346                                0, 0,
    347                                num_fingers);
    348   Dispatch(&fling_cancel);
    349 
    350   float dx = x_offset / steps;
    351   float dy = y_offset / steps;
    352   for (int i = 0; i < steps; ++i) {
    353     timestamp += step_delay;
    354     ui::ScrollEvent move(ui::ET_SCROLL,
    355                          start,
    356                          timestamp,
    357                          0,
    358                          dx, dy,
    359                          dx, dy,
    360                          num_fingers);
    361     Dispatch(&move);
    362   }
    363 
    364   ui::ScrollEvent fling_start(ui::ET_SCROLL_FLING_START,
    365                               start,
    366                               timestamp,
    367                               0,
    368                               x_offset, y_offset,
    369                               x_offset, y_offset,
    370                               num_fingers);
    371   Dispatch(&fling_start);
    372 }
    373 
    374 void EventGenerator::ScrollSequence(const gfx::Point& start,
    375                                     const base::TimeDelta& step_delay,
    376                                     const std::vector<gfx::Point>& offsets,
    377                                     int num_fingers) {
    378   int steps = offsets.size();
    379   base::TimeDelta timestamp = ui::EventTimeForNow();
    380   ui::ScrollEvent fling_cancel(ui::ET_SCROLL_FLING_CANCEL,
    381                                start,
    382                                timestamp,
    383                                0,
    384                                0, 0,
    385                                0, 0,
    386                                num_fingers);
    387   Dispatch(&fling_cancel);
    388 
    389   for (int i = 0; i < steps; ++i) {
    390     timestamp += step_delay;
    391     ui::ScrollEvent scroll(ui::ET_SCROLL,
    392                            start,
    393                            timestamp,
    394                            0,
    395                            offsets[i].x(), offsets[i].y(),
    396                            offsets[i].x(), offsets[i].y(),
    397                            num_fingers);
    398     Dispatch(&scroll);
    399   }
    400 
    401   ui::ScrollEvent fling_start(ui::ET_SCROLL_FLING_START,
    402                               start,
    403                               timestamp,
    404                               0,
    405                               offsets[steps - 1].x(), offsets[steps - 1].y(),
    406                               offsets[steps - 1].x(), offsets[steps - 1].y(),
    407                               num_fingers);
    408   Dispatch(&fling_start);
    409 }
    410 
    411 void EventGenerator::PressKey(ui::KeyboardCode key_code, int flags) {
    412   DispatchKeyEvent(true, key_code, flags);
    413 }
    414 
    415 void EventGenerator::ReleaseKey(ui::KeyboardCode key_code, int flags) {
    416   DispatchKeyEvent(false, key_code, flags);
    417 }
    418 
    419 void EventGenerator::Dispatch(ui::Event* event) {
    420   DoDispatchEvent(event, async_);
    421 }
    422 
    423 void EventGenerator::DispatchKeyEvent(bool is_press,
    424                                       ui::KeyboardCode key_code,
    425                                       int flags) {
    426 #if defined(OS_WIN)
    427   UINT key_press = WM_KEYDOWN;
    428   uint16 character = ui::GetCharacterFromKeyCode(key_code, flags);
    429   if (is_press && character) {
    430     MSG native_event = { NULL, WM_KEYDOWN, key_code, 0 };
    431     TestKeyEvent keyev(native_event, flags, false);
    432     Dispatch(&keyev);
    433     // On Windows, WM_KEYDOWN event is followed by WM_CHAR with a character
    434     // if the key event cooresponds to a real character.
    435     key_press = WM_CHAR;
    436     key_code = static_cast<ui::KeyboardCode>(character);
    437   }
    438   MSG native_event =
    439       { NULL, (is_press ? key_press : WM_KEYUP), key_code, 0 };
    440   TestKeyEvent keyev(native_event, flags, key_press == WM_CHAR);
    441 #else
    442   ui::EventType type = is_press ? ui::ET_KEY_PRESSED : ui::ET_KEY_RELEASED;
    443 #if defined(USE_X11)
    444   scoped_ptr<XEvent> native_event(new XEvent);
    445   ui::InitXKeyEventForTesting(type, key_code, flags, native_event.get());
    446   TestKeyEvent keyev(native_event.get(), flags, false);
    447 #else
    448   ui::KeyEvent keyev(type, key_code, flags, false);
    449 #endif  // USE_X11
    450 #endif  // OS_WIN
    451   Dispatch(&keyev);
    452 }
    453 
    454 void EventGenerator::UpdateCurrentRootWindow(const gfx::Point& point) {
    455   current_root_window_ = delegate_->GetRootWindowAt(point);
    456 }
    457 
    458 void EventGenerator::PressButton(int flag) {
    459   if (!(flags_ & flag)) {
    460     flags_ |= flag;
    461     grab_ = flags_ & kAllButtonMask;
    462     gfx::Point location = GetLocationInCurrentRoot();
    463     ui::MouseEvent mouseev(ui::ET_MOUSE_PRESSED, location, location, flags_);
    464     Dispatch(&mouseev);
    465   }
    466 }
    467 
    468 void EventGenerator::ReleaseButton(int flag) {
    469   if (flags_ & flag) {
    470     gfx::Point location = GetLocationInCurrentRoot();
    471     ui::MouseEvent mouseev(ui::ET_MOUSE_RELEASED, location,
    472                            location, flags_);
    473     Dispatch(&mouseev);
    474     flags_ ^= flag;
    475   }
    476   grab_ = flags_ & kAllButtonMask;
    477 }
    478 
    479 void EventGenerator::ConvertPointFromTarget(const aura::Window* target,
    480                                             gfx::Point* point) const {
    481   DCHECK(point);
    482   aura::client::ScreenPositionClient* client =
    483       delegate_->GetScreenPositionClient(target);
    484   if (client)
    485     client->ConvertPointToScreen(target, point);
    486   else
    487     aura::Window::ConvertPointToTarget(target, target->GetRootWindow(), point);
    488 }
    489 
    490 void EventGenerator::ConvertPointToTarget(const aura::Window* target,
    491                                           gfx::Point* point) const {
    492   DCHECK(point);
    493   aura::client::ScreenPositionClient* client =
    494       delegate_->GetScreenPositionClient(target);
    495   if (client)
    496     client->ConvertPointFromScreen(target, point);
    497   else
    498     aura::Window::ConvertPointToTarget(target->GetRootWindow(), target, point);
    499 }
    500 
    501 gfx::Point EventGenerator::GetLocationInCurrentRoot() const {
    502   gfx::Point p(current_location_);
    503   ConvertPointToTarget(current_root_window_, &p);
    504   return p;
    505 }
    506 
    507 gfx::Point EventGenerator::CenterOfWindow(const Window* window) const {
    508   gfx::Point center = gfx::Rect(window->bounds().size()).CenterPoint();
    509   ConvertPointFromTarget(window, &center);
    510   return center;
    511 }
    512 
    513 void EventGenerator::DoDispatchEvent(ui::Event* event, bool async) {
    514   if (async) {
    515     ui::Event* pending_event;
    516     if (event->IsKeyEvent()) {
    517       pending_event = new ui::KeyEvent(*static_cast<ui::KeyEvent*>(event));
    518     } else if (event->IsMouseEvent()) {
    519       pending_event = new ui::MouseEvent(*static_cast<ui::MouseEvent*>(event));
    520     } else if (event->IsTouchEvent()) {
    521       pending_event = new ui::TouchEvent(*static_cast<ui::TouchEvent*>(event));
    522     } else if (event->IsScrollEvent()) {
    523       pending_event =
    524           new ui::ScrollEvent(*static_cast<ui::ScrollEvent*>(event));
    525     } else {
    526       NOTREACHED() << "Invalid event type";
    527       return;
    528     }
    529     if (pending_events_.empty()) {
    530       base::MessageLoopProxy::current()->PostTask(
    531           FROM_HERE,
    532           base::Bind(&EventGenerator::DispatchNextPendingEvent,
    533                      base::Unretained(this)));
    534     }
    535     pending_events_.push_back(pending_event);
    536   } else {
    537     RootWindowHostDelegate* root_window_host_delegate =
    538         current_root_window_->AsRootWindowHostDelegate();
    539     if (event->IsKeyEvent()) {
    540       root_window_host_delegate->OnHostKeyEvent(
    541           static_cast<ui::KeyEvent*>(event));
    542     } else if (event->IsMouseEvent()) {
    543       root_window_host_delegate->OnHostMouseEvent(
    544           static_cast<ui::MouseEvent*>(event));
    545     } else if (event->IsTouchEvent()) {
    546       root_window_host_delegate->OnHostTouchEvent(
    547           static_cast<ui::TouchEvent*>(event));
    548     } else if (event->IsScrollEvent()) {
    549       root_window_host_delegate->OnHostScrollEvent(
    550           static_cast<ui::ScrollEvent*>(event));
    551     } else {
    552       NOTREACHED() << "Invalid event type";
    553     }
    554   }
    555 }
    556 
    557 void EventGenerator::DispatchNextPendingEvent() {
    558   DCHECK(!pending_events_.empty());
    559   ui::Event* event = pending_events_.front();
    560   DoDispatchEvent(event, false);
    561   pending_events_.pop_front();
    562   delete event;
    563   if (!pending_events_.empty()) {
    564     base::MessageLoopProxy::current()->PostTask(
    565         FROM_HERE,
    566         base::Bind(&EventGenerator::DispatchNextPendingEvent,
    567                    base::Unretained(this)));
    568   }
    569 }
    570 
    571 
    572 }  // namespace test
    573 }  // namespace aura
    574