// Copyright (c) 2012 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "ui/aura/root_window.h"
#include <vector>
#include "base/bind.h"
#include "base/command_line.h"
#include "base/debug/trace_event.h"
#include "base/logging.h"
#include "base/message_loop/message_loop.h"
#include "ui/aura/client/capture_client.h"
#include "ui/aura/client/cursor_client.h"
#include "ui/aura/client/event_client.h"
#include "ui/aura/client/focus_client.h"
#include "ui/aura/client/screen_position_client.h"
#include "ui/aura/env.h"
#include "ui/aura/root_window_observer.h"
#include "ui/aura/root_window_transformer.h"
#include "ui/aura/window.h"
#include "ui/aura/window_delegate.h"
#include "ui/aura/window_targeter.h"
#include "ui/aura/window_tracker.h"
#include "ui/aura/window_tree_host.h"
#include "ui/base/hit_test.h"
#include "ui/base/view_prop.h"
#include "ui/compositor/dip_util.h"
#include "ui/compositor/layer.h"
#include "ui/compositor/layer_animator.h"
#include "ui/events/event.h"
#include "ui/events/gestures/gesture_recognizer.h"
#include "ui/events/gestures/gesture_types.h"
#include "ui/gfx/display.h"
#include "ui/gfx/point3_f.h"
#include "ui/gfx/point_conversions.h"
#include "ui/gfx/screen.h"
#include "ui/gfx/size_conversions.h"
using std::vector;
typedef ui::EventDispatchDetails DispatchDetails;
namespace aura {
namespace {
const char kRootWindowForAcceleratedWidget[] =
"__AURA_ROOT_WINDOW_ACCELERATED_WIDGET__";
// Returns true if |target| has a non-client (frame) component at |location|,
// in window coordinates.
bool IsNonClientLocation(Window* target, const gfx::Point& location) {
if (!target->delegate())
return false;
int hit_test_code = target->delegate()->GetNonClientComponent(location);
return hit_test_code != HTCLIENT && hit_test_code != HTNOWHERE;
}
float GetDeviceScaleFactorFromDisplay(Window* window) {
gfx::Display display = gfx::Screen::GetScreenFor(window)->
GetDisplayNearestWindow(window);
DCHECK(display.is_valid());
return display.device_scale_factor();
}
Window* ConsumerToWindow(ui::GestureConsumer* consumer) {
return consumer ? static_cast<Window*>(consumer) : NULL;
}
void SetLastMouseLocation(const Window* root_window,
const gfx::Point& location_in_root) {
client::ScreenPositionClient* client =
client::GetScreenPositionClient(root_window);
if (client) {
gfx::Point location_in_screen = location_in_root;
client->ConvertPointToScreen(root_window, &location_in_screen);
Env::GetInstance()->set_last_mouse_location(location_in_screen);
} else {
Env::GetInstance()->set_last_mouse_location(location_in_root);
}
}
RootWindowHost* CreateHost(RootWindow* root_window,
const RootWindow::CreateParams& params) {
RootWindowHost* host = params.host ?
params.host : RootWindowHost::Create(params.initial_bounds);
host->set_delegate(root_window);
return host;
}
bool IsUsingEventProcessorForDispatch(const ui::Event& event) {
return event.IsKeyEvent() ||
event.IsScrollEvent();
}
class SimpleRootWindowTransformer : public RootWindowTransformer {
public:
SimpleRootWindowTransformer(const Window* root_window,
const gfx::Transform& transform)
: root_window_(root_window),
transform_(transform) {
}
// RootWindowTransformer overrides:
virtual gfx::Transform GetTransform() const OVERRIDE {
return transform_;
}
virtual gfx::Transform GetInverseTransform() const OVERRIDE {
gfx::Transform invert;
if (!transform_.GetInverse(&invert))
return transform_;
return invert;
}
virtual gfx::Rect GetRootWindowBounds(
const gfx::Size& host_size) const OVERRIDE {
gfx::Rect bounds(host_size);
gfx::RectF new_bounds(ui::ConvertRectToDIP(root_window_->layer(), bounds));
transform_.TransformRect(&new_bounds);
return gfx::Rect(gfx::ToFlooredSize(new_bounds.size()));
}
virtual gfx::Insets GetHostInsets() const OVERRIDE {
return gfx::Insets();
}
private:
virtual ~SimpleRootWindowTransformer() {}
const Window* root_window_;
const gfx::Transform transform_;
DISALLOW_COPY_AND_ASSIGN(SimpleRootWindowTransformer);
};
} // namespace
RootWindow::CreateParams::CreateParams(const gfx::Rect& a_initial_bounds)
: initial_bounds(a_initial_bounds),
host(NULL) {
}
////////////////////////////////////////////////////////////////////////////////
// RootWindow, public:
RootWindow::RootWindow(const CreateParams& params)
: window_(new Window(NULL)),
host_(CreateHost(this, params)),
touch_ids_down_(0),
last_cursor_(ui::kCursorNull),
mouse_pressed_handler_(NULL),
mouse_moved_handler_(NULL),
event_dispatch_target_(NULL),
old_dispatch_target_(NULL),
synthesize_mouse_move_(false),
move_hold_count_(0),
dispatching_held_event_(false),
repost_event_factory_(this),
held_event_factory_(this) {
window()->set_dispatcher(this);
window()->SetName("RootWindow");
window()->set_event_targeter(
scoped_ptr<ui::EventTargeter>(new WindowTargeter()));
compositor_.reset(new ui::Compositor(host_->GetAcceleratedWidget()));
DCHECK(compositor_.get());
prop_.reset(new ui::ViewProp(host_->GetAcceleratedWidget(),
kRootWindowForAcceleratedWidget,
this));
ui::GestureRecognizer::Get()->AddGestureEventHelper(this);
}
RootWindow::~RootWindow() {
TRACE_EVENT0("shutdown", "RootWindow::Destructor");
ui::GestureRecognizer::Get()->RemoveGestureEventHelper(this);
// Make sure to destroy the compositor before terminating so that state is
// cleared and we don't hit asserts.
compositor_.reset();
// An observer may have been added by an animation on the RootWindow.
window()->layer()->GetAnimator()->RemoveObserver(this);
// Destroy child windows while we're still valid. This is also done by
// ~Window, but by that time any calls to virtual methods overriden here (such
// as GetRootWindow()) result in Window's implementation. By destroying here
// we ensure GetRootWindow() still returns this.
window()->RemoveOrDestroyChildren();
// Destroying/removing child windows may try to access |host_| (eg.
// GetAcceleratedWidget())
host_.reset(NULL);
window()->set_dispatcher(NULL);
}
// static
RootWindow* RootWindow::GetForAcceleratedWidget(
gfx::AcceleratedWidget widget) {
return reinterpret_cast<RootWindow*>(
ui::ViewProp::GetValue(widget, kRootWindowForAcceleratedWidget));
}
void RootWindow::Init() {
compositor()->SetScaleAndSize(GetDeviceScaleFactorFromDisplay(window()),
host_->GetBounds().size());
window()->Init(ui::LAYER_NOT_DRAWN);
compositor()->SetRootLayer(window()->layer());
transformer_.reset(
new SimpleRootWindowTransformer(window(), gfx::Transform()));
UpdateRootWindowSize(host_->GetBounds().size());
Env::GetInstance()->NotifyRootWindowInitialized(this);
window()->Show();
}
void RootWindow::PrepareForShutdown() {
host_->PrepareForShutdown();
// discard synthesize event request as well.
synthesize_mouse_move_ = false;
}
void RootWindow::RepostEvent(const ui::LocatedEvent& event) {
DCHECK(event.type() == ui::ET_MOUSE_PRESSED ||
event.type() == ui::ET_GESTURE_TAP_DOWN);
// We allow for only one outstanding repostable event. This is used
// in exiting context menus. A dropped repost request is allowed.
if (event.type() == ui::ET_MOUSE_PRESSED) {
held_repostable_event_.reset(
new ui::MouseEvent(
static_cast<const ui::MouseEvent&>(event),
static_cast<aura::Window*>(event.target()),
window()));
base::MessageLoop::current()->PostNonNestableTask(
FROM_HERE,
base::Bind(base::IgnoreResult(&RootWindow::DispatchHeldEvents),
repost_event_factory_.GetWeakPtr()));
} else {
DCHECK(event.type() == ui::ET_GESTURE_TAP_DOWN);
held_repostable_event_.reset();
// TODO(rbyers): Reposing of gestures is tricky to get
// right, so it's not yet supported. crbug.com/170987.
}
}
RootWindowHostDelegate* RootWindow::AsRootWindowHostDelegate() {
return this;
}
void RootWindow::SetHostSize(const gfx::Size& size_in_pixel) {
DispatchDetails details = DispatchHeldEvents();
if (details.dispatcher_destroyed)
return;
gfx::Rect bounds = host_->GetBounds();
bounds.set_size(size_in_pixel);
host_->SetBounds(bounds);
// Requery the location to constrain it within the new root window size.
gfx::Point point;
if (host_->QueryMouseLocation(&point)) {
SetLastMouseLocation(window(),
ui::ConvertPointToDIP(window()->layer(), point));
}
synthesize_mouse_move_ = false;
}
void RootWindow::SetHostBounds(const gfx::Rect& bounds_in_pixel) {
DCHECK(!bounds_in_pixel.IsEmpty());
DispatchDetails details = DispatchHeldEvents();
if (details.dispatcher_destroyed)
return;
host_->SetBounds(bounds_in_pixel);
synthesize_mouse_move_ = false;
}
void RootWindow::SetCursor(gfx::NativeCursor cursor) {
last_cursor_ = cursor;
// A lot of code seems to depend on NULL cursors actually showing an arrow,
// so just pass everything along to the host.
host_->SetCursor(cursor);
}
void RootWindow::OnCursorVisibilityChanged(bool show) {
// Clear any existing mouse hover effects when the cursor becomes invisible.
// Note we do not need to dispatch a mouse enter when the cursor becomes
// visible because that can only happen in response to a mouse event, which
// will trigger its own mouse enter.
if (!show)
DispatchMouseExitAtPoint(GetLastMouseLocationInRoot());
host_->OnCursorVisibilityChanged(show);
}
void RootWindow::OnMouseEventsEnableStateChanged(bool enabled) {
// Send entered / exited so that visual state can be updated to match
// mouse events state.
PostMouseMoveEventAfterWindowChange();
// TODO(mazda): Add code to disable mouse events when |enabled| == false.
}
void RootWindow::MoveCursorTo(const gfx::Point& location_in_dip) {
gfx::Point host_location(location_in_dip);
ConvertPointToHost(&host_location);
MoveCursorToInternal(location_in_dip, host_location);
}
void RootWindow::MoveCursorToHostLocation(const gfx::Point& host_location) {
gfx::Point root_location(host_location);
ConvertPointFromHost(&root_location);
MoveCursorToInternal(root_location, host_location);
}
void RootWindow::ScheduleRedrawRect(const gfx::Rect& damage_rect) {
compositor_->ScheduleRedrawRect(damage_rect);
}
Window* RootWindow::GetGestureTarget(ui::GestureEvent* event) {
Window* target = NULL;
if (!event->IsEndingEvent()) {
// The window that received the start event (e.g. scroll begin) needs to
// receive the end event (e.g. scroll end).
target = client::GetCaptureWindow(window());
}
if (!target) {
target = ConsumerToWindow(
ui::GestureRecognizer::Get()->GetTargetForGestureEvent(*event));
}
return target;
}
void RootWindow::DispatchGestureEvent(ui::GestureEvent* event) {
DispatchDetails details = DispatchHeldEvents();
if (details.dispatcher_destroyed)
return;
Window* target = GetGestureTarget(event);
if (target) {
event->ConvertLocationToTarget(window(), target);
DispatchDetails details = DispatchEvent(target, event);
if (details.dispatcher_destroyed)
return;
}
}
void RootWindow::OnWindowDestroying(Window* window) {
DispatchMouseExitToHidingWindow(window);
OnWindowHidden(window, WINDOW_DESTROYED);
if (window->IsVisible() &&
window->ContainsPointInRoot(GetLastMouseLocationInRoot())) {
PostMouseMoveEventAfterWindowChange();
}
}
void RootWindow::OnWindowBoundsChanged(Window* window,
bool contained_mouse_point) {
if (contained_mouse_point ||
(window->IsVisible() &&
window->ContainsPointInRoot(GetLastMouseLocationInRoot()))) {
PostMouseMoveEventAfterWindowChange();
}
}
void RootWindow::DispatchMouseExitToHidingWindow(Window* window) {
// The mouse capture is intentionally ignored. Think that a mouse enters
// to a window, the window sets the capture, the mouse exits the window,
// and then it releases the capture. In that case OnMouseExited won't
// be called. So it is natural not to emit OnMouseExited even though
// |window| is the capture window.
gfx::Point last_mouse_location = GetLastMouseLocationInRoot();
if (window->Contains(mouse_moved_handler_) &&
window->ContainsPointInRoot(last_mouse_location))
DispatchMouseExitAtPoint(last_mouse_location);
}
void RootWindow::DispatchMouseExitAtPoint(const gfx::Point& point) {
ui::MouseEvent event(ui::ET_MOUSE_EXITED, point, point, ui::EF_NONE);
DispatchDetails details =
DispatchMouseEnterOrExit(event, ui::ET_MOUSE_EXITED);
if (details.dispatcher_destroyed)
return;
}
void RootWindow::OnWindowVisibilityChanged(Window* window, bool is_visible) {
if (!is_visible)
OnWindowHidden(window, WINDOW_HIDDEN);
if (window->ContainsPointInRoot(GetLastMouseLocationInRoot()))
PostMouseMoveEventAfterWindowChange();
}
void RootWindow::OnWindowTransformed(Window* window, bool contained_mouse) {
if (contained_mouse ||
(window->IsVisible() &&
window->ContainsPointInRoot(GetLastMouseLocationInRoot()))) {
PostMouseMoveEventAfterWindowChange();
}
}
void RootWindow::OnKeyboardMappingChanged() {
FOR_EACH_OBSERVER(RootWindowObserver, observers_,
OnKeyboardMappingChanged(this));
}
void RootWindow::OnRootWindowHostCloseRequested() {
FOR_EACH_OBSERVER(RootWindowObserver, observers_,
OnRootWindowHostCloseRequested(this));
}
void RootWindow::AddRootWindowObserver(RootWindowObserver* observer) {
observers_.AddObserver(observer);
}
void RootWindow::RemoveRootWindowObserver(RootWindowObserver* observer) {
observers_.RemoveObserver(observer);
}
void RootWindow::ConvertPointToHost(gfx::Point* point) const {
gfx::Point3F point_3f(*point);
GetRootTransform().TransformPoint(&point_3f);
*point = gfx::ToFlooredPoint(point_3f.AsPointF());
}
void RootWindow::ConvertPointFromHost(gfx::Point* point) const {
gfx::Point3F point_3f(*point);
GetInverseRootTransform().TransformPoint(&point_3f);
*point = gfx::ToFlooredPoint(point_3f.AsPointF());
}
void RootWindow::ProcessedTouchEvent(ui::TouchEvent* event,
Window* window,
ui::EventResult result) {
scoped_ptr<ui::GestureRecognizer::Gestures> gestures;
gestures.reset(ui::GestureRecognizer::Get()->
ProcessTouchEventForGesture(*event, result, window));
DispatchDetails details = ProcessGestures(gestures.get());
if (details.dispatcher_destroyed)
return;
}
void RootWindow::HoldPointerMoves() {
if (!move_hold_count_)
held_event_factory_.InvalidateWeakPtrs();
++move_hold_count_;
TRACE_EVENT_ASYNC_BEGIN0("ui", "RootWindow::HoldPointerMoves", this);
}
void RootWindow::ReleasePointerMoves() {
--move_hold_count_;
DCHECK_GE(move_hold_count_, 0);
if (!move_hold_count_ && held_move_event_) {
// We don't want to call DispatchHeldEvents directly, because this might be
// called from a deep stack while another event, in which case dispatching
// another one may not be safe/expected. Instead we post a task, that we
// may cancel if HoldPointerMoves is called again before it executes.
base::MessageLoop::current()->PostNonNestableTask(
FROM_HERE,
base::Bind(base::IgnoreResult(&RootWindow::DispatchHeldEvents),
held_event_factory_.GetWeakPtr()));
}
TRACE_EVENT_ASYNC_END0("ui", "RootWindow::HoldPointerMoves", this);
}
gfx::Point RootWindow::GetLastMouseLocationInRoot() const {
gfx::Point location = Env::GetInstance()->last_mouse_location();
client::ScreenPositionClient* client =
client::GetScreenPositionClient(window());
if (client)
client->ConvertPointFromScreen(window(), &location);
return location;
}
void RootWindow::SetRootWindowTransformer(
scoped_ptr<RootWindowTransformer> transformer) {
transformer_ = transformer.Pass();
host_->SetInsets(transformer_->GetHostInsets());
window()->SetTransform(transformer_->GetTransform());
// If the layer is not animating, then we need to update the root window
// size immediately.
if (!window()->layer()->GetAnimator()->is_animating())
UpdateRootWindowSize(host_->GetBounds().size());
}
gfx::Transform RootWindow::GetRootTransform() const {
float scale = ui::GetDeviceScaleFactor(window()->layer());
gfx::Transform transform;
transform.Scale(scale, scale);
transform *= transformer_->GetTransform();
return transform;
}
void RootWindow::SetTransform(const gfx::Transform& transform) {
scoped_ptr<RootWindowTransformer> transformer(
new SimpleRootWindowTransformer(window(), transform));
SetRootWindowTransformer(transformer.Pass());
}
////////////////////////////////////////////////////////////////////////////////
// RootWindow, private:
void RootWindow::TransformEventForDeviceScaleFactor(ui::LocatedEvent* event) {
event->UpdateForRootTransform(GetInverseRootTransform());
}
void RootWindow::MoveCursorToInternal(const gfx::Point& root_location,
const gfx::Point& host_location) {
host_->MoveCursorTo(host_location);
SetLastMouseLocation(window(), root_location);
client::CursorClient* cursor_client = client::GetCursorClient(window());
if (cursor_client) {
const gfx::Display& display =
gfx::Screen::GetScreenFor(window())->GetDisplayNearestWindow(window());
cursor_client->SetDisplay(display);
}
synthesize_mouse_move_ = false;
}
ui::EventDispatchDetails RootWindow::DispatchMouseEnterOrExit(
const ui::MouseEvent& event,
ui::EventType type) {
if (!mouse_moved_handler_ || !mouse_moved_handler_->delegate())
return DispatchDetails();
ui::MouseEvent translated_event(event,
window(),
mouse_moved_handler_,
type,
event.flags() | ui::EF_IS_SYNTHESIZED);
return DispatchEvent(mouse_moved_handler_, &translated_event);
}
ui::EventDispatchDetails RootWindow::ProcessGestures(
ui::GestureRecognizer::Gestures* gestures) {
DispatchDetails details;
if (!gestures || gestures->empty())
return details;
Window* target = GetGestureTarget(gestures->get().at(0));
for (size_t i = 0; i < gestures->size(); ++i) {
ui::GestureEvent* event = gestures->get().at(i);
event->ConvertLocationToTarget(window(), target);
details = DispatchEvent(target, event);
if (details.dispatcher_destroyed || details.target_destroyed)
break;
}
return details;
}
void RootWindow::OnWindowAddedToRootWindow(Window* attached) {
if (attached->IsVisible() &&
attached->ContainsPointInRoot(GetLastMouseLocationInRoot())) {
PostMouseMoveEventAfterWindowChange();
}
}
void RootWindow::OnWindowRemovedFromRootWindow(Window* detached,
Window* new_root) {
DCHECK(aura::client::GetCaptureWindow(window()) != window());
DispatchMouseExitToHidingWindow(detached);
OnWindowHidden(detached, new_root ? WINDOW_MOVING : WINDOW_HIDDEN);
if (detached->IsVisible() &&
detached->ContainsPointInRoot(GetLastMouseLocationInRoot())) {
PostMouseMoveEventAfterWindowChange();
}
}
void RootWindow::OnWindowHidden(Window* invisible, WindowHiddenReason reason) {
// Do not clear the capture, and the |event_dispatch_target_| if the
// window is moving across root windows, because the target itself
// is actually still visible and clearing them stops further event
// processing, which can cause unexpected behaviors. See
// crbug.com/157583
if (reason != WINDOW_MOVING) {
Window* capture_window = aura::client::GetCaptureWindow(window());
// If the ancestor of the capture window is hidden,
// release the capture.
if (invisible->Contains(capture_window) && invisible != window())
capture_window->ReleaseCapture();
if (invisible->Contains(event_dispatch_target_))
event_dispatch_target_ = NULL;
if (invisible->Contains(old_dispatch_target_))
old_dispatch_target_ = NULL;
}
// If the ancestor of any event handler windows are invisible, release the
// pointer to those windows.
if (invisible->Contains(mouse_pressed_handler_))
mouse_pressed_handler_ = NULL;
if (invisible->Contains(mouse_moved_handler_))
mouse_moved_handler_ = NULL;
CleanupGestureState(invisible);
}
void RootWindow::CleanupGestureState(Window* window) {
ui::GestureRecognizer::Get()->CancelActiveTouches(window);
ui::GestureRecognizer::Get()->CleanupStateForConsumer(window);
const Window::Windows& windows = window->children();
for (Window::Windows::const_iterator iter = windows.begin();
iter != windows.end();
++iter) {
CleanupGestureState(*iter);
}
}
void RootWindow::UpdateRootWindowSize(const gfx::Size& host_size) {
window()->SetBounds(transformer_->GetRootWindowBounds(host_size));
}
////////////////////////////////////////////////////////////////////////////////
// RootWindow, aura::client::CaptureDelegate implementation:
void RootWindow::UpdateCapture(Window* old_capture,
Window* new_capture) {
// |mouse_moved_handler_| may have been set to a Window in a different root
// (see below). Clear it here to ensure we don't end up referencing a stale
// Window.
if (mouse_moved_handler_ && !window()->Contains(mouse_moved_handler_))
mouse_moved_handler_ = NULL;
if (old_capture && old_capture->GetRootWindow() == window() &&
old_capture->delegate()) {
// Send a capture changed event with bogus location data.
ui::MouseEvent event(ui::ET_MOUSE_CAPTURE_CHANGED, gfx::Point(),
gfx::Point(), 0);
DispatchDetails details = DispatchEvent(old_capture, &event);
if (details.dispatcher_destroyed)
return;
old_capture->delegate()->OnCaptureLost();
}
if (new_capture) {
// Make all subsequent mouse events go to the capture window. We shouldn't
// need to send an event here as OnCaptureLost() should take care of that.
if (mouse_moved_handler_ || Env::GetInstance()->IsMouseButtonDown())
mouse_moved_handler_ = new_capture;
} else {
// Make sure mouse_moved_handler gets updated.
DispatchDetails details = SynthesizeMouseMoveEvent();
if (details.dispatcher_destroyed)
return;
}
mouse_pressed_handler_ = NULL;
}
void RootWindow::OnOtherRootGotCapture() {
mouse_moved_handler_ = NULL;
mouse_pressed_handler_ = NULL;
}
void RootWindow::SetNativeCapture() {
host_->SetCapture();
}
void RootWindow::ReleaseNativeCapture() {
host_->ReleaseCapture();
}
////////////////////////////////////////////////////////////////////////////////
// RootWindow, ui::EventProcessor implementation:
ui::EventTarget* RootWindow::GetRootTarget() {
return window();
}
void RootWindow::PrepareEventForDispatch(ui::Event* event) {
if (event->IsMouseEvent() ||
event->IsScrollEvent() ||
event->IsTouchEvent() ||
event->IsGestureEvent()) {
TransformEventForDeviceScaleFactor(static_cast<ui::LocatedEvent*>(event));
}
}
////////////////////////////////////////////////////////////////////////////////
// RootWindow, ui::EventDispatcherDelegate implementation:
bool RootWindow::CanDispatchToTarget(ui::EventTarget* target) {
return event_dispatch_target_ == target;
}
ui::EventDispatchDetails RootWindow::PreDispatchEvent(ui::EventTarget* target,
ui::Event* event) {
if (!dispatching_held_event_ && IsUsingEventProcessorForDispatch(*event)) {
DispatchDetails details = DispatchHeldEvents();
if (details.dispatcher_destroyed || details.target_destroyed)
return details;
Window* target_window = static_cast<Window*>(target);
if (event->IsScrollEvent()) {
PreDispatchLocatedEvent(target_window,
static_cast<ui::ScrollEvent*>(event));
}
}
old_dispatch_target_ = event_dispatch_target_;
event_dispatch_target_ = static_cast<Window*>(target);
return DispatchDetails();
}
ui::EventDispatchDetails RootWindow::PostDispatchEvent(ui::EventTarget* target,
const ui::Event& event) {
DispatchDetails details;
if (target != event_dispatch_target_)
details.target_destroyed = true;
event_dispatch_target_ = old_dispatch_target_;
old_dispatch_target_ = NULL;
#ifndef NDEBUG
DCHECK(!event_dispatch_target_ || window()->Contains(event_dispatch_target_));
#endif
return details;
}
////////////////////////////////////////////////////////////////////////////////
// RootWindow, ui::GestureEventHelper implementation:
bool RootWindow::CanDispatchToConsumer(ui::GestureConsumer* consumer) {
Window* consumer_window = ConsumerToWindow(consumer);;
return (consumer_window && consumer_window->GetRootWindow() == window());
}
void RootWindow::DispatchPostponedGestureEvent(ui::GestureEvent* event) {
DispatchGestureEvent(event);
}
void RootWindow::DispatchCancelTouchEvent(ui::TouchEvent* event) {
OnHostTouchEvent(event);
}
////////////////////////////////////////////////////////////////////////////////
// RootWindow, ui::LayerAnimationObserver implementation:
void RootWindow::OnLayerAnimationEnded(
ui::LayerAnimationSequence* animation) {
UpdateRootWindowSize(host_->GetBounds().size());
}
void RootWindow::OnLayerAnimationScheduled(
ui::LayerAnimationSequence* animation) {
}
void RootWindow::OnLayerAnimationAborted(
ui::LayerAnimationSequence* animation) {
}
////////////////////////////////////////////////////////////////////////////////
// RootWindow, RootWindowHostDelegate implementation:
bool RootWindow::OnHostKeyEvent(ui::KeyEvent* event) {
DispatchDetails details = OnEventFromSource(event);
if (details.dispatcher_destroyed)
event->SetHandled();
return event->handled();
}
bool RootWindow::OnHostMouseEvent(ui::MouseEvent* event) {
DispatchDetails details = OnHostMouseEventImpl(event);
return event->handled() || details.dispatcher_destroyed;
}
bool RootWindow::OnHostScrollEvent(ui::ScrollEvent* event) {
DispatchDetails details = OnEventFromSource(event);
if (details.dispatcher_destroyed)
event->SetHandled();
return event->handled();
}
bool RootWindow::OnHostTouchEvent(ui::TouchEvent* event) {
if ((event->type() == ui::ET_TOUCH_MOVED)) {
if (move_hold_count_) {
Window* null_window = static_cast<Window*>(NULL);
held_move_event_.reset(
new ui::TouchEvent(*event, null_window, null_window));
return true;
} else {
// We may have a held event for a period between the time move_hold_count_
// fell to 0 and the DispatchHeldEvents executes. Since we're going to
// dispatch the new event directly below, we can reset the old one.
held_move_event_.reset();
}
}
DispatchDetails details = DispatchHeldEvents();
if (details.dispatcher_destroyed)
return false;
details = DispatchTouchEventImpl(event);
if (details.dispatcher_destroyed)
return true;
return event->handled();
}
void RootWindow::OnHostCancelMode() {
ui::CancelModeEvent event;
Window* focused_window = client::GetFocusClient(window())->GetFocusedWindow();
DispatchDetails details =
DispatchEvent(focused_window ? focused_window : window(), &event);
if (details.dispatcher_destroyed)
return;
}
void RootWindow::OnHostActivated() {
Env::GetInstance()->RootWindowActivated(this);
}
void RootWindow::OnHostLostWindowCapture() {
Window* capture_window = client::GetCaptureWindow(window());
if (capture_window && capture_window->GetRootWindow() == window())
capture_window->ReleaseCapture();
}
void RootWindow::OnHostLostMouseGrab() {
mouse_pressed_handler_ = NULL;
mouse_moved_handler_ = NULL;
}
void RootWindow::OnHostPaint(const gfx::Rect& damage_rect) {
compositor_->ScheduleRedrawRect(damage_rect);
}
void RootWindow::OnHostMoved(const gfx::Point& origin) {
TRACE_EVENT1("ui", "RootWindow::OnHostMoved",
"origin", origin.ToString());
FOR_EACH_OBSERVER(RootWindowObserver, observers_,
OnRootWindowHostMoved(this, origin));
}
void RootWindow::OnHostResized(const gfx::Size& size) {
TRACE_EVENT1("ui", "RootWindow::OnHostResized",
"size", size.ToString());
DispatchDetails details = DispatchHeldEvents();
if (details.dispatcher_destroyed)
return;
// The compositor should have the same size as the native root window host.
// Get the latest scale from display because it might have been changed.
compositor_->SetScaleAndSize(GetDeviceScaleFactorFromDisplay(window()), size);
// The layer, and the observers should be notified of the
// transformed size of the root window.
UpdateRootWindowSize(host_->GetBounds().size());
FOR_EACH_OBSERVER(RootWindowObserver, observers_,
OnRootWindowHostResized(this));
}
float RootWindow::GetDeviceScaleFactor() {
return compositor()->device_scale_factor();
}
RootWindow* RootWindow::AsRootWindow() {
return this;
}
const RootWindow* RootWindow::AsRootWindow() const {
return this;
}
ui::EventProcessor* RootWindow::GetEventProcessor() {
return this;
}
////////////////////////////////////////////////////////////////////////////////
// RootWindow, private:
ui::EventDispatchDetails RootWindow::OnHostMouseEventImpl(
ui::MouseEvent* event) {
if (event->type() == ui::ET_MOUSE_DRAGGED ||
(event->flags() & ui::EF_IS_SYNTHESIZED)) {
if (move_hold_count_) {
Window* null_window = static_cast<Window*>(NULL);
held_move_event_.reset(
new ui::MouseEvent(*event, null_window, null_window));
event->SetHandled();
return DispatchDetails();
} else {
// We may have a held event for a period between the time move_hold_count_
// fell to 0 and the DispatchHeldEvents executes. Since we're going to
// dispatch the new event directly below, we can reset the old one.
held_move_event_.reset();
}
}
DispatchDetails details = DispatchHeldEvents();
if (details.dispatcher_destroyed)
return details;
return DispatchMouseEventImpl(event);
}
ui::EventDispatchDetails RootWindow::DispatchMouseEventImpl(
ui::MouseEvent* event) {
TransformEventForDeviceScaleFactor(event);
Window* target = mouse_pressed_handler_ ?
mouse_pressed_handler_ : client::GetCaptureWindow(window());
if (!target)
target = window()->GetEventHandlerForPoint(event->location());
return DispatchMouseEventToTarget(event, target);
}
ui::EventDispatchDetails RootWindow::DispatchMouseEventRepost(
ui::MouseEvent* event) {
if (event->type() != ui::ET_MOUSE_PRESSED)
return DispatchDetails();
Window* target = client::GetCaptureWindow(window());
WindowEventDispatcher* dispatcher = this;
if (!target) {
target = window()->GetEventHandlerForPoint(event->location());
} else {
dispatcher = target->GetDispatcher();
CHECK(dispatcher); // Capture window better be in valid root.
}
dispatcher->mouse_pressed_handler_ = NULL;
return dispatcher->DispatchMouseEventToTarget(event, target);
}
ui::EventDispatchDetails RootWindow::DispatchMouseEventToTarget(
ui::MouseEvent* event,
Window* target) {
client::CursorClient* cursor_client = client::GetCursorClient(window());
if (cursor_client &&
!cursor_client->IsMouseEventsEnabled() &&
(event->flags() & ui::EF_IS_SYNTHESIZED))
return DispatchDetails();
static const int kMouseButtonFlagMask =
ui::EF_LEFT_MOUSE_BUTTON |
ui::EF_MIDDLE_MOUSE_BUTTON |
ui::EF_RIGHT_MOUSE_BUTTON;
// WARNING: because of nested message loops |this| may be deleted after
// dispatching any event. Do not use AutoReset or the like here.
SetLastMouseLocation(window(), event->location());
synthesize_mouse_move_ = false;
switch (event->type()) {
case ui::ET_MOUSE_EXITED:
if (!target) {
DispatchDetails details =
DispatchMouseEnterOrExit(*event, ui::ET_MOUSE_EXITED);
if (details.dispatcher_destroyed)
return details;
mouse_moved_handler_ = NULL;
}
break;
case ui::ET_MOUSE_MOVED:
// Send an exit to the current |mouse_moved_handler_| and an enter to
// |target|. Take care that both us and |target| aren't destroyed during
// dispatch.
if (target != mouse_moved_handler_) {
aura::Window* old_mouse_moved_handler = mouse_moved_handler_;
WindowTracker destroyed_tracker;
if (target)
destroyed_tracker.Add(target);
DispatchDetails details =
DispatchMouseEnterOrExit(*event, ui::ET_MOUSE_EXITED);
if (details.dispatcher_destroyed)
return details;
// If the |mouse_moved_handler_| changes out from under us, assume a
// nested message loop ran and we don't need to do anything.
if (mouse_moved_handler_ != old_mouse_moved_handler)
return DispatchDetails();
if (destroyed_tracker.Contains(target)) {
destroyed_tracker.Remove(target);
mouse_moved_handler_ = target;
DispatchDetails details =
DispatchMouseEnterOrExit(*event, ui::ET_MOUSE_ENTERED);
if (details.dispatcher_destroyed)
return details;
} else {
mouse_moved_handler_ = NULL;
return DispatchDetails();
}
}
break;
case ui::ET_MOUSE_PRESSED:
// Don't set the mouse pressed handler for non client mouse down events.
// These are only sent by Windows and are not always followed with non
// client mouse up events which causes subsequent mouse events to be
// sent to the wrong target.
if (!(event->flags() & ui::EF_IS_NON_CLIENT) && !mouse_pressed_handler_)
mouse_pressed_handler_ = target;
Env::GetInstance()->set_mouse_button_flags(
event->flags() & kMouseButtonFlagMask);
break;
case ui::ET_MOUSE_RELEASED:
mouse_pressed_handler_ = NULL;
Env::GetInstance()->set_mouse_button_flags(event->flags() &
kMouseButtonFlagMask & ~event->changed_button_flags());
break;
default:
break;
}
if (target) {
event->ConvertLocationToTarget(window(), target);
if (IsNonClientLocation(target, event->location()))
event->set_flags(event->flags() | ui::EF_IS_NON_CLIENT);
return DispatchEvent(target, event);
}
return DispatchDetails();
}
ui::EventDispatchDetails RootWindow::DispatchTouchEventImpl(
ui::TouchEvent* event) {
switch (event->type()) {
case ui::ET_TOUCH_PRESSED:
touch_ids_down_ |= (1 << event->touch_id());
Env::GetInstance()->set_touch_down(touch_ids_down_ != 0);
break;
// Handle ET_TOUCH_CANCELLED only if it has a native event.
case ui::ET_TOUCH_CANCELLED:
if (!event->HasNativeEvent())
break;
// fallthrough
case ui::ET_TOUCH_RELEASED:
touch_ids_down_ = (touch_ids_down_ | (1 << event->touch_id())) ^
(1 << event->touch_id());
Env::GetInstance()->set_touch_down(touch_ids_down_ != 0);
break;
default:
break;
}
TransformEventForDeviceScaleFactor(event);
Window* target = client::GetCaptureWindow(window());
if (!target) {
target = ConsumerToWindow(
ui::GestureRecognizer::Get()->GetTouchLockedTarget(*event));
if (!target) {
target = ConsumerToWindow(ui::GestureRecognizer::Get()->
GetTargetForLocation(event->location()));
}
}
// The gesture recognizer processes touch events in the system coordinates. So
// keep a copy of the touch event here before possibly converting the event to
// a window's local coordinate system.
ui::TouchEvent event_for_gr(*event);
ui::EventResult result = ui::ER_UNHANDLED;
if (!target && !window()->bounds().Contains(event->location())) {
// If the initial touch is outside the root window, target the root.
target = window();
DispatchDetails details = DispatchEvent(target ? target : NULL, event);
if (details.dispatcher_destroyed)
return details;
result = event->result();
} else {
// We only come here when the first contact was within the root window.
if (!target) {
target = window()->GetEventHandlerForPoint(event->location());
if (!target)
return DispatchDetails();
}
event->ConvertLocationToTarget(window(), target);
DispatchDetails details = DispatchEvent(target, event);
if (details.dispatcher_destroyed)
return details;
result = event->result();
}
// Get the list of GestureEvents from GestureRecognizer.
scoped_ptr<ui::GestureRecognizer::Gestures> gestures;
gestures.reset(ui::GestureRecognizer::Get()->
ProcessTouchEventForGesture(event_for_gr, result, target));
return ProcessGestures(gestures.get());
}
ui::EventDispatchDetails RootWindow::DispatchHeldEvents() {
if (!held_repostable_event_ && !held_move_event_)
return DispatchDetails();
CHECK(!dispatching_held_event_);
dispatching_held_event_ = true;
DispatchDetails dispatch_details;
if (held_repostable_event_) {
if (held_repostable_event_->type() == ui::ET_MOUSE_PRESSED) {
scoped_ptr<ui::MouseEvent> mouse_event(
static_cast<ui::MouseEvent*>(held_repostable_event_.release()));
dispatch_details = DispatchMouseEventRepost(mouse_event.get());
} else {
// TODO(rbyers): GESTURE_TAP_DOWN not yet supported: crbug.com/170987.
NOTREACHED();
}
if (dispatch_details.dispatcher_destroyed)
return dispatch_details;
}
if (held_move_event_ && held_move_event_->IsMouseEvent()) {
// If a mouse move has been synthesized, the target location is suspect,
// so drop the held event.
if (!synthesize_mouse_move_) {
dispatch_details = DispatchMouseEventImpl(
static_cast<ui::MouseEvent*>(held_move_event_.get()));
}
if (!dispatch_details.dispatcher_destroyed)
held_move_event_.reset();
} else if (held_move_event_ && held_move_event_->IsTouchEvent()) {
dispatch_details = DispatchTouchEventImpl(
static_cast<ui::TouchEvent*>(held_move_event_.get()));
if (!dispatch_details.dispatcher_destroyed)
held_move_event_.reset();
}
if (!dispatch_details.dispatcher_destroyed)
dispatching_held_event_ = false;
return dispatch_details;
}
void RootWindow::PostMouseMoveEventAfterWindowChange() {
if (synthesize_mouse_move_)
return;
synthesize_mouse_move_ = true;
base::MessageLoop::current()->PostNonNestableTask(
FROM_HERE,
base::Bind(&RootWindow::SynthesizeMouseMoveEventAsync,
held_event_factory_.GetWeakPtr()));
}
ui::EventDispatchDetails RootWindow::SynthesizeMouseMoveEvent() {
DispatchDetails details;
if (!synthesize_mouse_move_)
return details;
synthesize_mouse_move_ = false;
gfx::Point root_mouse_location = GetLastMouseLocationInRoot();
if (!window()->bounds().Contains(root_mouse_location))
return details;
gfx::Point host_mouse_location = root_mouse_location;
ConvertPointToHost(&host_mouse_location);
ui::MouseEvent event(ui::ET_MOUSE_MOVED,
host_mouse_location,
host_mouse_location,
ui::EF_IS_SYNTHESIZED);
return OnHostMouseEventImpl(&event);
}
void RootWindow::SynthesizeMouseMoveEventAsync() {
DispatchDetails details = SynthesizeMouseMoveEvent();
if (details.dispatcher_destroyed)
return;
}
gfx::Transform RootWindow::GetInverseRootTransform() const {
float scale = ui::GetDeviceScaleFactor(window()->layer());
gfx::Transform transform;
transform.Scale(1.0f / scale, 1.0f / scale);
return transformer_->GetInverseTransform() * transform;
}
void RootWindow::PreDispatchLocatedEvent(Window* target,
ui::LocatedEvent* event) {
int flags = event->flags();
if (IsNonClientLocation(target, event->location()))
flags |= ui::EF_IS_NON_CLIENT;
event->set_flags(flags);
if (!dispatching_held_event_) {
SetLastMouseLocation(window(), event->location());
synthesize_mouse_move_ = false;
}
}
} // namespace aura