blob: 9639db660227a9b0d600b4aa8aecf99e07c93ac6 [file] [log] [blame]
// Copyright 2014 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 "services/ui/ws/window_tree.h"
#include <stddef.h>
#include <utility>
#include "base/bind.h"
#include "base/callback_helpers.h"
#include "base/macros.h"
#include "base/memory/ptr_util.h"
#include "build/build_config.h"
#include "mojo/public/cpp/bindings/map.h"
#include "services/ui/common/util.h"
#include "services/ui/display/screen_manager.h"
#include "services/ui/ws/cursor_location_manager.h"
#include "services/ui/ws/debug_utils.h"
#include "services/ui/ws/default_access_policy.h"
#include "services/ui/ws/display.h"
#include "services/ui/ws/display_manager.h"
#include "services/ui/ws/event_dispatcher.h"
#include "services/ui/ws/event_location.h"
#include "services/ui/ws/event_matcher.h"
#include "services/ui/ws/focus_controller.h"
#include "services/ui/ws/frame_generator.h"
#include "services/ui/ws/modal_window_controller.h"
#include "services/ui/ws/operation.h"
#include "services/ui/ws/platform_display.h"
#include "services/ui/ws/server_window.h"
#include "services/ui/ws/server_window_observer.h"
#include "services/ui/ws/user_display_manager.h"
#include "services/ui/ws/window_manager_display_root.h"
#include "services/ui/ws/window_manager_state.h"
#include "services/ui/ws/window_server.h"
#include "services/ui/ws/window_tree_binding.h"
#include "ui/base/cursor/cursor.h"
#include "ui/display/display.h"
#include "ui/display/display_list.h"
#include "ui/display/screen_base.h"
#include "ui/display/types/display_constants.h"
#include "ui/platform_window/mojo/ime_type_converters.h"
#include "ui/platform_window/text_input_state.h"
using mojo::InterfaceRequest;
using EventProperties = std::unordered_map<std::string, std::vector<uint8_t>>;
namespace ui {
namespace ws {
namespace {
bool HasPositiveInset(const gfx::Insets& insets) {
return insets.width() > 0 || insets.height() > 0 || insets.left() > 0 ||
insets.right() > 0;
}
FrameGenerator* GetFrameGenerator(WindowManagerDisplayRoot* display_root) {
return display_root->display()->platform_display()
? display_root->display()->platform_display()->GetFrameGenerator()
: nullptr;
}
display::ViewportMetrics TransportMetricsToDisplayMetrics(
const ui::mojom::WmViewportMetrics& transport_metrics) {
display::ViewportMetrics viewport_metrics;
viewport_metrics.bounds_in_pixels = transport_metrics.bounds_in_pixels;
viewport_metrics.device_scale_factor = transport_metrics.device_scale_factor;
viewport_metrics.ui_scale_factor = transport_metrics.ui_scale_factor;
return viewport_metrics;
}
} // namespace
class TargetedEvent : public ServerWindowObserver {
public:
TargetedEvent(ServerWindow* target,
const ui::Event& event,
const EventLocation& event_location,
WindowTree::DispatchEventCallback callback)
: target_(target),
event_(ui::Event::Clone(event)),
event_location_(event_location),
callback_(std::move(callback)) {
target_->AddObserver(this);
}
~TargetedEvent() override {
if (target_)
target_->RemoveObserver(this);
}
ServerWindow* target() { return target_; }
std::unique_ptr<ui::Event> TakeEvent() { return std::move(event_); }
const EventLocation& event_location() const { return event_location_; }
WindowTree::DispatchEventCallback TakeCallback() {
return std::move(callback_);
}
private:
// ServerWindowObserver:
void OnWindowDestroyed(ServerWindow* window) override {
DCHECK_EQ(target_, window);
target_->RemoveObserver(this);
target_ = nullptr;
}
ServerWindow* target_;
std::unique_ptr<ui::Event> event_;
const EventLocation event_location_;
WindowTree::DispatchEventCallback callback_;
DISALLOW_COPY_AND_ASSIGN(TargetedEvent);
};
struct WindowTree::DragMoveState {
// Whether we've queued a move to |queued_cursor_location_| when we get an
// ack from WmMoveDragImage.
bool has_queued_drag_window_move = false;
// When |has_queued_drag_window_move_| is true, this is a location which
// should be sent to the window manager as soon as it acked the last one.
gfx::Point queued_cursor_location;
};
WindowTree::WindowTree(WindowServer* window_server,
bool is_for_embedding,
ServerWindow* root,
std::unique_ptr<AccessPolicy> access_policy)
: window_server_(window_server),
is_for_embedding_(is_for_embedding),
id_(window_server_->GetAndAdvanceNextClientId()),
access_policy_(std::move(access_policy)),
event_ack_id_(0),
window_manager_internal_(nullptr),
drag_weak_factory_(this) {
if (root)
roots_.insert(root);
access_policy_->Init(id_, this);
}
WindowTree::~WindowTree() {
DestroyWindows();
// We alert the WindowManagerState that we're destroying this state here
// because WindowManagerState would attempt to use things that wouldn't have
// been cleaned up by OnWindowDestroyingTreeImpl().
if (window_manager_state_) {
window_manager_state_->OnWillDestroyTree(this);
window_manager_state_.reset();
}
}
void WindowTree::Init(std::unique_ptr<WindowTreeBinding> binding,
mojom::WindowTreePtr tree) {
DCHECK(!binding_);
binding_ = std::move(binding);
if (roots_.empty())
return;
std::vector<const ServerWindow*> to_send;
CHECK_EQ(1u, roots_.size());
const ServerWindow* root = *roots_.begin();
GetUnknownWindowsFrom(root, &to_send);
Display* display = GetDisplay(root);
int64_t display_id = display ? display->GetId() : display::kInvalidDisplayId;
const ServerWindow* focused_window =
display ? display->GetFocusedWindow() : nullptr;
if (focused_window)
focused_window = access_policy_->GetWindowForFocusChange(focused_window);
ClientWindowId focused_window_id;
if (focused_window)
IsWindowKnown(focused_window, &focused_window_id);
const bool drawn = root->parent() && root->parent()->IsDrawn();
client()->OnEmbed(WindowToWindowData(to_send.front()), std::move(tree),
display_id, ClientWindowIdToTransportId(focused_window_id),
drawn, root->current_local_surface_id());
}
void WindowTree::OnAcceleratedWidgetAvailableForDisplay(Display* display) {
DCHECK(window_manager_internal_);
// TODO(sad): Use GpuSurfaceTracker on platforms where a gpu::SurfaceHandle is
// not the same as a gfx::AcceleratedWidget.
window_manager_internal_->WmOnAcceleratedWidgetForDisplay(
display->GetId(), display->platform_display()->GetAcceleratedWidget());
}
void WindowTree::ConfigureWindowManager(
bool automatically_create_display_roots) {
// ConfigureWindowManager() should be called early on, before anything
// else. |waiting_for_top_level_window_info_| must be null as if
// |waiting_for_top_level_window_info_| is non-null it means we're about to
// create an associated interface, which doesn't work with pause/resume.
// TODO(sky): DCHECK temporary until 626869 is sorted out.
DCHECK(!waiting_for_top_level_window_info_);
DCHECK(!window_manager_internal_);
automatically_create_display_roots_ = automatically_create_display_roots;
window_manager_internal_ = binding_->GetWindowManager();
window_manager_internal_->OnConnect();
window_manager_state_ = std::make_unique<WindowManagerState>(this);
}
bool WindowTree::IsWindowKnown(const ServerWindow* window,
ClientWindowId* id) const {
if (!window)
return false;
auto iter = window_to_client_id_map_.find(window);
if (iter == window_to_client_id_map_.end())
return false;
if (id)
*id = iter->second;
return true;
}
bool WindowTree::HasRoot(const ServerWindow* window) const {
return roots_.count(window) > 0;
}
const ServerWindow* WindowTree::GetWindowByClientId(
const ClientWindowId& id) const {
auto iter = client_id_to_window_map_.find(id);
return iter == client_id_to_window_map_.end() ? nullptr : iter->second;
}
const Display* WindowTree::GetDisplay(const ServerWindow* window) const {
return window ? display_manager()->GetDisplayContaining(window) : nullptr;
}
const WindowManagerDisplayRoot* WindowTree::GetWindowManagerDisplayRoot(
const ServerWindow* window) const {
return window ? display_manager()->GetWindowManagerDisplayRoot(window)
: nullptr;
}
DisplayManager* WindowTree::display_manager() {
return window_server_->display_manager();
}
const DisplayManager* WindowTree::display_manager() const {
return window_server_->display_manager();
}
void WindowTree::PrepareForWindowServerShutdown() {
window_manager_internal_client_binding_.reset();
binding_->ResetClientForShutdown();
if (window_manager_internal_)
window_manager_internal_ = binding_->GetWindowManager();
}
void WindowTree::AddRootForWindowManager(const ServerWindow* root) {
if (!automatically_create_display_roots_)
return;
DCHECK(automatically_create_display_roots_);
DCHECK(window_manager_internal_);
const ClientWindowId client_window_id = root->frame_sink_id();
AddToMaps(root, client_window_id);
roots_.insert(root);
Display* ws_display = GetDisplay(root);
DCHECK(ws_display);
window_manager_internal_->WmNewDisplayAdded(
ws_display->GetDisplay(), WindowToWindowData(root),
root->parent()->IsDrawn(), root->current_local_surface_id());
}
void WindowTree::OnWillDestroyTree(WindowTree* tree) {
DCHECK_NE(tree, this); // This function is not called for |this|.
if (event_source_wms_ && event_source_wms_->window_tree() == tree)
event_source_wms_ = nullptr;
// Notify our client if |tree| was embedded in any of our windows.
for (const auto* tree_root : tree->roots_) {
const bool owns_tree_root = tree_root->owning_tree_id() == id_;
if (owns_tree_root)
client()->OnEmbeddedAppDisconnected(TransportIdForWindow(tree_root));
}
if (window_manager_state_)
window_manager_state_->OnWillDestroyTree(tree);
}
void WindowTree::OnWmDisplayModified(const display::Display& display) {
window_manager_internal_->WmDisplayModified(display);
}
void WindowTree::NotifyChangeCompleted(
uint32_t change_id,
mojom::WindowManagerErrorCode error_code) {
client()->OnChangeCompleted(
change_id, error_code == mojom::WindowManagerErrorCode::SUCCESS);
}
void WindowTree::OnWmMoveDragImageAck() {
if (drag_move_state_->has_queued_drag_window_move) {
gfx::Point queued_location = drag_move_state_->queued_cursor_location;
drag_move_state_.reset();
OnDragMoved(queued_location);
} else {
drag_move_state_.reset();
}
}
ServerWindow* WindowTree::ProcessSetDisplayRoot(
const display::Display& display_to_create,
const display::ViewportMetrics& viewport_metrics,
bool is_primary_display,
const ClientWindowId& client_window_id,
const std::vector<display::Display>& mirrors) {
DCHECK(window_manager_state_); // Only called for window manager.
DVLOG(3) << "SetDisplayRoot client=" << id_
<< " global window_id=" << client_window_id.ToString();
if (automatically_create_display_roots_) {
DVLOG(1) << "SetDisplayRoot is only applicable when "
<< "automatically_create_display_roots is false";
return nullptr;
}
ServerWindow* window = GetWindowByClientId(client_window_id);
const bool is_moving_to_new_display =
window && window->parent() && base::ContainsKey(roots_, window);
if (!window || (window->parent() && !is_moving_to_new_display)) {
DVLOG(1) << "SetDisplayRoot called with invalid window id "
<< client_window_id.ToString();
return nullptr;
}
if (base::ContainsKey(roots_, window) && !is_moving_to_new_display) {
DVLOG(1) << "SetDisplayRoot called with existing root";
return nullptr;
}
Display* display = display_manager()->GetDisplayById(display_to_create.id());
const bool display_already_existed = display != nullptr;
if (!display) {
// Create a display if the window manager is extending onto a new display.
display = display_manager()->AddDisplayForWindowManager(
is_primary_display, display_to_create, viewport_metrics);
} else if (!display->window_manager_display_root()) {
// Init the root if the display already existed as a mirroring destination.
display->InitWindowManagerDisplayRoots();
}
if (!mirrors.empty())
NOTIMPLEMENTED() << "TODO(crbug.com/806318): Mus+Viz mirroring/unified";
DCHECK(display);
WindowManagerDisplayRoot* display_root =
display->window_manager_display_root();
DCHECK(display_root);
display_root->root()->RemoveAllChildren();
// NOTE: this doesn't resize the window in any way. We assume the client takes
// care of any modifications it needs to do.
roots_.insert(window);
Operation op(this, window_server_, OperationType::ADD_WINDOW);
ServerWindow* old_parent =
is_moving_to_new_display ? window->parent() : nullptr;
display_root->root()->Add(window);
if (is_moving_to_new_display) {
DCHECK(old_parent);
window_manager_state_->DeleteWindowManagerDisplayRoot(old_parent);
}
if (display_already_existed &&
display->platform_display()->GetAcceleratedWidget()) {
// Notify the window manager that the dispay's accelerated widget is already
// available, if the display is being reused for a new window tree host.
window_manager_internal_->WmOnAcceleratedWidgetForDisplay(
display->GetId(), display->platform_display()->GetAcceleratedWidget());
}
return window;
}
bool WindowTree::ProcessSwapDisplayRoots(int64_t display_id1,
int64_t display_id2) {
DCHECK(window_manager_state_); // Can only be called by the window manager.
DVLOG(3) << "SwapDisplayRoots display_id1=" << display_id2
<< " display_id2=" << display_id2;
if (automatically_create_display_roots_) {
DVLOG(1) << "SwapDisplayRoots only applicable when window-manager creates "
<< "display roots";
return false;
}
Display* display1 = display_manager()->GetDisplayById(display_id1);
Display* display2 = display_manager()->GetDisplayById(display_id2);
if (!display1 || !display2) {
DVLOG(1) << "SwapDisplayRoots called with unknown display ids";
return false;
}
WindowManagerDisplayRoot* display_root1 =
display1->window_manager_display_root();
WindowManagerDisplayRoot* display_root2 =
display2->window_manager_display_root();
if (!display_root1->GetClientVisibleRoot() ||
!display_root2->GetClientVisibleRoot()) {
DVLOG(1) << "SetDisplayRoot called with displays that have not been "
<< "configured";
return false;
}
display_root1->root()->Add(display_root2->GetClientVisibleRoot());
display_root2->root()->Add(display_root1->GetClientVisibleRoot());
// TODO(sky): this is race condition here if one is valid and one null.
FrameGenerator* frame_generator1 = GetFrameGenerator(display_root1);
FrameGenerator* frame_generator2 = GetFrameGenerator(display_root2);
if (frame_generator1 && frame_generator2)
frame_generator1->SwapSurfaceWith(frame_generator2);
return true;
}
bool WindowTree::ProcessSetBlockingContainers(
std::vector<mojom::BlockingContainersPtr>
transport_all_blocking_containers) {
DCHECK(window_manager_state_); // Can only be called by the window manager.
std::vector<BlockingContainers> all_containers;
for (auto& transport_container : transport_all_blocking_containers) {
BlockingContainers blocking_containers;
blocking_containers.system_modal_container = GetWindowByClientId(
MakeClientWindowId(transport_container->system_modal_container_id));
if (!blocking_containers.system_modal_container) {
DVLOG(1) << "SetBlockingContainers called with unknown modal container";
return false;
}
blocking_containers.min_container = GetWindowByClientId(
MakeClientWindowId(transport_container->min_container_id));
all_containers.push_back(blocking_containers);
}
window_manager_state_->event_dispatcher()
->modal_window_controller()
->SetBlockingContainers(all_containers);
return true;
}
bool WindowTree::SetCapture(const ClientWindowId& client_window_id) {
ServerWindow* window = GetWindowByClientId(client_window_id);
WindowManagerDisplayRoot* display_root = GetWindowManagerDisplayRoot(window);
ServerWindow* current_capture_window =
display_root ? display_root->window_manager_state()->capture_window()
: nullptr;
if (window && window->IsDrawn() && display_root &&
access_policy_->CanSetCapture(window) &&
(!current_capture_window ||
access_policy_->CanSetCapture(current_capture_window))) {
Operation op(this, window_server_, OperationType::SET_CAPTURE);
return display_root->window_manager_state()->SetCapture(window, id_);
}
return false;
}
bool WindowTree::ReleaseCapture(const ClientWindowId& client_window_id) {
ServerWindow* window = GetWindowByClientId(client_window_id);
WindowManagerDisplayRoot* display_root = GetWindowManagerDisplayRoot(window);
ServerWindow* current_capture_window =
display_root ? display_root->window_manager_state()->capture_window()
: nullptr;
if (!window || !display_root ||
(current_capture_window &&
!access_policy_->CanSetCapture(current_capture_window)) ||
window != current_capture_window) {
return false;
}
Operation op(this, window_server_, OperationType::RELEASE_CAPTURE);
return display_root->window_manager_state()->SetCapture(nullptr,
kInvalidClientId);
}
bool WindowTree::NewWindow(
const ClientWindowId& client_window_id,
const std::map<std::string, std::vector<uint8_t>>& properties) {
DVLOG(3) << "new window client=" << id_
<< " window_id=" << client_window_id.ToString();
if (!IsValidIdForNewWindow(client_window_id)) {
DVLOG(1) << "NewWindow failed (id is not valid for client)";
return false;
}
DCHECK(!GetWindowByClientId(client_window_id));
DCHECK_EQ(id_, client_window_id.client_id());
ServerWindow* window =
window_server_->CreateServerWindow(client_window_id, properties);
created_windows_.insert(window);
AddToMaps(window, client_window_id);
return true;
}
bool WindowTree::AddWindow(const ClientWindowId& parent_id,
const ClientWindowId& child_id) {
ServerWindow* parent = GetWindowByClientId(parent_id);
ServerWindow* child = GetWindowByClientId(child_id);
DVLOG(3) << "add window client=" << id_
<< " client parent window_id=" << parent_id.ToString()
<< " global window_id=" << DebugWindowId(parent)
<< " client child window_id= " << child_id.ToString()
<< " global window_id=" << DebugWindowId(child);
if (!parent) {
DVLOG(1) << "AddWindow failed (no parent)";
return false;
}
if (!child) {
DVLOG(1) << "AddWindow failed (no child)";
return false;
}
if (child->parent() == parent) {
DVLOG(1) << "AddWindow failed (already has parent)";
return false;
}
if (child->Contains(parent)) {
DVLOG(1) << "AddWindow failed (child contains parent)";
return false;
}
if (!access_policy_->CanAddWindow(parent, child)) {
DVLOG(1) << "AddWindow failed (access denied)";
return false;
}
Operation op(this, window_server_, OperationType::ADD_WINDOW);
parent->Add(child);
return true;
}
bool WindowTree::AddTransientWindow(const ClientWindowId& window_id,
const ClientWindowId& transient_window_id) {
ServerWindow* window = GetWindowByClientId(window_id);
ServerWindow* transient_window = GetWindowByClientId(transient_window_id);
if (window && transient_window && !transient_window->Contains(window) &&
access_policy_->CanAddTransientWindow(window, transient_window)) {
Operation op(this, window_server_, OperationType::ADD_TRANSIENT_WINDOW);
return window->AddTransientWindow(transient_window);
}
return false;
}
bool WindowTree::DeleteWindow(const ClientWindowId& window_id) {
ServerWindow* window = GetWindowByClientId(window_id);
DVLOG(3) << "removing window from parent client=" << id_
<< " client window_id= " << window_id.ToString()
<< " global window_id=" << DebugWindowId(window);
if (!window)
return false;
if (roots_.count(window) > 0) {
// Deleting a root behaves as an unembed.
window_server_->OnTreeMessagedClient(id_);
RemoveRoot(window, RemoveRootReason::UNEMBED);
return true;
}
if (!access_policy_->CanDeleteWindow(window) &&
!ShouldRouteToWindowManager(window)) {
return false;
}
// Have the owner of the tree service the actual delete.
WindowTree* tree = window_server_->GetTreeWithId(window->owning_tree_id());
return tree && tree->DeleteWindowImpl(this, window);
}
bool WindowTree::SetModalType(const ClientWindowId& window_id,
ModalType modal_type) {
ServerWindow* window = GetWindowByClientId(window_id);
if (!window) {
DVLOG(1) << "SetModalType failed (invalid id)";
return false;
}
if (is_for_embedding_ && modal_type == MODAL_TYPE_SYSTEM) {
DVLOG(1) << "SetModalType failed (not allowed for embedded clients)";
return false;
}
if (ShouldRouteToWindowManager(window)) {
WindowTree* wm_tree = GetWindowManagerDisplayRoot(window)
->window_manager_state()
->window_tree();
wm_tree->window_manager_internal_->WmSetModalType(
wm_tree->TransportIdForWindow(window), modal_type);
return true;
}
if (!access_policy_->CanSetModal(window)) {
DVLOG(1) << "SetModalType failed (access denied)";
return false;
}
if (window->modal_type() == modal_type)
return true;
window->SetModalType(modal_type);
return true;
}
bool WindowTree::SetChildModalParent(
const ClientWindowId& window_id,
const ClientWindowId& modal_parent_window_id) {
ServerWindow* window = GetWindowByClientId(window_id);
ServerWindow* modal_parent_window =
GetWindowByClientId(modal_parent_window_id);
// A value of null for |modal_parent_window| resets the modal parent.
if (!window) {
DVLOG(1) << "SetChildModalParent failed (invalid id)";
return false;
}
if (!access_policy_->CanSetChildModalParent(window, modal_parent_window)) {
DVLOG(1) << "SetChildModalParent failed (access denied)";
return false;
}
window->SetChildModalParent(modal_parent_window);
return true;
}
std::vector<const ServerWindow*> WindowTree::GetWindowTree(
const ClientWindowId& window_id) const {
const ServerWindow* window = GetWindowByClientId(window_id);
std::vector<const ServerWindow*> windows;
if (window)
GetWindowTreeImpl(window, &windows);
return windows;
}
bool WindowTree::SetWindowVisibility(const ClientWindowId& window_id,
bool visible) {
ServerWindow* window = GetWindowByClientId(window_id);
DVLOG(3) << "SetWindowVisibility client=" << id_
<< " client window_id= " << window_id.ToString()
<< " global window_id=" << DebugWindowId(window);
if (!window) {
DVLOG(1) << "SetWindowVisibility failed (no window)";
return false;
}
if (!access_policy_->CanChangeWindowVisibility(window) ||
(!can_change_root_window_visibility_ && HasRoot(window))) {
DVLOG(1) << "SetWindowVisibility failed (access policy denied change)";
return false;
}
if (window->visible() == visible)
return true;
Operation op(this, window_server_, OperationType::SET_WINDOW_VISIBILITY);
window->SetVisible(visible);
return true;
}
bool WindowTree::SetWindowOpacity(const ClientWindowId& window_id,
float opacity) {
ServerWindow* window = GetWindowByClientId(window_id);
if (!window || !access_policy_->CanChangeWindowOpacity(window))
return false;
if (window->opacity() == opacity)
return true;
Operation op(this, window_server_, OperationType::SET_WINDOW_OPACITY);
window->SetOpacity(opacity);
return true;
}
bool WindowTree::SetFocus(const ClientWindowId& window_id) {
ServerWindow* window = GetWindowByClientId(window_id);
ServerWindow* currently_focused = window_server_->GetFocusedWindow();
DVLOG(3) << "SetFocusedWindow client=" << id_
<< " client window_id=" << window_id.ToString()
<< " window=" << DebugWindowId(window);
if (currently_focused == window)
return true;
Display* display = GetDisplay(window);
if (window && (!display || !window->can_focus() || !window->IsDrawn())) {
DVLOG(1) << "SetFocus failed (window cannot be focused)";
return false;
}
if (!access_policy_->CanSetFocus(window)) {
DVLOG(1) << "SetFocus failed (blocked by access policy)";
return false;
}
Operation op(this, window_server_, OperationType::SET_FOCUS);
bool success = window_server_->SetFocusedWindow(window);
if (!success)
DVLOG(1) << "SetFocus failed (could not SetFocusedWindow)";
return success;
}
bool WindowTree::Embed(const ClientWindowId& window_id,
mojom::WindowTreeClientPtr window_tree_client,
uint32_t flags) {
if (!window_tree_client || !CanEmbed(window_id))
return false;
ServerWindow* window = GetWindowByClientId(window_id);
PrepareForEmbed(window);
// mojom::kEmbedFlagEmbedderInterceptsEvents is inherited, otherwise an
// embedder could effectively circumvent it by embedding itself.
if (embedder_intercepts_events_)
flags = mojom::kEmbedFlagEmbedderInterceptsEvents;
window_server_->EmbedAtWindow(window, std::move(window_tree_client), flags,
base::WrapUnique(new DefaultAccessPolicy));
client()->OnFrameSinkIdAllocated(ClientWindowIdToTransportId(window_id),
window->frame_sink_id());
return true;
}
void WindowTree::DispatchInputEvent(ServerWindow* target,
const ui::Event& event,
const EventLocation& event_location,
DispatchEventCallback callback) {
if (event_ack_id_) {
// This is currently waiting for an event ack. Add it to the queue.
event_queue_.push(std::make_unique<TargetedEvent>(
target, event, event_location, std::move(callback)));
// TODO(sad): If the |event_queue_| grows too large, then this should notify
// Display, so that it can stop sending events.
return;
}
// If there are events in the queue, then store this new event in the queue,
// and dispatch the latest event from the queue instead that still has a live
// target.
if (!event_queue_.empty()) {
event_queue_.push(std::make_unique<TargetedEvent>(
target, event, event_location, std::move(callback)));
return;
}
DispatchInputEventImpl(target, event, event_location, std::move(callback));
}
bool WindowTree::IsWaitingForNewTopLevelWindow(uint32_t wm_change_id) {
return waiting_for_top_level_window_info_ &&
waiting_for_top_level_window_info_->wm_change_id == wm_change_id;
}
viz::FrameSinkId WindowTree::OnWindowManagerCreatedTopLevelWindow(
uint32_t wm_change_id,
uint32_t client_change_id,
const ServerWindow* window) {
DCHECK(IsWaitingForNewTopLevelWindow(wm_change_id));
std::unique_ptr<WaitingForTopLevelWindowInfo>
waiting_for_top_level_window_info(
std::move(waiting_for_top_level_window_info_));
binding_->SetIncomingMethodCallProcessingPaused(false);
// We were paused, so the id should still be valid.
DCHECK(IsValidIdForNewWindow(
waiting_for_top_level_window_info->client_window_id));
if (!window) {
client()->OnChangeCompleted(client_change_id, false);
return viz::FrameSinkId();
}
AddToMaps(window, waiting_for_top_level_window_info->client_window_id);
roots_.insert(window);
Display* display = GetDisplay(window);
int64_t display_id = display ? display->GetId() : display::kInvalidDisplayId;
const bool drawn = window->parent() && window->parent()->IsDrawn();
client()->OnTopLevelCreated(client_change_id, WindowToWindowData(window),
display_id, drawn,
window->current_local_surface_id());
return waiting_for_top_level_window_info->client_window_id;
}
void WindowTree::AddActivationParent(const ClientWindowId& window_id) {
ServerWindow* window = GetWindowByClientId(window_id);
if (window)
window->set_is_activation_parent(true);
else
DVLOG(1) << "AddActivationParent failed (invalid window id)";
}
void WindowTree::OnChangeCompleted(uint32_t change_id, bool success) {
client()->OnChangeCompleted(change_id, success);
}
void WindowTree::OnAccelerator(uint32_t accelerator_id,
const ui::Event& event,
AcceleratorCallback callback) {
DVLOG(3) << "OnAccelerator client=" << id_;
DCHECK(window_manager_internal_); // Only valid for the window manager.
if (callback) {
GenerateEventAckId();
accelerator_ack_callback_ = std::move(callback);
} else {
DCHECK_EQ(0u, event_ack_id_);
DCHECK(!accelerator_ack_callback_);
}
// TODO(moshayedi): crbug.com/617167. Don't clone even once we map
// mojom::Event directly to ui::Event.
window_manager_internal_->OnAccelerator(event_ack_id_, accelerator_id,
ui::Event::Clone(event));
}
void WindowTree::OnEventOccurredOutsideOfModalWindow(
const ServerWindow* modal_window) {
DCHECK(window_manager_internal_);
// Only tell the window manager about windows it created.
if (modal_window->owning_tree_id() != id_)
return;
ClientWindowId client_window_id;
const bool is_known = IsWindowKnown(modal_window, &client_window_id);
// The window manager knows all windows.
DCHECK(is_known);
window_manager_internal_->OnEventBlockedByModalWindow(
ClientWindowIdToTransportId(client_window_id));
}
void WindowTree::OnCursorTouchVisibleChanged(bool enabled) {
DCHECK(window_manager_internal_);
window_manager_internal_->OnCursorTouchVisibleChanged(enabled);
}
void WindowTree::OnDisplayDestroying(int64_t display_id) {
DCHECK(window_manager_internal_);
if (automatically_create_display_roots_)
window_manager_internal_->WmDisplayRemoved(display_id);
// For the else case the client should detect removal directly.
}
void WindowTree::ClientJankinessChanged(WindowTree* tree) {
tree->janky_ = !tree->janky_;
// Don't inform the client if it is the source of jank (which generally only
// happens while debugging).
if (window_manager_internal_ && tree != this) {
window_manager_internal_->WmClientJankinessChanged(
tree->id(), tree->janky());
}
}
void WindowTree::ProcessWindowBoundsChanged(
const ServerWindow* window,
const gfx::Rect& old_bounds,
const gfx::Rect& new_bounds,
bool originated_change,
const base::Optional<viz::LocalSurfaceId>& local_surface_id) {
ClientWindowId client_window_id;
if (originated_change || !IsWindowKnown(window, &client_window_id))
return;
client()->OnWindowBoundsChanged(ClientWindowIdToTransportId(client_window_id),
old_bounds, new_bounds, local_surface_id);
}
void WindowTree::ProcessWindowTransformChanged(
const ServerWindow* window,
const gfx::Transform& old_transform,
const gfx::Transform& new_transform,
bool originated_change) {
ClientWindowId client_window_id;
if (originated_change || !IsWindowKnown(window, &client_window_id))
return;
client()->OnWindowTransformChanged(
ClientWindowIdToTransportId(client_window_id), old_transform,
new_transform);
}
void WindowTree::ProcessClientAreaChanged(
const ServerWindow* window,
const gfx::Insets& new_client_area,
const std::vector<gfx::Rect>& new_additional_client_areas,
bool originated_change) {
ClientWindowId client_window_id;
if (originated_change || !IsWindowKnown(window, &client_window_id))
return;
client()->OnClientAreaChanged(
ClientWindowIdToTransportId(client_window_id), new_client_area,
std::vector<gfx::Rect>(new_additional_client_areas));
}
void WindowTree::ProcessWillChangeWindowHierarchy(
const ServerWindow* window,
const ServerWindow* new_parent,
const ServerWindow* old_parent,
bool originated_change) {
if (originated_change)
return;
const bool old_drawn = window->IsDrawn();
const bool new_drawn =
window->visible() && new_parent && new_parent->IsDrawn();
if (old_drawn == new_drawn)
return;
NotifyDrawnStateChanged(window, new_drawn);
}
void WindowTree::ProcessWindowPropertyChanged(
const ServerWindow* window,
const std::string& name,
const std::vector<uint8_t>* new_data,
bool originated_change) {
if (originated_change)
return;
ClientWindowId client_window_id;
if (!IsWindowKnown(window, &client_window_id))
return;
base::Optional<std::vector<uint8_t>> data;
if (new_data)
data.emplace(*new_data);
client()->OnWindowSharedPropertyChanged(
ClientWindowIdToTransportId(client_window_id), name, data);
}
void WindowTree::ProcessWindowHierarchyChanged(const ServerWindow* window,
const ServerWindow* new_parent,
const ServerWindow* old_parent,
bool originated_change) {
const bool knows_new = new_parent && IsWindowKnown(new_parent);
if (originated_change || (window_server_->current_operation_type() ==
OperationType::DELETE_WINDOW) ||
(window_server_->current_operation_type() == OperationType::EMBED) ||
window_server_->DidTreeMessageClient(id_)) {
return;
}
if (!access_policy_->ShouldNotifyOnHierarchyChange(window, &new_parent,
&old_parent)) {
return;
}
// Inform the client of any new windows and update the set of windows we know
// about.
std::vector<const ServerWindow*> to_send;
if (!IsWindowKnown(window))
GetUnknownWindowsFrom(window, &to_send);
const bool knows_old = old_parent && IsWindowKnown(old_parent);
if (!knows_old && !knows_new)
return;
const Id new_parent_client_window_id =
knows_new ? TransportIdForWindow(new_parent) : kInvalidTransportId;
const Id old_parent_client_window_id =
knows_old ? TransportIdForWindow(old_parent) : kInvalidTransportId;
const Id client_window_id =
window ? TransportIdForWindow(window) : kInvalidTransportId;
client()->OnWindowHierarchyChanged(
client_window_id, old_parent_client_window_id,
new_parent_client_window_id, WindowsToWindowDatas(to_send));
window_server_->OnTreeMessagedClient(id_);
}
void WindowTree::ProcessWindowReorder(const ServerWindow* window,
const ServerWindow* relative_window,
mojom::OrderDirection direction,
bool originated_change) {
DCHECK_EQ(window->parent(), relative_window->parent());
ClientWindowId client_window_id, relative_client_window_id;
if (originated_change || !IsWindowKnown(window, &client_window_id) ||
!IsWindowKnown(relative_window, &relative_client_window_id) ||
window_server_->DidTreeMessageClient(id_))
return;
// Do not notify ordering changes of the root windows, since the client
// doesn't know about the ancestors of the roots, and so can't do anything
// about this ordering change of the root.
if (HasRoot(window) || HasRoot(relative_window))
return;
client()->OnWindowReordered(
ClientWindowIdToTransportId(client_window_id),
ClientWindowIdToTransportId(relative_client_window_id), direction);
window_server_->OnTreeMessagedClient(id_);
}
void WindowTree::ProcessWindowDeleted(ServerWindow* window,
bool originated_change) {
created_windows_.erase(window);
ClientWindowId client_window_id;
if (!IsWindowKnown(window, &client_window_id))
return;
if (HasRoot(window))
RemoveRoot(window, RemoveRootReason::DELETED);
else
RemoveFromMaps(window);
if (originated_change)
return;
client()->OnWindowDeleted(ClientWindowIdToTransportId(client_window_id));
window_server_->OnTreeMessagedClient(id_);
}
void WindowTree::ProcessWillChangeWindowVisibility(const ServerWindow* window,
bool originated_change) {
if (originated_change)
return;
ClientWindowId client_window_id;
if (IsWindowKnown(window, &client_window_id)) {
client()->OnWindowVisibilityChanged(
ClientWindowIdToTransportId(client_window_id), !window->visible());
return;
}
bool window_target_drawn_state;
if (window->visible()) {
// Window is being hidden, won't be drawn.
window_target_drawn_state = false;
} else {
// Window is being shown. Window will be drawn if its parent is drawn.
window_target_drawn_state = window->parent() && window->parent()->IsDrawn();
}
NotifyDrawnStateChanged(window, window_target_drawn_state);
}
void WindowTree::ProcessWindowOpacityChanged(const ServerWindow* window,
float old_opacity,
float new_opacity,
bool originated_change) {
if (originated_change)
return;
ClientWindowId client_window_id;
if (IsWindowKnown(window, &client_window_id)) {
client()->OnWindowOpacityChanged(
ClientWindowIdToTransportId(client_window_id), old_opacity,
new_opacity);
}
}
void WindowTree::ProcessCursorChanged(const ServerWindow* window,
const ui::CursorData& cursor,
bool originated_change) {
if (originated_change)
return;
ClientWindowId client_window_id;
if (!IsWindowKnown(window, &client_window_id))
return;
client()->OnWindowCursorChanged(ClientWindowIdToTransportId(client_window_id),
cursor);
}
void WindowTree::ProcessFocusChanged(const ServerWindow* old_focused_window,
const ServerWindow* new_focused_window) {
if (window_server_->current_operation_type() == OperationType::SET_FOCUS &&
window_server_->IsOperationSource(id_)) {
return;
}
const ServerWindow* window =
new_focused_window
? access_policy_->GetWindowForFocusChange(new_focused_window)
: nullptr;
ClientWindowId client_window_id;
// If the window isn't known we'll supply null, which is ok.
IsWindowKnown(window, &client_window_id);
client()->OnWindowFocused(ClientWindowIdToTransportId(client_window_id));
}
void WindowTree::ProcessTransientWindowAdded(
const ServerWindow* window,
const ServerWindow* transient_window,
bool originated_change) {
if (originated_change)
return;
ClientWindowId client_window_id, transient_client_window_id;
if (!IsWindowKnown(window, &client_window_id) ||
!IsWindowKnown(transient_window, &transient_client_window_id)) {
return;
}
client()->OnTransientWindowAdded(
ClientWindowIdToTransportId(client_window_id),
ClientWindowIdToTransportId(transient_client_window_id));
}
void WindowTree::ProcessTransientWindowRemoved(
const ServerWindow* window,
const ServerWindow* transient_window,
bool originated_change) {
if (originated_change)
return;
ClientWindowId client_window_id, transient_client_window_id;
if (!IsWindowKnown(window, &client_window_id) ||
!IsWindowKnown(transient_window, &transient_client_window_id)) {
return;
}
client()->OnTransientWindowRemoved(
ClientWindowIdToTransportId(client_window_id),
ClientWindowIdToTransportId(transient_client_window_id));
}
void WindowTree::ProcessWindowSurfaceChanged(
ServerWindow* window,
const viz::SurfaceInfo& surface_info) {
ClientWindowId client_window_id;
if (!IsWindowKnown(window, &client_window_id))
return;
client()->OnWindowSurfaceChanged(
ClientWindowIdToTransportId(client_window_id), surface_info);
}
void WindowTree::SendToPointerWatcher(const ui::Event& event,
ServerWindow* target_window,
int64_t display_id) {
if (!EventMatchesPointerWatcher(event))
return;
ClientWindowId client_window_id;
// Ignore the return value from IsWindowKnown() as in the case of the client
// not knowing the window we'll send 0, which corresponds to no window.
IsWindowKnown(target_window, &client_window_id);
client()->OnPointerEventObserved(
ui::Event::Clone(event), ClientWindowIdToTransportId(client_window_id),
display_id);
}
Id WindowTree::ClientWindowIdToTransportId(
const ClientWindowId& client_window_id) const {
if (client_window_id.client_id() == id_)
return client_window_id.sink_id();
const Id client_id = client_window_id.client_id();
return (client_id << 32) | client_window_id.sink_id();
}
bool WindowTree::ShouldRouteToWindowManager(const ServerWindow* window) const {
if (window_manager_state_)
return false; // We are the window manager, don't route to ourself.
// If the client created this window, then do not route it through the WM.
if (window->owning_tree_id() == id_)
return false;
// If the client did not create the window, then it must be the root of the
// client. If not, that means the client should not know about this window,
// and so do not route the request to the WM.
if (roots_.count(window) == 0)
return false;
return IsWindowCreatedByWindowManager(window);
}
void WindowTree::ProcessCaptureChanged(const ServerWindow* new_capture,
const ServerWindow* old_capture,
bool originated_change) {
ClientWindowId new_capture_window_client_id;
ClientWindowId old_capture_window_client_id;
const bool new_capture_window_known =
IsWindowKnown(new_capture, &new_capture_window_client_id);
const bool old_capture_window_known =
IsWindowKnown(old_capture, &old_capture_window_client_id);
if (!new_capture_window_known && !old_capture_window_known)
return;
if (originated_change && ((window_server_->current_operation_type() ==
OperationType::RELEASE_CAPTURE) ||
(window_server_->current_operation_type() ==
OperationType::SET_CAPTURE))) {
return;
}
client()->OnCaptureChanged(
ClientWindowIdToTransportId(new_capture_window_client_id),
ClientWindowIdToTransportId(old_capture_window_client_id));
}
Id WindowTree::TransportIdForWindow(const ServerWindow* window) const {
auto iter = window_to_client_id_map_.find(window);
DCHECK(iter != window_to_client_id_map_.end());
return ClientWindowIdToTransportId(iter->second);
}
bool WindowTree::IsValidIdForNewWindow(const ClientWindowId& id) const {
// Reserve 0 to indicate a null window.
return client_id_to_window_map_.count(id) == 0u &&
access_policy_->IsValidIdForNewWindow(id) && id != ClientWindowId();
}
bool WindowTree::CanReorderWindow(const ServerWindow* window,
const ServerWindow* relative_window,
mojom::OrderDirection direction) const {
if (!window) {
DVLOG(1) << "CanReorderWindow failed (invalid window)";
return false;
}
if (!relative_window) {
DVLOG(1) << "CanReorderWindow failed (invalid relative window)";
return false;
}
if (!window->parent()) {
DVLOG(1) << "CanReorderWindow failed (no parent)";
return false;
}
if (window->parent() != relative_window->parent()) {
DVLOG(1) << "CanReorderWindow failed (parents differ)";
return false;
}
if (!access_policy_->CanReorderWindow(window, relative_window, direction)) {
DVLOG(1) << "CanReorderWindow failed (access policy denied)";
return false;
}
const ServerWindow::Windows& children = window->parent()->children();
const size_t child_i =
std::find(children.begin(), children.end(), window) - children.begin();
const size_t target_i =
std::find(children.begin(), children.end(), relative_window) -
children.begin();
if ((direction == mojom::OrderDirection::ABOVE && child_i == target_i + 1) ||
(direction == mojom::OrderDirection::BELOW && child_i + 1 == target_i)) {
DVLOG(1) << "CanReorderWindow failed (already in position)";
return false;
}
return true;
}
bool WindowTree::RemoveWindowFromParent(
const ClientWindowId& client_window_id) {
ServerWindow* window = GetWindowByClientId(client_window_id);
DVLOG(3) << "removing window from parent client=" << id_
<< " client window_id= " << client_window_id
<< " global window_id=" << DebugWindowId(window);
if (!window) {
DVLOG(1) << "RemoveWindowFromParent failed (invalid window id="
<< client_window_id.ToString() << ")";
return false;
}
if (!window->parent()) {
DVLOG(1) << "RemoveWindowFromParent failed (no parent id="
<< client_window_id.ToString() << ")";
return false;
}
if (!access_policy_->CanRemoveWindowFromParent(window)) {
DVLOG(1) << "RemoveWindowFromParent failed (access policy disallowed id="
<< client_window_id.ToString() << ")";
return false;
}
Operation op(this, window_server_, OperationType::REMOVE_WINDOW_FROM_PARENT);
window->parent()->Remove(window);
return true;
}
bool WindowTree::DeleteWindowImpl(WindowTree* source, ServerWindow* window) {
DCHECK(window);
DCHECK_EQ(window->owning_tree_id(), id_);
Operation op(source, window_server_, OperationType::DELETE_WINDOW);
delete window;
return true;
}
void WindowTree::GetUnknownWindowsFrom(
const ServerWindow* window,
std::vector<const ServerWindow*>* windows) {
if (!access_policy_->CanGetWindowTree(window))
return;
// This function is called in the context of a hierarchy change when the
// parent wasn't known. We need to tell the client about the window so that
// it can set the parent correctly.
windows->push_back(window);
if (IsWindowKnown(window))
return;
const ClientWindowId client_window_id = window->frame_sink_id();
AddToMaps(window, client_window_id);
if (!access_policy_->CanDescendIntoWindowForWindowTree(window))
return;
const ServerWindow::Windows& children = window->children();
for (ServerWindow* child : children)
GetUnknownWindowsFrom(child, windows);
}
void WindowTree::AddToMaps(const ServerWindow* window,
const ClientWindowId& client_window_id) {
DCHECK_EQ(0u, client_id_to_window_map_.count(client_window_id));
client_id_to_window_map_[client_window_id] = window;
window_to_client_id_map_[window] = client_window_id;
}
bool WindowTree::RemoveFromMaps(const ServerWindow* window) {
auto iter = window_to_client_id_map_.find(window);
if (iter == window_to_client_id_map_.end())
return false;
client_id_to_window_map_.erase(iter->second);
window_to_client_id_map_.erase(iter);
return true;
}
void WindowTree::RemoveFromKnown(const ServerWindow* window,
std::vector<ServerWindow*>* created_windows) {
// TODO(sky): const_cast here is a bit ick.
if (created_windows_.count(const_cast<ServerWindow*>(window))) {
if (created_windows)
created_windows->push_back(const_cast<ServerWindow*>(window));
return;
}
RemoveFromMaps(window);
for (ServerWindow* child : window->children())
RemoveFromKnown(child, created_windows);
}
void WindowTree::RemoveRoot(ServerWindow* window, RemoveRootReason reason) {
DCHECK(roots_.count(window) > 0);
roots_.erase(window);
if (window->owning_tree_id() == id_) {
// This client created the window. If this client is the window manager and
// display roots are manually created, then |window| is a display root and
// needs be cleaned.
if (window_manager_state_ && !automatically_create_display_roots_) {
// The window manager is asking to delete the root it created.
window_manager_state_->DeleteWindowManagerDisplayRoot(window->parent());
DeleteWindowImpl(this, window);
}
return;
}
const Id client_window_id = TransportIdForWindow(window);
if (reason == RemoveRootReason::EMBED) {
client()->OnUnembed(client_window_id);
client()->OnWindowDeleted(client_window_id);
window_server_->OnTreeMessagedClient(id_);
}
// This client no longer knows about |window|. Unparent any windows created
// by this client that were parented to descendants of |window|.
std::vector<ServerWindow*> created_windows;
RemoveFromKnown(window, &created_windows);
for (ServerWindow* created_window : created_windows)
created_window->parent()->Remove(created_window);
if (reason == RemoveRootReason::UNEMBED) {
// Notify the owner of the window it no longer has a client embedded in it.
// Owner is null in the case of the windowmanager unembedding itself from
// a root.
WindowTree* owning_tree =
window_server_->GetTreeWithId(window->owning_tree_id());
if (owning_tree) {
DCHECK(owning_tree && owning_tree != this);
owning_tree->client()->OnEmbeddedAppDisconnected(
owning_tree->TransportIdForWindow(window));
}
window->OnEmbeddedAppDisconnected();
}
}
std::vector<mojom::WindowDataPtr> WindowTree::WindowsToWindowDatas(
const std::vector<const ServerWindow*>& windows) {
std::vector<mojom::WindowDataPtr> array(windows.size());
for (size_t i = 0; i < windows.size(); ++i)
array[i] = WindowToWindowData(windows[i]);
return array;
}
mojom::WindowDataPtr WindowTree::WindowToWindowData(
const ServerWindow* window) {
DCHECK(IsWindowKnown(window));
const ServerWindow* parent = window->parent();
const ServerWindow* transient_parent = window->transient_parent();
// If the parent or transient parent isn't known, it means it is not visible
// to the client and should not be sent over.
if (!IsWindowKnown(parent))
parent = nullptr;
if (!IsWindowKnown(transient_parent))
transient_parent = nullptr;
mojom::WindowDataPtr window_data(mojom::WindowData::New());
window_data->parent_id =
parent ? TransportIdForWindow(parent) : kInvalidTransportId;
window_data->window_id =
window ? TransportIdForWindow(window) : kInvalidTransportId;
window_data->transient_parent_id =
transient_parent ? TransportIdForWindow(transient_parent)
: kInvalidTransportId;
window_data->bounds = window->bounds();
window_data->properties = mojo::MapToUnorderedMap(window->properties());
window_data->visible = window->visible();
return window_data;
}
void WindowTree::GetWindowTreeImpl(
const ServerWindow* window,
std::vector<const ServerWindow*>* windows) const {
DCHECK(window);
if (!access_policy_->CanGetWindowTree(window))
return;
windows->push_back(window);
if (!access_policy_->CanDescendIntoWindowForWindowTree(window))
return;
const ServerWindow::Windows& children = window->children();
for (ServerWindow* child : children)
GetWindowTreeImpl(child, windows);
}
void WindowTree::NotifyDrawnStateChanged(const ServerWindow* window,
bool new_drawn_value) {
// Even though we don't know about window, it may be an ancestor of our root,
// in which case the change may effect our roots drawn state.
if (roots_.empty())
return;
for (auto* root : roots_) {
if (window->Contains(root) && (new_drawn_value != root->IsDrawn())) {
client()->OnWindowParentDrawnStateChanged(TransportIdForWindow(root),
new_drawn_value);
}
}
}
void WindowTree::DestroyWindows() {
if (created_windows_.empty())
return;
Operation op(this, window_server_, OperationType::DELETE_WINDOW);
// If we get here from the destructor we're not going to get
// ProcessWindowDeleted(). Copy the map and delete from the copy so that we
// don't have to worry about whether |created_windows_| changes or not.
std::set<ServerWindow*> created_windows_copy;
created_windows_.swap(created_windows_copy);
// A sibling can be a transient parent of another window so we detach windows
// from their transient parents to avoid double deletes.
for (ServerWindow* window : created_windows_copy) {
ServerWindow* transient_parent = window->transient_parent();
if (transient_parent)
transient_parent->RemoveTransientWindow(window);
}
for (ServerWindow* window : created_windows_copy)
delete window;
}
bool WindowTree::CanEmbed(const ClientWindowId& window_id) const {
const ServerWindow* window = GetWindowByClientId(window_id);
return window && access_policy_->CanEmbed(window);
}
void WindowTree::PrepareForEmbed(ServerWindow* window) {
DCHECK(window);
// Only allow a node to be the root for one client.
WindowTree* existing_owner = window_server_->GetTreeWithRoot(window);
Operation op(this, window_server_, OperationType::EMBED);
RemoveChildrenAsPartOfEmbed(window);
if (existing_owner) {
// Never message the originating client.
window_server_->OnTreeMessagedClient(id_);
existing_owner->RemoveRoot(window, RemoveRootReason::EMBED);
}
}
void WindowTree::RemoveChildrenAsPartOfEmbed(ServerWindow* window) {
CHECK(window);
while (!window->children().empty())
window->Remove(window->children().front());
}
uint32_t WindowTree::GenerateEventAckId() {
DCHECK(!event_ack_id_);
// We do not want to create a sequential id for each event, because that can
// leak some information to the client. So instead, manufacture the id
// randomly.
event_ack_id_ = 0x1000000 | (rand() & 0xffffff);
return event_ack_id_;
}
void WindowTree::DispatchInputEventImpl(ServerWindow* target,
const ui::Event& event,
const EventLocation& event_location,
DispatchEventCallback callback) {
// DispatchInputEventImpl() is called so often that log level 4 is used.
DVLOG(4) << "DispatchInputEventImpl client=" << id_;
GenerateEventAckId();
event_ack_callback_ = std::move(callback);
WindowManagerDisplayRoot* display_root = GetWindowManagerDisplayRoot(target);
DCHECK(display_root);
event_source_wms_ = display_root->window_manager_state();
// Should only get events from windows attached to a host.
DCHECK(event_source_wms_);
bool matched_pointer_watcher = EventMatchesPointerWatcher(event);
// Pass the root window of the display supplying the event. This is necessary
// for Ash to determine the event position in the unified desktop mode, where
// each physical display mirrors part of a single virtual display.
Display* display = window_server_->display_manager()->GetDisplayById(
event_location.display_id);
WindowManagerDisplayRoot* event_display_root = nullptr;
if (display && window_manager_state_) {
event_display_root = display->window_manager_display_root();
}
ServerWindow* display_root_window =
event_display_root ? event_display_root->GetClientVisibleRoot() : nullptr;
client()->OnWindowInputEvent(
event_ack_id_, TransportIdForWindow(target), event_location.display_id,
display_root_window ? TransportIdForWindow(display_root_window) : Id(),
event_location.raw_location, ui::Event::Clone(event),
matched_pointer_watcher);
}
bool WindowTree::EventMatchesPointerWatcher(const ui::Event& event) const {
if (!has_pointer_watcher_)
return false;
if (!event.IsPointerEvent())
return false;
if (pointer_watcher_want_moves_ && event.type() == ui::ET_POINTER_MOVED)
return true;
return event.type() == ui::ET_POINTER_DOWN ||
event.type() == ui::ET_POINTER_UP ||
event.type() == ui::ET_POINTER_WHEEL_CHANGED;
}
ClientWindowId WindowTree::MakeClientWindowId(Id transport_window_id) const {
// If the client didn't specify the id portion of the window_id use the id of
// the client.
if (!ClientIdFromTransportId(transport_window_id))
return ClientWindowId(id_, transport_window_id);
return ClientWindowId(ClientIdFromTransportId(transport_window_id),
ClientWindowIdFromTransportId(transport_window_id));
}
mojom::WindowTreeClientPtr
WindowTree::GetAndRemoveScheduledEmbedWindowTreeClient(
const base::UnguessableToken& token) {
auto iter = scheduled_embeds_.find(token);
if (iter != scheduled_embeds_.end()) {
mojom::WindowTreeClientPtr client = std::move(iter->second);
scheduled_embeds_.erase(iter);
return client;
}
// There are no clients above the window manager.
if (window_manager_internal_)
return nullptr;
// Use the root to find the client that embedded this. For non-window manager
// connections there should be only one root (a WindowTreeClient can only be
// embedded once). During shutdown there may be no roots.
if (roots_.size() != 1)
return nullptr;
const ServerWindow* root = *roots_.begin();
WindowTree* owning_tree =
window_server_->GetTreeWithId(root->owning_tree_id());
if (!owning_tree)
return nullptr;
DCHECK_NE(this, owning_tree);
return owning_tree->GetAndRemoveScheduledEmbedWindowTreeClient(token);
}
void WindowTree::NewWindow(
uint32_t change_id,
Id transport_window_id,
const base::Optional<std::unordered_map<std::string, std::vector<uint8_t>>>&
transport_properties) {
std::map<std::string, std::vector<uint8_t>> properties;
if (transport_properties.has_value())
properties = mojo::UnorderedMapToMap(transport_properties.value());
client()->OnChangeCompleted(
change_id,
NewWindow(MakeClientWindowId(transport_window_id), properties));
}
void WindowTree::NewTopLevelWindow(
uint32_t change_id,
Id transport_window_id,
const std::unordered_map<std::string, std::vector<uint8_t>>&
transport_properties) {
// TODO(sky): rather than DCHECK, have this kill connection.
DCHECK(!window_manager_internal_); // Not valid for the windowmanager.
DCHECK(!waiting_for_top_level_window_info_);
const ClientWindowId client_window_id =
MakeClientWindowId(transport_window_id);
// TODO(sky): need a way for client to provide context to figure out display.
Display* display = display_manager()->displays().empty()
? nullptr
: *(display_manager()->displays().begin());
// TODO(sky): move checks to accesspolicy.
WindowManagerDisplayRoot* display_root =
display && !is_for_embedding_ ? display->window_manager_display_root()
: nullptr;
if (!display_root ||
display_root->window_manager_state()->window_tree() == this ||
!IsValidIdForNewWindow(client_window_id)) {
client()->OnChangeCompleted(change_id, false);
return;
}
// The server creates the real window. Any further messages from the client
// may try to alter the window. Pause incoming messages so that we know we
// can't get a message for a window before the window is created. Once the
// window is created we'll resume processing.
binding_->SetIncomingMethodCallProcessingPaused(true);
const uint32_t wm_change_id =
window_server_->GenerateWindowManagerChangeId(this, change_id);
waiting_for_top_level_window_info_.reset(
new WaitingForTopLevelWindowInfo(client_window_id, wm_change_id));
display_root->window_manager_state()
->window_tree()
->window_manager_internal_->WmCreateTopLevelWindow(
wm_change_id, client_window_id, transport_properties);
}
void WindowTree::DeleteWindow(uint32_t change_id, Id transport_window_id) {
client()->OnChangeCompleted(
change_id, DeleteWindow(MakeClientWindowId(transport_window_id)));
}
void WindowTree::AddWindow(uint32_t change_id, Id parent_id, Id child_id) {
client()->OnChangeCompleted(
change_id,
AddWindow(MakeClientWindowId(parent_id), MakeClientWindowId(child_id)));
}
void WindowTree::RemoveWindowFromParent(uint32_t change_id, Id window_id) {
client()->OnChangeCompleted(
change_id, RemoveWindowFromParent(MakeClientWindowId(window_id)));
}
void WindowTree::AddTransientWindow(uint32_t change_id,
Id window,
Id transient_window) {
client()->OnChangeCompleted(
change_id, AddTransientWindow(MakeClientWindowId(window),
MakeClientWindowId(transient_window)));
}
void WindowTree::RemoveTransientWindowFromParent(uint32_t change_id,
Id transient_window_id) {
bool success = false;
ServerWindow* transient_window =
GetWindowByClientId(MakeClientWindowId(transient_window_id));
if (transient_window && transient_window->transient_parent() &&
access_policy_->CanRemoveTransientWindowFromParent(transient_window)) {
success = true;
Operation op(this, window_server_,
OperationType::REMOVE_TRANSIENT_WINDOW_FROM_PARENT);
transient_window->transient_parent()->RemoveTransientWindow(
transient_window);
}
client()->OnChangeCompleted(change_id, success);
}
void WindowTree::SetModalType(uint32_t change_id,
Id window_id,
ModalType modal_type) {
client()->OnChangeCompleted(
change_id, SetModalType(MakeClientWindowId(window_id), modal_type));
}
void WindowTree::SetChildModalParent(uint32_t change_id,
Id window_id,
Id parent_window_id) {
client()->OnChangeCompleted(
change_id, SetChildModalParent(MakeClientWindowId(window_id),
MakeClientWindowId(parent_window_id)));
}
void WindowTree::ReorderWindow(uint32_t change_id,
Id window_id,
Id relative_window_id,
mojom::OrderDirection direction) {
// TODO(erg): This implementation allows reordering two windows that are
// children of a parent window which the two implementations can't see. There
// should be a security check to prevent this.
bool success = false;
ServerWindow* window = GetWindowByClientId(MakeClientWindowId(window_id));
ServerWindow* relative_window =
GetWindowByClientId(MakeClientWindowId(relative_window_id));
DVLOG(3) << "reorder client=" << id_ << " client window_id=" << window_id
<< " global window_id=" << DebugWindowId(window)
<< " relative client window_id=" << relative_window_id
<< " relative global window_id=" << DebugWindowId(relative_window);
if (CanReorderWindow(window, relative_window, direction)) {
success = true;
Operation op(this, window_server_, OperationType::REORDER_WINDOW);
window->Reorder(relative_window, direction);
window_server_->ProcessWindowReorder(window, relative_window, direction);
}
client()->OnChangeCompleted(change_id, success);
}
void WindowTree::GetWindowTree(
Id window_id,
const base::Callback<void(std::vector<mojom::WindowDataPtr>)>& callback) {
std::vector<const ServerWindow*> windows(
GetWindowTree(MakeClientWindowId(window_id)));
callback.Run(WindowsToWindowDatas(windows));
}
void WindowTree::SetCapture(uint32_t change_id, Id window_id) {
client()->OnChangeCompleted(change_id,
SetCapture(MakeClientWindowId(window_id)));
}
void WindowTree::ReleaseCapture(uint32_t change_id, Id window_id) {
client()->OnChangeCompleted(change_id,
ReleaseCapture(MakeClientWindowId(window_id)));
}
void WindowTree::StartPointerWatcher(bool want_moves) {
has_pointer_watcher_ = true;
pointer_watcher_want_moves_ = want_moves;
}
void WindowTree::StopPointerWatcher() {
has_pointer_watcher_ = false;
pointer_watcher_want_moves_ = false;
}
void WindowTree::SetWindowBounds(
uint32_t change_id,
Id window_id,
const gfx::Rect& bounds,
const base::Optional<viz::LocalSurfaceId>& local_surface_id) {
ServerWindow* window = GetWindowByClientId(MakeClientWindowId(window_id));
if (window && ShouldRouteToWindowManager(window)) {
DVLOG(3) << "Redirecting request to change bounds for "
<< DebugWindowId(window) << " to window manager...";
const uint32_t wm_change_id =
window_server_->GenerateWindowManagerChangeId(this, change_id);
// |window_id| may be a client id, use the id from the window to ensure
// the windowmanager doesn't get an id it doesn't know about.
WindowManagerDisplayRoot* display_root =
GetWindowManagerDisplayRoot(window);
WindowTree* wm_tree = display_root->window_manager_state()->window_tree();
wm_tree->window_manager_internal_->WmSetBounds(
wm_change_id, wm_tree->TransportIdForWindow(window), std::move(bounds));
return;
}
DVLOG(3) << "SetWindowBounds window_id=" << window_id
<< " global window_id=" << DebugWindowId(window)
<< " bounds=" << bounds.ToString() << " local_surface_id="
<< (local_surface_id ? local_surface_id->ToString() : "null");
if (!window) {
DVLOG(1) << "SetWindowBounds failed (invalid window id)";
client()->OnChangeCompleted(change_id, false);
return;
}
// Only the owner of the window can change the bounds.
bool success = access_policy_->CanSetWindowBounds(window);
if (success) {
Operation op(this, window_server_, OperationType::SET_WINDOW_BOUNDS);
window->SetBounds(bounds, local_surface_id);
} else {
DVLOG(1) << "SetWindowBounds failed (access denied)";
}
client()->OnChangeCompleted(change_id, success);
}
void WindowTree::SetWindowTransform(uint32_t change_id,
Id window_id,
const gfx::Transform& transform) {
// Clients shouldn't have a need to set the transform of the embed root, so
// we don't bother routing it to the window-manager.
ServerWindow* window = GetWindowByClientId(MakeClientWindowId(window_id));
DVLOG(3) << "SetWindowTransform client window_id=" << window_id
<< " global window_id=" << DebugWindowId(window)
<< " transform=" << transform.ToString();
if (!window) {
DVLOG(1) << "SetWindowTransform failed (invalid window id)";
client()->OnChangeCompleted(change_id, false);
return;
}
// Only the owner of the window can change the bounds.
const bool success = access_policy_->CanSetWindowTransform(window);
if (success) {
Operation op(this, window_server_, OperationType::SET_WINDOW_TRANSFORM);
window->SetTransform(transform);
} else {
DVLOG(1) << "SetWindowTransform failed (access denied)";
}
client()->OnChangeCompleted(change_id, success);
}
void WindowTree::SetWindowVisibility(uint32_t change_id,
Id transport_window_id,
bool visible) {
client()->OnChangeCompleted(
change_id,
SetWindowVisibility(MakeClientWindowId(transport_window_id), visible));
}
void WindowTree::SetWindowProperty(
uint32_t change_id,
Id transport_window_id,
const std::string& name,
const base::Optional<std::vector<uint8_t>>& value) {
ServerWindow* window =
GetWindowByClientId(MakeClientWindowId(transport_window_id));
if (window && ShouldRouteToWindowManager(window)) {
const uint32_t wm_change_id =
window_server_->GenerateWindowManagerChangeId(this, change_id);
WindowManagerDisplayRoot* display_root =
GetWindowManagerDisplayRoot(window);
WindowTree* wm_tree = display_root->window_manager_state()->window_tree();
wm_tree->window_manager_internal_->WmSetProperty(
wm_change_id, wm_tree->TransportIdForWindow(window), name, value);
return;
}
const bool success = window && access_policy_->CanSetWindowProperties(window);
if (success) {
Operation op(this, window_server_, OperationType::SET_WINDOW_PROPERTY);
if (!value.has_value()) {
window->SetProperty(name, nullptr);
} else {
window->SetProperty(name, &value.value());
}
}
client()->OnChangeCompleted(change_id, success);
}
void WindowTree::SetWindowOpacity(uint32_t change_id,
Id window_id,
float opacity) {
client()->OnChangeCompleted(
change_id, SetWindowOpacity(MakeClientWindowId(window_id), opacity));
}
void WindowTree::AttachCompositorFrameSink(
Id transport_window_id,
viz::mojom::CompositorFrameSinkRequest compositor_frame_sink,
viz::mojom::CompositorFrameSinkClientPtr client) {
ServerWindow* window =
GetWindowByClientId(MakeClientWindowId(transport_window_id));
if (!window) {
DVLOG(1) << "AttachCompositorFrameSink failed (invalid window id)";
return;
}
const bool success = access_policy_->CanSetWindowCompositorFrameSink(window);
if (!success) {
DVLOG(1) << "AttachCompositorFrameSink failed (access denied)";
return;
}
window->CreateCompositorFrameSink(std::move(compositor_frame_sink),
std::move(client));
}
void WindowTree::SetWindowTextInputState(Id transport_window_id,
ui::mojom::TextInputStatePtr state) {
ServerWindow* window =
GetWindowByClientId(MakeClientWindowId(transport_window_id));
bool success = window && access_policy_->CanSetWindowTextInputState(window);
if (success)
window->SetTextInputState(state.To<ui::TextInputState>());
}
void WindowTree::SetImeVisibility(Id transport_window_id,
bool visible,
ui::mojom::TextInputStatePtr state) {
ServerWindow* window =
GetWindowByClientId(MakeClientWindowId(transport_window_id));
bool success = window && access_policy_->CanSetWindowTextInputState(window);
if (success) {
if (!state.is_null())
window->SetTextInputState(state.To<ui::TextInputState>());
Display* display = GetDisplay(window);
if (display)
display->SetImeVisibility(window, visible);
}
}
void WindowTree::OnWindowInputEventAck(uint32_t event_id,
mojom::EventResult result) {
// DispatchInputEventImpl() is called so often that log level 4 is used.
DVLOG(4) << "OnWindowInputEventAck client=" << id_;
if (event_ack_id_ == 0 || event_id != event_ack_id_ || !event_ack_callback_) {
// TODO(sad): Something bad happened. Kill the client?
NOTIMPLEMENTED() << ": Wrong event acked. event_id=" << event_id
<< ", event_ack_id_=" << event_ack_id_;
DVLOG(1) << "OnWindowInputEventAck supplied unexpected event_id";
return;
}
event_ack_id_ = 0;
if (janky_)
event_source_wms_->window_tree()->ClientJankinessChanged(this);
event_source_wms_ = nullptr;
base::ResetAndReturn(&event_ack_callback_).Run(result);
if (!event_queue_.empty()) {
DCHECK(!event_ack_id_);
ServerWindow* target = nullptr;
std::unique_ptr<ui::Event> event;
DispatchEventCallback callback;
EventLocation event_location;
do {
std::unique_ptr<TargetedEvent> targeted_event =
std::move(event_queue_.front());
event_queue_.pop();
target = targeted_event->target();
event = targeted_event->TakeEvent();
event_location = targeted_event->event_location();
callback = targeted_event->TakeCallback();
} while (!event_queue_.empty() && !GetDisplay(target));
if (GetDisplay(target)) {
DispatchInputEventImpl(target, *event, event_location,
std::move(callback));
} else {
// If the window is no longer valid (or not in a display), then there is
// no point in dispatching to the client, but we need to run the callback
// so that WindowManagerState isn't still waiting for an ack. We only
// need run the last callback as they should all target the same
// WindowManagerState and WindowManagerState is at most waiting on one
// ack.
std::move(callback).Run(mojom::EventResult::UNHANDLED);
}
}
}
void WindowTree::SetClientArea(Id transport_window_id,
const gfx::Insets& insets,
const base::Optional<std::vector<gfx::Rect>>&
transport_additional_client_areas) {
ServerWindow* window =
GetWindowByClientId(MakeClientWindowId(transport_window_id));
DVLOG(3) << "SetClientArea client window_id=" << transport_window_id
<< " global window_id=" << DebugWindowId(window)
<< " insets=" << insets.top() << " " << insets.left() << " "
<< insets.bottom() << " " << insets.right();
if (!window) {
DVLOG(1) << "SetClientArea failed (invalid window id)";
return;
}
if (!access_policy_->CanSetClientArea(window)) {
DVLOG(1) << "SetClientArea failed (access denied)";
return;
}
Operation op(this, window_server_, OperationType::SET_CLIENT_AREA);
window->SetClientArea(insets, transport_additional_client_areas.value_or(
std::vector<gfx::Rect>()));
}
void WindowTree::SetHitTestMask(Id transport_window_id,
const base::Optional<gfx::Rect>& mask) {
ServerWindow* window =
GetWindowByClientId(MakeClientWindowId(transport_window_id));
if (!window) {
DVLOG(1) << "SetHitTestMask failed (invalid window id)";
return;
}
if (!access_policy_->CanSetHitTestMask(window)) {
DVLOG(1) << "SetHitTestMask failed (access denied)";
return;
}
if (mask)
window->SetHitTestMask(*mask);
else
window->ClearHitTestMask();
}
void WindowTree::SetCanAcceptDrops(Id window_id, bool accepts_drops) {
ServerWindow* window = GetWindowByClientId(MakeClientWindowId(window_id));
if (!window) {
DVLOG(1) << "SetCanAcceptDrops failed (invalid window id)";
return;
}
if (!access_policy_->CanSetAcceptDrops(window)) {
DVLOG(1) << "SetAcceptsDrops failed (access denied)";
return;
}
window->SetCanAcceptDrops(accepts_drops);
}
void WindowTree::Embed(Id transport_window_id,
mojom::WindowTreeClientPtr client,
uint32_t flags,
const EmbedCallback& callback) {
callback.Run(
Embed(MakeClientWindowId(transport_window_id), std::move(client), flags));
}
void WindowTree::ScheduleEmbed(mojom::WindowTreeClientPtr client,
const ScheduleEmbedCallback& callback) {
const base::UnguessableToken token = base::UnguessableToken::Create();
scheduled_embeds_[token] = std::move(client);
callback.Run(token);
}
void WindowTree::EmbedUsingToken(Id transport_window_id,
const base::UnguessableToken& token,
uint32_t flags,
const EmbedUsingTokenCallback& callback) {
mojom::WindowTreeClientPtr client =
GetAndRemoveScheduledEmbedWindowTreeClient(token);
if (!client) {
DVLOG(1) << "EmbedUsingToken failed, no ScheduleEmbed(), token="
<< token.ToString();
callback.Run(false);
return;
}
Embed(transport_window_id, std::move(client), flags, callback);
}
void WindowTree::SetFocus(uint32_t change_id, Id transport_window_id) {
client()->OnChangeCompleted(
change_id, SetFocus(MakeClientWindowId(transport_window_id)));
}
void WindowTree::SetCanFocus(Id transport_window_id, bool can_focus) {
ServerWindow* window =
GetWindowByClientId(MakeClientWindowId(transport_window_id));
if (!window) {
DVLOG(1) << "SetCanFocus failed (invalid window id)";
return;
}
if (ShouldRouteToWindowManager(window)) {
WindowManagerDisplayRoot* display_root =
GetWindowManagerDisplayRoot(window);
WindowTree* wm_tree = display_root->window_manager_state()->window_tree();
wm_tree->window_manager_internal_->WmSetCanFocus(transport_window_id,
can_focus);
} else if (access_policy_->CanSetFocus(window)) {
window->set_can_focus(can_focus);
} else {
DVLOG(1) << "SetCanFocus failed (access denied)";
}
}
void WindowTree::SetEventTargetingPolicy(Id transport_window_id,
mojom::EventTargetingPolicy policy) {
ServerWindow* window =
GetWindowByClientId(MakeClientWindowId(transport_window_id));
// TODO(riajiang): check |event_queue_| is empty for |window|.
if (window && access_policy_->CanSetEventTargetingPolicy(window))
window->set_event_targeting_policy(policy);
}
void WindowTree::SetCursor(uint32_t change_id,
Id transport_window_id,
ui::CursorData cursor) {
ServerWindow* window =
GetWindowByClientId(MakeClientWindowId(transport_window_id));
if (!window) {
DVLOG(1) << "SetCursor failed (invalid id)";
client()->OnChangeCompleted(change_id, false);
return;
}
// Only the owner of the window can change the bounds.
bool success = access_policy_->CanSetCursorProperties(window);
if (!success) {
DVLOG(1) << "SetCursor failed (access denied)";
client()->OnChangeCompleted(change_id, false);
return;
}
// If the cursor is custom, it must have valid frames.
if (cursor.cursor_type() == ui::CursorType::kCustom) {
if (cursor.cursor_frames().empty()) {
DVLOG(1) << "SetCursor failed (no frames with custom cursor)";
client()->OnChangeCompleted(change_id, false);
return;
}
for (const SkBitmap& bitmap : cursor.cursor_frames()) {
if (bitmap.drawsNothing()) {
DVLOG(1) << "SetCursor failed (cursor frame draws nothing)";
client()->OnChangeCompleted(change_id, false);
return;
}
}
}
Operation op(this, window_server_,
OperationType::SET_WINDOW_PREDEFINED_CURSOR);
window->SetCursor(std::move(cursor));
client()->OnChangeCompleted(change_id, success);
}
void WindowTree::DeactivateWindow(Id window_id) {
ServerWindow* window = GetWindowByClientId(MakeClientWindowId(window_id));
if (!window) {
DVLOG(1) << "DeactivateWindow failed (invalid id)";
return;
}
WindowManagerDisplayRoot* display_root = GetWindowManagerDisplayRoot(window);
if (!display_root) {
// The window isn't parented. There's nothing to do.
DVLOG(1) << "DeactivateWindow failed (window unparented)";
return;
}
WindowTree* wm_tree = display_root->window_manager_state()->window_tree();
wm_tree->window_manager_internal_->WmDeactivateWindow(
wm_tree->TransportIdForWindow(window));
}
void WindowTree::StackAbove(uint32_t change_id, Id above_id, Id below_id) {
ServerWindow* above = GetWindowByClientId(MakeClientWindowId(above_id));
if (!above) {
DVLOG(1) << "StackAbove failed (invalid above id)";
client()->OnChangeCompleted(change_id, false);
return;
}
ServerWindow* below = GetWindowByClientId(MakeClientWindowId(below_id));
if (!below) {
DVLOG(1) << "StackAbove failed (invalid below id)";
client()->OnChangeCompleted(change_id, false);
return;
}
if (!access_policy_->CanStackAbove(above, below)) {
DVLOG(1) << "StackAbove failed (access denied)";
client()->OnChangeCompleted(change_id, false);
return;
}
ServerWindow* parent = above->parent();
ServerWindow* below_parent = below->parent();
if (!parent) {
DVLOG(1) << "StackAbove failed (above unparented)";
client()->OnChangeCompleted(change_id, false);
return;
}
if (!below_parent) {
DVLOG(1) << "StackAbove failed (below unparented)";
client()->OnChangeCompleted(change_id, false);
return;
}
if (parent != below_parent) {
DVLOG(1) << "StackAbove failed (windows have different parents)";
client()->OnChangeCompleted(change_id, false);
return;
}
WindowManagerDisplayRoot* display_root = GetWindowManagerDisplayRoot(above);
if (!display_root) {
DVLOG(1) << "StackAbove (no display root)";
client()->OnChangeCompleted(change_id, false);
return;
}
// Window reordering assumes that it is the owner of parent who is sending
// the message, and does not deal gracefully with other clients reordering
// their windows. So tell the window manager to send us a reorder message.
WindowTree* wm_tree = display_root->window_manager_state()->window_tree();
const uint32_t wm_change_id =
window_server_->GenerateWindowManagerChangeId(this, change_id);
wm_tree->window_manager_internal_->WmStackAbove(
wm_change_id, wm_tree->TransportIdForWindow(above),
wm_tree->TransportIdForWindow(below));
}
void WindowTree::StackAtTop(uint32_t change_id, Id window_id) {
ServerWindow* window = GetWindowByClientId(MakeClientWindowId(window_id));
if (!window) {
DVLOG(1) << "StackAtTop failed (invalid id)";
client()->OnChangeCompleted(change_id, false);
return;
}
if (!access_policy_->CanStackAtTop(window)) {
DVLOG(1) << "StackAtTop failed (access denied)";
client()->OnChangeCompleted(change_id, false);
return;
}
ServerWindow* parent = window->parent();
if (!parent) {
DVLOG(1) << "StackAtTop failed (window unparented)";
client()->OnChangeCompleted(change_id, false);
return;
}
DCHECK(!parent->children().empty());
if (parent->children().back() == window) {
// Ignore this call; the client didn't know they were already at the top.
DVLOG(3) << "StackAtTop ignored (already at top)";
client()->OnChangeCompleted(change_id, true);
return;
}
WindowManagerDisplayRoot* display_root = GetWindowManagerDisplayRoot(window);
if (!display_root) {
DVLOG(1) << "StackAtTop (no display root)";
client()->OnChangeCompleted(change_id, false);
return;
}
// Window reordering assumes that it is the owner of parent who is sending
// the message, and does not deal gracefully with other clients reordering
// their windows. So tell the window manager to send us a reorder message.
WindowTree* wm_tree = display_root->window_manager_state()->window_tree();
const uint32_t wm_change_id =
window_server_->GenerateWindowManagerChangeId(this, change_id);
wm_tree->window_manager_internal_->WmStackAtTop(
wm_change_id, wm_tree->TransportIdForWindow(window));
}
void WindowTree::PerformWmAction(Id window_id, const std::string& action) {
ServerWindow* window = GetWindowByClientId(MakeClientWindowId(window_id));
if (!window) {
DVLOG(1) << "PerformWmAction(" << action << ") failed (invalid id)";
return;
}
if (!access_policy_->CanPerformWmAction(window)) {
DVLOG(1) << "PerformWmAction(" << action << ") failed (access denied)";
return;
}
WindowManagerDisplayRoot* display_root = GetWindowManagerDisplayRoot(window);
if (!display_root) {
DVLOG(1) << "PerformWmAction(" << action << ") failed (no display root)";
return;
}
WindowTree* wm_tree = display_root->window_manager_state()->window_tree();
wm_tree->window_manager_internal_->WmPerformWmAction(
wm_tree->TransportIdForWindow(window), action);
}
void WindowTree::GetWindowManagerClient(
mojo::AssociatedInterfaceRequest<mojom::WindowManagerClient> internal) {
if (!access_policy_->CanSetWindowManager() || !window_manager_internal_ ||
window_manager_internal_client_binding_) {
return;
}
window_manager_internal_client_binding_.reset(
new mojo::AssociatedBinding<mojom::WindowManagerClient>(
this, std::move(internal)));
}
void WindowTree::GetCursorLocationMemory(
const GetCursorLocationMemoryCallback& callback) {
callback.Run(
display_manager()->cursor_location_manager()->GetCursorLocationMemory());
}
void WindowTree::PerformDragDrop(
uint32_t change_id,
Id source_window_id,
const gfx::Point& screen_location,
const std::unordered_map<std::string, std::vector<uint8_t>>& drag_data,
const SkBitmap& drag_image,
const gfx::Vector2d& drag_image_offset,
uint32_t drag_operation,
ui::mojom::PointerKind source) {
// TODO(erg): SkBitmap is the wrong data type for the drag image; we should
// be passing ImageSkias once http://crbug.com/655874 is implemented.
ServerWindow* window =
GetWindowByClientId(MakeClientWindowId(source_window_id));
bool success = window && access_policy_->CanInitiateDragLoop(window);
if (!success || !ShouldRouteToWindowManager(window)) {
// We need to fail this move loop change, otherwise the client will just be
// waiting for |change_id|.
DVLOG(1) << "PerformDragDrop failed (access denied)";
client()->OnPerformDragDropCompleted(change_id, false,
mojom::kDropEffectNone);
return;
}
WindowManagerDisplayRoot* display_root = GetWindowManagerDisplayRoot(window);
if (!display_root) {
// The window isn't parented. There's nothing to do.
DVLOG(1) << "PerformDragDrop failed (window unparented)";
client()->OnPerformDragDropCompleted(change_id, false,
mojom::kDropEffectNone);
return;
}
if (window_server_->in_move_loop() || window_server_->in_drag_loop()) {
// Either the window manager is servicing a window drag or we're servicing
// a drag and drop operation. We can't start a second drag.
DVLOG(1) << "PerformDragDrop failed (already performing a drag)";
client()->OnPerformDragDropCompleted(change_id, false,
mojom::kDropEffectNone);
return;
}
WindowManagerState* wms = display_root->window_manager_state();
// Send the drag representation to the window manager.
wms->window_tree()->window_manager_internal_->WmBuildDragImage(
screen_location, drag_image, drag_image_offset, source);
// Here, we need to dramatically change how the mouse pointer works. Once
// we've started a drag drop operation, cursor events don't go to windows as
// normal.
window_server_->StartDragLoop(change_id, window, this);
wms->SetDragDropSourceWindow(this, window, this, drag_data, drag_operation);
}
void WindowTree::CancelDragDrop(Id window_id) {
ServerWindow* window = GetWindowByClientId(MakeClientWindowId(window_id));
if (!window) {
DVLOG(1) << "CancelDragDrop failed (no window)";
return;
}
if (window != window_server_->GetCurrentDragLoopWindow()) {
DVLOG(1) << "CancelDragDrop failed (not the drag loop window)";
return;
}
if (window_server_->GetCurrentDragLoopInitiator() != this) {
DVLOG(1) << "CancelDragDrop failed (not the drag initiator)";
return;
}
WindowManagerDisplayRoot* display_root = GetWindowManagerDisplayRoot(window);
if (!display_root) {
DVLOG(1) << "CancelDragDrop failed (no such window manager display root)";
return;
}
WindowManagerState* wms = display_root->window_manager_state();
wms->CancelDragDrop();
}
void WindowTree::PerformWindowMove(uint32_t change_id,
Id window_id,
ui::mojom::MoveLoopSource source,
const gfx::Point& cursor) {
ServerWindow* window = GetWindowByClientId(MakeClientWindowId(window_id));
bool success = window && access_policy_->CanInitiateMoveLoop(window);
if (!success || !ShouldRouteToWindowManager(window)) {
// We need to fail this move loop change, otherwise the client will just be
// waiting for |change_id|.
DVLOG(1) << "PerformWindowMove failed (access denied)";
OnChangeCompleted(change_id, false);
return;
}
WindowManagerDisplayRoot* display_root = GetWindowManagerDisplayRoot(window);
if (!display_root) {
// The window isn't parented. There's nothing to do.
DVLOG(1) << "PerformWindowMove failed (window unparented)";
OnChangeCompleted(change_id, false);
return;
}
if (window_server_->in_move_loop() || window_server_->in_drag_loop()) {
// Either the window manager is servicing a window drag or we're servicing
// a drag and drop operation. We can't start a second drag.
DVLOG(1) << "PerformWindowMove failed (already performing a drag)";
OnChangeCompleted(change_id, false);
return;
}
// When we perform a window move loop, we give the window manager non client
// capture. Because of how the capture public interface currently works,
// SetCapture() will check whether the mouse cursor is currently in the
// non-client area and if so, will redirect messages to the window
// manager. (And normal window movement relies on this behaviour.)
WindowManagerState* wms = display_root->window_manager_state();
wms->SetCapture(window, wms->window_tree()->id());
const uint32_t wm_change_id =
window_server_->GenerateWindowManagerChangeId(this, change_id);
window_server_->StartMoveLoop(wm_change_id, window, this, window->bounds());
wms->window_tree()->window_manager_internal_->WmPerformMoveLoop(
wm_change_id, wms->window_tree()->TransportIdForWindow(window), source,
cursor);
}
void WindowTree::CancelWindowMove(Id window_id) {
ServerWindow* window = GetWindowByClientId(MakeClientWindowId(window_id));
if (!window) {
DVLOG(1) << "CancelWindowMove failed (invalid window id)";
return;
}
bool success = access_policy_->CanInitiateMoveLoop(window);
if (!success) {
DVLOG(1) << "CancelWindowMove failed (access denied)";
return;
}
if (window != window_server_->GetCurrentMoveLoopWindow()) {
DVLOG(1) << "CancelWindowMove failed (not the move loop window)";
return;
}
if (window_server_->GetCurrentMoveLoopInitiator() != this) {
DVLOG(1) << "CancelWindowMove failed (not the move loop initiator)";
return;
}
WindowManagerDisplayRoot* display_root = GetWindowManagerDisplayRoot(window);
if (!display_root) {
DVLOG(1) << "CancelWindowMove failed (no such window manager display root)";
return;
}
WindowManagerState* wms = display_root->window_manager_state();
wms->window_tree()->window_manager_internal_->WmCancelMoveLoop(
window_server_->GetCurrentMoveLoopChangeId());
}
void WindowTree::AddAccelerators(
std::vector<mojom::WmAcceleratorPtr> accelerators,
const AddAcceleratorsCallback& callback) {
DCHECK(window_manager_state_);
bool success = true;
for (auto iter = accelerators.begin(); iter != accelerators.end(); ++iter) {
if (!window_manager_state_->event_dispatcher()->AddAccelerator(
iter->get()->id, std::move(iter->get()->event_matcher)))
success = false;
}
callback.Run(success);
}
void WindowTree::RemoveAccelerator(uint32_t id) {
window_manager_state_->event_dispatcher()->RemoveAccelerator(id);
}
void WindowTree::AddActivationParent(Id transport_window_id) {
AddActivationParent(MakeClientWindowId(transport_window_id));
}
void WindowTree::RemoveActivationParent(Id transport_window_id) {
ServerWindow* window =
GetWindowByClientId(MakeClientWindowId(transport_window_id));
if (!window) {
DVLOG(1) << "RemoveActivationParent failed (invalid window id)";
return;
}
window->set_is_activation_parent(false);
}
void WindowTree::SetExtendedHitRegionForChildren(
Id window_id,
const gfx::Insets& mouse_insets,
const gfx::Insets& touch_insets) {
ServerWindow* window = GetWindowByClientId(MakeClientWindowId(window_id));
// Extended hit test region should only be set by the owner of the window.
if (!window) {
DVLOG(1) << "SetExtendedHitRegionForChildren failed (invalid window id)";
return;
}
if (window->owning_tree_id() != id_) {
DVLOG(1) << "SetExtendedHitRegionForChildren failed (supplied window that "
<< "client does not own)";
return;
}
if (HasPositiveInset(mouse_insets) || HasPositiveInset(touch_insets)) {
DVLOG(1) << "SetExtendedHitRegionForChildren failed (insets must be "
<< "negative)";
return;
}
window->set_extended_hit_test_regions_for_children(mouse_insets,
touch_insets);
}
void WindowTree::SetKeyEventsThatDontHideCursor(
std::vector<::ui::mojom::EventMatcherPtr> dont_hide_cursor_list) {
DCHECK(window_manager_state_);
window_manager_state_->SetKeyEventsThatDontHideCursor(
std::move(dont_hide_cursor_list));
}
void WindowTree::SetDisplayRoot(const display::Display& display,
mojom::WmViewportMetricsPtr viewport_metrics,
bool is_primary_display,
Id window_id,
const std::vector<display::Display>& mirrors,
const SetDisplayRootCallback& callback) {
ServerWindow* display_root = ProcessSetDisplayRoot(
display, TransportMetricsToDisplayMetrics(*viewport_metrics),
is_primary_display, MakeClientWindowId(window_id), mirrors);
if (!display_root) {
callback.Run(false);
return;
}
display_root->parent()->SetVisible(true);
callback.Run(true);
}
void WindowTree::SetDisplayConfiguration(
const std::vector<display::Display>& displays,
std::vector<ui::mojom::WmViewportMetricsPtr> transport_metrics,
int64_t primary_display_id,
int64_t internal_display_id,
const std::vector<display::Display>& mirrors,
const SetDisplayConfigurationCallback& callback) {
std::vector<display::ViewportMetrics> metrics;
for (auto& transport_ptr : transport_metrics)
metrics.push_back(TransportMetricsToDisplayMetrics(*transport_ptr));
callback.Run(display_manager()->SetDisplayConfiguration(
displays, metrics, primary_display_id, internal_display_id, mirrors));
}
void WindowTree::SwapDisplayRoots(int64_t display_id1,
int64_t display_id2,
const SwapDisplayRootsCallback& callback) {
DCHECK(window_manager_state_); // Only applicable to the window manager.
callback.Run(ProcessSwapDisplayRoots(display_id1, display_id2));
}
void WindowTree::SetBlockingContainers(
std::vector<::ui::mojom::BlockingContainersPtr> blocking_containers,
const SetBlockingContainersCallback& callback) {
DCHECK(window_manager_state_); // Only applicable to the window manager.
callback.Run(ProcessSetBlockingContainers(std::move(blocking_containers)));
}
void WindowTree::WmResponse(uint32_t change_id, bool response) {
if (window_server_->in_move_loop() &&
window_server_->GetCurrentMoveLoopChangeId() == change_id) {
ServerWindow* window = window_server_->GetCurrentMoveLoopWindow();
if (window->owning_tree_id() != id_) {
window_server_->WindowManagerSentBogusMessage();
window = nullptr;
} else {
WindowManagerDisplayRoot* display_root =
GetWindowManagerDisplayRoot(window);
if (display_root) {
WindowManagerState* wms = display_root->window_manager_state();
// Clear the implicit capture.
wms->SetCapture(nullptr, false);
}
}
if (!response && window) {
// Our move loop didn't succeed, which means that we must restore the
// original bounds of the window.
window->SetBounds(window_server_->GetCurrentMoveLoopRevertBounds(),
base::nullopt);
}
window_server_->EndMoveLoop();
}
window_server_->WindowManagerChangeCompleted(change_id, response);
}
void WindowTree::WmSetBoundsResponse(uint32_t change_id) {
// The window manager will always give a response of false to the client
// because it will always update the bounds of the top level window itself
// which will overwrite the request made by the client.
WmResponse(change_id, false);
}
void WindowTree::WmRequestClose(Id transport_window_id) {
ServerWindow* window =
GetWindowByClientId(MakeClientWindowId(transport_window_id));
WindowTree* tree = window_server_->GetTreeWithRoot(window);
if (tree && tree != this) {
tree->client()->RequestClose(tree->TransportIdForWindow(window));
}
// TODO(sky): think about what else case means.
}
void WindowTree::WmSetFrameDecorationValues(
mojom::FrameDecorationValuesPtr values) {
DCHECK(window_manager_state_);
window_manager_state_->SetFrameDecorationValues(std::move(values));
}
void WindowTree::WmSetNonClientCursor(Id window_id, ui::CursorData cursor) {
DCHECK(window_manager_state_);
ServerWindow* window = GetWindowByClientId(MakeClientWindowId(window_id));
if (!window) {
DVLOG(1) << "WmSetNonClientCursor failed (invalid window id)";
return;
}
window->SetNonClientCursor(std::move(cursor));
}
void WindowTree::WmLockCursor() {
DCHECK(window_manager_state_);
window_manager_state_->cursor_state().LockCursor();
}
void WindowTree::WmUnlockCursor() {
DCHECK(window_manager_state_);
window_manager_state_->cursor_state().UnlockCursor();
}
void WindowTree::WmSetCursorVisible(bool visible) {
DCHECK(window_manager_state_);
window_manager_state_->cursor_state().SetCursorVisible(visible);
}
void WindowTree::WmSetCursorSize(ui::CursorSize cursor_size) {
DCHECK(window_manager_state_);
window_manager_state_->cursor_state().SetCursorSize(cursor_size);
}
void WindowTree::WmSetGlobalOverrideCursor(
base::Optional<ui::CursorData> cursor) {
DCHECK(window_manager_state_);
window_manager_state_->cursor_state().SetGlobalOverrideCursor(cursor);
}
void WindowTree::WmMoveCursorToDisplayLocation(const gfx::Point& display_pixels,
int64_t display_id) {
DCHECK(window_manager_state_);
window_manager_state_->SetCursorLocation(display_pixels, display_id);
}
void WindowTree::WmConfineCursorToBounds(const gfx::Rect& bounds_in_pixels,
int64_t display_id) {
DCHECK(window_manager_state_);
Display* display = display_manager()->GetDisplayById(display_id);
if (!display) {
DVLOG(1) << "WmConfineCursorToBounds failed (invalid display id)";
return;
}
PlatformDisplay* platform_display = display->platform_display();
if (!platform_display) {
DVLOG(1) << "WmConfineCursorToBounds failed (no platform display)";
return;
}
platform_display->ConfineCursorToBounds(bounds_in_pixels);
}
void WindowTree::WmSetCursorTouchVisible(bool enabled) {
DCHECK(window_manager_state_);
window_manager_state_->SetCursorTouchVisible(enabled);
}
void WindowTree::OnWmCreatedTopLevelWindow(uint32_t change_id,
Id transport_window_id) {
ServerWindow* window =
GetWindowByClientId(MakeClientWindowId(transport_window_id));
if (window && window->owning_tree_id() != id_) {
DVLOG(1) << "OnWmCreatedTopLevelWindow failed (invalid window id)";
window_server_->WindowManagerSentBogusMessage();
window = nullptr;
}
window_server_->WindowManagerCreatedTopLevelWindow(this, change_id, window);
}
void WindowTree::OnAcceleratorAck(uint32_t event_id,
mojom::EventResult result,
const EventProperties& properties) {
DVLOG(3) << "OnAcceleratorAck client=" << id_;
if (event_ack_id_ == 0 || event_id != event_ack_id_ ||
!accelerator_ack_callback_) {
DVLOG(1) << "OnAcceleratorAck failed (invalid event id)";
window_server_->WindowManagerSentBogusMessage();
return;
}
event_ack_id_ = 0;
DCHECK(window_manager_state_);
base::ResetAndReturn(&accelerator_ack_callback_).Run(result, properties);
}
bool WindowTree::HasRootForAccessPolicy(const ServerWindow* window) const {
return HasRoot(window);
}
bool WindowTree::IsWindowKnownForAccessPolicy(
const ServerWindow* window) const {
return IsWindowKnown(window);
}
bool WindowTree::IsWindowRootOfAnotherTreeForAccessPolicy(
const ServerWindow* window) const {
WindowTree* tree = window_server_->GetTreeWithRoot(window);
return tree && tree != this;
}
bool WindowTree::IsWindowCreatedByWindowManager(
const ServerWindow* window) const {
// The WindowManager is attached to the root of the Display, if there isn't a
// WindowManager attached, the window manager didn't create this window.
const WindowManagerDisplayRoot* display_root =
GetWindowManagerDisplayRoot(window);
if (!display_root)
return false;
return display_root->window_manager_state()->window_tree()->id() ==
window->owning_tree_id();
}
bool WindowTree::ShouldInterceptEventsForAccessPolicy(
const ServerWindow* window) const {
if (is_for_embedding_)
return false;
while (window) {
// Find the first window created by this client. If there is an embedding,
// it'll be here.
if (window->owning_tree_id() == id_) {
// embedded_tree->embedder_intercepts_events() indicates Embed() was
// called with kEmbedFlagEmbedderInterceptsEvents. In this case the
// embedder needs to see the window so that it knows the event is
// targetted at it.
WindowTree* embedded_tree = window_server_->GetTreeWithRoot(window);
if (embedded_tree && embedded_tree->embedder_intercepts_events())
return true;
return false;
}
window = window->parent();
}
return false;
}
void WindowTree::OnDragMoved(const gfx::Point& location) {
DCHECK(window_server_->in_drag_loop());
DCHECK_EQ(this, window_server_->GetCurrentDragLoopInitiator());
ServerWindow* window = window_server_->GetCurrentDragLoopWindow();
WindowManagerDisplayRoot* display_root = GetWindowManagerDisplayRoot(window);
if (!display_root)
return;
if (drag_move_state_) {
drag_move_state_->has_queued_drag_window_move = true;
drag_move_state_->queued_cursor_location = location;
} else {
WindowManagerState* wms = display_root->window_manager_state();
drag_move_state_ = std::make_unique<DragMoveState>();
wms->window_tree()->window_manager_internal_->WmMoveDragImage(
location, base::Bind(&WindowTree::OnWmMoveDragImageAck,
drag_weak_factory_.GetWeakPtr()));
}
}
void WindowTree::OnDragCompleted(bool success, uint32_t action_taken) {
DCHECK(window_server_->in_drag_loop());
if (window_server_->GetCurrentDragLoopInitiator() != this)
return;
uint32_t change_id = window_server_->GetCurrentDragLoopChangeId();
ServerWindow* window = window_server_->GetCurrentDragLoopWindow();
WindowManagerDisplayRoot* display_root = GetWindowManagerDisplayRoot(window);
if (!display_root)
return;
window_server_->EndDragLoop();
WindowManagerState* wms = display_root->window_manager_state();
wms->EndDragDrop();
wms->window_tree()->window_manager_internal_->WmDestroyDragImage();
drag_weak_factory_.InvalidateWeakPtrs();
client()->OnPerformDragDropCompleted(change_id, success, action_taken);
}
DragTargetConnection* WindowTree::GetDragTargetForWindow(
const ServerWindow* window) {
if (!window)
return nullptr;
DragTargetConnection* connection = window_server_->GetTreeWithRoot(window);
if (connection)
return connection;
return window_server_->GetTreeWithId(window->owning_tree_id());
}
void WindowTree::PerformOnDragDropStart(
const std::unordered_map<std::string, std::vector<uint8_t>>& mime_data) {
client()->OnDragDropStart(mime_data);
}
void WindowTree::PerformOnDragEnter(
const ServerWindow* window,
uint32_t event_flags,
const gfx::Point& cursor_offset,
uint32_t effect_bitmask,
const base::Callback<void(uint32_t)>& callback) {
ClientWindowId client_window_id;
if (!IsWindowKnown(window, &client_window_id)) {
NOTREACHED();
callback.Run(0);
return;
}
client()->OnDragEnter(ClientWindowIdToTransportId(client_window_id),
event_flags, cursor_offset, effect_bitmask, callback);
}
void WindowTree::PerformOnDragOver(
const ServerWindow* window,
uint32_t event_flags,
const gfx::Point& cursor_offset,
uint32_t effect_bitmask,
const base::Callback<void(uint32_t)>& callback) {
ClientWindowId client_window_id;
if (!IsWindowKnown(window, &client_window_id)) {
NOTREACHED();
callback.Run(0);
return;
}
client()->OnDragOver(ClientWindowIdToTransportId(client_window_id),
event_flags, cursor_offset, effect_bitmask, callback);
}
void WindowTree::PerformOnDragLeave(const ServerWindow* window) {
ClientWindowId client_window_id;
if (!IsWindowKnown(window, &client_window_id)) {
NOTREACHED();
return;
}
client()->OnDragLeave(ClientWindowIdToTransportId(client_window_id));
}
void WindowTree::PerformOnCompleteDrop(
const ServerWindow* window,
uint32_t event_flags,
const gfx::Point& cursor_offset,
uint32_t effect_bitmask,
const base::Callback<void(uint32_t)>& callback) {
ClientWindowId client_window_id;
if (!IsWindowKnown(window, &client_window_id)) {
NOTREACHED();
callback.Run(0);
return;
}
client()->OnCompleteDrop(ClientWindowIdToTransportId(client_window_id),
event_flags, cursor_offset, effect_bitmask,
callback);
}
void WindowTree::PerformOnDragDropDone() {
client()->OnDragDropDone();
}
} // namespace ws
} // namespace ui