window_event_dispatcher.cc revision 23730a6e56a168d1879203e4b3819bb36e3d8f1f
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 "ui/aura/window_event_dispatcher.h"
6
7#include "base/bind.h"
8#include "base/debug/trace_event.h"
9#include "base/logging.h"
10#include "base/message_loop/message_loop.h"
11#include "ui/aura/client/capture_client.h"
12#include "ui/aura/client/cursor_client.h"
13#include "ui/aura/client/event_client.h"
14#include "ui/aura/client/focus_client.h"
15#include "ui/aura/client/screen_position_client.h"
16#include "ui/aura/env.h"
17#include "ui/aura/window.h"
18#include "ui/aura/window_delegate.h"
19#include "ui/aura/window_targeter.h"
20#include "ui/aura/window_tracker.h"
21#include "ui/aura/window_tree_host.h"
22#include "ui/base/hit_test.h"
23#include "ui/compositor/dip_util.h"
24#include "ui/events/event.h"
25#include "ui/events/gestures/gesture_recognizer.h"
26#include "ui/events/gestures/gesture_types.h"
27
28typedef ui::EventDispatchDetails DispatchDetails;
29
30namespace aura {
31
32namespace {
33
34// Returns true if |target| has a non-client (frame) component at |location|,
35// in window coordinates.
36bool IsNonClientLocation(Window* target, const gfx::Point& location) {
37  if (!target->delegate())
38    return false;
39  int hit_test_code = target->delegate()->GetNonClientComponent(location);
40  return hit_test_code != HTCLIENT && hit_test_code != HTNOWHERE;
41}
42
43Window* ConsumerToWindow(ui::GestureConsumer* consumer) {
44  return consumer ? static_cast<Window*>(consumer) : NULL;
45}
46
47void SetLastMouseLocation(const Window* root_window,
48                          const gfx::Point& location_in_root) {
49  client::ScreenPositionClient* client =
50      client::GetScreenPositionClient(root_window);
51  if (client) {
52    gfx::Point location_in_screen = location_in_root;
53    client->ConvertPointToScreen(root_window, &location_in_screen);
54    Env::GetInstance()->set_last_mouse_location(location_in_screen);
55  } else {
56    Env::GetInstance()->set_last_mouse_location(location_in_root);
57  }
58}
59
60bool IsEventCandidateForHold(const ui::Event& event) {
61  if (event.type() == ui::ET_TOUCH_MOVED)
62    return true;
63  if (event.type() == ui::ET_MOUSE_DRAGGED)
64    return true;
65  if (event.IsMouseEvent() && (event.flags() & ui::EF_IS_SYNTHESIZED))
66    return true;
67  return false;
68}
69
70}  // namespace
71
72////////////////////////////////////////////////////////////////////////////////
73// WindowEventDispatcher, public:
74
75WindowEventDispatcher::WindowEventDispatcher(WindowTreeHost* host)
76    : host_(host),
77      touch_ids_down_(0),
78      mouse_pressed_handler_(NULL),
79      mouse_moved_handler_(NULL),
80      event_dispatch_target_(NULL),
81      old_dispatch_target_(NULL),
82      synthesize_mouse_move_(false),
83      move_hold_count_(0),
84      dispatching_held_event_(false),
85      repost_event_factory_(this),
86      held_event_factory_(this) {
87  ui::GestureRecognizer::Get()->AddGestureEventHelper(this);
88}
89
90WindowEventDispatcher::~WindowEventDispatcher() {
91  TRACE_EVENT0("shutdown", "WindowEventDispatcher::Destructor");
92  ui::GestureRecognizer::Get()->RemoveGestureEventHelper(this);
93}
94
95void WindowEventDispatcher::RepostEvent(const ui::LocatedEvent& event) {
96  DCHECK(event.type() == ui::ET_MOUSE_PRESSED ||
97         event.type() == ui::ET_GESTURE_TAP_DOWN);
98  // We allow for only one outstanding repostable event. This is used
99  // in exiting context menus.  A dropped repost request is allowed.
100  if (event.type() == ui::ET_MOUSE_PRESSED) {
101    held_repostable_event_.reset(
102        new ui::MouseEvent(
103            static_cast<const ui::MouseEvent&>(event),
104            static_cast<aura::Window*>(event.target()),
105            window()));
106    base::MessageLoop::current()->PostNonNestableTask(
107        FROM_HERE, base::Bind(
108            base::IgnoreResult(&WindowEventDispatcher::DispatchHeldEvents),
109            repost_event_factory_.GetWeakPtr()));
110  } else {
111    DCHECK(event.type() == ui::ET_GESTURE_TAP_DOWN);
112    held_repostable_event_.reset();
113    // TODO(rbyers): Reposing of gestures is tricky to get
114    // right, so it's not yet supported.  crbug.com/170987.
115  }
116}
117
118void WindowEventDispatcher::OnMouseEventsEnableStateChanged(bool enabled) {
119  // Send entered / exited so that visual state can be updated to match
120  // mouse events state.
121  PostMouseMoveEventAfterWindowChange();
122  // TODO(mazda): Add code to disable mouse events when |enabled| == false.
123}
124
125void WindowEventDispatcher::DispatchCancelModeEvent() {
126  ui::CancelModeEvent event;
127  Window* focused_window = client::GetFocusClient(window())->GetFocusedWindow();
128  if (focused_window && !window()->Contains(focused_window))
129    focused_window = NULL;
130  DispatchDetails details =
131      DispatchEvent(focused_window ? focused_window : window(), &event);
132  if (details.dispatcher_destroyed)
133    return;
134}
135
136Window* WindowEventDispatcher::GetGestureTarget(ui::GestureEvent* event) {
137  Window* target = NULL;
138  if (!event->IsEndingEvent()) {
139    // The window that received the start event (e.g. scroll begin) needs to
140    // receive the end event (e.g. scroll end).
141    target = client::GetCaptureWindow(window());
142  }
143  if (!target) {
144    target = ConsumerToWindow(
145        ui::GestureRecognizer::Get()->GetTargetForGestureEvent(*event));
146  }
147
148  return target;
149}
150
151void WindowEventDispatcher::DispatchGestureEvent(ui::GestureEvent* event) {
152  DispatchDetails details = DispatchHeldEvents();
153  if (details.dispatcher_destroyed)
154    return;
155
156  Window* target = GetGestureTarget(event);
157  if (target) {
158    event->ConvertLocationToTarget(window(), target);
159    DispatchDetails details = DispatchEvent(target, event);
160    if (details.dispatcher_destroyed)
161      return;
162  }
163}
164
165void WindowEventDispatcher::OnWindowDestroying(Window* window) {
166  DispatchMouseExitToHidingWindow(window);
167  if (window->IsVisible() &&
168      window->ContainsPointInRoot(GetLastMouseLocationInRoot())) {
169    PostMouseMoveEventAfterWindowChange();
170  }
171
172  // Hiding the window releases capture which can implicitly destroy the window
173  // so the window may no longer be valid after this call.
174  OnWindowHidden(window, WINDOW_DESTROYED);
175}
176
177void WindowEventDispatcher::OnWindowBoundsChanged(Window* window,
178                                                  bool contained_mouse_point) {
179  if (contained_mouse_point ||
180      (window->IsVisible() &&
181       window->ContainsPointInRoot(GetLastMouseLocationInRoot()))) {
182    PostMouseMoveEventAfterWindowChange();
183  }
184}
185
186void WindowEventDispatcher::DispatchMouseExitToHidingWindow(Window* window) {
187  // The mouse capture is intentionally ignored. Think that a mouse enters
188  // to a window, the window sets the capture, the mouse exits the window,
189  // and then it releases the capture. In that case OnMouseExited won't
190  // be called. So it is natural not to emit OnMouseExited even though
191  // |window| is the capture window.
192  gfx::Point last_mouse_location = GetLastMouseLocationInRoot();
193  if (window->Contains(mouse_moved_handler_) &&
194      window->ContainsPointInRoot(last_mouse_location))
195    DispatchMouseExitAtPoint(last_mouse_location);
196}
197
198void WindowEventDispatcher::DispatchMouseExitAtPoint(const gfx::Point& point) {
199  ui::MouseEvent event(ui::ET_MOUSE_EXITED, point, point, ui::EF_NONE,
200                       ui::EF_NONE);
201  DispatchDetails details =
202      DispatchMouseEnterOrExit(event, ui::ET_MOUSE_EXITED);
203  if (details.dispatcher_destroyed)
204    return;
205}
206
207void WindowEventDispatcher::OnWindowVisibilityChanged(Window* window,
208                                                      bool is_visible) {
209  if (window->ContainsPointInRoot(GetLastMouseLocationInRoot()))
210    PostMouseMoveEventAfterWindowChange();
211
212  // Hiding the window releases capture which can implicitly destroy the window
213  // so the window may no longer be valid after this call.
214  if (!is_visible)
215    OnWindowHidden(window, WINDOW_HIDDEN);
216}
217
218void WindowEventDispatcher::OnWindowTransformed(Window* window,
219                                                bool contained_mouse) {
220  if (contained_mouse ||
221      (window->IsVisible() &&
222       window->ContainsPointInRoot(GetLastMouseLocationInRoot()))) {
223    PostMouseMoveEventAfterWindowChange();
224  }
225}
226
227void WindowEventDispatcher::ProcessedTouchEvent(ui::TouchEvent* event,
228                                                Window* window,
229                                                ui::EventResult result) {
230  scoped_ptr<ui::GestureRecognizer::Gestures> gestures;
231  gestures.reset(ui::GestureRecognizer::Get()->
232      ProcessTouchEventForGesture(*event, result, window));
233  DispatchDetails details = ProcessGestures(gestures.get());
234  if (details.dispatcher_destroyed)
235    return;
236}
237
238void WindowEventDispatcher::HoldPointerMoves() {
239  if (!move_hold_count_)
240    held_event_factory_.InvalidateWeakPtrs();
241  ++move_hold_count_;
242  TRACE_EVENT_ASYNC_BEGIN0("ui", "WindowEventDispatcher::HoldPointerMoves",
243                           this);
244}
245
246void WindowEventDispatcher::ReleasePointerMoves() {
247  --move_hold_count_;
248  DCHECK_GE(move_hold_count_, 0);
249  if (!move_hold_count_ && held_move_event_) {
250    // We don't want to call DispatchHeldEvents directly, because this might be
251    // called from a deep stack while another event, in which case dispatching
252    // another one may not be safe/expected.  Instead we post a task, that we
253    // may cancel if HoldPointerMoves is called again before it executes.
254    base::MessageLoop::current()->PostNonNestableTask(
255        FROM_HERE, base::Bind(
256          base::IgnoreResult(&WindowEventDispatcher::DispatchHeldEvents),
257          held_event_factory_.GetWeakPtr()));
258  }
259  TRACE_EVENT_ASYNC_END0("ui", "WindowEventDispatcher::HoldPointerMoves", this);
260}
261
262gfx::Point WindowEventDispatcher::GetLastMouseLocationInRoot() const {
263  gfx::Point location = Env::GetInstance()->last_mouse_location();
264  client::ScreenPositionClient* client =
265      client::GetScreenPositionClient(window());
266  if (client)
267    client->ConvertPointFromScreen(window(), &location);
268  return location;
269}
270
271void WindowEventDispatcher::OnHostLostMouseGrab() {
272  mouse_pressed_handler_ = NULL;
273  mouse_moved_handler_ = NULL;
274}
275
276void WindowEventDispatcher::OnHostResized(const gfx::Size& size) {
277  TRACE_EVENT1("ui", "WindowEventDispatcher::OnHostResized",
278               "size", size.ToString());
279
280  DispatchDetails details = DispatchHeldEvents();
281  if (details.dispatcher_destroyed)
282    return;
283
284  // Constrain the mouse position within the new root Window size.
285  gfx::Point point;
286  if (host_->QueryMouseLocation(&point)) {
287    SetLastMouseLocation(window(),
288                         ui::ConvertPointToDIP(window()->layer(), point));
289  }
290  synthesize_mouse_move_ = false;
291}
292
293void WindowEventDispatcher::OnCursorMovedToRootLocation(
294    const gfx::Point& root_location) {
295  SetLastMouseLocation(window(), root_location);
296  synthesize_mouse_move_ = false;
297}
298
299////////////////////////////////////////////////////////////////////////////////
300// WindowEventDispatcher, private:
301
302Window* WindowEventDispatcher::window() {
303  return host_->window();
304}
305
306const Window* WindowEventDispatcher::window() const {
307  return host_->window();
308}
309
310void WindowEventDispatcher::TransformEventForDeviceScaleFactor(
311    ui::LocatedEvent* event) {
312  event->UpdateForRootTransform(host_->GetInverseRootTransform());
313}
314
315ui::EventDispatchDetails WindowEventDispatcher::DispatchMouseEnterOrExit(
316    const ui::MouseEvent& event,
317    ui::EventType type) {
318  if (event.type() != ui::ET_MOUSE_CAPTURE_CHANGED &&
319      !(event.flags() & ui::EF_IS_SYNTHESIZED)) {
320    SetLastMouseLocation(window(), event.root_location());
321  }
322
323  if (!mouse_moved_handler_ || !mouse_moved_handler_->delegate() ||
324      !window()->Contains(mouse_moved_handler_))
325    return DispatchDetails();
326
327  // |event| may be an event in the process of being dispatched to a target (in
328  // which case its locations will be in the event's target's coordinate
329  // system), or a synthetic event created in root-window (in which case, the
330  // event's target will be NULL, and the event will be in the root-window's
331  // coordinate system.
332  aura::Window* target = static_cast<Window*>(event.target());
333  if (!target)
334    target = window();
335  ui::MouseEvent translated_event(event,
336                                  target,
337                                  mouse_moved_handler_,
338                                  type,
339                                  event.flags() | ui::EF_IS_SYNTHESIZED);
340  return DispatchEvent(mouse_moved_handler_, &translated_event);
341}
342
343ui::EventDispatchDetails WindowEventDispatcher::ProcessGestures(
344    ui::GestureRecognizer::Gestures* gestures) {
345  DispatchDetails details;
346  if (!gestures || gestures->empty())
347    return details;
348
349  Window* target = GetGestureTarget(gestures->get().at(0));
350  for (size_t i = 0; i < gestures->size(); ++i) {
351    ui::GestureEvent* event = gestures->get().at(i);
352    event->ConvertLocationToTarget(window(), target);
353    details = DispatchEvent(target, event);
354    if (details.dispatcher_destroyed || details.target_destroyed)
355      break;
356  }
357  return details;
358}
359
360void WindowEventDispatcher::OnWindowAddedToRootWindow(Window* attached) {
361  if (attached->IsVisible() &&
362      attached->ContainsPointInRoot(GetLastMouseLocationInRoot())) {
363    PostMouseMoveEventAfterWindowChange();
364  }
365}
366
367void WindowEventDispatcher::OnWindowRemovedFromRootWindow(Window* detached,
368                                                          Window* new_root) {
369  DCHECK(aura::client::GetCaptureWindow(window()) != window());
370
371  DispatchMouseExitToHidingWindow(detached);
372  if (detached->IsVisible() &&
373      detached->ContainsPointInRoot(GetLastMouseLocationInRoot())) {
374    PostMouseMoveEventAfterWindowChange();
375  }
376
377  // Hiding the window releases capture which can implicitly destroy the window
378  // so the window may no longer be valid after this call.
379  OnWindowHidden(detached, new_root ? WINDOW_MOVING : WINDOW_HIDDEN);
380}
381
382void WindowEventDispatcher::OnWindowHidden(Window* invisible,
383                                           WindowHiddenReason reason) {
384  // If the window the mouse was pressed in becomes invisible, it should no
385  // longer receive mouse events.
386  if (invisible->Contains(mouse_pressed_handler_))
387    mouse_pressed_handler_ = NULL;
388  if (invisible->Contains(mouse_moved_handler_))
389    mouse_moved_handler_ = NULL;
390
391  // If events are being dispatched from a nested message-loop, and the target
392  // of the outer loop is hidden or moved to another dispatcher during
393  // dispatching events in the inner loop, then reset the target for the outer
394  // loop.
395  if (invisible->Contains(old_dispatch_target_))
396    old_dispatch_target_ = NULL;
397
398  CleanupGestureState(invisible);
399
400  // Do not clear the capture, and the |event_dispatch_target_| if the
401  // window is moving across root windows, because the target itself
402  // is actually still visible and clearing them stops further event
403  // processing, which can cause unexpected behaviors. See
404  // crbug.com/157583
405  if (reason != WINDOW_MOVING) {
406    Window* capture_window = aura::client::GetCaptureWindow(window());
407
408    if (invisible->Contains(event_dispatch_target_))
409      event_dispatch_target_ = NULL;
410
411    // If the ancestor of the capture window is hidden, release the capture.
412    // Note that this may delete the window so do not use capture_window
413    // after this.
414    if (invisible->Contains(capture_window) && invisible != window())
415      capture_window->ReleaseCapture();
416  }
417}
418
419void WindowEventDispatcher::CleanupGestureState(Window* window) {
420  ui::GestureRecognizer::Get()->CancelActiveTouches(window);
421  ui::GestureRecognizer::Get()->CleanupStateForConsumer(window);
422  const Window::Windows& windows = window->children();
423  for (Window::Windows::const_iterator iter = windows.begin();
424      iter != windows.end();
425      ++iter) {
426    CleanupGestureState(*iter);
427  }
428}
429
430////////////////////////////////////////////////////////////////////////////////
431// WindowEventDispatcher, aura::client::CaptureDelegate implementation:
432
433void WindowEventDispatcher::UpdateCapture(Window* old_capture,
434                                          Window* new_capture) {
435  // |mouse_moved_handler_| may have been set to a Window in a different root
436  // (see below). Clear it here to ensure we don't end up referencing a stale
437  // Window.
438  if (mouse_moved_handler_ && !window()->Contains(mouse_moved_handler_))
439    mouse_moved_handler_ = NULL;
440
441  if (old_capture && old_capture->GetRootWindow() == window() &&
442      old_capture->delegate()) {
443    // Send a capture changed event with bogus location data.
444    ui::MouseEvent event(ui::ET_MOUSE_CAPTURE_CHANGED, gfx::Point(),
445                         gfx::Point(), 0, 0);
446
447    DispatchDetails details = DispatchEvent(old_capture, &event);
448    if (details.dispatcher_destroyed)
449      return;
450
451    old_capture->delegate()->OnCaptureLost();
452  }
453
454  if (new_capture) {
455    // Make all subsequent mouse events go to the capture window. We shouldn't
456    // need to send an event here as OnCaptureLost() should take care of that.
457    if (mouse_moved_handler_ || Env::GetInstance()->IsMouseButtonDown())
458      mouse_moved_handler_ = new_capture;
459  } else {
460    // Make sure mouse_moved_handler gets updated.
461    DispatchDetails details = SynthesizeMouseMoveEvent();
462    if (details.dispatcher_destroyed)
463      return;
464  }
465  mouse_pressed_handler_ = NULL;
466}
467
468void WindowEventDispatcher::OnOtherRootGotCapture() {
469  mouse_moved_handler_ = NULL;
470  mouse_pressed_handler_ = NULL;
471}
472
473void WindowEventDispatcher::SetNativeCapture() {
474  host_->SetCapture();
475}
476
477void WindowEventDispatcher::ReleaseNativeCapture() {
478  host_->ReleaseCapture();
479}
480
481////////////////////////////////////////////////////////////////////////////////
482// WindowEventDispatcher, ui::EventProcessor implementation:
483ui::EventTarget* WindowEventDispatcher::GetRootTarget() {
484  return window();
485}
486
487void WindowEventDispatcher::PrepareEventForDispatch(ui::Event* event) {
488  if (dispatching_held_event_) {
489    // The held events are already in |window()|'s coordinate system. So it is
490    // not necessary to apply the transform to convert from the host's
491    // coordinate system to |window()|'s coordinate system.
492    return;
493  }
494  if (event->IsMouseEvent() ||
495      event->IsScrollEvent() ||
496      event->IsTouchEvent() ||
497      event->IsGestureEvent()) {
498    TransformEventForDeviceScaleFactor(static_cast<ui::LocatedEvent*>(event));
499  }
500}
501
502////////////////////////////////////////////////////////////////////////////////
503// WindowEventDispatcher, ui::EventDispatcherDelegate implementation:
504
505bool WindowEventDispatcher::CanDispatchToTarget(ui::EventTarget* target) {
506  return event_dispatch_target_ == target;
507}
508
509ui::EventDispatchDetails WindowEventDispatcher::PreDispatchEvent(
510    ui::EventTarget* target,
511    ui::Event* event) {
512  Window* target_window = static_cast<Window*>(target);
513  CHECK(window()->Contains(target_window));
514
515  if (!dispatching_held_event_) {
516    bool can_be_held = IsEventCandidateForHold(*event);
517    if (!move_hold_count_ || !can_be_held) {
518      if (can_be_held)
519        held_move_event_.reset();
520      DispatchDetails details = DispatchHeldEvents();
521      if (details.dispatcher_destroyed || details.target_destroyed)
522        return details;
523    }
524  }
525
526  if (event->IsMouseEvent()) {
527    PreDispatchMouseEvent(target_window, static_cast<ui::MouseEvent*>(event));
528  } else if (event->IsScrollEvent()) {
529    PreDispatchLocatedEvent(target_window,
530                            static_cast<ui::ScrollEvent*>(event));
531  } else if (event->IsTouchEvent()) {
532    PreDispatchTouchEvent(target_window, static_cast<ui::TouchEvent*>(event));
533  }
534  old_dispatch_target_ = event_dispatch_target_;
535  event_dispatch_target_ = static_cast<Window*>(target);
536  return DispatchDetails();
537}
538
539ui::EventDispatchDetails WindowEventDispatcher::PostDispatchEvent(
540    ui::EventTarget* target,
541    const ui::Event& event) {
542  DispatchDetails details;
543  if (!target || target != event_dispatch_target_)
544    details.target_destroyed = true;
545  event_dispatch_target_ = old_dispatch_target_;
546  old_dispatch_target_ = NULL;
547#ifndef NDEBUG
548  DCHECK(!event_dispatch_target_ || window()->Contains(event_dispatch_target_));
549#endif
550
551  if (event.IsTouchEvent() && !details.target_destroyed) {
552    // Do not let 'held' touch events contribute to any gestures.
553    if (!held_move_event_ || !held_move_event_->IsTouchEvent()) {
554      ui::TouchEvent orig_event(static_cast<const ui::TouchEvent&>(event),
555                                static_cast<Window*>(event.target()), window());
556      // Get the list of GestureEvents from GestureRecognizer.
557      scoped_ptr<ui::GestureRecognizer::Gestures> gestures;
558      gestures.reset(ui::GestureRecognizer::Get()->
559          ProcessTouchEventForGesture(orig_event, event.result(),
560                                      static_cast<Window*>(target)));
561      return ProcessGestures(gestures.get());
562    }
563  }
564
565  return details;
566}
567
568////////////////////////////////////////////////////////////////////////////////
569// WindowEventDispatcher, ui::GestureEventHelper implementation:
570
571bool WindowEventDispatcher::CanDispatchToConsumer(
572    ui::GestureConsumer* consumer) {
573  Window* consumer_window = ConsumerToWindow(consumer);
574  return (consumer_window && consumer_window->GetRootWindow() == window());
575}
576
577void WindowEventDispatcher::DispatchPostponedGestureEvent(
578    ui::GestureEvent* event) {
579  DispatchGestureEvent(event);
580}
581
582void WindowEventDispatcher::DispatchCancelTouchEvent(ui::TouchEvent* event) {
583  DispatchDetails details = OnEventFromSource(event);
584  if (details.dispatcher_destroyed)
585    return;
586}
587
588////////////////////////////////////////////////////////////////////////////////
589// WindowEventDispatcher, private:
590
591ui::EventDispatchDetails WindowEventDispatcher::DispatchHeldEvents() {
592  if (!held_repostable_event_ && !held_move_event_)
593    return DispatchDetails();
594
595  CHECK(!dispatching_held_event_);
596  dispatching_held_event_ = true;
597
598  DispatchDetails dispatch_details;
599  if (held_repostable_event_) {
600    if (held_repostable_event_->type() == ui::ET_MOUSE_PRESSED) {
601      scoped_ptr<ui::MouseEvent> mouse_event(
602          static_cast<ui::MouseEvent*>(held_repostable_event_.release()));
603      dispatch_details = OnEventFromSource(mouse_event.get());
604    } else {
605      // TODO(rbyers): GESTURE_TAP_DOWN not yet supported: crbug.com/170987.
606      NOTREACHED();
607    }
608    if (dispatch_details.dispatcher_destroyed)
609      return dispatch_details;
610  }
611
612  if (held_move_event_) {
613    // If a mouse move has been synthesized, the target location is suspect,
614    // so drop the held mouse event.
615    if (held_move_event_->IsTouchEvent() ||
616        (held_move_event_->IsMouseEvent() && !synthesize_mouse_move_)) {
617      dispatch_details = OnEventFromSource(held_move_event_.get());
618    }
619    if (!dispatch_details.dispatcher_destroyed)
620      held_move_event_.reset();
621  }
622
623  if (!dispatch_details.dispatcher_destroyed)
624    dispatching_held_event_ = false;
625  return dispatch_details;
626}
627
628void WindowEventDispatcher::PostMouseMoveEventAfterWindowChange() {
629  if (synthesize_mouse_move_)
630    return;
631  synthesize_mouse_move_ = true;
632  base::MessageLoop::current()->PostNonNestableTask(
633      FROM_HERE,
634      base::Bind(base::IgnoreResult(
635          &WindowEventDispatcher::SynthesizeMouseMoveEvent),
636          held_event_factory_.GetWeakPtr()));
637}
638
639ui::EventDispatchDetails WindowEventDispatcher::SynthesizeMouseMoveEvent() {
640  DispatchDetails details;
641  if (!synthesize_mouse_move_)
642    return details;
643  synthesize_mouse_move_ = false;
644  gfx::Point root_mouse_location = GetLastMouseLocationInRoot();
645  if (!window()->bounds().Contains(root_mouse_location))
646    return details;
647  gfx::Point host_mouse_location = root_mouse_location;
648  host_->ConvertPointToHost(&host_mouse_location);
649  ui::MouseEvent event(ui::ET_MOUSE_MOVED,
650                       host_mouse_location,
651                       host_mouse_location,
652                       ui::EF_IS_SYNTHESIZED,
653                       0);
654  return OnEventFromSource(&event);
655}
656
657void WindowEventDispatcher::PreDispatchLocatedEvent(Window* target,
658                                                    ui::LocatedEvent* event) {
659  int flags = event->flags();
660  if (IsNonClientLocation(target, event->location()))
661    flags |= ui::EF_IS_NON_CLIENT;
662  event->set_flags(flags);
663
664  if (!dispatching_held_event_ &&
665      (event->IsMouseEvent() || event->IsScrollEvent()) &&
666      !(event->flags() & ui::EF_IS_SYNTHESIZED)) {
667    if (event->type() != ui::ET_MOUSE_CAPTURE_CHANGED)
668      SetLastMouseLocation(window(), event->root_location());
669    synthesize_mouse_move_ = false;
670  }
671}
672
673void WindowEventDispatcher::PreDispatchMouseEvent(Window* target,
674                                                  ui::MouseEvent* event) {
675  client::CursorClient* cursor_client = client::GetCursorClient(window());
676  if (cursor_client &&
677      !cursor_client->IsMouseEventsEnabled() &&
678      (event->flags() & ui::EF_IS_SYNTHESIZED)) {
679    event->SetHandled();
680    return;
681  }
682
683  if (IsEventCandidateForHold(*event) && !dispatching_held_event_) {
684    if (move_hold_count_) {
685      if (!(event->flags() & ui::EF_IS_SYNTHESIZED) &&
686          event->type() != ui::ET_MOUSE_CAPTURE_CHANGED) {
687        SetLastMouseLocation(window(), event->root_location());
688      }
689      held_move_event_.reset(new ui::MouseEvent(*event, target, window()));
690      event->SetHandled();
691      return;
692    } else {
693      // We may have a held event for a period between the time move_hold_count_
694      // fell to 0 and the DispatchHeldEvents executes. Since we're going to
695      // dispatch the new event directly below, we can reset the old one.
696      held_move_event_.reset();
697    }
698  }
699
700  const int kMouseButtonFlagMask = ui::EF_LEFT_MOUSE_BUTTON |
701                                   ui::EF_MIDDLE_MOUSE_BUTTON |
702                                   ui::EF_RIGHT_MOUSE_BUTTON;
703  switch (event->type()) {
704    case ui::ET_MOUSE_EXITED:
705      if (!target || target == window()) {
706        DispatchDetails details =
707            DispatchMouseEnterOrExit(*event, ui::ET_MOUSE_EXITED);
708        if (details.dispatcher_destroyed) {
709          event->SetHandled();
710          return;
711        }
712        mouse_moved_handler_ = NULL;
713      }
714      break;
715    case ui::ET_MOUSE_MOVED:
716      // Send an exit to the current |mouse_moved_handler_| and an enter to
717      // |target|. Take care that both us and |target| aren't destroyed during
718      // dispatch.
719      if (target != mouse_moved_handler_) {
720        aura::Window* old_mouse_moved_handler = mouse_moved_handler_;
721        WindowTracker live_window;
722        live_window.Add(target);
723        DispatchDetails details =
724            DispatchMouseEnterOrExit(*event, ui::ET_MOUSE_EXITED);
725        if (details.dispatcher_destroyed) {
726          event->SetHandled();
727          return;
728        }
729        // If the |mouse_moved_handler_| changes out from under us, assume a
730        // nested message loop ran and we don't need to do anything.
731        if (mouse_moved_handler_ != old_mouse_moved_handler) {
732          event->SetHandled();
733          return;
734        }
735        if (!live_window.Contains(target) || details.target_destroyed) {
736          mouse_moved_handler_ = NULL;
737          event->SetHandled();
738          return;
739        }
740        live_window.Remove(target);
741
742        mouse_moved_handler_ = target;
743        details = DispatchMouseEnterOrExit(*event, ui::ET_MOUSE_ENTERED);
744        if (details.dispatcher_destroyed || details.target_destroyed) {
745          event->SetHandled();
746          return;
747        }
748      }
749      break;
750    case ui::ET_MOUSE_PRESSED:
751      // Don't set the mouse pressed handler for non client mouse down events.
752      // These are only sent by Windows and are not always followed with non
753      // client mouse up events which causes subsequent mouse events to be
754      // sent to the wrong target.
755      if (!(event->flags() & ui::EF_IS_NON_CLIENT) && !mouse_pressed_handler_)
756        mouse_pressed_handler_ = target;
757      Env::GetInstance()->set_mouse_button_flags(
758          event->flags() & kMouseButtonFlagMask);
759      break;
760    case ui::ET_MOUSE_RELEASED:
761      mouse_pressed_handler_ = NULL;
762      Env::GetInstance()->set_mouse_button_flags(event->flags() &
763          kMouseButtonFlagMask & ~event->changed_button_flags());
764      break;
765    default:
766      break;
767  }
768
769  PreDispatchLocatedEvent(target, event);
770}
771
772void WindowEventDispatcher::PreDispatchTouchEvent(Window* target,
773                                                  ui::TouchEvent* event) {
774  switch (event->type()) {
775    case ui::ET_TOUCH_PRESSED:
776      touch_ids_down_ |= (1 << event->touch_id());
777      Env::GetInstance()->set_touch_down(touch_ids_down_ != 0);
778      break;
779
780      // Handle ET_TOUCH_CANCELLED only if it has a native event.
781    case ui::ET_TOUCH_CANCELLED:
782      if (!event->HasNativeEvent())
783        break;
784      // fallthrough
785    case ui::ET_TOUCH_RELEASED:
786      touch_ids_down_ = (touch_ids_down_ | (1 << event->touch_id())) ^
787            (1 << event->touch_id());
788      Env::GetInstance()->set_touch_down(touch_ids_down_ != 0);
789      break;
790
791    case ui::ET_TOUCH_MOVED:
792      if (move_hold_count_ && !dispatching_held_event_) {
793        held_move_event_.reset(new ui::TouchEvent(*event, target, window()));
794        event->SetHandled();
795        return;
796      }
797      break;
798
799    default:
800      NOTREACHED();
801      break;
802  }
803  PreDispatchLocatedEvent(target, event);
804}
805
806}  // namespace aura
807