window_event_dispatcher.cc revision 03b57e008b61dfcb1fbad3aea950ae0e001748b0
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 observer_manager_(this), 86 repost_event_factory_(this), 87 held_event_factory_(this) { 88 ui::GestureRecognizer::Get()->AddGestureEventHelper(this); 89 Env::GetInstance()->AddObserver(this); 90} 91 92WindowEventDispatcher::~WindowEventDispatcher() { 93 TRACE_EVENT0("shutdown", "WindowEventDispatcher::Destructor"); 94 Env::GetInstance()->RemoveObserver(this); 95 ui::GestureRecognizer::Get()->RemoveGestureEventHelper(this); 96} 97 98void WindowEventDispatcher::RepostEvent(const ui::LocatedEvent& event) { 99 DCHECK(event.type() == ui::ET_MOUSE_PRESSED || 100 event.type() == ui::ET_GESTURE_TAP_DOWN); 101 // We allow for only one outstanding repostable event. This is used 102 // in exiting context menus. A dropped repost request is allowed. 103 if (event.type() == ui::ET_MOUSE_PRESSED) { 104 held_repostable_event_.reset( 105 new ui::MouseEvent( 106 static_cast<const ui::MouseEvent&>(event), 107 static_cast<aura::Window*>(event.target()), 108 window())); 109 base::MessageLoop::current()->PostNonNestableTask( 110 FROM_HERE, base::Bind( 111 base::IgnoreResult(&WindowEventDispatcher::DispatchHeldEvents), 112 repost_event_factory_.GetWeakPtr())); 113 } else { 114 DCHECK(event.type() == ui::ET_GESTURE_TAP_DOWN); 115 held_repostable_event_.reset(); 116 // TODO(rbyers): Reposing of gestures is tricky to get 117 // right, so it's not yet supported. crbug.com/170987. 118 } 119} 120 121void WindowEventDispatcher::OnMouseEventsEnableStateChanged(bool enabled) { 122 // Send entered / exited so that visual state can be updated to match 123 // mouse events state. 124 PostSynthesizeMouseMove(); 125 // TODO(mazda): Add code to disable mouse events when |enabled| == false. 126} 127 128void WindowEventDispatcher::DispatchCancelModeEvent() { 129 ui::CancelModeEvent event; 130 Window* focused_window = client::GetFocusClient(window())->GetFocusedWindow(); 131 if (focused_window && !window()->Contains(focused_window)) 132 focused_window = NULL; 133 DispatchDetails details = 134 DispatchEvent(focused_window ? focused_window : window(), &event); 135 if (details.dispatcher_destroyed) 136 return; 137} 138 139void WindowEventDispatcher::DispatchGestureEvent(ui::GestureEvent* event) { 140 DispatchDetails details = DispatchHeldEvents(); 141 if (details.dispatcher_destroyed) 142 return; 143 144 Window* target = GetGestureTarget(event); 145 if (target) { 146 event->ConvertLocationToTarget(window(), target); 147 DispatchDetails details = DispatchEvent(target, event); 148 if (details.dispatcher_destroyed) 149 return; 150 } 151} 152 153DispatchDetails WindowEventDispatcher::DispatchMouseExitAtPoint( 154 const gfx::Point& point) { 155 ui::MouseEvent event(ui::ET_MOUSE_EXITED, point, point, ui::EF_NONE, 156 ui::EF_NONE); 157 return DispatchMouseEnterOrExit(event, ui::ET_MOUSE_EXITED); 158} 159 160void WindowEventDispatcher::ProcessedTouchEvent(ui::TouchEvent* event, 161 Window* window, 162 ui::EventResult result) { 163 ui::TouchEvent orig_event(*event, window, this->window()); 164 // Once we've fully migrated to the eager gesture detector, we won't need to 165 // pass an event here. 166 scoped_ptr<ui::GestureRecognizer::Gestures> gestures( 167 ui::GestureRecognizer::Get()->ProcessTouchEventOnAsyncAck( 168 orig_event, result, window)); 169 DispatchDetails details = ProcessGestures(gestures.get()); 170 if (details.dispatcher_destroyed) 171 return; 172} 173 174void WindowEventDispatcher::HoldPointerMoves() { 175 if (!move_hold_count_) 176 held_event_factory_.InvalidateWeakPtrs(); 177 ++move_hold_count_; 178 TRACE_EVENT_ASYNC_BEGIN0("ui", "WindowEventDispatcher::HoldPointerMoves", 179 this); 180} 181 182void WindowEventDispatcher::ReleasePointerMoves() { 183 --move_hold_count_; 184 DCHECK_GE(move_hold_count_, 0); 185 if (!move_hold_count_ && held_move_event_) { 186 // We don't want to call DispatchHeldEvents directly, because this might be 187 // called from a deep stack while another event, in which case dispatching 188 // another one may not be safe/expected. Instead we post a task, that we 189 // may cancel if HoldPointerMoves is called again before it executes. 190 base::MessageLoop::current()->PostNonNestableTask( 191 FROM_HERE, base::Bind( 192 base::IgnoreResult(&WindowEventDispatcher::DispatchHeldEvents), 193 held_event_factory_.GetWeakPtr())); 194 } 195 TRACE_EVENT_ASYNC_END0("ui", "WindowEventDispatcher::HoldPointerMoves", this); 196} 197 198gfx::Point WindowEventDispatcher::GetLastMouseLocationInRoot() const { 199 gfx::Point location = Env::GetInstance()->last_mouse_location(); 200 client::ScreenPositionClient* client = 201 client::GetScreenPositionClient(window()); 202 if (client) 203 client->ConvertPointFromScreen(window(), &location); 204 return location; 205} 206 207void WindowEventDispatcher::OnHostLostMouseGrab() { 208 mouse_pressed_handler_ = NULL; 209 mouse_moved_handler_ = NULL; 210} 211 212void WindowEventDispatcher::OnCursorMovedToRootLocation( 213 const gfx::Point& root_location) { 214 SetLastMouseLocation(window(), root_location); 215 synthesize_mouse_move_ = false; 216} 217 218void WindowEventDispatcher::OnPostNotifiedWindowDestroying(Window* window) { 219 OnWindowHidden(window, WINDOW_DESTROYED); 220} 221 222//////////////////////////////////////////////////////////////////////////////// 223// WindowEventDispatcher, private: 224 225Window* WindowEventDispatcher::window() { 226 return host_->window(); 227} 228 229const Window* WindowEventDispatcher::window() const { 230 return host_->window(); 231} 232 233void WindowEventDispatcher::TransformEventForDeviceScaleFactor( 234 ui::LocatedEvent* event) { 235 event->UpdateForRootTransform(host_->GetInverseRootTransform()); 236} 237 238void WindowEventDispatcher::DispatchMouseExitToHidingWindow(Window* window) { 239 // The mouse capture is intentionally ignored. Think that a mouse enters 240 // to a window, the window sets the capture, the mouse exits the window, 241 // and then it releases the capture. In that case OnMouseExited won't 242 // be called. So it is natural not to emit OnMouseExited even though 243 // |window| is the capture window. 244 gfx::Point last_mouse_location = GetLastMouseLocationInRoot(); 245 if (window->Contains(mouse_moved_handler_) && 246 window->ContainsPointInRoot(last_mouse_location)) { 247 DispatchDetails details = DispatchMouseExitAtPoint(last_mouse_location); 248 if (details.dispatcher_destroyed) 249 return; 250 } 251} 252 253ui::EventDispatchDetails WindowEventDispatcher::DispatchMouseEnterOrExit( 254 const ui::MouseEvent& event, 255 ui::EventType type) { 256 if (event.type() != ui::ET_MOUSE_CAPTURE_CHANGED && 257 !(event.flags() & ui::EF_IS_SYNTHESIZED)) { 258 SetLastMouseLocation(window(), event.root_location()); 259 } 260 261 if (!mouse_moved_handler_ || !mouse_moved_handler_->delegate() || 262 !window()->Contains(mouse_moved_handler_)) 263 return DispatchDetails(); 264 265 // |event| may be an event in the process of being dispatched to a target (in 266 // which case its locations will be in the event's target's coordinate 267 // system), or a synthetic event created in root-window (in which case, the 268 // event's target will be NULL, and the event will be in the root-window's 269 // coordinate system. 270 aura::Window* target = static_cast<Window*>(event.target()); 271 if (!target) 272 target = window(); 273 ui::MouseEvent translated_event(event, 274 target, 275 mouse_moved_handler_, 276 type, 277 event.flags() | ui::EF_IS_SYNTHESIZED); 278 return DispatchEvent(mouse_moved_handler_, &translated_event); 279} 280 281ui::EventDispatchDetails WindowEventDispatcher::ProcessGestures( 282 ui::GestureRecognizer::Gestures* gestures) { 283 DispatchDetails details; 284 if (!gestures || gestures->empty()) 285 return details; 286 287 Window* target = GetGestureTarget(gestures->get().at(0)); 288 if (!target) 289 return details; 290 291 for (size_t i = 0; i < gestures->size(); ++i) { 292 ui::GestureEvent* event = gestures->get().at(i); 293 event->ConvertLocationToTarget(window(), target); 294 details = DispatchEvent(target, event); 295 if (details.dispatcher_destroyed || details.target_destroyed) 296 break; 297 } 298 return details; 299} 300 301void WindowEventDispatcher::OnWindowHidden(Window* invisible, 302 WindowHiddenReason reason) { 303 // If the window the mouse was pressed in becomes invisible, it should no 304 // longer receive mouse events. 305 if (invisible->Contains(mouse_pressed_handler_)) 306 mouse_pressed_handler_ = NULL; 307 if (invisible->Contains(mouse_moved_handler_)) 308 mouse_moved_handler_ = NULL; 309 310 // If events are being dispatched from a nested message-loop, and the target 311 // of the outer loop is hidden or moved to another dispatcher during 312 // dispatching events in the inner loop, then reset the target for the outer 313 // loop. 314 if (invisible->Contains(old_dispatch_target_)) 315 old_dispatch_target_ = NULL; 316 317 invisible->CleanupGestureState(); 318 319 // Do not clear the capture, and the |event_dispatch_target_| if the 320 // window is moving across hosts, because the target itself is actually still 321 // visible and clearing them stops further event processing, which can cause 322 // unexpected behaviors. See crbug.com/157583 323 if (reason != WINDOW_MOVING) { 324 // We don't ask |invisible| here, because invisible may have been removed 325 // from the window hierarchy already by the time this function is called 326 // (OnWindowDestroyed). 327 client::CaptureClient* capture_client = 328 client::GetCaptureClient(host_->window()); 329 Window* capture_window = 330 capture_client ? capture_client->GetCaptureWindow() : NULL; 331 332 if (invisible->Contains(event_dispatch_target_)) 333 event_dispatch_target_ = NULL; 334 335 // If the ancestor of the capture window is hidden, release the capture. 336 // Note that this may delete the window so do not use capture_window 337 // after this. 338 if (invisible->Contains(capture_window) && invisible != window()) 339 capture_window->ReleaseCapture(); 340 } 341} 342 343Window* WindowEventDispatcher::GetGestureTarget(ui::GestureEvent* event) { 344 Window* target = NULL; 345 if (!event->IsEndingEvent()) { 346 // The window that received the start event (e.g. scroll begin) needs to 347 // receive the end event (e.g. scroll end). 348 target = client::GetCaptureWindow(window()); 349 } 350 if (!target) { 351 target = ConsumerToWindow( 352 ui::GestureRecognizer::Get()->GetTargetForGestureEvent(*event)); 353 } 354 355 return target; 356} 357 358//////////////////////////////////////////////////////////////////////////////// 359// WindowEventDispatcher, aura::client::CaptureDelegate implementation: 360 361void WindowEventDispatcher::UpdateCapture(Window* old_capture, 362 Window* new_capture) { 363 // |mouse_moved_handler_| may have been set to a Window in a different root 364 // (see below). Clear it here to ensure we don't end up referencing a stale 365 // Window. 366 if (mouse_moved_handler_ && !window()->Contains(mouse_moved_handler_)) 367 mouse_moved_handler_ = NULL; 368 369 if (old_capture && old_capture->GetRootWindow() == window() && 370 old_capture->delegate()) { 371 // Send a capture changed event with bogus location data. 372 ui::MouseEvent event(ui::ET_MOUSE_CAPTURE_CHANGED, gfx::Point(), 373 gfx::Point(), 0, 0); 374 375 DispatchDetails details = DispatchEvent(old_capture, &event); 376 if (details.dispatcher_destroyed) 377 return; 378 379 old_capture->delegate()->OnCaptureLost(); 380 } 381 382 if (new_capture) { 383 // Make all subsequent mouse events go to the capture window. We shouldn't 384 // need to send an event here as OnCaptureLost() should take care of that. 385 if (mouse_moved_handler_ || Env::GetInstance()->IsMouseButtonDown()) 386 mouse_moved_handler_ = new_capture; 387 } else { 388 // Make sure mouse_moved_handler gets updated. 389 DispatchDetails details = SynthesizeMouseMoveEvent(); 390 if (details.dispatcher_destroyed) 391 return; 392 } 393 mouse_pressed_handler_ = NULL; 394} 395 396void WindowEventDispatcher::OnOtherRootGotCapture() { 397 // Windows provides the TrackMouseEvents API which allows us to rely on the 398 // OS to send us the mouse exit events (WM_MOUSELEAVE). Additionally on 399 // desktop Windows, every top level window could potentially have its own 400 // root window, in which case this function will get called whenever those 401 // windows grab mouse capture. Sending mouse exit messages in these cases 402 // causes subtle bugs like (crbug.com/394672). 403#if !defined(OS_WIN) 404 if (mouse_moved_handler_) { 405 // Dispatch a mouse exit to reset any state associated with hover. This is 406 // important when going from no window having capture to a window having 407 // capture because we do not dispatch ET_MOUSE_CAPTURE_CHANGED in this case. 408 DispatchDetails details = DispatchMouseExitAtPoint( 409 GetLastMouseLocationInRoot()); 410 if (details.dispatcher_destroyed) 411 return; 412 } 413#endif 414 415 mouse_moved_handler_ = NULL; 416 mouse_pressed_handler_ = NULL; 417} 418 419void WindowEventDispatcher::SetNativeCapture() { 420 host_->SetCapture(); 421} 422 423void WindowEventDispatcher::ReleaseNativeCapture() { 424 host_->ReleaseCapture(); 425} 426 427//////////////////////////////////////////////////////////////////////////////// 428// WindowEventDispatcher, ui::EventProcessor implementation: 429ui::EventTarget* WindowEventDispatcher::GetRootTarget() { 430 return window(); 431} 432 433void WindowEventDispatcher::PrepareEventForDispatch(ui::Event* event) { 434 if (dispatching_held_event_) { 435 // The held events are already in |window()|'s coordinate system. So it is 436 // not necessary to apply the transform to convert from the host's 437 // coordinate system to |window()|'s coordinate system. 438 return; 439 } 440 if (event->IsMouseEvent() || 441 event->IsScrollEvent() || 442 event->IsTouchEvent() || 443 event->IsGestureEvent()) { 444 TransformEventForDeviceScaleFactor(static_cast<ui::LocatedEvent*>(event)); 445 } 446} 447 448//////////////////////////////////////////////////////////////////////////////// 449// WindowEventDispatcher, ui::EventDispatcherDelegate implementation: 450 451bool WindowEventDispatcher::CanDispatchToTarget(ui::EventTarget* target) { 452 return event_dispatch_target_ == target; 453} 454 455ui::EventDispatchDetails WindowEventDispatcher::PreDispatchEvent( 456 ui::EventTarget* target, 457 ui::Event* event) { 458 Window* target_window = static_cast<Window*>(target); 459 CHECK(window()->Contains(target_window)); 460 461 if (!dispatching_held_event_) { 462 bool can_be_held = IsEventCandidateForHold(*event); 463 if (!move_hold_count_ || !can_be_held) { 464 if (can_be_held) 465 held_move_event_.reset(); 466 DispatchDetails details = DispatchHeldEvents(); 467 if (details.dispatcher_destroyed || details.target_destroyed) 468 return details; 469 } 470 } 471 472 if (event->IsMouseEvent()) { 473 PreDispatchMouseEvent(target_window, static_cast<ui::MouseEvent*>(event)); 474 } else if (event->IsScrollEvent()) { 475 PreDispatchLocatedEvent(target_window, 476 static_cast<ui::ScrollEvent*>(event)); 477 } else if (event->IsTouchEvent()) { 478 PreDispatchTouchEvent(target_window, static_cast<ui::TouchEvent*>(event)); 479 } 480 old_dispatch_target_ = event_dispatch_target_; 481 event_dispatch_target_ = static_cast<Window*>(target); 482 return DispatchDetails(); 483} 484 485ui::EventDispatchDetails WindowEventDispatcher::PostDispatchEvent( 486 ui::EventTarget* target, 487 const ui::Event& event) { 488 DispatchDetails details; 489 if (!target || target != event_dispatch_target_) 490 details.target_destroyed = true; 491 event_dispatch_target_ = old_dispatch_target_; 492 old_dispatch_target_ = NULL; 493#ifndef NDEBUG 494 DCHECK(!event_dispatch_target_ || window()->Contains(event_dispatch_target_)); 495#endif 496 497 if (event.IsTouchEvent() && !details.target_destroyed) { 498 // Do not let 'held' touch events contribute to any gestures unless it is 499 // being dispatched. 500 if (dispatching_held_event_ || !held_move_event_ || 501 !held_move_event_->IsTouchEvent()) { 502 // If the event is being handled asynchronously, ignore it. 503 if(event.result() & ui::ER_CONSUMED) 504 return details; 505 scoped_ptr<ui::GestureRecognizer::Gestures> gestures; 506 507 // Once we've fully migrated to the eager gesture detector, we won't 508 // need to pass an event here. 509 ui::TouchEvent orig_event(static_cast<const ui::TouchEvent&>(event), 510 static_cast<Window*>(event.target()), 511 window()); 512 gestures.reset( 513 ui::GestureRecognizer::Get()->ProcessTouchEventPostDispatch( 514 orig_event, event.result(), static_cast<Window*>(target))); 515 516 return ProcessGestures(gestures.get()); 517 } 518 } 519 520 return details; 521} 522 523//////////////////////////////////////////////////////////////////////////////// 524// WindowEventDispatcher, ui::GestureEventHelper implementation: 525 526bool WindowEventDispatcher::CanDispatchToConsumer( 527 ui::GestureConsumer* consumer) { 528 Window* consumer_window = ConsumerToWindow(consumer); 529 return (consumer_window && consumer_window->GetRootWindow() == window()); 530} 531 532void WindowEventDispatcher::DispatchCancelTouchEvent(ui::TouchEvent* event) { 533 DispatchDetails details = OnEventFromSource(event); 534 if (details.dispatcher_destroyed) 535 return; 536} 537 538//////////////////////////////////////////////////////////////////////////////// 539// WindowEventDispatcher, WindowObserver implementation: 540 541void WindowEventDispatcher::OnWindowDestroying(Window* window) { 542 if (!host_->window()->Contains(window)) 543 return; 544 545 DispatchMouseExitToHidingWindow(window); 546 SynthesizeMouseMoveAfterChangeToWindow(window); 547} 548 549void WindowEventDispatcher::OnWindowDestroyed(Window* window) { 550 // We observe all windows regardless of what root Window (if any) they're 551 // attached to. 552 observer_manager_.Remove(window); 553} 554 555void WindowEventDispatcher::OnWindowAddedToRootWindow(Window* attached) { 556 if (!observer_manager_.IsObserving(attached)) 557 observer_manager_.Add(attached); 558 559 if (!host_->window()->Contains(attached)) 560 return; 561 562 SynthesizeMouseMoveAfterChangeToWindow(attached); 563} 564 565void WindowEventDispatcher::OnWindowRemovingFromRootWindow(Window* detached, 566 Window* new_root) { 567 if (!host_->window()->Contains(detached)) 568 return; 569 570 DCHECK(client::GetCaptureWindow(window()) != window()); 571 572 DispatchMouseExitToHidingWindow(detached); 573 SynthesizeMouseMoveAfterChangeToWindow(detached); 574 575 // Hiding the window releases capture which can implicitly destroy the window 576 // so the window may no longer be valid after this call. 577 OnWindowHidden(detached, new_root ? WINDOW_MOVING : WINDOW_HIDDEN); 578} 579 580void WindowEventDispatcher::OnWindowVisibilityChanging(Window* window, 581 bool visible) { 582 if (!host_->window()->Contains(window)) 583 return; 584 585 DispatchMouseExitToHidingWindow(window); 586} 587 588void WindowEventDispatcher::OnWindowVisibilityChanged(Window* window, 589 bool visible) { 590 if (!host_->window()->Contains(window)) 591 return; 592 593 if (window->ContainsPointInRoot(GetLastMouseLocationInRoot())) 594 PostSynthesizeMouseMove(); 595 596 // Hiding the window releases capture which can implicitly destroy the window 597 // so the window may no longer be valid after this call. 598 if (!visible) 599 OnWindowHidden(window, WINDOW_HIDDEN); 600} 601 602void WindowEventDispatcher::OnWindowBoundsChanged(Window* window, 603 const gfx::Rect& old_bounds, 604 const gfx::Rect& new_bounds) { 605 if (!host_->window()->Contains(window)) 606 return; 607 608 if (window == host_->window()) { 609 TRACE_EVENT1("ui", "WindowEventDispatcher::OnWindowBoundsChanged(root)", 610 "size", new_bounds.size().ToString()); 611 612 DispatchDetails details = DispatchHeldEvents(); 613 if (details.dispatcher_destroyed) 614 return; 615 616 synthesize_mouse_move_ = false; 617 } 618 619 if (window->IsVisible() && !window->ignore_events()) { 620 gfx::Rect old_bounds_in_root = old_bounds, new_bounds_in_root = new_bounds; 621 Window::ConvertRectToTarget(window->parent(), host_->window(), 622 &old_bounds_in_root); 623 Window::ConvertRectToTarget(window->parent(), host_->window(), 624 &new_bounds_in_root); 625 gfx::Point last_mouse_location = GetLastMouseLocationInRoot(); 626 if (old_bounds_in_root.Contains(last_mouse_location) != 627 new_bounds_in_root.Contains(last_mouse_location)) { 628 PostSynthesizeMouseMove(); 629 } 630 } 631} 632 633void WindowEventDispatcher::OnWindowTransforming(Window* window) { 634 if (!host_->window()->Contains(window)) 635 return; 636 637 SynthesizeMouseMoveAfterChangeToWindow(window); 638} 639 640void WindowEventDispatcher::OnWindowTransformed(Window* window) { 641 if (!host_->window()->Contains(window)) 642 return; 643 644 SynthesizeMouseMoveAfterChangeToWindow(window); 645} 646 647/////////////////////////////////////////////////////////////////////////////// 648// WindowEventDispatcher, EnvObserver implementation: 649 650void WindowEventDispatcher::OnWindowInitialized(Window* window) { 651 observer_manager_.Add(window); 652} 653 654//////////////////////////////////////////////////////////////////////////////// 655// WindowEventDispatcher, private: 656 657ui::EventDispatchDetails WindowEventDispatcher::DispatchHeldEvents() { 658 if (!held_repostable_event_ && !held_move_event_) 659 return DispatchDetails(); 660 661 CHECK(!dispatching_held_event_); 662 dispatching_held_event_ = true; 663 664 DispatchDetails dispatch_details; 665 if (held_repostable_event_) { 666 if (held_repostable_event_->type() == ui::ET_MOUSE_PRESSED) { 667 scoped_ptr<ui::MouseEvent> mouse_event( 668 static_cast<ui::MouseEvent*>(held_repostable_event_.release())); 669 dispatch_details = OnEventFromSource(mouse_event.get()); 670 } else { 671 // TODO(rbyers): GESTURE_TAP_DOWN not yet supported: crbug.com/170987. 672 NOTREACHED(); 673 } 674 if (dispatch_details.dispatcher_destroyed) 675 return dispatch_details; 676 } 677 678 if (held_move_event_) { 679 // If a mouse move has been synthesized, the target location is suspect, 680 // so drop the held mouse event. 681 if (held_move_event_->IsTouchEvent() || 682 (held_move_event_->IsMouseEvent() && !synthesize_mouse_move_)) { 683 dispatch_details = OnEventFromSource(held_move_event_.get()); 684 } 685 if (!dispatch_details.dispatcher_destroyed) 686 held_move_event_.reset(); 687 } 688 689 if (!dispatch_details.dispatcher_destroyed) 690 dispatching_held_event_ = false; 691 return dispatch_details; 692} 693 694void WindowEventDispatcher::PostSynthesizeMouseMove() { 695 if (synthesize_mouse_move_) 696 return; 697 synthesize_mouse_move_ = true; 698 base::MessageLoop::current()->PostNonNestableTask( 699 FROM_HERE, 700 base::Bind(base::IgnoreResult( 701 &WindowEventDispatcher::SynthesizeMouseMoveEvent), 702 held_event_factory_.GetWeakPtr())); 703} 704 705void WindowEventDispatcher::SynthesizeMouseMoveAfterChangeToWindow( 706 Window* window) { 707 if (window->IsVisible() && 708 window->ContainsPointInRoot(GetLastMouseLocationInRoot())) { 709 PostSynthesizeMouseMove(); 710 } 711} 712 713ui::EventDispatchDetails WindowEventDispatcher::SynthesizeMouseMoveEvent() { 714 DispatchDetails details; 715 if (!synthesize_mouse_move_) 716 return details; 717 synthesize_mouse_move_ = false; 718 719 // If one of the mouse buttons is currently down, then do not synthesize a 720 // mouse-move event. In such cases, aura could synthesize a DRAGGED event 721 // instead of a MOVED event, but in multi-display/multi-host scenarios, the 722 // DRAGGED event can be synthesized in the incorrect host. So avoid 723 // synthesizing any events at all. 724 if (Env::GetInstance()->mouse_button_flags()) 725 return details; 726 727 gfx::Point root_mouse_location = GetLastMouseLocationInRoot(); 728 if (!window()->bounds().Contains(root_mouse_location)) 729 return details; 730 gfx::Point host_mouse_location = root_mouse_location; 731 host_->ConvertPointToHost(&host_mouse_location); 732 ui::MouseEvent event(ui::ET_MOUSE_MOVED, 733 host_mouse_location, 734 host_mouse_location, 735 ui::EF_IS_SYNTHESIZED, 736 0); 737 return OnEventFromSource(&event); 738} 739 740void WindowEventDispatcher::PreDispatchLocatedEvent(Window* target, 741 ui::LocatedEvent* event) { 742 int flags = event->flags(); 743 if (IsNonClientLocation(target, event->location())) 744 flags |= ui::EF_IS_NON_CLIENT; 745 event->set_flags(flags); 746 747 if (!dispatching_held_event_ && 748 (event->IsMouseEvent() || event->IsScrollEvent()) && 749 !(event->flags() & ui::EF_IS_SYNTHESIZED)) { 750 if (event->type() != ui::ET_MOUSE_CAPTURE_CHANGED) 751 SetLastMouseLocation(window(), event->root_location()); 752 synthesize_mouse_move_ = false; 753 } 754} 755 756void WindowEventDispatcher::PreDispatchMouseEvent(Window* target, 757 ui::MouseEvent* event) { 758 client::CursorClient* cursor_client = client::GetCursorClient(window()); 759 // We allow synthesized mouse exit events through even if mouse events are 760 // disabled. This ensures that hover state, etc on controls like buttons is 761 // cleared. 762 if (cursor_client && 763 !cursor_client->IsMouseEventsEnabled() && 764 (event->flags() & ui::EF_IS_SYNTHESIZED) && 765 (event->type() != ui::ET_MOUSE_EXITED)) { 766 event->SetHandled(); 767 return; 768 } 769 770 if (IsEventCandidateForHold(*event) && !dispatching_held_event_) { 771 if (move_hold_count_) { 772 if (!(event->flags() & ui::EF_IS_SYNTHESIZED) && 773 event->type() != ui::ET_MOUSE_CAPTURE_CHANGED) { 774 SetLastMouseLocation(window(), event->root_location()); 775 } 776 held_move_event_.reset(new ui::MouseEvent(*event, target, window())); 777 event->SetHandled(); 778 return; 779 } else { 780 // We may have a held event for a period between the time move_hold_count_ 781 // fell to 0 and the DispatchHeldEvents executes. Since we're going to 782 // dispatch the new event directly below, we can reset the old one. 783 held_move_event_.reset(); 784 } 785 } 786 787 const int kMouseButtonFlagMask = ui::EF_LEFT_MOUSE_BUTTON | 788 ui::EF_MIDDLE_MOUSE_BUTTON | 789 ui::EF_RIGHT_MOUSE_BUTTON; 790 switch (event->type()) { 791 case ui::ET_MOUSE_EXITED: 792 if (!target || target == window()) { 793 DispatchDetails details = 794 DispatchMouseEnterOrExit(*event, ui::ET_MOUSE_EXITED); 795 if (details.dispatcher_destroyed) { 796 event->SetHandled(); 797 return; 798 } 799 mouse_moved_handler_ = NULL; 800 } 801 break; 802 case ui::ET_MOUSE_MOVED: 803 // Send an exit to the current |mouse_moved_handler_| and an enter to 804 // |target|. Take care that both us and |target| aren't destroyed during 805 // dispatch. 806 if (target != mouse_moved_handler_) { 807 aura::Window* old_mouse_moved_handler = mouse_moved_handler_; 808 WindowTracker live_window; 809 live_window.Add(target); 810 DispatchDetails details = 811 DispatchMouseEnterOrExit(*event, ui::ET_MOUSE_EXITED); 812 if (details.dispatcher_destroyed) { 813 event->SetHandled(); 814 return; 815 } 816 // If the |mouse_moved_handler_| changes out from under us, assume a 817 // nested message loop ran and we don't need to do anything. 818 if (mouse_moved_handler_ != old_mouse_moved_handler) { 819 event->SetHandled(); 820 return; 821 } 822 if (!live_window.Contains(target) || details.target_destroyed) { 823 mouse_moved_handler_ = NULL; 824 event->SetHandled(); 825 return; 826 } 827 live_window.Remove(target); 828 829 mouse_moved_handler_ = target; 830 details = DispatchMouseEnterOrExit(*event, ui::ET_MOUSE_ENTERED); 831 if (details.dispatcher_destroyed || details.target_destroyed) { 832 event->SetHandled(); 833 return; 834 } 835 } 836 break; 837 case ui::ET_MOUSE_PRESSED: 838 // Don't set the mouse pressed handler for non client mouse down events. 839 // These are only sent by Windows and are not always followed with non 840 // client mouse up events which causes subsequent mouse events to be 841 // sent to the wrong target. 842 if (!(event->flags() & ui::EF_IS_NON_CLIENT) && !mouse_pressed_handler_) 843 mouse_pressed_handler_ = target; 844 Env::GetInstance()->set_mouse_button_flags( 845 event->flags() & kMouseButtonFlagMask); 846 break; 847 case ui::ET_MOUSE_RELEASED: 848 mouse_pressed_handler_ = NULL; 849 Env::GetInstance()->set_mouse_button_flags(event->flags() & 850 kMouseButtonFlagMask & ~event->changed_button_flags()); 851 break; 852 default: 853 break; 854 } 855 856 PreDispatchLocatedEvent(target, event); 857} 858 859void WindowEventDispatcher::PreDispatchTouchEvent(Window* target, 860 ui::TouchEvent* event) { 861 switch (event->type()) { 862 case ui::ET_TOUCH_PRESSED: 863 touch_ids_down_ |= (1 << event->touch_id()); 864 Env::GetInstance()->set_touch_down(touch_ids_down_ != 0); 865 break; 866 867 // Handle ET_TOUCH_CANCELLED only if it has a native event. 868 case ui::ET_TOUCH_CANCELLED: 869 if (!event->HasNativeEvent()) 870 break; 871 // fallthrough 872 case ui::ET_TOUCH_RELEASED: 873 touch_ids_down_ = (touch_ids_down_ | (1 << event->touch_id())) ^ 874 (1 << event->touch_id()); 875 Env::GetInstance()->set_touch_down(touch_ids_down_ != 0); 876 break; 877 878 case ui::ET_TOUCH_MOVED: 879 if (move_hold_count_ && !dispatching_held_event_) { 880 held_move_event_.reset(new ui::TouchEvent(*event, target, window())); 881 event->SetHandled(); 882 return; 883 } 884 break; 885 886 default: 887 NOTREACHED(); 888 break; 889 } 890 891 if (dispatching_held_event_ || !held_move_event_ || 892 !held_move_event_->IsTouchEvent()) { 893 ui::TouchEvent orig_event(*event, target, window()); 894 895 // If the touch event is invalid in some way, the gesture recognizer will 896 // reject it. This must call |StopPropagation()|, in order to prevent the 897 // touch from being acked in |PostDispatchEvent|. 898 if (!ui::GestureRecognizer::Get()->ProcessTouchEventPreDispatch(orig_event, 899 target)) { 900 event->StopPropagation(); 901 } 902 } 903 904 PreDispatchLocatedEvent(target, event); 905} 906 907} // namespace aura 908