// 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 "base/bind.h" #include "base/stl_util.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/server_window.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 { 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), is_embed_root_(false) { 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<const ServerWindow*> 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()), tree.Pass(), 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() { 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(); } mojom::ErrorCode WindowTreeImpl::NewWindow(const WindowId& window_id) { if (window_id.connection_id != id_) return mojom::ERROR_CODE_ILLEGAL_ARGUMENT; if (window_map_.find(window_id.window_id) != window_map_.end()) return mojom::ERROR_CODE_VALUE_IN_USE; window_map_[window_id.window_id] = connection_manager_->CreateServerWindow(window_id); known_windows_.insert(WindowIdToTransportId(window_id)); return mojom::ERROR_CODE_NONE; } 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)) { ConnectionManager::ScopedChange change(this, connection_manager_, false); parent->Add(child); return true; } return false; } std::vector<const ServerWindow*> WindowTreeImpl::GetWindowTree( const WindowId& window_id) const { const ServerWindow* window = GetWindow(window_id); std::vector<const ServerWindow*> 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; } ConnectionManager::ScopedChange change(this, connection_manager_, false); 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, client.Pass()); if (is_embed_root_) *connection_id = new_connection->id(); return true; } 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::Rect& old_client_area, const gfx::Rect& new_client_area, bool originated_change) { if (originated_change || !IsWindowKnown(window)) return; client()->OnClientAreaChanged(WindowIdToTransportId(window->id()), Rect::From(old_client_area), Rect::From(new_client_area)); } 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<uint8_t>* new_data, bool originated_change) { if (originated_change) return; Array<uint8_t> data; if (new_data) data = Array<uint8_t>::From(*new_data); client()->OnWindowSharedPropertyChanged(WindowIdToTransportId(window->id()), String(name), data.Pass()); } 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<const ServerWindow*> unused; GetUnknownWindowsFrom(window, &unused); } if (originated_change || connection_manager_->is_processing_delete_window() || 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<const ServerWindow*> 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)) return; client()->OnWindowReordered(WindowIdToTransportId(window->id()), WindowIdToTransportId(relative_window->id()), direction); } 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::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())); } 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<const ServerWindow*> 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_); ConnectionManager::ScopedChange change(source, connection_manager_, true); delete window; return true; } void WindowTreeImpl::GetUnknownWindowsFrom( const ServerWindow* window, std::vector<const ServerWindow*>* 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<const ServerWindow*> children(window->GetChildren()); for (size_t i = 0; i < children.size(); ++i) GetUnknownWindowsFrom(children[i], windows); } void WindowTreeImpl::RemoveFromKnown( const ServerWindow* window, std::vector<ServerWindow*>* 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<const ServerWindow*> 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<ServerWindow*> 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<mojom::WindowDataPtr> WindowTreeImpl::WindowsToWindowDatas( const std::vector<const ServerWindow*>& windows) { Array<mojom::WindowDataPtr> array(windows.size()); for (size_t i = 0; i < windows.size(); ++i) array[i] = WindowToWindowData(windows[i]).Pass(); return array.Pass(); } 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<String, Array<uint8_t>>::From(window->properties()); window_data->visible = window->visible(); window_data->drawn = window->IsDrawn(); window_data->viewport_metrics = connection_manager_->GetViewportMetricsForWindow(window); return window_data.Pass(); } void WindowTreeImpl::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; std::vector<const ServerWindow*> 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()) { ConnectionManager::ScopedChange change(this, connection_manager_, true); // 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); 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); ConnectionManager::ScopedChange change(this, connection_manager_, true); 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<ServerWindow*> children = window->GetChildren(); for (size_t i = 0; i < children.size(); ++i) window->Remove(children[i]); } void WindowTreeImpl::NewWindow( Id transport_window_id, const Callback<void(mojom::ErrorCode)>& callback) { callback.Run(NewWindow(WindowIdFromTransportId(transport_window_id))); } void WindowTreeImpl::DeleteWindow(Id transport_window_id, const Callback<void(bool)>& callback) { ServerWindow* window = GetWindow(WindowIdFromTransportId(transport_window_id)); bool success = false; if (window && access_policy_->CanDeleteWindow(window)) { WindowTreeImpl* connection = connection_manager_->GetConnection(window->id().connection_id); success = connection && connection->DeleteWindowImpl(this, window); } callback.Run(success); } void WindowTreeImpl::AddWindow(Id parent_id, Id child_id, const Callback<void(bool)>& callback) { callback.Run(AddWindow(WindowIdFromTransportId(parent_id), WindowIdFromTransportId(child_id))); } void WindowTreeImpl::RemoveWindowFromParent( Id window_id, const Callback<void(bool)>& callback) { bool success = false; ServerWindow* window = GetWindow(WindowIdFromTransportId(window_id)); if (window && window->parent() && access_policy_->CanRemoveWindowFromParent(window)) { success = true; ConnectionManager::ScopedChange change(this, connection_manager_, false); window->parent()->Remove(window); } callback.Run(success); } void WindowTreeImpl::ReorderWindow(Id window_id, Id relative_window_id, mojom::OrderDirection direction, const Callback<void(bool)>& callback) { 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; ConnectionManager::ScopedChange change(this, connection_manager_, false); window->parent()->Reorder(window, relative_window, direction); connection_manager_->ProcessWindowReorder(window, relative_window, direction); } callback.Run(success); } void WindowTreeImpl::GetWindowTree( Id window_id, const Callback<void(Array<mojom::WindowDataPtr>)>& callback) { std::vector<const ServerWindow*> windows( GetWindowTree(WindowIdFromTransportId(window_id))); callback.Run(WindowsToWindowDatas(windows)); } void WindowTreeImpl::SetWindowBounds(Id window_id, mojo::RectPtr bounds, const Callback<void(bool)>& callback) { ServerWindow* window = GetWindow(WindowIdFromTransportId(window_id)); const bool success = window && access_policy_->CanSetWindowBounds(window); if (success) { ConnectionManager::ScopedChange change(this, connection_manager_, false); window->SetBounds(bounds.To<gfx::Rect>()); } callback.Run(success); } void WindowTreeImpl::SetWindowVisibility(Id transport_window_id, bool visible, const Callback<void(bool)>& callback) { callback.Run(SetWindowVisibility(WindowIdFromTransportId(transport_window_id), visible)); } void WindowTreeImpl::SetWindowProperty( uint32_t window_id, const mojo::String& name, mojo::Array<uint8_t> value, const mojo::Callback<void(bool)>& callback) { ServerWindow* window = GetWindow(WindowIdFromTransportId(window_id)); const bool success = window && access_policy_->CanSetWindowProperties(window); if (success) { ConnectionManager::ScopedChange change(this, connection_manager_, false); if (value.is_null()) { window->SetProperty(name, nullptr); } else { std::vector<uint8_t> data = value.To<std::vector<uint8_t>>(); window->SetProperty(name, &data); } } callback.Run(success); } void WindowTreeImpl::RequestSurface( Id window_id, mojom::SurfaceType type, mojo::InterfaceRequest<mojom::Surface> 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, surface.Pass(), client.Pass()); } void WindowTreeImpl::SetWindowTextInputState(uint32_t window_id, mojo::TextInputStatePtr state) { ServerWindow* window = GetWindow(WindowIdFromTransportId(window_id)); bool success = window && access_policy_->CanSetWindowTextInputState(window); if (success) window->SetTextInputState(state.To<ui::TextInputState>()); } 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<ui::TextInputState>()); WindowTreeHostImpl* host = GetHost(); if (host) host->SetImeVisibility(window, visible); } } void WindowTreeImpl::SetClientArea(Id transport_window_id, mojo::RectPtr rect) { ServerWindow* window = GetWindow(WindowIdFromTransportId(transport_window_id)); if (!window || !access_policy_->CanSetClientArea(window)) return; if (rect.is_null()) window->SetClientArea(gfx::Rect(window->bounds().size())); else window->SetClientArea(rect.To<gfx::Rect>()); } 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), client.Pass(), policy_bitmask, &connection_id); callback.Run(result, connection_id); } void WindowTreeImpl::SetFocus(uint32_t window_id) { ServerWindow* window = GetWindow(WindowIdFromTransportId(window_id)); // TODO(beng): consider shifting non-policy drawn check logic to VTH's // FocusController. if (window && window->IsDrawn() && access_policy_->CanSetFocus(window)) { ConnectionManager::ScopedChange change(this, connection_manager_, false); WindowTreeHostImpl* host = GetHost(); if (host) host->SetFocusedWindow(window); } } void WindowTreeImpl::SetPreferredSize( uint32_t window_id, mojo::SizePtr size, const SetPreferredSizeCallback& callback) { if (!GetHost() || !GetHost()->window_manager()) return; // TODO(sky): verify window_id is valid for the client. GetHost()->window_manager()->SetPreferredSize(window_id, size.Pass(), callback); } void WindowTreeImpl::SetBounds(uint32_t window_id, mojo::RectPtr bounds, const SetBoundsCallback& callback) { if (!GetHost() || !GetHost()->window_manager()) return; // TODO(sky): verify window_id is valid for the client. GetHost()->window_manager()->SetBounds(window_id, bounds.Pass(), callback); } void WindowTreeImpl::SetShowState(uint32_t window_id, mojom::ShowState show_state, const SetShowStateCallback& callback) { if (!GetHost() || !GetHost()->window_manager()) return; // TODO(sky): verify window_id is valid for the client. GetHost()->window_manager()->SetShowState(window_id, show_state, callback); } void WindowTreeImpl::GetDisplays(const GetDisplaysCallback& callback) { if (!GetHost() || !GetHost()->window_manager()) return; GetHost()->window_manager()->GetDisplays(callback); } 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