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
......@@ -6,9 +6,12 @@
#include "components/viz/host/host_frame_sink_manager.h"
#include "services/ui/ws2/window_host_frame_sink_client.h"
#include "services/ui/ws2/window_service_client.h"
#include "ui/aura/env.h"
#include "ui/aura/window.h"
#include "ui/aura/window_targeter.h"
#include "ui/compositor/compositor.h"
#include "ui/events/event_handler.h"
DEFINE_UI_CLASS_PROPERTY_TYPE(ui::ws2::ClientWindow*);
......@@ -18,6 +21,186 @@ namespace {
DEFINE_OWNED_UI_CLASS_PROPERTY_KEY(ui::ws2::ClientWindow,
kClientWindowKey,
nullptr);
// Returns true if |location| is in the non-client area (or outside the bounds
// of the window). A return value of false means the location is in the client
// area.
bool IsLocationInNonClientArea(const aura::Window* window,
const gfx::Point& location) {
const ClientWindow* client_window = ClientWindow::GetMayBeNull(window);
if (!client_window || !client_window->IsTopLevel())
return false;
// Locations outside the bounds, assume it's in extended hit test area, which
// is non-client area.
if (!gfx::Rect(window->bounds().size()).Contains(location))
return true;
gfx::Rect client_area(window->bounds().size());
client_area.Inset(client_window->client_area());
if (client_area.Contains(location))
return false;
for (const auto& rect : client_window->additional_client_areas()) {
if (rect.Contains(location))
return false;
}
return true;
}
// WindowTargeter used for ClientWindows. This is used for two purposes:
// . If the location is in the non-client area, then child Windows are not
// considered. This is done to ensure the delegate of the window (which is
// local) sees the event.
// . To ensure |WindowServiceClient::intercepts_events_| is honored.
class ClientWindowTargeter : public aura::WindowTargeter {
public:
explicit ClientWindowTargeter(ClientWindow* client_window)
: client_window_(client_window) {}
~ClientWindowTargeter() override = default;
// aura::WindowTargeter:
ui::EventTarget* FindTargetForEvent(ui::EventTarget* event_target,
ui::Event* event) override {
aura::Window* window = static_cast<aura::Window*>(event_target);
DCHECK_EQ(window, client_window_->window());
if (client_window_->embedded_window_service_client() &&
client_window_->owning_window_service_client() &&
client_window_->owning_window_service_client()->intercepts_events()) {
return event_target->CanAcceptEvent(*event) ? window : nullptr;
}
return aura::WindowTargeter::FindTargetForEvent(event_target, event);
}
private:
ClientWindow* const client_window_;
DISALLOW_COPY_AND_ASSIGN(ClientWindowTargeter);
};
// ClientWindowEventHandler is used to forward events to the client.
// ClientWindowEventHandler adds itself to the pre-phase to ensure it's
// considered before the Window's delegate (or other EventHandlers).
class ClientWindowEventHandler : public ui::EventHandler {
public:
explicit ClientWindowEventHandler(ClientWindow* client_window)
: client_window_(client_window) {
client_window->window()->AddPreTargetHandler(
this, ui::EventTarget::Priority::kSystem);
}
~ClientWindowEventHandler() override {
client_window_->window()->RemovePreTargetHandler(this);
}
ClientWindow* client_window() { return client_window_; }
// ui::EventHandler:
void OnEvent(ui::Event* event) override {
// Because we StopPropagation() in the pre-phase an event should never be
// received for other phases.
DCHECK_EQ(event->phase(), EP_PRETARGET);
// Events typically target the embedded client. Exceptions include the
// embedder intercepts events, or the window is a top-level and the event is
// in the non-client area.
WindowServiceClient* client =
client_window_->owning_window_service_client() &&
client_window_->owning_window_service_client()
->intercepts_events()
? client_window_->owning_window_service_client()
: client_window_->embedded_window_service_client();
DCHECK(client);
client->SendEventToClient(client_window_->window(), *event);
// The event was forwarded to the remote client. We don't want it handled
// locally too.
event->StopPropagation();
}
private:
ClientWindow* const client_window_;
DISALLOW_COPY_AND_ASSIGN(ClientWindowEventHandler);
};
// ui::EventHandler used for top-levels. Some events that target the non-client
// area are not sent to the client, instead are handled locally. For example,
// if a press occurs in the non-client area, then the event is not sent to
// the client, it's handled locally.
class TopLevelEventHandler : public ClientWindowEventHandler {
public:
explicit TopLevelEventHandler(ClientWindow* client_window)
: ClientWindowEventHandler(client_window) {
// Top-levels should always have an owning_window_service_client().
// OnEvent() assumes this.
DCHECK(client_window->owning_window_service_client());
}
~TopLevelEventHandler() override = default;
// ClientWindowEventHandler:
void OnEvent(ui::Event* event) override {
if (event->phase() != EP_PRETARGET) {
// All work is done in the pre-phase. If this branch is hit, it means
// event propagation was not stopped, and normal processing should
// continue. Early out to avoid sending the event to the client again.
return;
}
if (!event->IsLocatedEvent()) {
ClientWindowEventHandler::OnEvent(event);
return;
}
// This code does has two specific behaviors. It's used to ensure events
// go to the right target (either local, or the remote client).
// . a press-release sequence targets only one. If in non-client area then
// local, otherwise remote client.
// . mouse-moves (not drags) go to both targets.
// TODO(sky): handle touch events too.
// TODO(sky): this also needs to handle capture changed and if the window
// (or any ancestors) change visibility.
bool stop_propagation = false;
if (client_window()->HasNonClientArea() && event->IsMouseEvent()) {
if (!mouse_down_state_) {
if (event->type() == ui::ET_MOUSE_PRESSED) {
mouse_down_state_ = std::make_unique<MouseDownState>();
mouse_down_state_->in_non_client_area = IsLocationInNonClientArea(
client_window()->window(), event->AsLocatedEvent()->location());
if (mouse_down_state_->in_non_client_area)
return; // Don't send presses to client.
stop_propagation = true;
}
} else {
// Else case, in a press-release.
const bool was_press_in_non_client_area =
mouse_down_state_->in_non_client_area;
if (event->type() == ui::ET_MOUSE_RELEASED &&
event->AsMouseEvent()->button_flags() ==
event->AsMouseEvent()->changed_button_flags()) {
mouse_down_state_.reset();
}
if (was_press_in_non_client_area)
return; // Don't send release to client since press didn't go there.
stop_propagation = true;
}
}
client_window()->owning_window_service_client()->SendEventToClient(
client_window()->window(), *event);
if (stop_propagation)
event->StopPropagation();
}
private:
struct MouseDownState {
bool in_non_client_area = false;
};
// Non-null while in a mouse press-drag-release cycle.
std::unique_ptr<MouseDownState> mouse_down_state_;
DISALLOW_COPY_AND_ASSIGN(TopLevelEventHandler);
};
} // namespace
ClientWindow::~ClientWindow() = default;
......@@ -25,17 +208,19 @@ ClientWindow::~ClientWindow() = default;
// static
ClientWindow* ClientWindow::Create(aura::Window* window,
WindowServiceClient* client,
const viz::FrameSinkId& frame_sink_id) {
const viz::FrameSinkId& frame_sink_id,
bool is_top_level) {
DCHECK(!GetMayBeNull(window));
// Owned by |window|.
ClientWindow* client_window = new ClientWindow(window, client, frame_sink_id);
ClientWindow* client_window =
new ClientWindow(window, client, frame_sink_id, is_top_level);
window->SetProperty(kClientWindowKey, client_window);
return client_window;
}
// static
const ClientWindow* ClientWindow::GetMayBeNull(const aura::Window* window) {
return window->GetProperty(kClientWindowKey);
return window ? window->GetProperty(kClientWindowKey) : nullptr;
}
void ClientWindow::SetFrameSinkId(const viz::FrameSinkId& frame_sink_id) {
......@@ -61,6 +246,32 @@ void ClientWindow::SetFrameSinkId(const viz::FrameSinkId& frame_sink_id) {
window_host_frame_sink_client_->OnFrameSinkIdChanged();
}
void ClientWindow::SetClientArea(
const gfx::Insets& insets,
const std::vector<gfx::Rect>& additional_client_areas) {
if (client_area_ == insets &&
additional_client_areas == additional_client_areas_) {
return;
}
additional_client_areas_ = additional_client_areas;
client_area_ = insets;
// TODO(sky): update cursor if over this window.
NOTIMPLEMENTED_LOG_ONCE();
}
bool ClientWindow::HasNonClientArea() const {
return owning_window_service_client_ &&
owning_window_service_client_->IsTopLevel(window_) &&
(!client_area_.IsEmpty() || !additional_client_areas_.empty());
}
bool ClientWindow::IsTopLevel() const {
return owning_window_service_client_ &&
owning_window_service_client_->IsTopLevel(window_);
}
void ClientWindow::AttachCompositorFrameSink(
viz::mojom::CompositorFrameSinkRequest compositor_frame_sink,
viz::mojom::CompositorFrameSinkClientPtr client) {
......@@ -74,10 +285,24 @@ void ClientWindow::AttachCompositorFrameSink(
ClientWindow::ClientWindow(aura::Window* window,
WindowServiceClient* client,
const viz::FrameSinkId& frame_sink_id)
const viz::FrameSinkId& frame_sink_id,
bool is_top_level)
: window_(window),
owning_window_service_client_(client),
frame_sink_id_(frame_sink_id) {}
frame_sink_id_(frame_sink_id) {
if (is_top_level)
event_handler_ = std::make_unique<TopLevelEventHandler>(this);
else
event_handler_ = std::make_unique<ClientWindowEventHandler>(this);
window_->SetEventTargeter(std::make_unique<ClientWindowTargeter>(this));
// In order for a window to receive events it must have a target_handler()
// (see Window::CanAcceptEvent()). Normally the delegate is the TargetHandler,
// but if the delegate is null, then so is the target_handler(). Set
// |event_handler_| as the target_handler() to force the Window to accept
// events.
if (!window_->delegate())
window_->SetTargetHandler(event_handler_.get());
}
} // namespace ws2
} // namespace ui
......@@ -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