// 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 "components/mus/ws/window_tree_impl.h" #include #include "base/bind.h" #include "base/macros.h" #include "base/stl_util.h" #include "components/mus/ws/client_connection.h" #include "components/mus/ws/connection_manager.h" #include "components/mus/ws/default_access_policy.h" #include "components/mus/ws/display_manager.h" #include "components/mus/ws/operation.h" #include "components/mus/ws/server_window.h" #include "components/mus/ws/server_window_observer.h" #include "components/mus/ws/window_manager_access_policy.h" #include "components/mus/ws/window_tree_host_impl.h" #include "mojo/converters/geometry/geometry_type_converters.h" #include "mojo/converters/ime/ime_type_converters.h" #include "mojo/converters/input_events/input_events_type_converters.h" #include "mojo/converters/surfaces/surfaces_type_converters.h" #include "ui/platform_window/text_input_state.h" using mojo::Array; using mojo::Callback; using mojo::InterfaceRequest; using mojo::Rect; using mojo::String; namespace mus { namespace ws { class TargetedEvent : public ServerWindowObserver { public: TargetedEvent(ServerWindow* target, mojom::EventPtr event) : target_(target), event_(std::move(event)) { target_->AddObserver(this); } ~TargetedEvent() override { if (target_) target_->RemoveObserver(this); } ServerWindow* target() { return target_; } mojom::EventPtr event() { return std::move(event_); } private: // ServerWindowObserver: void OnWindowDestroyed(ServerWindow* window) override { DCHECK_EQ(target_, window); target_->RemoveObserver(this); target_ = nullptr; } ServerWindow* target_; mojom::EventPtr event_; DISALLOW_COPY_AND_ASSIGN(TargetedEvent); }; WindowTreeImpl::WindowTreeImpl(ConnectionManager* connection_manager, ConnectionSpecificId creator_id, const WindowId& root_id, uint32_t policy_bitmask) : connection_manager_(connection_manager), id_(connection_manager_->GetAndAdvanceNextConnectionId()), creator_id_(creator_id), client_(nullptr), event_ack_id_(0), is_embed_root_(false), window_manager_internal_(nullptr) { ServerWindow* window = GetWindow(root_id); CHECK(window); root_.reset(new WindowId(root_id)); if (window->GetRoot() == window) { access_policy_.reset(new WindowManagerAccessPolicy(id_, this)); is_embed_root_ = true; } else { access_policy_.reset(new DefaultAccessPolicy(id_, this)); is_embed_root_ = (policy_bitmask & WindowTree::ACCESS_POLICY_EMBED_ROOT) != 0; } } WindowTreeImpl::~WindowTreeImpl() { DestroyWindows(); } void WindowTreeImpl::Init(mojom::WindowTreeClient* client, mojom::WindowTreePtr tree) { DCHECK(!client_); client_ = client; std::vector to_send; if (root_.get()) GetUnknownWindowsFrom(GetWindow(*root_), &to_send); // TODO(beng): verify that host can actually be nullptr here. WindowTreeHostImpl* host = GetHost(); const ServerWindow* focused_window = host ? host->GetFocusedWindow() : nullptr; if (focused_window) focused_window = access_policy_->GetWindowForFocusChange(focused_window); const Id focused_window_transport_id(WindowIdToTransportId( focused_window ? focused_window->id() : WindowId())); client->OnEmbed(id_, WindowToWindowData(to_send.front()), std::move(tree), focused_window_transport_id, is_embed_root_ ? WindowTree::ACCESS_POLICY_EMBED_ROOT : WindowTree::ACCESS_POLICY_DEFAULT); } const ServerWindow* WindowTreeImpl::GetWindow(const WindowId& id) const { if (id_ == id.connection_id) { WindowMap::const_iterator i = window_map_.find(id.window_id); return i == window_map_.end() ? NULL : i->second; } return connection_manager_->GetWindow(id); } bool WindowTreeImpl::IsRoot(const WindowId& id) const { return root_.get() && *root_ == id; } WindowTreeHostImpl* WindowTreeImpl::GetHost() const { return root_.get() ? connection_manager_->GetWindowTreeHostByWindow(GetWindow(*root_)) : nullptr; } void WindowTreeImpl::OnWillDestroyWindowTreeImpl(WindowTreeImpl* connection) { if (creator_id_ == connection->id()) creator_id_ = kInvalidConnectionId; const ServerWindow* connection_root = connection->root_ ? connection->GetWindow(*connection->root_) : nullptr; if (connection_root && ((connection_root->id().connection_id == id_ && window_map_.count(connection_root->id().window_id) > 0) || (is_embed_root_ && IsWindowKnown(connection_root)))) { client()->OnEmbeddedAppDisconnected( WindowIdToTransportId(*connection->root_)); } if (root_.get() && root_->connection_id == connection->id()) root_.reset(); } void WindowTreeImpl::NotifyChangeCompleted( uint32_t change_id, mojom::WindowManagerErrorCode error_code) { client_->OnChangeCompleted( change_id, error_code == mojom::WINDOW_MANAGER_ERROR_CODE_SUCCESS); } bool WindowTreeImpl::NewWindow( const WindowId& window_id, const std::map>& properties) { if (window_id.connection_id != id_) return false; if (window_map_.find(window_id.window_id) != window_map_.end()) return false; window_map_[window_id.window_id] = connection_manager_->CreateServerWindow(window_id, properties); known_windows_.insert(WindowIdToTransportId(window_id)); return true; } bool WindowTreeImpl::AddWindow(const WindowId& parent_id, const WindowId& child_id) { ServerWindow* parent = GetWindow(parent_id); ServerWindow* child = GetWindow(child_id); if (parent && child && child->parent() != parent && !child->Contains(parent) && access_policy_->CanAddWindow(parent, child)) { Operation op(this, connection_manager_, OperationType::ADD_WINDOW); parent->Add(child); return true; } return false; } bool WindowTreeImpl::AddTransientWindow(const WindowId& window_id, const WindowId& transient_window_id) { ServerWindow* window = GetWindow(window_id); ServerWindow* transient_window = GetWindow(transient_window_id); if (window && transient_window && !transient_window->Contains(window) && access_policy_->CanAddTransientWindow(window, transient_window)) { Operation op(this, connection_manager_, OperationType::ADD_TRANSIENT_WINDOW); window->AddTransientWindow(transient_window); return true; } return false; } std::vector WindowTreeImpl::GetWindowTree( const WindowId& window_id) const { const ServerWindow* window = GetWindow(window_id); std::vector windows; if (window) GetWindowTreeImpl(window, &windows); return windows; } bool WindowTreeImpl::SetWindowVisibility(const WindowId& window_id, bool visible) { ServerWindow* window = GetWindow(window_id); if (!window || window->visible() == visible || !access_policy_->CanChangeWindowVisibility(window)) { return false; } Operation op(this, connection_manager_, OperationType::SET_WINDOW_VISIBILITY); window->SetVisible(visible); return true; } bool WindowTreeImpl::Embed(const WindowId& window_id, mojom::WindowTreeClientPtr client, uint32_t policy_bitmask, ConnectionSpecificId* connection_id) { *connection_id = kInvalidConnectionId; if (!client.get() || !CanEmbed(window_id, policy_bitmask)) return false; PrepareForEmbed(window_id); WindowTreeImpl* new_connection = connection_manager_->EmbedAtWindow( id_, window_id, policy_bitmask, std::move(client)); if (is_embed_root_) *connection_id = new_connection->id(); return true; } void WindowTreeImpl::DispatchInputEvent(ServerWindow* target, mojom::EventPtr event) { if (event_ack_id_) { // This is currently waiting for an event ack. Add it to the queue. event_queue_.push( make_scoped_ptr(new TargetedEvent(target, std::move(event)))); // TODO(sad): If the |event_queue_| grows too large, then this should notify // WindowTreeHostImpl, 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( make_scoped_ptr(new TargetedEvent(target, std::move(event)))); return; } DispatchInputEventImpl(target, std::move(event)); } void WindowTreeImpl::ProcessWindowBoundsChanged(const ServerWindow* window, const gfx::Rect& old_bounds, const gfx::Rect& new_bounds, bool originated_change) { if (originated_change || !IsWindowKnown(window)) return; client()->OnWindowBoundsChanged(WindowIdToTransportId(window->id()), Rect::From(old_bounds), Rect::From(new_bounds)); } void WindowTreeImpl::ProcessClientAreaChanged( const ServerWindow* window, const gfx::Insets& new_client_area, const std::vector& new_additional_client_areas, bool originated_change) { if (originated_change || !IsWindowKnown(window)) return; client()->OnClientAreaChanged( WindowIdToTransportId(window->id()), mojo::Insets::From(new_client_area), mojo::Array::From(new_additional_client_areas)); } void WindowTreeImpl::ProcessViewportMetricsChanged( const mojom::ViewportMetrics& old_metrics, const mojom::ViewportMetrics& new_metrics, bool originated_change) { client()->OnWindowViewportMetricsChanged(old_metrics.Clone(), new_metrics.Clone()); } void WindowTreeImpl::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 WindowTreeImpl::ProcessWindowPropertyChanged( const ServerWindow* window, const std::string& name, const std::vector* new_data, bool originated_change) { if (originated_change) return; Array data; if (new_data) data = Array::From(*new_data); client()->OnWindowSharedPropertyChanged(WindowIdToTransportId(window->id()), String(name), std::move(data)); } void WindowTreeImpl::ProcessWindowHierarchyChanged( const ServerWindow* window, const ServerWindow* new_parent, const ServerWindow* old_parent, bool originated_change) { if (originated_change && !IsWindowKnown(window) && new_parent && IsWindowKnown(new_parent)) { std::vector unused; GetUnknownWindowsFrom(window, &unused); } if (originated_change || (connection_manager_->current_operation_type() == OperationType::DELETE_WINDOW) || (connection_manager_->current_operation_type() == OperationType::EMBED) || connection_manager_->DidConnectionMessageClient(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 to_send; if (!IsWindowKnown(window)) GetUnknownWindowsFrom(window, &to_send); const WindowId new_parent_id(new_parent ? new_parent->id() : WindowId()); const WindowId old_parent_id(old_parent ? old_parent->id() : WindowId()); client()->OnWindowHierarchyChanged( WindowIdToTransportId(window->id()), WindowIdToTransportId(new_parent_id), WindowIdToTransportId(old_parent_id), WindowsToWindowDatas(to_send)); connection_manager_->OnConnectionMessagedClient(id_); } void WindowTreeImpl::ProcessWindowReorder(const ServerWindow* window, const ServerWindow* relative_window, mojom::OrderDirection direction, bool originated_change) { if (originated_change || !IsWindowKnown(window) || !IsWindowKnown(relative_window) || connection_manager_->DidConnectionMessageClient(id_)) return; client()->OnWindowReordered(WindowIdToTransportId(window->id()), WindowIdToTransportId(relative_window->id()), direction); connection_manager_->OnConnectionMessagedClient(id_); } void WindowTreeImpl::ProcessWindowDeleted(const WindowId& window, bool originated_change) { if (window.connection_id == id_) window_map_.erase(window.window_id); const bool in_known = known_windows_.erase(WindowIdToTransportId(window)) > 0; if (IsRoot(window)) root_.reset(); if (originated_change) return; if (in_known) { client()->OnWindowDeleted(WindowIdToTransportId(window)); connection_manager_->OnConnectionMessagedClient(id_); } } void WindowTreeImpl::ProcessWillChangeWindowVisibility( const ServerWindow* window, bool originated_change) { if (originated_change) return; if (IsWindowKnown(window)) { client()->OnWindowVisibilityChanged(WindowIdToTransportId(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 WindowTreeImpl::ProcessCursorChanged(const ServerWindow* window, int32_t cursor_id, bool originated_change) { if (originated_change) return; client()->OnWindowPredefinedCursorChanged(WindowIdToTransportId(window->id()), mojom::Cursor(cursor_id)); } void WindowTreeImpl::ProcessFocusChanged( const ServerWindow* old_focused_window, const ServerWindow* new_focused_window) { const ServerWindow* window = new_focused_window ? access_policy_->GetWindowForFocusChange(new_focused_window) : nullptr; client()->OnWindowFocused(window ? WindowIdToTransportId(window->id()) : WindowIdToTransportId(WindowId())); } void WindowTreeImpl::ProcessTransientWindowAdded( const ServerWindow* window, const ServerWindow* transient_window, bool originated_change) { if (originated_change) return; client()->OnTransientWindowAdded( WindowIdToTransportId(window->id()), WindowIdToTransportId(transient_window->id())); } void WindowTreeImpl::ProcessTransientWindowRemoved( const ServerWindow* window, const ServerWindow* transient_window, bool originated_change) { if (originated_change) return; client()->OnTransientWindowRemoved( WindowIdToTransportId(window->id()), WindowIdToTransportId(transient_window->id())); } bool WindowTreeImpl::ShouldRouteToWindowManager( const ServerWindow* window) const { // If the client created this window, then do not route it through the WM. if (window->id().connection_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 (!root_ || *root_ != window->id()) return false; // The WindowManager is attached to the root of the WindowTreeHost, if there // isn't a WindowManager attached no need to route to it. WindowTreeHostImpl* host = GetHost(); if (!host || !host->GetWindowTree() || !host->GetWindowTree()->window_manager_internal_) { return false; } // Requests coming from the WM should not be routed through the WM again. const bool is_wm = host->GetWindowTree() == this; return is_wm ? false : true; } bool WindowTreeImpl::IsWindowKnown(const ServerWindow* window) const { return known_windows_.count(WindowIdToTransportId(window->id())) > 0; } bool WindowTreeImpl::CanReorderWindow(const ServerWindow* window, const ServerWindow* relative_window, mojom::OrderDirection direction) const { if (!window || !relative_window) return false; if (!window->parent() || window->parent() != relative_window->parent()) return false; if (!access_policy_->CanReorderWindow(window, relative_window, direction)) return false; std::vector children = window->parent()->GetChildren(); 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::ORDER_DIRECTION_ABOVE && child_i == target_i + 1) || (direction == mojom::ORDER_DIRECTION_BELOW && child_i + 1 == target_i)) { return false; } return true; } bool WindowTreeImpl::DeleteWindowImpl(WindowTreeImpl* source, ServerWindow* window) { DCHECK(window); DCHECK_EQ(window->id().connection_id, id_); Operation op(source, connection_manager_, OperationType::DELETE_WINDOW); delete window; return true; } void WindowTreeImpl::GetUnknownWindowsFrom( const ServerWindow* window, std::vector* windows) { if (IsWindowKnown(window) || !access_policy_->CanGetWindowTree(window)) return; windows->push_back(window); known_windows_.insert(WindowIdToTransportId(window->id())); if (!access_policy_->CanDescendIntoWindowForWindowTree(window)) return; std::vector children(window->GetChildren()); for (size_t i = 0; i < children.size(); ++i) GetUnknownWindowsFrom(children[i], windows); } void WindowTreeImpl::RemoveFromKnown( const ServerWindow* window, std::vector* local_windows) { if (window->id().connection_id == id_) { if (local_windows) local_windows->push_back(GetWindow(window->id())); return; } known_windows_.erase(WindowIdToTransportId(window->id())); std::vector children = window->GetChildren(); for (size_t i = 0; i < children.size(); ++i) RemoveFromKnown(children[i], local_windows); } void WindowTreeImpl::RemoveRoot() { CHECK(root_.get()); const WindowId root_id(*root_); root_.reset(); // No need to do anything if we created the window. if (root_id.connection_id == id_) return; client()->OnUnembed(); client()->OnWindowDeleted(WindowIdToTransportId(root_id)); connection_manager_->OnConnectionMessagedClient(id_); // This connection no longer knows about the window. Unparent any windows that // were parented to windows in the root. std::vector local_windows; RemoveFromKnown(GetWindow(root_id), &local_windows); for (size_t i = 0; i < local_windows.size(); ++i) local_windows[i]->parent()->Remove(local_windows[i]); } Array WindowTreeImpl::WindowsToWindowDatas( const std::vector& windows) { Array array(windows.size()); for (size_t i = 0; i < windows.size(); ++i) array[i] = WindowToWindowData(windows[i]); return array; } mojom::WindowDataPtr WindowTreeImpl::WindowToWindowData( const ServerWindow* window) { DCHECK(IsWindowKnown(window)); const ServerWindow* parent = window->parent(); // If the parent isn't known, it means the parent is not visible to us (not // in roots), and should not be sent over. if (parent && !IsWindowKnown(parent)) parent = NULL; mojom::WindowDataPtr window_data(mojom::WindowData::New()); window_data->parent_id = WindowIdToTransportId(parent ? parent->id() : WindowId()); window_data->window_id = WindowIdToTransportId(window->id()); window_data->bounds = Rect::From(window->bounds()); window_data->properties = mojo::Map>::From(window->properties()); window_data->visible = window->visible(); window_data->drawn = window->IsDrawn(); window_data->viewport_metrics = connection_manager_->GetViewportMetricsForWindow(window); return window_data; } void WindowTreeImpl::GetWindowTreeImpl( const ServerWindow* window, std::vector* windows) const { DCHECK(window); if (!access_policy_->CanGetWindowTree(window)) return; windows->push_back(window); if (!access_policy_->CanDescendIntoWindowForWindowTree(window)) return; std::vector children(window->GetChildren()); for (size_t i = 0; i < children.size(); ++i) GetWindowTreeImpl(children[i], windows); } void WindowTreeImpl::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 (!root_.get()) return; const ServerWindow* root = GetWindow(*root_); DCHECK(root); if (window->Contains(root) && (new_drawn_value != root->IsDrawn())) { client()->OnWindowDrawnStateChanged(WindowIdToTransportId(root->id()), new_drawn_value); } } void WindowTreeImpl::DestroyWindows() { if (window_map_.empty()) return; Operation op(this, connection_manager_, 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 |window_map_| changes or not. WindowMap window_map_copy; window_map_.swap(window_map_copy); // A sibling can be a transient parent of another window so we detach windows // from their transient parents to avoid double deletes. for (auto& pair : window_map_copy) { ServerWindow* transient_parent = pair.second->transient_parent(); if (transient_parent) transient_parent->RemoveTransientWindow(pair.second); } STLDeleteValues(&window_map_copy); } bool WindowTreeImpl::CanEmbed(const WindowId& window_id, uint32_t policy_bitmask) const { const ServerWindow* window = GetWindow(window_id); return window && access_policy_->CanEmbed(window, policy_bitmask); } void WindowTreeImpl::PrepareForEmbed(const WindowId& window_id) { const ServerWindow* window = GetWindow(window_id); DCHECK(window); // Only allow a node to be the root for one connection. WindowTreeImpl* existing_owner = connection_manager_->GetConnectionWithRoot(window_id); Operation op(this, connection_manager_, OperationType::EMBED); RemoveChildrenAsPartOfEmbed(window_id); if (existing_owner) { // Never message the originating connection. connection_manager_->OnConnectionMessagedClient(id_); existing_owner->RemoveRoot(); } } void WindowTreeImpl::RemoveChildrenAsPartOfEmbed(const WindowId& window_id) { ServerWindow* window = GetWindow(window_id); CHECK(window); CHECK(window->id().connection_id == window_id.connection_id); std::vector children = window->GetChildren(); for (size_t i = 0; i < children.size(); ++i) window->Remove(children[i]); } void WindowTreeImpl::DispatchInputEventImpl(ServerWindow* target, mojom::EventPtr event) { 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 from // the event pointer. event_ack_id_ = 0x1000000 | (reinterpret_cast(event.get()) & 0xffffff); client()->OnWindowInputEvent( event_ack_id_, WindowIdToTransportId(target->id()), std::move(event)); } void WindowTreeImpl::NewWindow( uint32_t change_id, Id transport_window_id, mojo::Map> transport_properties) { std::map> properties; if (!transport_properties.is_null()) { properties = transport_properties.To>>(); } client_->OnChangeCompleted( change_id, NewWindow(WindowIdFromTransportId(transport_window_id), properties)); } void WindowTreeImpl::DeleteWindow(uint32_t change_id, Id transport_window_id) { ServerWindow* window = GetWindow(WindowIdFromTransportId(transport_window_id)); bool success = false; bool should_close = window && (access_policy_->CanDeleteWindow(window) || ShouldRouteToWindowManager(window)); if (should_close) { WindowTreeImpl* connection = connection_manager_->GetConnection(window->id().connection_id); success = connection && connection->DeleteWindowImpl(this, window); } client_->OnChangeCompleted(change_id, success); } void WindowTreeImpl::AddWindow(uint32_t change_id, Id parent_id, Id child_id) { client_->OnChangeCompleted(change_id, AddWindow(WindowIdFromTransportId(parent_id), WindowIdFromTransportId(child_id))); } void WindowTreeImpl::RemoveWindowFromParent(uint32_t change_id, Id window_id) { bool success = false; ServerWindow* window = GetWindow(WindowIdFromTransportId(window_id)); if (window && window->parent() && access_policy_->CanRemoveWindowFromParent(window)) { success = true; Operation op(this, connection_manager_, OperationType::REMOVE_WINDOW_FROM_PARENT); window->parent()->Remove(window); } client_->OnChangeCompleted(change_id, success); } void WindowTreeImpl::AddTransientWindow(uint32_t change_id, Id window, Id transient_window) { client_->OnChangeCompleted( change_id, AddTransientWindow(WindowIdFromTransportId(window), WindowIdFromTransportId(transient_window))); } void WindowTreeImpl::RemoveTransientWindowFromParent(uint32_t change_id, Id transient_window_id) { bool success = false; ServerWindow* transient_window = GetWindow(WindowIdFromTransportId(transient_window_id)); if (transient_window->transient_parent() && access_policy_->CanRemoveTransientWindowFromParent(transient_window)) { success = true; Operation op(this, connection_manager_, OperationType::REMOVE_TRANSIENT_WINDOW_FROM_PARENT); transient_window->transient_parent()->RemoveTransientWindow( transient_window); } client_->OnChangeCompleted(change_id, success); } void WindowTreeImpl::ReorderWindow(uint32_t change_id, Id window_id, Id relative_window_id, mojom::OrderDirection direction) { bool success = false; ServerWindow* window = GetWindow(WindowIdFromTransportId(window_id)); ServerWindow* relative_window = GetWindow(WindowIdFromTransportId(relative_window_id)); if (CanReorderWindow(window, relative_window, direction)) { success = true; Operation op(this, connection_manager_, OperationType::REORDER_WINDOW); window->Reorder(relative_window, direction); connection_manager_->ProcessWindowReorder(window, relative_window, direction); } client_->OnChangeCompleted(change_id, success); } void WindowTreeImpl::GetWindowTree( Id window_id, const Callback)>& callback) { std::vector windows( GetWindowTree(WindowIdFromTransportId(window_id))); callback.Run(WindowsToWindowDatas(windows)); } void WindowTreeImpl::SetWindowBounds(uint32_t change_id, Id window_id, mojo::RectPtr bounds) { ServerWindow* window = GetWindow(WindowIdFromTransportId(window_id)); if (window && ShouldRouteToWindowManager(window)) { const uint32_t wm_change_id = connection_manager_->GenerateWindowManagerChangeId(this, change_id); GetHost()->GetWindowTree()->window_manager_internal_->WmSetBounds( wm_change_id, window_id, std::move(bounds)); return; } // Only the owner of the window can change the bounds. bool success = window && access_policy_->CanSetWindowBounds(window); if (success) { Operation op(this, connection_manager_, OperationType::SET_WINDOW_BOUNDS); window->SetBounds(bounds.To()); } client_->OnChangeCompleted(change_id, success); } void WindowTreeImpl::SetWindowVisibility(uint32_t change_id, Id transport_window_id, bool visible) { client_->OnChangeCompleted( change_id, SetWindowVisibility( WindowIdFromTransportId(transport_window_id), visible)); } void WindowTreeImpl::SetWindowProperty(uint32_t change_id, uint32_t window_id, const mojo::String& name, mojo::Array value) { ServerWindow* window = GetWindow(WindowIdFromTransportId(window_id)); if (window && ShouldRouteToWindowManager(window)) { const uint32_t wm_change_id = connection_manager_->GenerateWindowManagerChangeId(this, change_id); GetHost()->GetWindowTree()->window_manager_internal_->WmSetProperty( wm_change_id, window_id, name, std::move(value)); return; } const bool success = window && access_policy_->CanSetWindowProperties(window); if (success) { Operation op(this, connection_manager_, OperationType::SET_WINDOW_PROPERTY); if (value.is_null()) { window->SetProperty(name, nullptr); } else { std::vector data = value.To>(); window->SetProperty(name, &data); } } client_->OnChangeCompleted(change_id, success); } void WindowTreeImpl::AttachSurface( Id window_id, mojom::SurfaceType type, mojo::InterfaceRequest surface, mojom::SurfaceClientPtr client) { ServerWindow* window = GetWindow(WindowIdFromTransportId(window_id)); const bool success = window && access_policy_->CanSetWindowSurface(window, type); if (!success) return; window->CreateSurface(type, std::move(surface), std::move(client)); } void WindowTreeImpl::SetWindowTextInputState(Id window_id, mojo::TextInputStatePtr state) { ServerWindow* window = GetWindow(WindowIdFromTransportId(window_id)); bool success = window && access_policy_->CanSetWindowTextInputState(window); if (success) window->SetTextInputState(state.To()); } void WindowTreeImpl::SetImeVisibility(Id transport_window_id, bool visible, mojo::TextInputStatePtr state) { ServerWindow* window = GetWindow(WindowIdFromTransportId(transport_window_id)); bool success = window && access_policy_->CanSetWindowTextInputState(window); if (success) { if (!state.is_null()) window->SetTextInputState(state.To()); WindowTreeHostImpl* host = GetHost(); if (host) host->SetImeVisibility(window, visible); } } void WindowTreeImpl::OnWindowInputEventAck(uint32_t event_id) { if (event_ack_id_ == 0 || event_id != event_ack_id_) { // TODO(sad): Something bad happened. Kill the client? NOTIMPLEMENTED() << "Wrong event acked."; } event_ack_id_ = 0; GetHost()->OnEventAck(this); if (!event_queue_.empty()) { DCHECK(!event_ack_id_); ServerWindow* target = nullptr; mojom::EventPtr event; do { scoped_ptr targeted_event = std::move(event_queue_.front()); event_queue_.pop(); target = targeted_event->target(); event = targeted_event->event(); } while (!event_queue_.empty() && !target); if (target) DispatchInputEventImpl(target, std::move(event)); } } void WindowTreeImpl::SetClientArea( Id transport_window_id, mojo::InsetsPtr insets, mojo::Array transport_additional_client_areas) { ServerWindow* window = GetWindow(WindowIdFromTransportId(transport_window_id)); if (!window || !access_policy_->CanSetClientArea(window)) return; std::vector additional_client_areas = transport_additional_client_areas.To>(); window->SetClientArea(insets.To(), additional_client_areas); } void WindowTreeImpl::Embed(Id transport_window_id, mojom::WindowTreeClientPtr client, uint32_t policy_bitmask, const EmbedCallback& callback) { ConnectionSpecificId connection_id = kInvalidConnectionId; const bool result = Embed(WindowIdFromTransportId(transport_window_id), std::move(client), policy_bitmask, &connection_id); callback.Run(result, connection_id); } void WindowTreeImpl::SetFocus(uint32_t change_id, Id window_id) { ServerWindow* window = GetWindow(WindowIdFromTransportId(window_id)); // TODO(beng): consider shifting non-policy drawn check logic to VTH's // FocusController. WindowTreeHostImpl* host = GetHost(); const bool success = window && window->IsDrawn() && access_policy_->CanSetFocus(window) && host; if (success) { Operation op(this, connection_manager_, OperationType::SET_FOCUS); host->SetFocusedWindow(window); } client_->OnChangeCompleted(change_id, success); } void WindowTreeImpl::SetCanFocus(Id window_id, bool can_focus) { ServerWindow* window = GetWindow(WindowIdFromTransportId(window_id)); if (window && ShouldRouteToWindowManager(window)) window->set_can_focus(can_focus); } void WindowTreeImpl::SetPredefinedCursor(uint32_t change_id, Id window_id, mus::mojom::Cursor cursor_id) { ServerWindow* window = GetWindow(WindowIdFromTransportId(window_id)); // Only the owner of the window can change the bounds. bool success = window && access_policy_->CanSetCursorProperties(window); if (success) { Operation op(this, connection_manager_, OperationType::SET_WINDOW_PREDEFINED_CURSOR); window->SetPredefinedCursor(cursor_id); } client_->OnChangeCompleted(change_id, success); } void WindowTreeImpl::GetWindowManagerInternalClient( mojo::AssociatedInterfaceRequest internal) { if (!access_policy_->CanSetWindowManagerInternal() || window_manager_internal_) return; window_manager_internal_client_binding_.reset( new mojo::AssociatedBinding( this, internal.Pass())); window_manager_internal_ = connection_manager_->GetClientConnection(this) ->GetWindowManagerInternal(); } void WindowTreeImpl::WmResponse(uint32_t change_id, bool response) { if (GetHost() && GetHost()->GetWindowTree() == this) connection_manager_->WindowManagerChangeCompleted(change_id, response); } void WindowTreeImpl::WmRequestClose(Id transport_window_id) { // Only the WindowManager should be using this. if (!GetHost() || GetHost()->GetWindowTree() != this) return; const WindowId window_id(WindowIdFromTransportId(transport_window_id)); WindowTreeImpl* connection = connection_manager_->GetConnectionWithRoot(window_id); if (connection && connection != GetHost()->GetWindowTree()) connection->client_->RequestClose(transport_window_id); } bool WindowTreeImpl::IsRootForAccessPolicy(const WindowId& id) const { return IsRoot(id); } bool WindowTreeImpl::IsWindowKnownForAccessPolicy( const ServerWindow* window) const { return IsWindowKnown(window); } bool WindowTreeImpl::IsWindowRootOfAnotherConnectionForAccessPolicy( const ServerWindow* window) const { WindowTreeImpl* connection = connection_manager_->GetConnectionWithRoot(window->id()); return connection && connection != this; } bool WindowTreeImpl::IsDescendantOfEmbedRoot(const ServerWindow* window) { return is_embed_root_ && root_ && GetWindow(*root_)->Contains(window); } } // namespace ws } // namespace mus