diff options
Diffstat (limited to 'components/mus/public/cpp/lib/window_tree_client_impl.cc')
-rw-r--r-- | components/mus/public/cpp/lib/window_tree_client_impl.cc | 457 |
1 files changed, 457 insertions, 0 deletions
diff --git a/components/mus/public/cpp/lib/window_tree_client_impl.cc b/components/mus/public/cpp/lib/window_tree_client_impl.cc new file mode 100644 index 0000000..0fc663e --- /dev/null +++ b/components/mus/public/cpp/lib/window_tree_client_impl.cc @@ -0,0 +1,457 @@ +// 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/public/cpp/lib/window_tree_client_impl.h" + +#include "components/mus/public/cpp/lib/window_private.h" +#include "components/mus/public/cpp/util.h" +#include "components/mus/public/cpp/window_observer.h" +#include "components/mus/public/cpp/window_tree_connection.h" +#include "components/mus/public/cpp/window_tree_delegate.h" +#include "mojo/application/public/cpp/application_impl.h" +#include "mojo/application/public/cpp/connect.h" +#include "mojo/application/public/cpp/service_provider_impl.h" +#include "mojo/application/public/interfaces/service_provider.mojom.h" + +namespace mus { + +Id MakeTransportId(ConnectionSpecificId connection_id, + ConnectionSpecificId local_id) { + return (connection_id << 16) | local_id; +} + +// Helper called to construct a local window object from transport data. +Window* AddWindowToConnection(WindowTreeClientImpl* client, + Window* parent, + const mojo::ViewDataPtr& window_data) { + // We don't use the cto that takes a WindowTreeConnection here, since it will + // call back to the service and attempt to create a new view. + Window* window = WindowPrivate::LocalCreate(); + WindowPrivate private_window(window); + private_window.set_connection(client); + private_window.set_id(window_data->view_id); + private_window.set_visible(window_data->visible); + private_window.set_drawn(window_data->drawn); + private_window.LocalSetViewportMetrics(mojo::ViewportMetrics(), + *window_data->viewport_metrics); + private_window.set_properties( + window_data->properties + .To<std::map<std::string, std::vector<uint8_t>>>()); + client->AddWindow(window); + private_window.LocalSetBounds(mojo::Rect(), *window_data->bounds); + if (parent) + WindowPrivate(parent).LocalAddChild(window); + return window; +} + +Window* BuildWindowTree(WindowTreeClientImpl* client, + const mojo::Array<mojo::ViewDataPtr>& windows, + Window* initial_parent) { + std::vector<Window*> parents; + Window* root = NULL; + Window* last_window = NULL; + if (initial_parent) + parents.push_back(initial_parent); + for (size_t i = 0; i < windows.size(); ++i) { + if (last_window && windows[i]->parent_id == last_window->id()) { + parents.push_back(last_window); + } else if (!parents.empty()) { + while (parents.back()->id() != windows[i]->parent_id) + parents.pop_back(); + } + Window* window = AddWindowToConnection( + client, !parents.empty() ? parents.back() : NULL, windows[i]); + if (!last_window) + root = window; + last_window = window; + } + return root; +} + +WindowTreeConnection* WindowTreeConnection::Create( + WindowTreeDelegate* delegate, + mojo::InterfaceRequest<mojo::ViewTreeClient> request, + CreateType create_type) { + WindowTreeClientImpl* client = + new WindowTreeClientImpl(delegate, request.Pass()); + if (create_type == CreateType::WAIT_FOR_EMBED) + client->WaitForEmbed(); + return client; +} + +WindowTreeClientImpl::WindowTreeClientImpl( + WindowTreeDelegate* delegate, + mojo::InterfaceRequest<mojo::ViewTreeClient> request) + : connection_id_(0), + next_id_(1), + delegate_(delegate), + root_(nullptr), + capture_window_(nullptr), + focused_window_(nullptr), + activated_window_(nullptr), + binding_(this, request.Pass()), + is_embed_root_(false), + in_destructor_(false) {} + +WindowTreeClientImpl::~WindowTreeClientImpl() { + in_destructor_ = true; + + std::vector<Window*> non_owned; + while (!windows_.empty()) { + IdToWindowMap::iterator it = windows_.begin(); + if (OwnsWindow(it->second->id())) { + it->second->Destroy(); + } else { + non_owned.push_back(it->second); + windows_.erase(it); + } + } + + // Delete the non-owned views last. In the typical case these are roots. The + // exception is the window manager and embed roots, which may know about + // other random windows that it doesn't own. + // NOTE: we manually delete as we're a friend. + for (size_t i = 0; i < non_owned.size(); ++i) + delete non_owned[i]; + + delegate_->OnConnectionLost(this); +} + +void WindowTreeClientImpl::WaitForEmbed() { + DCHECK(!root_); + // OnEmbed() is the first function called. + binding_.WaitForIncomingMethodCall(); + // TODO(sky): deal with pipe being closed before we get OnEmbed(). +} + +void WindowTreeClientImpl::DestroyWindow(Id window_id) { + DCHECK(tree_); + tree_->DeleteView(window_id, ActionCompletedCallback()); +} + +void WindowTreeClientImpl::AddChild(Id child_id, Id parent_id) { + DCHECK(tree_); + tree_->AddView(parent_id, child_id, ActionCompletedCallback()); +} + +void WindowTreeClientImpl::RemoveChild(Id child_id, Id parent_id) { + DCHECK(tree_); + tree_->RemoveViewFromParent(child_id, ActionCompletedCallback()); +} + +void WindowTreeClientImpl::Reorder(Id window_id, + Id relative_window_id, + mojo::OrderDirection direction) { + DCHECK(tree_); + tree_->ReorderView(window_id, relative_window_id, direction, + ActionCompletedCallback()); +} + +bool WindowTreeClientImpl::OwnsWindow(Id id) const { + return HiWord(id) == connection_id_; +} + +void WindowTreeClientImpl::SetBounds(Id window_id, const mojo::Rect& bounds) { + DCHECK(tree_); + tree_->SetViewBounds(window_id, bounds.Clone(), ActionCompletedCallback()); +} + +void WindowTreeClientImpl::SetFocus(Id window_id) { + // In order for us to get here we had to have exposed a window, which implies + // we + // got a connection. + DCHECK(tree_); + tree_->SetFocus(window_id); +} + +void WindowTreeClientImpl::SetVisible(Id window_id, bool visible) { + DCHECK(tree_); + tree_->SetViewVisibility(window_id, visible, ActionCompletedCallback()); +} + +void WindowTreeClientImpl::SetProperty(Id window_id, + const std::string& name, + const std::vector<uint8_t>& data) { + DCHECK(tree_); + tree_->SetViewProperty(window_id, mojo::String(name), + mojo::Array<uint8_t>::From(data), + ActionCompletedCallback()); +} + +void WindowTreeClientImpl::SetViewTextInputState( + Id window_id, + mojo::TextInputStatePtr state) { + DCHECK(tree_); + tree_->SetViewTextInputState(window_id, state.Pass()); +} + +void WindowTreeClientImpl::SetImeVisibility(Id window_id, + bool visible, + mojo::TextInputStatePtr state) { + DCHECK(tree_); + tree_->SetImeVisibility(window_id, visible, state.Pass()); +} + +void WindowTreeClientImpl::Embed( + Id window_id, + mojo::ViewTreeClientPtr client, + uint32_t policy_bitmask, + const mojo::ViewTree::EmbedCallback& callback) { + DCHECK(tree_); + tree_->Embed(window_id, client.Pass(), policy_bitmask, callback); +} + +void WindowTreeClientImpl::RequestSurface( + Id window_id, + mojo::InterfaceRequest<mojo::Surface> surface, + mojo::SurfaceClientPtr client) { + DCHECK(tree_); + tree_->RequestSurface(window_id, surface.Pass(), client.Pass()); +} + +void WindowTreeClientImpl::AddWindow(Window* window) { + DCHECK(windows_.find(window->id()) == windows_.end()); + windows_[window->id()] = window; +} + +void WindowTreeClientImpl::RemoveWindow(Id window_id) { + if (focused_window_ && focused_window_->id() == window_id) + OnWindowFocused(0); + + IdToWindowMap::iterator it = windows_.find(window_id); + if (it != windows_.end()) + windows_.erase(it); +} + +void WindowTreeClientImpl::OnRootDestroyed(Window* root) { + DCHECK_EQ(root, root_); + root_ = nullptr; + + // When the root is gone we can't do anything useful. + if (!in_destructor_) + delete this; +} + +//////////////////////////////////////////////////////////////////////////////// +// WindowTreeClientImpl, WindowTreeConnection implementation: + +Id WindowTreeClientImpl::CreateWindowOnServer() { + DCHECK(tree_); + const Id window_id = MakeTransportId(connection_id_, next_id_++); + tree_->CreateView(window_id, [this](mojo::ErrorCode code) { + OnActionCompleted(code == mojo::ERROR_CODE_NONE); + }); + return window_id; +} + +Window* WindowTreeClientImpl::GetRoot() { + return root_; +} + +Window* WindowTreeClientImpl::GetWindowById(Id id) { + IdToWindowMap::const_iterator it = windows_.find(id); + return it != windows_.end() ? it->second : NULL; +} + +Window* WindowTreeClientImpl::GetFocusedWindow() { + return focused_window_; +} + +Window* WindowTreeClientImpl::CreateWindow() { + Window* window = new Window(this, CreateWindowOnServer()); + AddWindow(window); + return window; +} + +bool WindowTreeClientImpl::IsEmbedRoot() { + return is_embed_root_; +} + +ConnectionSpecificId WindowTreeClientImpl::GetConnectionId() { + return connection_id_; +} + +//////////////////////////////////////////////////////////////////////////////// +// WindowTreeClientImpl, ViewTreeClient implementation: + +void WindowTreeClientImpl::OnEmbed(ConnectionSpecificId connection_id, + mojo::ViewDataPtr root_data, + mojo::ViewTreePtr tree, + Id focused_window_id, + uint32 access_policy) { + if (tree) { + DCHECK(!tree_); + tree_ = tree.Pass(); + tree_.set_connection_error_handler([this]() { delete this; }); + } + connection_id_ = connection_id; + is_embed_root_ = + (access_policy & mojo::ViewTree::ACCESS_POLICY_EMBED_ROOT) != 0; + + DCHECK(!root_); + root_ = AddWindowToConnection(this, nullptr, root_data); + + focused_window_ = GetWindowById(focused_window_id); + + delegate_->OnEmbed(root_); +} + +void WindowTreeClientImpl::OnEmbeddedAppDisconnected(Id window_id) { + Window* window = GetWindowById(window_id); + if (window) { + FOR_EACH_OBSERVER(WindowObserver, *WindowPrivate(window).observers(), + OnWindowEmbeddedAppDisconnected(window)); + } +} + +void WindowTreeClientImpl::OnUnembed() { + delegate_->OnUnembed(); + // This will send out the various notifications. + delete this; +} + +void WindowTreeClientImpl::OnWindowBoundsChanged(Id window_id, + mojo::RectPtr old_bounds, + mojo::RectPtr new_bounds) { + Window* window = GetWindowById(window_id); + WindowPrivate(window).LocalSetBounds(*old_bounds, *new_bounds); +} + +namespace { + +void SetViewportMetricsOnDecendants(Window* root, + const mojo::ViewportMetrics& old_metrics, + const mojo::ViewportMetrics& new_metrics) { + WindowPrivate(root).LocalSetViewportMetrics(old_metrics, new_metrics); + const Window::Children& children = root->children(); + for (size_t i = 0; i < children.size(); ++i) + SetViewportMetricsOnDecendants(children[i], old_metrics, new_metrics); +} +} + +void WindowTreeClientImpl::OnWindowViewportMetricsChanged( + mojo::ViewportMetricsPtr old_metrics, + mojo::ViewportMetricsPtr new_metrics) { + Window* window = GetRoot(); + if (window) + SetViewportMetricsOnDecendants(window, *old_metrics, *new_metrics); +} + +void WindowTreeClientImpl::OnWindowHierarchyChanged( + Id window_id, + Id new_parent_id, + Id old_parent_id, + mojo::Array<mojo::ViewDataPtr> windows) { + Window* initial_parent = + windows.size() ? GetWindowById(windows[0]->parent_id) : NULL; + + const bool was_window_known = GetWindowById(window_id) != nullptr; + + BuildWindowTree(this, windows, initial_parent); + + // If the window was not known, then BuildWindowTree() will have created it + // and + // parented the window. + if (!was_window_known) + return; + + Window* new_parent = GetWindowById(new_parent_id); + Window* old_parent = GetWindowById(old_parent_id); + Window* window = GetWindowById(window_id); + if (new_parent) + WindowPrivate(new_parent).LocalAddChild(window); + else + WindowPrivate(old_parent).LocalRemoveChild(window); +} + +void WindowTreeClientImpl::OnWindowReordered(Id window_id, + Id relative_window_id, + mojo::OrderDirection direction) { + Window* window = GetWindowById(window_id); + Window* relative_window = GetWindowById(relative_window_id); + if (window && relative_window) + WindowPrivate(window).LocalReorder(relative_window, direction); +} + +void WindowTreeClientImpl::OnWindowDeleted(Id window_id) { + Window* window = GetWindowById(window_id); + if (window) + WindowPrivate(window).LocalDestroy(); +} + +void WindowTreeClientImpl::OnWindowVisibilityChanged(Id window_id, + bool visible) { + // TODO(sky): there is a race condition here. If this client and another + // client change the visibility at the same time the wrong value may be set. + // Deal with this some how. + Window* window = GetWindowById(window_id); + if (window) + WindowPrivate(window).LocalSetVisible(visible); +} + +void WindowTreeClientImpl::OnWindowDrawnStateChanged(Id window_id, bool drawn) { + Window* window = GetWindowById(window_id); + if (window) + WindowPrivate(window).LocalSetDrawn(drawn); +} + +void WindowTreeClientImpl::OnWindowSharedPropertyChanged( + Id window_id, + const mojo::String& name, + mojo::Array<uint8_t> new_data) { + Window* window = GetWindowById(window_id); + if (window) { + std::vector<uint8_t> data; + std::vector<uint8_t>* data_ptr = NULL; + if (!new_data.is_null()) { + data = new_data.To<std::vector<uint8_t>>(); + data_ptr = &data; + } + + window->SetSharedProperty(name, data_ptr); + } +} + +void WindowTreeClientImpl::OnWindowInputEvent( + Id window_id, + mojo::EventPtr event, + const mojo::Callback<void()>& ack_callback) { + Window* window = GetWindowById(window_id); + if (window) { + FOR_EACH_OBSERVER(WindowObserver, *WindowPrivate(window).observers(), + OnWindowInputEvent(window, event)); + } + ack_callback.Run(); +} + +void WindowTreeClientImpl::OnWindowFocused(Id focused_window_id) { + Window* focused = GetWindowById(focused_window_id); + Window* blurred = focused_window_; + // Update |focused_window_| before calling any of the observers, so that the + // observers get the correct result from calling |Window::HasFocus()|, + // |WindowTreeConnection::GetFocusedWindow()| etc. + focused_window_ = focused; + if (blurred) { + FOR_EACH_OBSERVER(WindowObserver, *WindowPrivate(blurred).observers(), + OnWindowFocusChanged(focused, blurred)); + } + if (focused) { + FOR_EACH_OBSERVER(WindowObserver, *WindowPrivate(focused).observers(), + OnWindowFocusChanged(focused, blurred)); + } +} + +//////////////////////////////////////////////////////////////////////////////// +// WindowTreeClientImpl, private: + +void WindowTreeClientImpl::OnActionCompleted(bool success) { + if (!change_acked_callback_.is_null()) + change_acked_callback_.Run(); +} + +mojo::Callback<void(bool)> WindowTreeClientImpl::ActionCompletedCallback() { + return [this](bool success) { OnActionCompleted(success); }; +} + +} // namespace mus |