Commit 148903c2 authored by Scott Violet's avatar Scott Violet Committed by Commit Bot

chromeos: wires up sending events to remote clients

There is still work to be done here, but this is a good step. The
interesting parts:
. An aura::WindowTargeter is used to ensure events target the right
  Window. In particular if one client intercepts events from another
  client.
. Custom EventHandlers are used to forward to the remote client. This
  is mildly tricky to ensure top-levels are handled correctly.

BUG=837692
TEST=covered by test

Change-Id: I58f0d23e7d7dd6c6c31f3cfbb44889588c4f8f84
Reviewed-on: https://chromium-review.googlesource.com/1054427
Commit-Queue: Scott Violet <sky@chromium.org>
Reviewed-by: default avatarSadrul Chowdhury <sadrul@chromium.org>
Cr-Commit-Position: refs/heads/master@{#558868}
parent 363e22e8
This diff is collapsed.
......@@ -5,17 +5,24 @@
#ifndef SERVICES_UI_WS2_CLIENT_WINDOW_H_
#define SERVICES_UI_WS2_CLIENT_WINDOW_H_
#include <vector>
#include "base/component_export.h"
#include "base/macros.h"
#include "components/viz/common/surfaces/frame_sink_id.h"
#include "services/ui/ws2/ids.h"
#include "services/viz/public/interfaces/compositing/compositor_frame_sink.mojom.h"
#include "ui/gfx/geometry/insets.h"
#include "ui/gfx/geometry/rect.h"
namespace aura {
class Window;
}
namespace ui {
class EventHandler;
namespace ws2 {
class WindowHostFrameSinkClient;
......@@ -30,9 +37,13 @@ class COMPONENT_EXPORT(WINDOW_SERVICE) ClientWindow {
// Creates a new ClientWindow. The lifetime of the ClientWindow is tied to
// that of the Window (the Window ends up owning the ClientWindow).
// |is_top_level| is true if the window represents a top-level window.
static ClientWindow* Create(aura::Window* window,
WindowServiceClient* client,
const viz::FrameSinkId& frame_sink_id);
const viz::FrameSinkId& frame_sink_id,
bool is_top_level);
aura::Window* window() { return window_; }
// Returns the ClientWindow associated with a window, null if not created yet.
static ClientWindow* GetMayBeNull(aura::Window* window) {
......@@ -61,6 +72,19 @@ class COMPONENT_EXPORT(WINDOW_SERVICE) ClientWindow {
void SetFrameSinkId(const viz::FrameSinkId& frame_sink_id);
const viz::FrameSinkId& frame_sink_id() const { return frame_sink_id_; }
const std::vector<gfx::Rect>& additional_client_areas() const {
return additional_client_areas_;
}
const gfx::Insets& client_area() const { return client_area_; }
void SetClientArea(const gfx::Insets& insets,
const std::vector<gfx::Rect>& additional_client_areas);
// Returns true if the window is a top-level window and there is at least some
// non-client area.
bool HasNonClientArea() const;
bool IsTopLevel() const;
void AttachCompositorFrameSink(
viz::mojom::CompositorFrameSinkRequest compositor_frame_sink,
viz::mojom::CompositorFrameSinkClientPtr client);
......@@ -68,7 +92,8 @@ class COMPONENT_EXPORT(WINDOW_SERVICE) ClientWindow {
private:
ClientWindow(aura::Window*,
WindowServiceClient* client,
const viz::FrameSinkId& frame_sink_id);
const viz::FrameSinkId& frame_sink_id,
bool is_top_level);
aura::Window* window_;
......@@ -99,6 +124,13 @@ class COMPONENT_EXPORT(WINDOW_SERVICE) ClientWindow {
// window.
std::unique_ptr<WindowHostFrameSinkClient> window_host_frame_sink_client_;
// Together |client_area_| and |additional_client_areas_| are used to specify
// the client area. See SetClientArea() in mojom for details.
gfx::Insets client_area_;
std::vector<gfx::Rect> additional_client_areas_;
std::unique_ptr<ui::EventHandler> event_handler_;
DISALLOW_COPY_AND_ASSIGN(ClientWindow);
};
......
......@@ -14,6 +14,17 @@
namespace ui {
namespace ws2 {
// static
std::unique_ptr<Event> PointerWatcher::CreateEventForClient(
const Event& event) {
// Client code expects to get PointerEvents.
if (event.IsMouseEvent())
return std::make_unique<ui::PointerEvent>(*event.AsMouseEvent());
if (event.IsTouchEvent())
return std::make_unique<ui::PointerEvent>(*event.AsTouchEvent());
return Event::Clone(event);
}
PointerWatcher::PointerWatcher(WindowServiceClient* client) : client_(client) {
aura::Env::GetInstance()->AddWindowEventDispatcherObserver(this);
}
......@@ -51,15 +62,8 @@ void PointerWatcher::OnWindowEventDispatcherStartedProcessing(
// only send pointer events if an event wasn't also sent to the client.
// Part of https://crbug.com/837692
std::unique_ptr<ui::Event> event_to_send;
// Client code expects to get PointerEvents.
if (event.IsMouseEvent())
event_to_send = std::make_unique<ui::PointerEvent>(*event.AsMouseEvent());
else if (event.IsTouchEvent())
event_to_send = std::make_unique<ui::PointerEvent>(*event.AsTouchEvent());
else
NOTREACHED();
client_->SendPointerWatcherEventToClient(dispatcher->host()->GetDisplayId(),
std::move(event_to_send));
CreateEventForClient(event));
}
} // namespace ws2
......
......@@ -5,10 +5,15 @@
#ifndef SERVICES_UI_WS2_POINTER_WATCHER_H_
#define SERVICES_UI_WS2_POINTER_WATCHER_H_
#include <memory>
#include "base/macros.h"
#include "ui/aura/window_event_dispatcher_observer.h"
namespace ui {
class Event;
namespace ws2 {
class WindowServiceClient;
......@@ -33,6 +38,10 @@ class PointerWatcher : public aura::WindowEventDispatcherObserver {
explicit PointerWatcher(WindowServiceClient* client);
~PointerWatcher() override;
// Applies any necessary transformations on the event before sending to the
// client.
static std::unique_ptr<Event> CreateEventForClient(const Event& event);
void set_types_to_watch(TypesToWatch types) { types_to_watch_ = types; }
private:
......
......@@ -20,7 +20,8 @@ std::unique_ptr<aura::Window> TestWindowServiceDelegate::NewTopLevel(
aura::PropertyConverter* property_converter,
const base::flat_map<std::string, std::vector<uint8_t>>& properties) {
std::unique_ptr<aura::Window> window =
std::make_unique<aura::Window>(nullptr);
std::make_unique<aura::Window>(delegate_for_next_top_level_);
delegate_for_next_top_level_ = nullptr;
window->Init(LAYER_NOT_DRAWN);
if (top_level_parent_)
top_level_parent_->AddChild(window.get());
......
......@@ -8,6 +8,10 @@
#include "base/macros.h"
#include "services/ui/ws2/window_service_delegate.h"
namespace aura {
class WindowDelegate;
}
namespace ui {
namespace ws2 {
......@@ -22,6 +26,10 @@ class TestWindowServiceDelegate : public WindowServiceDelegate {
top_level_parent_ = parent;
}
void set_delegate_for_next_top_level(aura::WindowDelegate* delegate) {
delegate_for_next_top_level_ = delegate;
}
// WindowServiceDelegate:
std::unique_ptr<aura::Window> NewTopLevel(
aura::PropertyConverter* property_converter,
......@@ -30,6 +38,7 @@ class TestWindowServiceDelegate : public WindowServiceDelegate {
private:
aura::Window* top_level_parent_;
aura::WindowDelegate* delegate_for_next_top_level_ = nullptr;
DISALLOW_COPY_AND_ASSIGN(TestWindowServiceDelegate);
};
......
......@@ -12,6 +12,12 @@
namespace ui {
namespace ws2 {
TestWindowTreeClient::InputEvent::InputEvent() = default;
TestWindowTreeClient::InputEvent::InputEvent(InputEvent&& other) = default;
TestWindowTreeClient::InputEvent::~InputEvent() = default;
TestWindowTreeClient::ObservedPointerEvent::ObservedPointerEvent() = default;
TestWindowTreeClient::ObservedPointerEvent::ObservedPointerEvent(
......@@ -25,6 +31,15 @@ TestWindowTreeClient::TestWindowTreeClient() {
TestWindowTreeClient::~TestWindowTreeClient() = default;
TestWindowTreeClient::InputEvent TestWindowTreeClient::PopInputEvent() {
if (input_events_.empty())
return InputEvent();
InputEvent event = std::move(input_events_.front());
input_events_.pop();
return event;
}
TestWindowTreeClient::ObservedPointerEvent
TestWindowTreeClient::PopObservedPointerEvent() {
if (observed_pointer_events_.empty())
......@@ -159,6 +174,19 @@ void TestWindowTreeClient::OnWindowInputEvent(
const gfx::PointF& event_location_in_screen_pixel_layout,
std::unique_ptr<ui::Event> event,
bool matches_pointer_watcher) {
tracker_.OnWindowInputEvent(window_id, *event, display_id,
event_location_in_screen_pixel_layout,
matches_pointer_watcher);
InputEvent input_event;
input_event.event_id = event_id;
input_event.window_id = window_id;
input_event.display_id = display_id;
input_event.event = std::move(event);
input_event.matches_pointer_watcher = matches_pointer_watcher;
input_events_.push(std::move(input_event));
if (tree_)
tree_->OnWindowInputEventAck(event_id, mojom::EventResult::HANDLED);
}
......
......@@ -21,6 +21,19 @@ namespace ws2 {
class TestWindowTreeClient : public mojom::WindowTreeClient,
public TestChangeTracker::Delegate {
public:
// Created every time OnWindowInputEvent() is called.
struct InputEvent {
InputEvent();
InputEvent(InputEvent&& other);
~InputEvent();
uint32_t event_id;
Id window_id;
int64_t display_id;
std::unique_ptr<ui::Event> event;
bool matches_pointer_watcher;
};
// An ObservedPointerEvent is created for each call to
// OnPointerEventObserved()
struct ObservedPointerEvent {
......@@ -36,6 +49,13 @@ class TestWindowTreeClient : public mojom::WindowTreeClient,
TestWindowTreeClient();
~TestWindowTreeClient() override;
std::queue<InputEvent>& input_events() { return input_events_; }
// Returns the oldest InputEvent that was received by way of
// OnWindowInputEvent(). If no events have been observed, |event| in the
// returned object is null.
InputEvent PopInputEvent();
std::queue<ObservedPointerEvent>& observed_pointer_events() {
return observed_pointer_events_;
}
......@@ -163,6 +183,7 @@ class TestWindowTreeClient : public mojom::WindowTreeClient,
mojom::WindowTreePtr tree_;
Id root_window_id_ = 0;
bool track_root_bounds_changes_ = false;
std::queue<InputEvent> input_events_;
std::queue<ObservedPointerEvent> observed_pointer_events_;
DISALLOW_COPY_AND_ASSIGN(TestWindowTreeClient);
......
......@@ -38,7 +38,8 @@ ClientWindow* WindowService::GetClientWindowForWindowCreateIfNecessary(
const viz::FrameSinkId frame_sink_id =
ClientWindowId(kWindowServerClientId, next_window_id_++);
CHECK_NE(0u, next_window_id_);
return ClientWindow::Create(window, nullptr, frame_sink_id);
const bool is_top_level = false;
return ClientWindow::Create(window, nullptr, frame_sink_id, is_top_level);
}
std::unique_ptr<WindowServiceClient> WindowService::CreateWindowServiceClient(
......
......@@ -77,6 +77,20 @@ WindowServiceClient::~WindowServiceClient() {
}
}
void WindowServiceClient::SendEventToClient(aura::Window* window,
const Event& event) {
// TODO(sky): remove event_id.
const uint32_t event_id = 1;
// Events should only come to windows connected to displays.
DCHECK(window->GetHost());
const int64_t display_id = window->GetHost()->GetDisplayId();
// TODO(sky): wire up |matches_pointer_watcher|.
const bool matches_pointer_watcher = false;
window_tree_client_->OnWindowInputEvent(
event_id, TransportIdForWindow(window), display_id, 0u, gfx::PointF(),
PointerWatcher::CreateEventForClient(event), matches_pointer_watcher);
}
void WindowServiceClient::SendPointerWatcherEventToClient(
int64_t display_id,
std::unique_ptr<ui::Event> event) {
......@@ -84,6 +98,11 @@ void WindowServiceClient::SendPointerWatcherEventToClient(
kInvalidTransportId, display_id);
}
bool WindowServiceClient::IsTopLevel(aura::Window* window) {
auto iter = FindClientRootWithRoot(window);
return iter != client_roots_.end() && (*iter)->is_top_level();
}
ClientRoot* WindowServiceClient::CreateClientRoot(
aura::Window* window,
mojom::WindowTreePtr window_tree) {
......@@ -206,13 +225,10 @@ bool WindowServiceClient::IsClientRootWindow(aura::Window* window) {
return window && FindClientRootWithRoot(window) != client_roots_.end();
}
bool WindowServiceClient::IsTopLevel(aura::Window* window) {
auto iter = FindClientRootWithRoot(window);
return iter != client_roots_.end() && (*iter)->is_top_level();
}
WindowServiceClient::ClientRoots::iterator
WindowServiceClient::FindClientRootWithRoot(aura::Window* window) {
if (!window)
return client_roots_.end();
for (auto iter = client_roots_.begin(); iter != client_roots_.end(); ++iter) {
if (iter->get()->window() == window)
return iter;
......@@ -234,10 +250,11 @@ bool WindowServiceClient::IsWindowRootOfAnotherClient(
aura::Window* WindowServiceClient::AddClientCreatedWindow(
const ClientWindowId& id,
bool is_top_level,
std::unique_ptr<aura::Window> window_ptr) {
aura::Window* window = window_ptr.get();
client_created_windows_.insert(std::move(window_ptr));
ClientWindow::Create(window, this, id);
ClientWindow::Create(window, this, id, is_top_level);
AddWindowToKnownWindows(window, id);
return window;
}
......@@ -374,8 +391,9 @@ bool WindowServiceClient::NewWindowImpl(
DVLOG(1) << "NewWindow failed (id is not valid for client)";
return false;
}
const bool is_top_level = false;
aura::Window* window = AddClientCreatedWindow(
client_window_id, std::make_unique<aura::Window>(nullptr));
client_window_id, is_top_level, std::make_unique<aura::Window>(nullptr));
SetWindowType(window, aura::GetWindowTypeFromProperties(properties));
for (auto& pair : properties) {
......@@ -585,10 +603,11 @@ bool WindowServiceClient::EmbedImpl(
mojom::WindowTreeClientPtr window_tree_client,
uint32_t flags) {
DVLOG(3) << "Embed window_id=" << window_id;
if (flags & mojom::kEmbedFlagEmbedderInterceptsEvents) {
// TODO: add support for this.
NOTIMPLEMENTED();
}
// mojom::kEmbedFlagEmbedderInterceptsEvents is inherited, otherwise an
// embedder could effectively circumvent it by embedding itself.
if (intercepts_events_)
flags = mojom::kEmbedFlagEmbedderInterceptsEvents;
aura::Window* window = GetWindowByClientId(window_id);
if (!window) {
......@@ -600,13 +619,10 @@ bool WindowServiceClient::EmbedImpl(
return false;
}
// mojom::kEmbedFlagEmbedderInterceptsEvents is inherited, otherwise an
// embedder could effectively circumvent it by embedding itself.
const bool intercepts_events = intercepts_events_;
auto new_client_binding = std::make_unique<WindowServiceClientBinding>();
new_client_binding->InitForEmbed(
window_service_, std::move(window_tree_client), intercepts_events, window,
window_service_, std::move(window_tree_client),
flags & mojom::kEmbedFlagEmbedderInterceptsEvents, window,
base::BindOnce(&WindowServiceClient::OnChildBindingConnectionLost,
base::Unretained(this), new_client_binding.get()));
......@@ -714,8 +730,9 @@ void WindowServiceClient::NewTopLevelWindow(
return;
}
top_level_ptr->set_owned_by_parent(false);
aura::Window* top_level =
AddClientCreatedWindow(client_window_id, std::move(top_level_ptr));
const bool is_top_level = true;
aura::Window* top_level = AddClientCreatedWindow(
client_window_id, is_top_level, std::move(top_level_ptr));
ClientWindow::GetMayBeNull(top_level)->SetFrameSinkId(client_window_id);
const int64_t display_id =
display::Screen::GetScreen()->GetDisplayNearestWindow(top_level).id();
......@@ -773,10 +790,26 @@ void WindowServiceClient::SetWindowTransform(uint32_t change_id,
}
void WindowServiceClient::SetClientArea(
Id window_id,
Id transport_window_id,
const gfx::Insets& insets,
const base::Optional<std::vector<gfx::Rect>>& additional_client_areas) {
NOTIMPLEMENTED();
const ClientWindowId window_id = MakeClientWindowId(transport_window_id);
aura::Window* window = GetWindowByClientId(window_id);
DVLOG(3) << "SetClientArea client window_id=" << window_id.ToString()
<< " insets=" << insets.ToString();
if (!window) {
DVLOG(1) << "SetClientArea failed (invalid window id)";
return;
}
if (!IsClientRootWindow(window) || !IsTopLevel(window)) {
DVLOG(1) << "SetClientArea failed (access denied)";
return;
}
ClientWindow* client_window = ClientWindow::GetMayBeNull(window);
DCHECK(client_window); // Must exist because of preceeding conditionals.
client_window->SetClientArea(
insets, additional_client_areas.value_or(std::vector<gfx::Rect>()));
}
void WindowServiceClient::SetHitTestMask(
......@@ -957,10 +990,9 @@ void WindowServiceClient::SetEventTargetingPolicy(
window->SetEventTargetingPolicy(policy);
}
void WindowServiceClient::OnWindowInputEventAck(
uint32_t event_id,
::ui::mojom::EventResult result) {
NOTIMPLEMENTED();
void WindowServiceClient::OnWindowInputEventAck(uint32_t event_id,
mojom::EventResult result) {
// TODO(sky): this is no longer needed, remove.
}
void WindowServiceClient::DeactivateWindow(Id window_id) {
......
......@@ -68,11 +68,20 @@ class COMPONENT_EXPORT(WINDOW_SERVICE) WindowServiceClient
void InitForEmbed(aura::Window* root, mojom::WindowTreePtr window_tree_ptr);
void InitFromFactory();
bool intercepts_events() const { return intercepts_events_; }
// Notifies the client than an event has been received.
void SendEventToClient(aura::Window* window, const ui::Event& event);
// Notifies the client that an event matching a pointer watcher has been
// received.
void SendPointerWatcherEventToClient(int64_t display_id,
std::unique_ptr<Event> event);
// Returns true if |window| was created by the client calling
// NewTopLevelWindow().
bool IsTopLevel(aura::Window* window);
WindowService* window_service() { return window_service_; }
private:
......@@ -119,9 +128,6 @@ class COMPONENT_EXPORT(WINDOW_SERVICE) WindowServiceClient
bool IsClientCreatedWindow(aura::Window* window);
bool IsClientRootWindow(aura::Window* window);
// Returns true if |window| was created by the client calling
// NewTopLevelWindow().
bool IsTopLevel(aura::Window* window);
ClientRoots::iterator FindClientRootWithRoot(aura::Window* window);
// Returns true if |window| has been exposed to this client. A client
......@@ -133,6 +139,7 @@ class COMPONENT_EXPORT(WINDOW_SERVICE) WindowServiceClient
// Called for windows created by the client (including top-levels).
aura::Window* AddClientCreatedWindow(
const ClientWindowId& id,
bool is_top_level,
std::unique_ptr<aura::Window> window_ptr);
// Adds/removes a Window from the set of windows known to the client. This
......@@ -230,7 +237,7 @@ class COMPONENT_EXPORT(WINDOW_SERVICE) WindowServiceClient
void SetWindowTransform(uint32_t change_id,
Id window_id,
const gfx::Transform& transform) override;
void SetClientArea(Id window_id,
void SetClientArea(Id transport_window_id,
const gfx::Insets& insets,
const base::Optional<std::vector<gfx::Rect>>&
additional_client_areas) override;
......@@ -329,11 +336,13 @@ class COMPONENT_EXPORT(WINDOW_SERVICE) WindowServiceClient
mojom::WindowTreeClient* window_tree_client_;
// If true the client sees all the decendants of windows with embeddings
// in them that were created by this client, and additionally any events
// normally targeted at a descendant are targeted at the first ancestor Window
// created by this client. This is done to allow a client to intercept events
// normally targeted at descendants and dispatch them using some other means.
// If true, all events that would normally target another client embedded by
// this client are sent to this client. For example, consider the Window
// hierarchy A->B->C where client 1 created A and B, client 1 embedded
// client 2 in window B, and client 2 created C. If an event occurs that would
// normally target C, then the event is instead sent to client 1 with a target
// of B. If true, any clients embedded by this client never get normal events
// (they can still observer pointer events).
const bool intercepts_events_;
// Controls whether the client can change the visibility of the roots.
......
......@@ -52,6 +52,15 @@ void WindowServiceClientTestHelper::SetWindowBounds(aura::Window* window,
local_surface_id);
}
void WindowServiceClientTestHelper::SetClientArea(
aura::Window* window,
const gfx::Insets& insets,
base::Optional<std::vector<gfx::Rect>> additional_client_areas) {
window_service_client_->SetClientArea(
window_service_client_->TransportIdForWindow(window), insets,
additional_client_areas);
}
void WindowServiceClientTestHelper::SetWindowProperty(
aura::Window* window,
const std::string& name,
......
......@@ -10,15 +10,17 @@
#include "base/containers/flat_map.h"
#include "base/macros.h"
#include "base/optional.h"
#include "services/ui/public/interfaces/window_tree_constants.mojom.h"
#include "services/ui/ws2/ids.h"
#include "ui/gfx/geometry/rect.h"
namespace aura {
class Window;
}
namespace gfx {
class Rect;
class Insets;
}
namespace ui {
......@@ -53,6 +55,11 @@ class WindowServiceClientTestHelper {
void SetWindowBounds(aura::Window* window,
const gfx::Rect& bounds,
uint32_t change_id = 1);
void SetClientArea(
aura::Window* window,
const gfx::Insets& insets,
base::Optional<std::vector<gfx::Rect>> additional_client_areas =
base::Optional<std::vector<gfx::Rect>>());
void SetWindowProperty(aura::Window* window,
const std::string& name,
const std::vector<uint8_t>& value,
......
......@@ -248,6 +248,198 @@ TEST(WindowServiceClientTest, WindowToWindowData) {
data->properties[ui::mojom::WindowManager::kAlwaysOnTop_Property]));
}
TEST(WindowServiceClientTest, MovePressDragRelease) {
WindowServiceTestHelper helper;
TestWindowTreeClient* window_tree_client = helper.window_tree_client();
aura::Window* top_level = helper.helper()->NewTopLevelWindow(1);
ASSERT_TRUE(top_level);
top_level->Show();
top_level->SetBounds(gfx::Rect(10, 10, 100, 100));
test::EventGenerator event_generator(helper.root());
{
event_generator.MoveMouseTo(50, 50);
std::unique_ptr<Event> event = window_tree_client->PopInputEvent().event;
ASSERT_TRUE(event);
EXPECT_EQ(ET_POINTER_ENTERED, event->type());
EXPECT_EQ(gfx::Point(40, 40), event->AsLocatedEvent()->location());
event = window_tree_client->PopInputEvent().event;
ASSERT_TRUE(event);
EXPECT_EQ(ET_POINTER_MOVED, event->type());
EXPECT_EQ(gfx::Point(40, 40), event->AsLocatedEvent()->location());
}
{
event_generator.PressLeftButton();
std::unique_ptr<Event> event = window_tree_client->PopInputEvent().event;
ASSERT_TRUE(event);
EXPECT_EQ(ET_POINTER_DOWN, event->type());
EXPECT_EQ(gfx::Point(40, 40), event->AsLocatedEvent()->location());
}
{
event_generator.MoveMouseTo(0, 0);
std::unique_ptr<Event> event = window_tree_client->PopInputEvent().event;
ASSERT_TRUE(event);
EXPECT_EQ(ET_POINTER_MOVED, event->type());
EXPECT_EQ(gfx::Point(-10, -10), event->AsLocatedEvent()->location());
}
{
event_generator.ReleaseLeftButton();
std::unique_ptr<Event> event = window_tree_client->PopInputEvent().event;
ASSERT_TRUE(event);
EXPECT_EQ(ET_POINTER_UP, event->type());
EXPECT_EQ(gfx::Point(-10, -10), event->AsLocatedEvent()->location());
}
}
class EventRecordingWindowDelegate : public aura::test::TestWindowDelegate {
public:
EventRecordingWindowDelegate() = default;
~EventRecordingWindowDelegate() override = default;
std::queue<std::unique_ptr<ui::Event>>& events() { return events_; }
std::unique_ptr<Event> PopEvent() {
if (events_.empty())
return nullptr;
auto event = std::move(events_.front());
events_.pop();
return event;
}
void ClearEvents() {
std::queue<std::unique_ptr<ui::Event>> events;
std::swap(events_, events);
}
// aura::test::TestWindowDelegate:
void OnEvent(ui::Event* event) override {
events_.push(ui::Event::Clone(*event));
}
private:
std::queue<std::unique_ptr<ui::Event>> events_;
DISALLOW_COPY_AND_ASSIGN(EventRecordingWindowDelegate);
};
TEST(WindowServiceClientTest, MoveFromClientToNonClient) {
EventRecordingWindowDelegate window_delegate;
WindowServiceTestHelper helper;
TestWindowTreeClient* window_tree_client = helper.window_tree_client();
helper.delegate()->set_delegate_for_next_top_level(&window_delegate);
aura::Window* top_level = helper.helper()->NewTopLevelWindow(1);
ASSERT_TRUE(top_level);
top_level->Show();
top_level->SetBounds(gfx::Rect(10, 10, 100, 100));
helper.helper()->SetClientArea(top_level, gfx::Insets(10, 0, 0, 0));
window_delegate.ClearEvents();
test::EventGenerator event_generator(helper.root());
{
event_generator.MoveMouseTo(50, 50);
// Move generates both an enter and move.
std::unique_ptr<Event> enter_event =
window_tree_client->PopInputEvent().event;
ASSERT_TRUE(enter_event);
EXPECT_EQ(ET_POINTER_ENTERED, enter_event->type());
EXPECT_EQ(gfx::Point(40, 40), enter_event->AsLocatedEvent()->location());
std::unique_ptr<Event> move_event =
window_tree_client->PopInputEvent().event;
ASSERT_TRUE(move_event);
EXPECT_EQ(ET_POINTER_MOVED, move_event->type());
EXPECT_EQ(gfx::Point(40, 40), move_event->AsLocatedEvent()->location());
// The delegate should see the same events.
ASSERT_EQ(2u, window_delegate.events().size());
enter_event = window_delegate.PopEvent();
EXPECT_EQ(ET_MOUSE_ENTERED, enter_event->type());
EXPECT_EQ(gfx::Point(40, 40), enter_event->AsLocatedEvent()->location());
move_event = window_delegate.PopEvent();
EXPECT_EQ(ET_MOUSE_MOVED, move_event->type());
EXPECT_EQ(gfx::Point(40, 40), move_event->AsLocatedEvent()->location());
}
// Move the mouse over the non-client area.
// The event is still sent to the client, and the delegate.
{
event_generator.MoveMouseTo(15, 16);
std::unique_ptr<Event> event = window_tree_client->PopInputEvent().event;
ASSERT_TRUE(event);
EXPECT_EQ(ET_POINTER_MOVED, event->type());
EXPECT_EQ(gfx::Point(5, 6), event->AsLocatedEvent()->location());
// Delegate should also get the events.
event = window_delegate.PopEvent();
EXPECT_EQ(ET_MOUSE_MOVED, event->type());
EXPECT_EQ(gfx::Point(5, 6), event->AsLocatedEvent()->location());
}
// Only the delegate should get the press in this case.
{
event_generator.PressLeftButton();
std::unique_ptr<Event> event = window_tree_client->PopInputEvent().event;
ASSERT_FALSE(event);
event = window_delegate.PopEvent();
EXPECT_EQ(ET_MOUSE_PRESSED, event->type());
EXPECT_EQ(gfx::Point(5, 6), event->AsLocatedEvent()->location());
}
// Move mouse into client area, only the delegate should get the move (drag).
{
event_generator.MoveMouseTo(35, 51);
std::unique_ptr<Event> event = window_tree_client->PopInputEvent().event;
ASSERT_FALSE(event);
event = window_delegate.PopEvent();
ASSERT_TRUE(event);
EXPECT_EQ(ET_MOUSE_DRAGGED, event->type());
EXPECT_EQ(gfx::Point(25, 41), event->AsLocatedEvent()->location());
}
// Release over client area, again only delegate should get it.
{
event_generator.ReleaseLeftButton();
std::unique_ptr<Event> event = window_tree_client->PopInputEvent().event;
ASSERT_FALSE(event);
event = window_delegate.PopEvent();
ASSERT_TRUE(event);
EXPECT_EQ(ET_MOUSE_RELEASED, event->type());
}
{
event_generator.MoveMouseTo(26, 50);
std::unique_ptr<Event> event = window_tree_client->PopInputEvent().event;
ASSERT_TRUE(event);
EXPECT_EQ(ET_POINTER_MOVED, event->type());
EXPECT_EQ(gfx::Point(16, 40), event->AsLocatedEvent()->location());
// Delegate should also get the events.
event = window_delegate.PopEvent();
EXPECT_EQ(ET_MOUSE_MOVED, event->type());
EXPECT_EQ(gfx::Point(16, 40), event->AsLocatedEvent()->location());
}
// Press in client area. Only the client should get the event.
{
event_generator.PressLeftButton();
std::unique_ptr<Event> event = window_tree_client->PopInputEvent().event;
ASSERT_TRUE(event);
EXPECT_EQ(ET_POINTER_DOWN, event->type());
EXPECT_EQ(gfx::Point(16, 40), event->AsLocatedEvent()->location());
event = window_delegate.PopEvent();
ASSERT_FALSE(event);
}
}
TEST(WindowServiceClientTest, PointerWatcher) {
WindowServiceTestHelper helper;
TestWindowTreeClient* window_tree_client = helper.window_tree_client();
......
......@@ -316,7 +316,7 @@ ui::EventDispatchDetails WindowEventDispatcher::DispatchMouseEnterOrExit(
ui::EventType type) {
Env::GetInstance()->env_controller()->UpdateStateForMouseEvent(window(),
event);
if (!mouse_moved_handler_ || !mouse_moved_handler_->delegate() ||
if (!mouse_moved_handler_ || !mouse_moved_handler_->HasTargetHandler() ||
!window()->Contains(mouse_moved_handler_))
return DispatchDetails();
......
......@@ -88,6 +88,8 @@ class EVENTS_EXPORT EventTarget {
// Sets |target_handler| as |target_handler_| and returns the old handler.
EventHandler* SetTargetHandler(EventHandler* target_handler);
bool HasTargetHandler() const { return target_handler_ != nullptr; }
protected:
EventHandler* target_handler() { return target_handler_; }
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment