blob: b82ed5f42f95cb80463877ae20113787b915b712 [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 "components/mus/connection_manager.h"
#include "base/logging.h"
#include "base/stl_util.h"
#include "cc/output/compositor_frame.h"
#include "cc/quads/shared_quad_state.h"
#include "cc/quads/surface_draw_quad.h"
#include "components/mus/client_connection.h"
#include "components/mus/connection_manager_delegate.h"
#include "components/mus/server_view.h"
#include "components/mus/view_coordinate_conversions.h"
#include "components/mus/view_tree_host_connection.h"
#include "components/mus/view_tree_impl.h"
#include "mojo/application/public/cpp/application_connection.h"
#include "mojo/converters/geometry/geometry_type_converters.h"
#include "mojo/converters/input_events/input_events_type_converters.h"
#include "mojo/converters/surfaces/surfaces_type_converters.h"
#include "ui/gfx/geometry/size_conversions.h"
namespace mus {
ConnectionManager::ScopedChange::ScopedChange(
ViewTreeImpl* connection,
ConnectionManager* connection_manager,
bool is_delete_view)
: connection_manager_(connection_manager),
connection_id_(connection->id()),
is_delete_view_(is_delete_view) {
connection_manager_->PrepareForChange(this);
}
ConnectionManager::ScopedChange::~ScopedChange() {
connection_manager_->FinishChange();
}
ConnectionManager::ConnectionManager(
ConnectionManagerDelegate* delegate,
const scoped_refptr<SurfacesState>& surfaces_state)
: delegate_(delegate),
surfaces_state_(surfaces_state),
next_connection_id_(1),
next_host_id_(0),
current_change_(nullptr),
in_destructor_(false) {}
ConnectionManager::~ConnectionManager() {
in_destructor_ = true;
// Copy the HostConnectionMap because it will be mutated as the connections
// are closed.
HostConnectionMap host_connection_map(host_connection_map_);
for (auto& pair : host_connection_map)
pair.second->CloseConnection();
STLDeleteValues(&connection_map_);
// All the connections should have been destroyed.
DCHECK(host_connection_map_.empty());
DCHECK(connection_map_.empty());
}
void ConnectionManager::AddHost(ViewTreeHostConnection* host_connection) {
DCHECK_EQ(0u, host_connection_map_.count(host_connection->view_tree_host()));
host_connection_map_[host_connection->view_tree_host()] = host_connection;
}
ServerView* ConnectionManager::CreateServerView(const ViewId& id) {
ServerView* view = new ServerView(this, id);
view->AddObserver(this);
return view;
}
ConnectionSpecificId ConnectionManager::GetAndAdvanceNextConnectionId() {
const ConnectionSpecificId id = next_connection_id_++;
DCHECK_LT(id, next_connection_id_);
return id;
}
uint16_t ConnectionManager::GetAndAdvanceNextHostId() {
const uint16_t id = next_host_id_++;
DCHECK_LT(id, next_host_id_);
return id;
}
void ConnectionManager::OnConnectionError(ClientConnection* connection) {
// This will be null if the root has been destroyed.
const ViewId* view_id = connection->service()->root();
ServerView* view =
view_id ? GetView(*connection->service()->root()) : nullptr;
// If the ViewTree root is a viewport root, then we'll wait until
// the root connection goes away to cleanup.
if (view && (GetRootView(view) == view))
return;
scoped_ptr<ClientConnection> connection_owner(connection);
connection_map_.erase(connection->service()->id());
// Notify remaining connections so that they can cleanup.
for (auto& pair : connection_map_) {
pair.second->service()->OnWillDestroyViewTreeImpl(connection->service());
}
}
void ConnectionManager::OnHostConnectionClosed(
ViewTreeHostConnection* connection) {
auto it = host_connection_map_.find(connection->view_tree_host());
DCHECK(it != host_connection_map_.end());
// Get the ClientConnection by ViewTreeImpl ID.
ConnectionMap::iterator service_connection_it =
connection_map_.find(it->first->GetViewTree()->id());
DCHECK(service_connection_it != connection_map_.end());
// Tear down the associated ViewTree connection.
// TODO(fsamuel): I don't think this is quite right, we should tear down all
// connections within the root's viewport. We should probably employ an
// observer pattern to do this. Each ViewTreeImpl should track its
// parent's lifetime.
host_connection_map_.erase(it);
OnConnectionError(service_connection_it->second);
// If we have no more roots left, let the app know so it can terminate.
if (!host_connection_map_.size())
delegate_->OnNoMoreRootConnections();
}
void ConnectionManager::EmbedAtView(ConnectionSpecificId creator_id,
const ViewId& view_id,
uint32_t policy_bitmask,
mojo::URLRequestPtr request) {
mojo::ViewTreePtr service_ptr;
ClientConnection* client_connection =
delegate_->CreateClientConnectionForEmbedAtView(
this, GetProxy(&service_ptr), creator_id, request.Pass(), view_id,
policy_bitmask);
AddConnection(client_connection);
client_connection->service()->Init(client_connection->client(),
service_ptr.Pass());
OnConnectionMessagedClient(client_connection->service()->id());
}
ViewTreeImpl* ConnectionManager::EmbedAtView(ConnectionSpecificId creator_id,
const ViewId& view_id,
uint32_t policy_bitmask,
mojo::ViewTreeClientPtr client) {
mojo::ViewTreePtr service_ptr;
ClientConnection* client_connection =
delegate_->CreateClientConnectionForEmbedAtView(
this, GetProxy(&service_ptr), creator_id, view_id, policy_bitmask,
client.Pass());
AddConnection(client_connection);
client_connection->service()->Init(client_connection->client(),
service_ptr.Pass());
OnConnectionMessagedClient(client_connection->service()->id());
return client_connection->service();
}
ViewTreeImpl* ConnectionManager::GetConnection(
ConnectionSpecificId connection_id) {
ConnectionMap::iterator i = connection_map_.find(connection_id);
return i == connection_map_.end() ? nullptr : i->second->service();
}
ServerView* ConnectionManager::GetView(const ViewId& id) {
for (auto& pair : host_connection_map_) {
if (pair.first->root_view()->id() == id)
return pair.first->root_view();
}
ViewTreeImpl* service = GetConnection(id.connection_id);
return service ? service->GetView(id) : nullptr;
}
bool ConnectionManager::IsViewAttachedToRoot(const ServerView* view) const {
for (auto& pair : host_connection_map_) {
if (pair.first->IsViewAttachedToRoot(view))
return true;
}
return false;
}
void ConnectionManager::SchedulePaint(const ServerView* view,
const gfx::Rect& bounds) {
for (auto& pair : host_connection_map_) {
if (pair.first->SchedulePaintIfInViewport(view, bounds))
return;
}
}
void ConnectionManager::OnConnectionMessagedClient(ConnectionSpecificId id) {
if (current_change_)
current_change_->MarkConnectionAsMessaged(id);
}
bool ConnectionManager::DidConnectionMessageClient(
ConnectionSpecificId id) const {
return current_change_ && current_change_->DidMessageConnection(id);
}
mojo::ViewportMetricsPtr ConnectionManager::GetViewportMetricsForView(
const ServerView* view) {
ViewTreeHostImpl* host = GetViewTreeHostByView(view);
if (host)
return host->GetViewportMetrics().Clone();
if (!host_connection_map_.empty())
return host_connection_map_.begin()->first->GetViewportMetrics().Clone();
mojo::ViewportMetricsPtr metrics = mojo::ViewportMetrics::New();
metrics->size_in_pixels = mojo::Size::New();
return metrics.Pass();
}
const ViewTreeImpl* ConnectionManager::GetConnectionWithRoot(
const ViewId& id) const {
for (auto& pair : connection_map_) {
if (pair.second->service()->IsRoot(id))
return pair.second->service();
}
return nullptr;
}
ViewTreeHostImpl* ConnectionManager::GetViewTreeHostByView(
const ServerView* view) {
return const_cast<ViewTreeHostImpl*>(
static_cast<const ConnectionManager*>(this)->GetViewTreeHostByView(view));
}
const ViewTreeHostImpl* ConnectionManager::GetViewTreeHostByView(
const ServerView* view) const {
while (view && view->parent())
view = view->parent();
for (auto& pair : host_connection_map_) {
if (view == pair.first->root_view())
return pair.first;
}
return nullptr;
}
ViewTreeImpl* ConnectionManager::GetEmbedRoot(ViewTreeImpl* service) {
while (service) {
const ViewId* root_id = service->root();
if (!root_id || root_id->connection_id == service->id())
return nullptr;
ViewTreeImpl* parent_service = GetConnection(root_id->connection_id);
service = parent_service;
if (service && service->is_embed_root())
return service;
}
return nullptr;
}
void ConnectionManager::ProcessViewBoundsChanged(const ServerView* view,
const gfx::Rect& old_bounds,
const gfx::Rect& new_bounds) {
for (auto& pair : connection_map_) {
pair.second->service()->ProcessViewBoundsChanged(
view, old_bounds, new_bounds, IsChangeSource(pair.first));
}
}
void ConnectionManager::ProcessWillChangeViewHierarchy(
const ServerView* view,
const ServerView* new_parent,
const ServerView* old_parent) {
for (auto& pair : connection_map_) {
pair.second->service()->ProcessWillChangeViewHierarchy(
view, new_parent, old_parent, IsChangeSource(pair.first));
}
}
void ConnectionManager::ProcessViewHierarchyChanged(
const ServerView* view,
const ServerView* new_parent,
const ServerView* old_parent) {
for (auto& pair : connection_map_) {
pair.second->service()->ProcessViewHierarchyChanged(
view, new_parent, old_parent, IsChangeSource(pair.first));
}
}
void ConnectionManager::ProcessViewReorder(
const ServerView* view,
const ServerView* relative_view,
const mojo::OrderDirection direction) {
for (auto& pair : connection_map_) {
pair.second->service()->ProcessViewReorder(view, relative_view, direction,
IsChangeSource(pair.first));
}
}
void ConnectionManager::ProcessViewDeleted(const ViewId& view) {
for (auto& pair : connection_map_) {
pair.second->service()->ProcessViewDeleted(view,
IsChangeSource(pair.first));
}
}
void ConnectionManager::ProcessViewportMetricsChanged(
const mojo::ViewportMetrics& old_metrics,
const mojo::ViewportMetrics& new_metrics) {
for (auto& pair : connection_map_) {
pair.second->service()->ProcessViewportMetricsChanged(
old_metrics, new_metrics, IsChangeSource(pair.first));
}
}
void ConnectionManager::PrepareForChange(ScopedChange* change) {
// Should only ever have one change in flight.
CHECK(!current_change_);
current_change_ = change;
}
void ConnectionManager::FinishChange() {
// PrepareForChange/FinishChange should be balanced.
CHECK(current_change_);
current_change_ = NULL;
}
void ConnectionManager::AddConnection(ClientConnection* connection) {
DCHECK_EQ(0u, connection_map_.count(connection->service()->id()));
connection_map_[connection->service()->id()] = connection;
}
scoped_ptr<cc::CompositorFrame>
ConnectionManager::UpdateViewTreeFromCompositorFrame(
const mojo::CompositorFramePtr& input) {
return ConvertToCompositorFrame(input, this);
}
SurfacesState* ConnectionManager::GetSurfacesState() {
return surfaces_state_.get();
}
void ConnectionManager::OnScheduleViewPaint(const ServerView* view) {
if (!in_destructor_)
SchedulePaint(view, gfx::Rect(view->bounds().size()));
}
const ServerView* ConnectionManager::GetRootView(const ServerView* view) const {
const ViewTreeHostImpl* host = GetViewTreeHostByView(view);
return host ? host->root_view() : nullptr;
}
void ConnectionManager::OnViewDestroyed(ServerView* view) {
if (!in_destructor_)
ProcessViewDeleted(view->id());
}
void ConnectionManager::OnWillChangeViewHierarchy(ServerView* view,
ServerView* new_parent,
ServerView* old_parent) {
if (in_destructor_)
return;
ProcessWillChangeViewHierarchy(view, new_parent, old_parent);
}
void ConnectionManager::OnViewHierarchyChanged(ServerView* view,
ServerView* new_parent,
ServerView* old_parent) {
if (in_destructor_)
return;
ProcessViewHierarchyChanged(view, new_parent, old_parent);
// TODO(beng): optimize.
if (old_parent)
SchedulePaint(old_parent, gfx::Rect(old_parent->bounds().size()));
if (new_parent)
SchedulePaint(new_parent, gfx::Rect(new_parent->bounds().size()));
}
void ConnectionManager::OnViewBoundsChanged(ServerView* view,
const gfx::Rect& old_bounds,
const gfx::Rect& new_bounds) {
if (in_destructor_)
return;
ProcessViewBoundsChanged(view, old_bounds, new_bounds);
if (!view->parent())
return;
// TODO(sky): optimize this.
SchedulePaint(view->parent(), old_bounds);
SchedulePaint(view->parent(), new_bounds);
}
void ConnectionManager::OnViewReordered(ServerView* view,
ServerView* relative,
mojo::OrderDirection direction) {
if (!in_destructor_)
SchedulePaint(view, gfx::Rect(view->bounds().size()));
}
void ConnectionManager::OnWillChangeViewVisibility(ServerView* view) {
if (in_destructor_)
return;
// Need to repaint if the view was drawn (which means it's in the process of
// hiding) or the view is transitioning to drawn.
if (view->parent() &&
(view->IsDrawn() || (!view->visible() && view->parent()->IsDrawn()))) {
SchedulePaint(view->parent(), view->bounds());
}
for (auto& pair : connection_map_) {
pair.second->service()->ProcessWillChangeViewVisibility(
view, IsChangeSource(pair.first));
}
}
void ConnectionManager::OnViewSharedPropertyChanged(
ServerView* view,
const std::string& name,
const std::vector<uint8_t>* new_data) {
for (auto& pair : connection_map_) {
pair.second->service()->ProcessViewPropertyChanged(
view, name, new_data, IsChangeSource(pair.first));
}
}
void ConnectionManager::OnViewTextInputStateChanged(
ServerView* view,
const ui::TextInputState& state) {
ViewTreeHostImpl* host = GetViewTreeHostByView(view);
host->UpdateTextInputState(view, state);
}
bool ConnectionManager::ConvertSurfaceDrawQuad(
const mojo::QuadPtr& input,
const mojo::CompositorFrameMetadataPtr& metadata,
cc::SharedQuadState* sqs,
cc::RenderPass* render_pass) {
unsigned int id = static_cast<unsigned int>(
input->surface_quad_state->surface.To<cc::SurfaceId>().id);
// TODO(fsamuel): Security checks:
// 1. We need to make sure the embedder can only position views it's allowed
// to access.
// 2. We need to make sure that the embedder cannot place views in areas
// outside of its own bounds.
ServerView* view = GetView(ViewIdFromTransportId(id));
// If a CompositorFrame message arrives late, say during a navigation, then
// it may contain view IDs that no longer exist.
if (!view || view->surface_id().is_null())
return false;
// TODO(fsamuel): This shouldn't be in the ConnectionManager. Let's find a
// better home for it. http://crbug.com/533029.
cc::SurfaceDrawQuad* surface_quad =
render_pass->CreateAndAppendDrawQuad<cc::SurfaceDrawQuad>();
surface_quad->SetAll(
sqs,
input->rect.To<gfx::Rect>(),
input->opaque_rect.To<gfx::Rect>(),
input->visible_rect.To<gfx::Rect>(),
input->needs_blending,
view->surface_id());
return true;
}
} // namespace mus