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