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