mozart: Initial commit of the view manager. The view manager implements the ViewManager interface and maintains the View hierarchy on behalf of applications. Note: This version does not support input yet. Review URL: https://codereview.chromium.org/1415493003 .
diff --git a/services/BUILD.gn b/services/BUILD.gn index 22a7c57..9a19a15 100644 --- a/services/BUILD.gn +++ b/services/BUILD.gn
@@ -49,6 +49,7 @@ "//services/device_info", "//services/files", "//services/kiosk_wm", + "//services/ui", "//services/native_viewport", "//services/surfaces", "//services/url_response_disk_cache",
diff --git a/services/ui/BUILD.gn b/services/ui/BUILD.gn new file mode 100644 index 0000000..bb93fba --- /dev/null +++ b/services/ui/BUILD.gn
@@ -0,0 +1,9 @@ +# Copyright 2015 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. + +group("ui") { + deps = [ + "//services/ui/view_manager", + ] +}
diff --git a/services/ui/README.md b/services/ui/README.md new file mode 100644 index 0000000..cccc9c3 --- /dev/null +++ b/services/ui/README.md
@@ -0,0 +1,4 @@ +# Mozart UI System + +This directory contains Mozart, an implementation of a composable view +management system named after the classical composer.
diff --git a/services/ui/view_manager/BUILD.gn b/services/ui/view_manager/BUILD.gn new file mode 100644 index 0000000..833582b --- /dev/null +++ b/services/ui/view_manager/BUILD.gn
@@ -0,0 +1,45 @@ +# Copyright 2015 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. + +import("//mojo/public/mojo_application.gni") +import("//testing/test.gni") + +mojo_native_application("view_manager") { + output_name = "view_manager_service" + + sources = [ + "main.cc", + "surface_manager.cc", + "surface_manager.h", + "view_host_impl.cc", + "view_host_impl.h", + "view_layout_request.cc", + "view_layout_request.h", + "view_manager_app.cc", + "view_manager_app.h", + "view_manager_impl.cc", + "view_manager_impl.h", + "view_registry.cc", + "view_registry.h", + "view_state.cc", + "view_state.h", + "view_tree_host_impl.cc", + "view_tree_host_impl.h", + "view_tree_state.cc", + "view_tree_state.h", + ] + + deps = [ + "//base", + "//mojo/application", + "//mojo/common", + "//mojo/common:tracing_impl", + "//mojo/environment:chromium", + "//mojo/converters/geometry", + "//mojo/public/cpp/bindings:bindings", + "//mojo/services/surfaces/cpp", + "//mojo/services/surfaces/interfaces", + "//mojo/services/ui/views/interfaces", + ] +}
diff --git a/services/ui/view_manager/README.md b/services/ui/view_manager/README.md new file mode 100644 index 0000000..c9109a9 --- /dev/null +++ b/services/ui/view_manager/README.md
@@ -0,0 +1,10 @@ +# Mozart View Manager + +This directory contains an implementation of the ViewManager interface. +It provides a composable view management system for used by other +applications. + +It doesn't make sense to run this application stand-alone since it +doesn't have any UI of its own to display. Instead, use the Mozart +Launcher or some other application to launch and embed the UI of some +other application using the view manager.
diff --git a/services/ui/view_manager/main.cc b/services/ui/view_manager/main.cc new file mode 100644 index 0000000..dbfdade --- /dev/null +++ b/services/ui/view_manager/main.cc
@@ -0,0 +1,12 @@ +// Copyright 2015 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 "mojo/application/application_runner_chromium.h" +#include "mojo/public/c/system/main.h" +#include "services/ui/view_manager/view_manager_app.h" + +MojoResult MojoMain(MojoHandle application_request) { + mojo::ApplicationRunnerChromium runner(new view_manager::ViewManagerApp); + return runner.Run(application_request); +}
diff --git a/services/ui/view_manager/surface_manager.cc b/services/ui/view_manager/surface_manager.cc new file mode 100644 index 0000000..24ec899 --- /dev/null +++ b/services/ui/view_manager/surface_manager.cc
@@ -0,0 +1,28 @@ +// Copyright 2015 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 "base/bind.h" +#include "base/bind_helpers.h" +#include "mojo/services/surfaces/cpp/surfaces_utils.h" +#include "mojo/services/surfaces/interfaces/quads.mojom.h" +#include "mojo/services/surfaces/interfaces/surfaces.mojom.h" +#include "services/ui/view_manager/surface_manager.h" + +namespace view_manager { + +SurfaceManager::SurfaceManager(mojo::SurfacePtr surfaces) + : surfaces_(surfaces.Pass()), surface_namespace_(0u) {} + +SurfaceManager::~SurfaceManager() {} + +mojo::SurfaceIdPtr SurfaceManager::CreateWrappedSurface( + mojo::SurfaceId* inner_surface_id) { + return inner_surface_id->Clone(); +} + +void SurfaceManager::DestroySurface(mojo::SurfaceIdPtr surface_id) { + // surfaces_->DestroySurface(surface_id->local); +} + +} // namespace view_manager
diff --git a/services/ui/view_manager/surface_manager.h b/services/ui/view_manager/surface_manager.h new file mode 100644 index 0000000..d5d2068 --- /dev/null +++ b/services/ui/view_manager/surface_manager.h
@@ -0,0 +1,34 @@ +// Copyright 2015 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. + +#ifndef SERVICES_UI_VIEW_MANAGER_SURFACE_MANAGER_H_ +#define SERVICES_UI_VIEW_MANAGER_SURFACE_MANAGER_H_ + +#include "base/macros.h" +#include "mojo/services/surfaces/interfaces/surfaces.mojom.h" + +namespace view_manager { + +// Manages surfaces on behalf of the view manager. +class SurfaceManager { + public: + explicit SurfaceManager(mojo::SurfacePtr surfaces); + ~SurfaceManager(); + + mojo::SurfaceIdPtr CreateWrappedSurface(mojo::SurfaceId* inner_surface_id); + + void DestroySurface(mojo::SurfaceIdPtr surface_id); + + private: + void OnSurfaceIdNamespaceAvailable(uint32_t id_namespace); + + mojo::SurfacePtr surfaces_; + uint32_t surface_namespace_; + + DISALLOW_COPY_AND_ASSIGN(SurfaceManager); +}; + +} // namespace view_manager + +#endif // SERVICES_UI_VIEW_MANAGER_SURFACE_MANAGER_H_
diff --git a/services/ui/view_manager/view_host_impl.cc b/services/ui/view_manager/view_host_impl.cc new file mode 100644 index 0000000..fef8f0b --- /dev/null +++ b/services/ui/view_manager/view_host_impl.cc
@@ -0,0 +1,53 @@ +// Copyright 2015 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 "base/bind.h" +#include "base/bind_helpers.h" +#include "services/ui/view_manager/view_host_impl.h" + +namespace view_manager { + +ViewHostImpl::ViewHostImpl( + ViewRegistry* registry, + ViewState* state, + mojo::InterfaceRequest<mojo::ui::ViewHost> view_host_request) + : registry_(registry), + state_(state), + binding_(this, view_host_request.Pass()) {} + +ViewHostImpl::~ViewHostImpl() {} + +void ViewHostImpl::GetServiceProvider( + mojo::InterfaceRequest<mojo::ServiceProvider> service_provider) { + state_->GetServiceProvider(service_provider.Pass()); +} + +void ViewHostImpl::RequestLayout() { + registry_->RequestLayout(state_); +} + +void ViewHostImpl::AddChild(uint32_t child_key, + mojo::ui::ViewTokenPtr child_view_token) { + registry_->AddChild(state_, child_key, child_view_token.Pass()); +} + +void ViewHostImpl::RemoveChild(uint32_t child_key) { + registry_->RemoveChild(state_, child_key); +} + +static void RunLayoutChildCallback( + const ViewHostImpl::LayoutChildCallback& callback, + mojo::ui::ViewLayoutInfoPtr info) { + callback.Run(info.Pass()); +} + +void ViewHostImpl::LayoutChild( + uint32_t child_key, + mojo::ui::ViewLayoutParamsPtr child_layout_params, + const LayoutChildCallback& callback) { + registry_->LayoutChild(state_, child_key, child_layout_params.Pass(), + base::Bind(&RunLayoutChildCallback, callback)); +} + +} // namespace view_manager
diff --git a/services/ui/view_manager/view_host_impl.h b/services/ui/view_manager/view_host_impl.h new file mode 100644 index 0000000..274d1f1 --- /dev/null +++ b/services/ui/view_manager/view_host_impl.h
@@ -0,0 +1,50 @@ +// Copyright 2015 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. + +#ifndef SERVICES_UI_VIEW_MANAGER_VIEW_HOST_IMPL_H_ +#define SERVICES_UI_VIEW_MANAGER_VIEW_HOST_IMPL_H_ + +#include "base/macros.h" +#include "mojo/public/cpp/bindings/binding.h" +#include "mojo/services/ui/views/interfaces/views.mojom.h" +#include "services/ui/view_manager/view_registry.h" +#include "services/ui/view_manager/view_state.h" + +namespace view_manager { + +// ViewHost interface implementation. +// This object is owned by its associated ViewState. +class ViewHostImpl : public mojo::ui::ViewHost { + public: + ViewHostImpl(ViewRegistry* registry, + ViewState* state, + mojo::InterfaceRequest<mojo::ui::ViewHost> view_host_request); + ~ViewHostImpl() override; + + void set_view_host_connection_error_handler(const base::Closure& handler) { + binding_.set_connection_error_handler(handler); + } + + private: + // |ViewHost|: + void GetServiceProvider( + mojo::InterfaceRequest<mojo::ServiceProvider> service_provider) override; + void RequestLayout() override; + void AddChild(uint32_t child_key, + mojo::ui::ViewTokenPtr child_view_token) override; + void RemoveChild(uint32_t child_key) override; + void LayoutChild(uint32_t child_key, + mojo::ui::ViewLayoutParamsPtr child_layout_params, + const LayoutChildCallback& callback) override; + + ViewRegistry* const registry_; + ViewState* const state_; + mojo::Binding<mojo::ui::ViewHost> binding_; + + DISALLOW_COPY_AND_ASSIGN(ViewHostImpl); +}; + +} // namespace view_manager + +#endif // SERVICES_UI_VIEW_MANAGER_VIEW_HOST_IMPL_H_
diff --git a/services/ui/view_manager/view_layout_request.cc b/services/ui/view_manager/view_layout_request.cc new file mode 100644 index 0000000..cbec836 --- /dev/null +++ b/services/ui/view_manager/view_layout_request.cc
@@ -0,0 +1,33 @@ +// Copyright 2015 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 "base/logging.h" +#include "services/ui/view_manager/view_layout_request.h" + +namespace view_manager { + +ViewLayoutRequest::ViewLayoutRequest( + mojo::ui::ViewLayoutParamsPtr layout_params) + : layout_params_(layout_params.Pass()), + was_dispatched_(false), + issued_(false) {} + +ViewLayoutRequest::~ViewLayoutRequest() { + if (!was_dispatched_) + DispatchLayoutInfo(nullptr); +} + +void ViewLayoutRequest::AddCallback(const ViewLayoutCallback& callback) { + DCHECK(!was_dispatched_); + callbacks_.emplace_back(callback); +} + +void ViewLayoutRequest::DispatchLayoutInfo(mojo::ui::ViewLayoutInfo* info) { + DCHECK(!was_dispatched_); + was_dispatched_ = true; + for (const auto& callback : callbacks_) + callback.Run(info ? info->Clone() : nullptr); +} + +} // namespace view_manager
diff --git a/services/ui/view_manager/view_layout_request.h b/services/ui/view_manager/view_layout_request.h new file mode 100644 index 0000000..8091d2f --- /dev/null +++ b/services/ui/view_manager/view_layout_request.h
@@ -0,0 +1,65 @@ +// Copyright 2015 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. + +#ifndef SERVICES_UI_VIEW_MANAGER_VIEW_LAYOUT_REQUEST_H_ +#define SERVICES_UI_VIEW_MANAGER_VIEW_LAYOUT_REQUEST_H_ + +#include <memory> +#include <vector> + +#include "base/callback.h" +#include "base/macros.h" +#include "mojo/services/ui/views/interfaces/layouts.mojom.h" + +namespace view_manager { + +using ViewLayoutCallback = base::Callback<void(mojo::ui::ViewLayoutInfoPtr)>; + +// Describes a pending layout request for a view. +class ViewLayoutRequest { + public: + explicit ViewLayoutRequest(mojo::ui::ViewLayoutParamsPtr layout_params); + + // Dispatches null layout info automatically if DispatchLayoutInfo was not + // called. + ~ViewLayoutRequest(); + + // Gets the layout parameters for this request. + // Does not confer ownership. + mojo::ui::ViewLayoutParams* layout_params() { return layout_params_.get(); } + + // Gets the layout parameters for this request and takes ownership. + mojo::ui::ViewLayoutParamsPtr TakeLayoutParams() { + return layout_params_.Pass(); + } + + // Adds a callback to this layout request. + // Must be called before dispatching. + void AddCallback(const ViewLayoutCallback& callback); + + // Returns true if the request has callbacks. + bool has_callbacks() { return !callbacks_.empty(); } + + // Sends the layout information to each client. + // Must be invoked exactly once before destroying the request to prevent + // dangling callbacks. + void DispatchLayoutInfo(mojo::ui::ViewLayoutInfo* info); + + // True if the request has been issued to the view. + // False if it is still pending in the queue. + bool issued() const { return issued_; } + void set_issued(bool value) { issued_ = value; } + + private: + mojo::ui::ViewLayoutParamsPtr layout_params_; + std::vector<ViewLayoutCallback> callbacks_; + bool was_dispatched_; + bool issued_; + + DISALLOW_COPY_AND_ASSIGN(ViewLayoutRequest); +}; + +} // namespace view_manager + +#endif // SERVICES_UI_VIEW_MANAGER_VIEW_LAYOUT_REQUEST_H_
diff --git a/services/ui/view_manager/view_manager_app.cc b/services/ui/view_manager/view_manager_app.cc new file mode 100644 index 0000000..2190590 --- /dev/null +++ b/services/ui/view_manager/view_manager_app.cc
@@ -0,0 +1,44 @@ +// Copyright 2015 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 "base/trace_event/trace_event.h" +#include "mojo/application/application_runner_chromium.h" +#include "mojo/common/tracing_impl.h" +#include "mojo/public/c/system/main.h" +#include "mojo/public/cpp/application/application_connection.h" +#include "mojo/public/cpp/application/application_impl.h" +#include "services/ui/view_manager/view_manager_app.h" +#include "services/ui/view_manager/view_manager_impl.h" + +namespace view_manager { + +ViewManagerApp::ViewManagerApp() : app_impl_(nullptr) {} + +ViewManagerApp::~ViewManagerApp() {} + +void ViewManagerApp::Initialize(mojo::ApplicationImpl* app_impl) { + app_impl_ = app_impl; + tracing_.Initialize(app_impl); + + mojo::SurfacePtr surfaces; + app_impl->ConnectToService("mojo:surfaces_service", &surfaces); + surface_manager_.reset(new SurfaceManager(surfaces.Pass())); + + registry_.reset(new ViewRegistry(surface_manager_.get())); +} + +bool ViewManagerApp::ConfigureIncomingConnection( + mojo::ApplicationConnection* connection) { + connection->AddService<mojo::ui::ViewManager>(this); + return true; +} + +void ViewManagerApp::Create( + mojo::ApplicationConnection* connection, + mojo::InterfaceRequest<mojo::ui::ViewManager> request) { + view_managers.AddBinding(new ViewManagerImpl(registry_.get()), + request.Pass()); +} + +} // namespace view_manager
diff --git a/services/ui/view_manager/view_manager_app.h b/services/ui/view_manager/view_manager_app.h new file mode 100644 index 0000000..ca37dea --- /dev/null +++ b/services/ui/view_manager/view_manager_app.h
@@ -0,0 +1,50 @@ +// Copyright 2015 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. + +#ifndef SERVICES_UI_VIEW_MANAGER_VIEW_MANAGER_APP_H_ +#define SERVICES_UI_VIEW_MANAGER_VIEW_MANAGER_APP_H_ + +#include <memory> + +#include "base/macros.h" +#include "base/memory/scoped_ptr.h" +#include "mojo/common/strong_binding_set.h" +#include "mojo/common/tracing_impl.h" +#include "mojo/public/cpp/application/application_delegate.h" +#include "mojo/services/ui/views/interfaces/view_manager.mojom.h" +#include "services/ui/view_manager/surface_manager.h" +#include "services/ui/view_manager/view_registry.h" + +namespace view_manager { + +// View manager application entry point. +class ViewManagerApp : public mojo::ApplicationDelegate, + public mojo::InterfaceFactory<mojo::ui::ViewManager> { + public: + ViewManagerApp(); + ~ViewManagerApp() override; + + private: + // |ApplicationDelegate|: + void Initialize(mojo::ApplicationImpl* app_impl) override; + bool ConfigureIncomingConnection( + mojo::ApplicationConnection* connection) override; + + // |InterfaceFactory<ViewManager>|: + void Create(mojo::ApplicationConnection* connection, + mojo::InterfaceRequest<mojo::ui::ViewManager> request) override; + + mojo::ApplicationImpl* app_impl_; + mojo::TracingImpl tracing_; + + mojo::StrongBindingSet<mojo::ui::ViewManager> view_managers; + std::unique_ptr<ViewRegistry> registry_; + std::unique_ptr<SurfaceManager> surface_manager_; // must come after registry + + DISALLOW_COPY_AND_ASSIGN(ViewManagerApp); +}; + +} // namespace view_manager + +#endif // SERVICES_UI_VIEW_MANAGER_VIEW_MANAGER_APP_H_
diff --git a/services/ui/view_manager/view_manager_impl.cc b/services/ui/view_manager/view_manager_impl.cc new file mode 100644 index 0000000..38cf975 --- /dev/null +++ b/services/ui/view_manager/view_manager_impl.cc
@@ -0,0 +1,33 @@ +// Copyright 2015 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/view_manager/view_host_impl.h" +#include "services/ui/view_manager/view_manager_impl.h" +#include "services/ui/view_manager/view_tree_host_impl.h" + +namespace view_manager { + +ViewManagerImpl::ViewManagerImpl(ViewRegistry* registry) + : registry_(registry) {} + +ViewManagerImpl::~ViewManagerImpl() {} + +void ViewManagerImpl::RegisterView( + mojo::ui::ViewPtr view, + mojo::InterfaceRequest<mojo::ui::ViewHost> view_host_request, + const RegisterViewCallback& callback) { + mojo::ui::ViewTokenPtr view_token = + registry_->RegisterView(view.Pass(), view_host_request.Pass()); + callback.Run(view_token.Pass()); +} + +void ViewManagerImpl::RegisterViewTree( + mojo::ui::ViewTreePtr view_tree, + mojo::InterfaceRequest<mojo::ui::ViewTreeHost> view_tree_host_request, + const RegisterViewTreeCallback& callback) { + registry_->RegisterViewTree(view_tree.Pass(), view_tree_host_request.Pass()); + callback.Run(); +} + +} // namespace view_manager
diff --git a/services/ui/view_manager/view_manager_impl.h b/services/ui/view_manager/view_manager_impl.h new file mode 100644 index 0000000..db84e4f --- /dev/null +++ b/services/ui/view_manager/view_manager_impl.h
@@ -0,0 +1,39 @@ +// Copyright 2015 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. + +#ifndef SERVICES_UI_VIEW_MANAGER_VIEW_MANAGER_IMPL_H_ +#define SERVICES_UI_VIEW_MANAGER_VIEW_MANAGER_IMPL_H_ + +#include "base/macros.h" +#include "mojo/common/strong_binding_set.h" +#include "mojo/services/ui/views/interfaces/view_manager.mojom.h" +#include "services/ui/view_manager/view_registry.h" + +namespace view_manager { + +// ViewManager interface implementation. +class ViewManagerImpl : public mojo::ui::ViewManager { + public: + explicit ViewManagerImpl(ViewRegistry* registry); + ~ViewManagerImpl() override; + + private: + // |ViewManager|: + void RegisterView( + mojo::ui::ViewPtr view, + mojo::InterfaceRequest<mojo::ui::ViewHost> view_host_request, + const RegisterViewCallback& callback) override; + void RegisterViewTree( + mojo::ui::ViewTreePtr view_tree, + mojo::InterfaceRequest<mojo::ui::ViewTreeHost> view_tree_host_request, + const RegisterViewTreeCallback& callback) override; + + ViewRegistry* registry_; + + DISALLOW_COPY_AND_ASSIGN(ViewManagerImpl); +}; + +} // namespace view_manager + +#endif // SERVICES_UI_VIEW_MANAGER_VIEW_MANAGER_IMPL_H_
diff --git a/services/ui/view_manager/view_registry.cc b/services/ui/view_manager/view_registry.cc new file mode 100644 index 0000000..182ae3d --- /dev/null +++ b/services/ui/view_manager/view_registry.cc
@@ -0,0 +1,725 @@ +// Copyright 2015 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 <algorithm> +#include <utility> + +#include "base/bind.h" +#include "base/bind_helpers.h" +#include "services/ui/view_manager/surface_manager.h" +#include "services/ui/view_manager/view_host_impl.h" +#include "services/ui/view_manager/view_registry.h" +#include "services/ui/view_manager/view_tree_host_impl.h" + +namespace view_manager { + +static bool AreViewLayoutParamsValid(mojo::ui::ViewLayoutParams* params) { + return params && params->constraints && params->constraints->min_width >= 0 && + params->constraints->max_width >= params->constraints->min_width && + params->constraints->min_height >= 0 && + params->constraints->max_height >= params->constraints->min_height && + params->device_pixel_ratio > 0; +} + +static std::ostream& operator<<(std::ostream& os, mojo::Size* size) { + return size + ? os << "{width=" << size->width << ", height=" << size->height + << "}" + : os << "{null}"; +} + +static std::ostream& operator<<(std::ostream& os, mojo::SurfaceId* surface_id) { + return surface_id + ? os << "{id_namespace=" << surface_id->id_namespace + << ", local=" << surface_id->local << "}" + : os << "{null}"; +} + +static std::ostream& operator<<(std::ostream& os, mojo::ui::ViewToken* token) { + return token ? os << "{token=" << token->value << "}" : os << "{null}"; +} + +static std::ostream& operator<<(std::ostream& os, ViewState* view_state) { + return view_state ? os << "{token=" << view_state->view_token_value() << "}" + : os << "{null}"; +} + +static std::ostream& operator<<(std::ostream& os, + mojo::ui::BoxConstraints* constraints) { + return constraints + ? os << "{min_width=" << constraints->min_width + << ", max_width=" << constraints->max_width + << ", min_height=" << constraints->min_height + << ", max_height=" << constraints->max_height << "}" + : os << "{null}"; +}; + +static std::ostream& operator<<(std::ostream& os, + mojo::ui::ViewLayoutParams* params) { + return params + ? os << "{constraints=" << params->constraints.get() + << ", device_pixel_ratio=" << params->device_pixel_ratio + << "}" + : os << "{null}"; +} + +static std::ostream& operator<<(std::ostream& os, + mojo::ui::ViewLayoutInfo* info) { + return info + ? os << "{size=" << info->size.get() + << ", surface_id=" << info->surface_id.get() << "}" + : os << "{null}"; +} + +ViewRegistry::ViewRegistry(SurfaceManager* surface_manager) + : surface_manager_(surface_manager), next_view_token_value_(1u) {} + +ViewRegistry::~ViewRegistry() {} + +mojo::ui::ViewTokenPtr ViewRegistry::RegisterView( + mojo::ui::ViewPtr view, + mojo::InterfaceRequest<mojo::ui::ViewHost> view_host_request) { + DCHECK(view); + uint32_t view_token_value = next_view_token_value_++; + DCHECK(!FindView(view_token_value)); + + // Create the state and bind host to it. + ViewState* view_state = new ViewState(view.Pass(), view_token_value); + ViewHostImpl* view_host = + new ViewHostImpl(this, view_state, view_host_request.Pass()); + view_state->set_view_host(view_host); + view_state->set_view_connection_error_handler( + base::Bind(&ViewRegistry::OnViewConnectionError, base::Unretained(this), + view_state)); + view_host->set_view_host_connection_error_handler( + base::Bind(&ViewRegistry::OnViewConnectionError, base::Unretained(this), + view_state)); + + // Add to registry and return token. + views_by_token_.insert({view_token_value, view_state}); + mojo::ui::ViewTokenPtr token = mojo::ui::ViewToken::New(); + token->value = view_state->view_token_value(); + DVLOG(1) << "RegisterView: view=" << view_state; + return token; +} + +void ViewRegistry::OnViewConnectionError(ViewState* view_state) { + DCHECK(IsViewStateRegisteredDebug(view_state)); + DVLOG(1) << "OnViewConnectionError: view=" << view_state; + + UnregisterView(view_state); +} + +void ViewRegistry::UnregisterView(ViewState* view_state) { + DCHECK(IsViewStateRegisteredDebug(view_state)); + DVLOG(1) << "UnregisterView: view=" << view_state; + + // Remove from parent or roots. + HijackView(view_state); + + // Remove from registry. + views_by_token_.erase(view_state->view_token_value()); + delete view_state; +} + +void ViewRegistry::RegisterViewTree( + mojo::ui::ViewTreePtr view_tree, + mojo::InterfaceRequest<mojo::ui::ViewTreeHost> view_tree_host_request) { + DCHECK(view_tree); + + // Create the state and bind host to it. + ViewTreeState* tree_state = new ViewTreeState(view_tree.Pass()); + ViewTreeHostImpl* tree_host = + new ViewTreeHostImpl(this, tree_state, view_tree_host_request.Pass()); + tree_state->set_view_tree_host(tree_host); + tree_state->set_view_tree_connection_error_handler( + base::Bind(&ViewRegistry::OnViewTreeConnectionError, + base::Unretained(this), tree_state)); + tree_host->set_view_tree_host_connection_error_handler( + base::Bind(&ViewRegistry::OnViewTreeConnectionError, + base::Unretained(this), tree_state)); + + // Add to registry. + view_trees_.push_back(tree_state); + DVLOG(1) << "RegisterViewTree: tree=" << tree_state; +} + +void ViewRegistry::OnViewTreeConnectionError(ViewTreeState* tree_state) { + DCHECK(IsViewTreeStateRegisteredDebug(tree_state)); + DVLOG(1) << "OnViewTreeConnectionError: tree=" << tree_state; + + UnregisterViewTree(tree_state); +} + +void ViewRegistry::UnregisterViewTree(ViewTreeState* tree_state) { + DCHECK(IsViewTreeStateRegisteredDebug(tree_state)); + DVLOG(1) << "UnregisterViewTree: tree=" << tree_state; + + // Unlink the root if needed. + if (tree_state->root()) + UnlinkRoot(tree_state); + + // Remove from registry. + view_trees_.erase(std::find_if( + view_trees_.begin(), view_trees_.end(), + [tree_state](ViewTreeState* other) { return tree_state == other; })); + delete tree_state; +} + +void ViewRegistry::RequestLayout(ViewState* view_state) { + DCHECK(IsViewStateRegisteredDebug(view_state)); + DVLOG(1) << "RequestLayout: view=" << view_state; + + InvalidateLayout(view_state); +} + +void ViewRegistry::AddChild(ViewState* parent_state, + uint32_t child_key, + mojo::ui::ViewTokenPtr child_view_token) { + DCHECK(IsViewStateRegisteredDebug(parent_state)); + DCHECK(child_view_token); + DVLOG(1) << "AddChild: parent=" << parent_state << ", child_key=" << child_key + << ", child=" << child_view_token.get(); + + // Check for duplicate children. + if (parent_state->children().find(child_key) != + parent_state->children().end()) { + LOG(ERROR) << "View attempted to add a child with a duplicate key: " + << "parent=" << parent_state << ", child_key=" << child_key + << ", child=" << child_view_token.get(); + UnregisterView(parent_state); + return; + } + + // Check whether the desired child view still exists. + // Adding a non-existent child still succeeds but the view manager will + // immediately report it as being unavailable. + ViewState* child_state = FindView(child_view_token->value); + if (!child_state) { + LinkChildAsUnavailable(parent_state, child_key); + return; + } + + // Check whether the child needs to be reparented. + // The old parent will receive an unavailable event. For interface symmetry, + // we deliberately do this even if the old and new parents are the same. + HijackView(child_state); + + // Link the child into its new parent. + LinkChild(parent_state, child_key, child_state); +} + +void ViewRegistry::RemoveChild(ViewState* parent_state, uint32_t child_key) { + DCHECK(IsViewStateRegisteredDebug(parent_state)); + DVLOG(1) << "RemoveChild: parent=" << parent_state + << ", child_key=" << child_key; + + // Check whether the child key exists in the parent. + auto child_it = parent_state->children().find(child_key); + if (child_it == parent_state->children().end()) { + LOG(ERROR) << "View attempted to remove a child with an invalid key: " + << "parent=" << parent_state << ", child_key=" << child_key; + UnregisterView(parent_state); + return; + } + + // Unlink the child from its parent. + UnlinkChild(parent_state, child_it); +} + +void ViewRegistry::LayoutChild( + ViewState* parent_state, + uint32_t child_key, + mojo::ui::ViewLayoutParamsPtr child_layout_params, + const ViewLayoutCallback& callback) { + DCHECK(IsViewStateRegisteredDebug(parent_state)); + DCHECK(child_layout_params); + DCHECK(child_layout_params->constraints); + DVLOG(1) << "LayoutChild: parent=" << parent_state + << ", child_key=" << child_key + << ", child_layout_params=" << child_layout_params.get(); + + // Check whether the layout parameters are well-formed. + if (!AreViewLayoutParamsValid(child_layout_params.get())) { + LOG(ERROR) << "View provided invalid child layout parameters: " + << "parent=" << parent_state << ", child_key=" << child_key + << ", child_layout_params=" << child_layout_params; + UnregisterView(parent_state); + callback.Run(nullptr); + return; + } + + // Check whether the child key exists in the parent. + auto child_it = parent_state->children().find(child_key); + if (child_it == parent_state->children().end()) { + LOG(ERROR) << "View attempted to layout a child with an invalid key: " + << "parent=" << parent_state << ", child_key=" << child_key + << ", child_layout_params=" << child_layout_params; + UnregisterView(parent_state); + callback.Run(nullptr); + return; + } + + SetLayout(child_it->second, child_layout_params.Pass(), callback); +} + +void ViewRegistry::RequestLayout(ViewTreeState* tree_state) { + DCHECK(IsViewTreeStateRegisteredDebug(tree_state)); + DVLOG(1) << "RequestLayout: tree=" << tree_state; + + InvalidateLayoutForRoot(tree_state); +} + +void ViewRegistry::SetRoot(ViewTreeState* tree_state, + uint32_t root_key, + mojo::ui::ViewTokenPtr root_view_token) { + DCHECK(IsViewTreeStateRegisteredDebug(tree_state)); + DCHECK(root_view_token); + DVLOG(1) << "SetRoot: tree=" << tree_state << ", root_key=" << root_key + << ", root=" << root_view_token.get(); + + // Check whether the desired root view still exists. + // Using a non-existent root view still succeeds but the view manager will + // immediately report it as being unavailable. + ViewState* root_state = FindView(root_view_token->value); + if (root_state) { + HijackView(root_state); + LinkRoot(tree_state, root_state, root_key); + } else { + SendRootUnavailable(tree_state, root_key); + } + tree_state->set_explicit_root(true); +} + +void ViewRegistry::ResetRoot(ViewTreeState* tree_state) { + DCHECK(IsViewTreeStateRegisteredDebug(tree_state)); + DVLOG(1) << "ResetRoot: tree=" << tree_state; + + if (tree_state->root()) + UnlinkRoot(tree_state); + tree_state->set_explicit_root(false); +} + +void ViewRegistry::LayoutRoot(ViewTreeState* tree_state, + mojo::ui::ViewLayoutParamsPtr root_layout_params, + const ViewLayoutCallback& callback) { + DCHECK(IsViewTreeStateRegisteredDebug(tree_state)); + DCHECK(root_layout_params); + DCHECK(root_layout_params->constraints); + DVLOG(1) << "LayoutRoot: tree=" << tree_state + << ", root_layout_params=" << root_layout_params.get(); + + // Check whether the layout parameters are well-formed. + if (!AreViewLayoutParamsValid(root_layout_params.get())) { + LOG(ERROR) << "View tree provided invalid root layout parameters: " + << "tree=" << tree_state + << ", root_layout_params=" << root_layout_params; + UnregisterViewTree(tree_state); + callback.Run(nullptr); + return; + } + + // Check whether the client called LayoutRoot without first having actually + // set a root. + if (!tree_state->explicit_root()) { + LOG(ERROR) << "View tree attempted to layout the rout without having " + "set one first: tree=" + << tree_state << ", root_layout_params=" << root_layout_params; + UnregisterViewTree(tree_state); + callback.Run(nullptr); + return; + } + + // Check whether the root is unavailable and therefore cannot be laid out. + // This is not an error. + if (!tree_state->root()) { + callback.Run(nullptr); + return; + } + + SetLayout(tree_state->root(), root_layout_params.Pass(), callback); +} + +ViewState* ViewRegistry::FindView(uint32_t view_token) { + auto it = views_by_token_.find(view_token); + return it != views_by_token_.end() ? it->second : nullptr; +} + +void ViewRegistry::LinkChild(ViewState* parent_state, + uint32_t child_key, + ViewState* child_state) { + DCHECK(IsViewStateRegisteredDebug(parent_state)); + DCHECK(parent_state->children().find(child_key) == + parent_state->children().end()); + DCHECK(IsViewStateRegisteredDebug(child_state)); + + DVLOG(2) << "Added child " << child_key << " {" + << child_state->view_token_value() << "} to parent {" + << parent_state->view_token_value() << "}"; + + parent_state->children().insert({child_key, child_state}); + child_state->SetParent(parent_state, child_key); + + // Schedule layout of the parent on behalf of its newly added child. + // We don't need to schedule layout of the child until the parent provides + // new layout parameters. + InvalidateLayoutForChild(parent_state, child_key); +} + +void ViewRegistry::LinkChildAsUnavailable(ViewState* parent_state, + uint32_t child_key) { + DCHECK(IsViewStateRegisteredDebug(parent_state)); + DCHECK(parent_state->children().find(child_key) == + parent_state->children().end()); + + DVLOG(2) << "Added unavailable child " << child_key << " to parent {" + << parent_state->view_token_value() << "}"; + + parent_state->children().insert({child_key, nullptr}); + SendChildUnavailable(parent_state, child_key); + + // Don't schedule layout for the parent just yet. Wait for it to + // remove its child in response to the OnChildUnavailable notification. +} + +void ViewRegistry::MarkChildAsUnavailable(ViewState* parent_state, + uint32_t child_key) { + DCHECK(IsViewStateRegisteredDebug(parent_state)); + auto child_it = parent_state->children().find(child_key); + DCHECK(child_it != parent_state->children().end()); + DCHECK(child_it->second); + + DVLOG(2) << "Marked unavailable child " << child_key << " {" + << child_it->second->view_token_value() << "} from parent {" + << parent_state->view_token_value() << "}"; + + ResetStateWhenUnlinking(child_it->second); + child_it->second->ResetContainer(); + child_it->second = nullptr; + SendChildUnavailable(parent_state, child_key); + + // Don't schedule layout for the parent just yet. Wait for it to + // remove its child in response to the OnChildUnavailable notification. + // We don't need to schedule layout for the child either since it will + // retain its old layout parameters. +} + +void ViewRegistry::UnlinkChild(ViewState* parent_state, + ViewState::ChildrenMap::iterator child_it) { + DCHECK(IsViewStateRegisteredDebug(parent_state)); + DCHECK(child_it != parent_state->children().end()); + + ViewState* child_state = child_it->second; + if (child_state) { + DVLOG(2) << "Removed child " << child_state->key() << " {" + << child_state->view_token_value() << "} from parent {" + << parent_state->view_token_value() << "}"; + ResetStateWhenUnlinking(child_it->second); + child_state->ResetContainer(); + } else { + DVLOG(2) << "Removed unavailable child " << child_it->first + << "} from parent {" << parent_state->view_token_value() << "}"; + } + parent_state->children().erase(child_it); + + // Schedule layout for the parent now that it has lost its child. + // We don't need to schedule layout for the child itself since it will + // retain its old layout parameters. + InvalidateLayout(parent_state); +} + +void ViewRegistry::LinkRoot(ViewTreeState* tree_state, + ViewState* root_state, + uint32_t root_key) { + DCHECK(IsViewTreeStateRegisteredDebug(tree_state)); + DCHECK(IsViewStateRegisteredDebug(root_state)); + DCHECK(!tree_state->root()); + DCHECK(!root_state->parent()); + + DVLOG(2) << "Linked view tree root " << root_key << " {" + << root_state->view_token_value() << "}"; + + tree_state->SetRoot(root_state, root_key); + + // Schedule layout of the tree on behalf of its newly added root. + // We don't need to schedule layout of the root until the tree provides + // new layout parameters. + InvalidateLayoutForRoot(tree_state); +} + +void ViewRegistry::UnlinkRoot(ViewTreeState* tree_state) { + DCHECK(IsViewTreeStateRegisteredDebug(tree_state)); + DCHECK(tree_state->root()); + + DVLOG(2) << "Unlinked view tree root " << tree_state->root()->key() << " {" + << tree_state->root()->view_token_value() << "}"; + + ResetStateWhenUnlinking(tree_state->root()); + tree_state->ResetRoot(); + + // We don't need to schedule layout for the root since it will retain + // its old layout parameters. +} + +void ViewRegistry::HijackView(ViewState* view_state) { + if (view_state->parent()) { + MarkChildAsUnavailable(view_state->parent(), view_state->key()); + } else if (view_state->tree()) { + ViewTreeState* tree_state = view_state->tree(); + uint32_t root_key = tree_state->root()->key(); + UnlinkRoot(tree_state); + SendRootUnavailable(tree_state, root_key); + } +} + +void ViewRegistry::InvalidateLayout(ViewState* view_state) { + DCHECK(IsViewStateRegisteredDebug(view_state)); + + // We can consider the layout request to have been satisfied if + // there is already a pending layout request in the queue that has not + // yet been issued (this is coalescing). Otherwise we must manufacture + // a new one based on the current layout parameters. + if (view_state->layout_params() && + (view_state->pending_layout_requests().empty() || + view_state->pending_layout_requests().back()->issued())) { + EnqueueLayoutRequest(view_state, view_state->layout_params().Clone()); + IssueNextViewLayoutRequest(view_state); + } +} + +void ViewRegistry::InvalidateLayoutForChild(ViewState* parent_state, + uint32_t child_key) { + DCHECK(IsViewStateRegisteredDebug(parent_state)); + DCHECK(parent_state->children().find(child_key) != + parent_state->children().end()); + + parent_state->children_needing_layout().insert(child_key); + InvalidateLayout(parent_state); +} + +void ViewRegistry::InvalidateLayoutForRoot(ViewTreeState* tree_state) { + DCHECK(IsViewTreeStateRegisteredDebug(tree_state)); + + if (!tree_state->layout_request_pending()) { + tree_state->set_layout_request_pending(true); + IssueNextViewTreeLayoutRequest(tree_state); + } +} + +void ViewRegistry::SetLayout(ViewState* view_state, + mojo::ui::ViewLayoutParamsPtr layout_params, + const ViewLayoutCallback& callback) { + DCHECK(IsViewStateRegisteredDebug(view_state)); + DCHECK(AreViewLayoutParamsValid(layout_params.get())); + + // Check whether the currently cached layout parameters are the same + // and we already have a result and we have no pending layout requests. + if (view_state->pending_layout_requests().empty() && + view_state->layout_params() && view_state->layout_info() && + view_state->layout_params()->Equals(*layout_params)) { + DVLOG(2) << "Layout cache hit"; + callback.Run(view_state->layout_info().Clone()); + return; + } + + // Check whether the layout parameters are different from the most + // recent pending layout request if we have one. + if (view_state->pending_layout_requests().empty() || + !view_state->pending_layout_requests().back()->layout_params()->Equals( + *layout_params)) { + // Enqueue a new request for these parameters. + EnqueueLayoutRequest(view_state, layout_params.Pass()); + } + + // Enlist ourselves into the callbacks for the pending request. + view_state->pending_layout_requests().back()->AddCallback(callback); + IssueNextViewLayoutRequest(view_state); +} + +void ViewRegistry::EnqueueLayoutRequest( + ViewState* view_state, + mojo::ui::ViewLayoutParamsPtr layout_params) { + DCHECK(IsViewStateRegisteredDebug(view_state)); + DCHECK(AreViewLayoutParamsValid(layout_params.get())); + + // Drop the previous layout request if it hasn't been issued yet. + // This may cause callbacks to be invoked will null information. + if (!view_state->pending_layout_requests().empty() && + !view_state->pending_layout_requests().back()->issued()) + view_state->pending_layout_requests().pop_back(); + + // Enqueue the new request. + view_state->pending_layout_requests().emplace_back( + std::unique_ptr<ViewLayoutRequest>( + new ViewLayoutRequest(layout_params.Pass()))); +} + +void ViewRegistry::IssueNextViewLayoutRequest(ViewState* view_state) { + DCHECK(IsViewStateRegisteredDebug(view_state)); + + if (!view_state->pending_layout_requests().empty() && + !view_state->pending_layout_requests().front()->issued()) { + view_state->pending_layout_requests().front()->set_issued(true); + SendViewLayoutRequest(view_state); + } +} + +void ViewRegistry::IssueNextViewTreeLayoutRequest(ViewTreeState* tree_state) { + DCHECK(IsViewTreeStateRegisteredDebug(tree_state)); + + if (tree_state->layout_request_pending() && + !tree_state->layout_request_issued()) { + tree_state->set_layout_request_pending(false); + tree_state->set_layout_request_issued(true); + SendViewTreeLayoutRequest(tree_state); + } +} + +void ViewRegistry::ResetStateWhenUnlinking(ViewState* view_state) { + // Clean up parent's recorded state for the child. + if (view_state->parent()) { + view_state->parent()->children_needing_layout().erase(view_state->key()); + } + + // Clean up child's recorded state for the parent or tree. + if (view_state->wrapped_surface()) { + surface_manager_->DestroySurface(view_state->wrapped_surface().Pass()); + } +} + +void ViewRegistry::SendChildUnavailable(ViewState* parent_state, + uint32_t child_key) { + DCHECK(IsViewStateRegisteredDebug(parent_state)); + + // TODO: Detect ANRs + DVLOG(1) << "SendChildUnavailable: child_key=" << child_key; + parent_state->view()->OnChildUnavailable(child_key, + base::Bind(&base::DoNothing)); +} + +void ViewRegistry::SendRootUnavailable(ViewTreeState* tree_state, + uint32_t root_key) { + DCHECK(IsViewTreeStateRegisteredDebug(tree_state)); + + // TODO: Detect ANRs + DVLOG(1) << "SendRootUnavailable: root_key=" << root_key; + tree_state->view_tree()->OnRootUnavailable(root_key, + base::Bind(&base::DoNothing)); +} + +void ViewRegistry::SendViewLayoutRequest(ViewState* view_state) { + DCHECK(IsViewStateRegisteredDebug(view_state)); + DCHECK(!view_state->pending_layout_requests().empty()); + DCHECK(view_state->pending_layout_requests().front()->issued()); + + // TODO: Detect ANRs + DVLOG(1) << "SendViewLayoutRequest: view.token=" + << view_state->view_token_value(); + view_state->view()->OnLayout( + view_state->pending_layout_requests().front()->layout_params()->Clone(), + mojo::Array<uint32_t>::From(view_state->children_needing_layout()), + base::Bind(&ViewRegistry::OnViewLayoutResult, base::Unretained(this), + view_state->GetWeakPtr())); + view_state->children_needing_layout().clear(); +} + +void ViewRegistry::SendViewTreeLayoutRequest(ViewTreeState* tree_state) { + DCHECK(IsViewTreeStateRegisteredDebug(tree_state)); + DCHECK(tree_state->layout_request_issued()); + + // TODO: Detect ANRs + DVLOG(1) << "SendViewTreeLayoutRequest"; + tree_state->view_tree()->OnLayout( + base::Bind(&ViewRegistry::OnViewTreeLayoutResult, base::Unretained(this), + tree_state->GetWeakPtr())); +} + +static bool IsSizeInBounds(mojo::ui::BoxConstraints* constraints, + mojo::Size* size) { + return size && size->width >= constraints->min_width && + size->width <= constraints->max_width && + size->height >= constraints->min_height && + size->height <= constraints->max_height; +} + +void ViewRegistry::OnViewLayoutResult(base::WeakPtr<ViewState> view_state_weak, + mojo::ui::ViewLayoutInfoPtr info) { + DCHECK(info); + DCHECK(info->surface_id); // checked by mojom + + ViewState* view_state = view_state_weak.get(); + if (!view_state) + return; + + DCHECK(!view_state->pending_layout_requests().empty()); + DCHECK(view_state->pending_layout_requests().front()->issued()); + + std::unique_ptr<ViewLayoutRequest> request( + std::move(view_state->pending_layout_requests().front())); + view_state->pending_layout_requests().erase( + view_state->pending_layout_requests().begin()); + + DVLOG(1) << "OnViewLayoutResult: view=" << view_state + << ", params=" << request->layout_params() + << ", info=" << info.get(); + + // Validate the layout info. + if (!IsSizeInBounds(request->layout_params()->constraints.get(), + info->size.get())) { + LOG(ERROR) << "View returned invalid size in its layout info: " + << "view=" << view_state + << ", params=" << request->layout_params() + << ", info=" << info.get(); + UnregisterView(view_state); + return; + } + + // Assume the parent or root will not see the new layout information if + // there are no callbacks so we need to inform it when things change. + const bool size_changed = + !view_state->layout_info() || + !view_state->layout_info()->size->Equals(*info->size); + const bool surface_changed = + !view_state->layout_info() || + !view_state->layout_info()->surface_id->Equals(*info->surface_id); + const bool recurse = + !request->has_callbacks() && (surface_changed || size_changed); + + view_state->layout_params() = request->TakeLayoutParams().Pass(); + view_state->layout_info() = info.Pass(); + + if (surface_changed) { + if (view_state->wrapped_surface()) + surface_manager_->DestroySurface(view_state->wrapped_surface().Pass()); + view_state->wrapped_surface() = surface_manager_->CreateWrappedSurface( + view_state->layout_info()->surface_id.get()); + } + + request->DispatchLayoutInfo(view_state->layout_info().get()); + + if (recurse) { + if (view_state->parent()) { + InvalidateLayoutForChild(view_state->parent(), view_state->key()); + } else if (view_state->tree()) { + InvalidateLayoutForRoot(view_state->tree()); + } + } + + IssueNextViewLayoutRequest(view_state); +} + +void ViewRegistry::OnViewTreeLayoutResult( + base::WeakPtr<ViewTreeState> tree_state_weak) { + ViewTreeState* tree_state = tree_state_weak.get(); + if (tree_state) { + DCHECK(tree_state->layout_request_issued()); + + DVLOG(1) << "OnViewTreeLayoutResult"; + + tree_state->set_layout_request_issued(false); + IssueNextViewTreeLayoutRequest(tree_state); + } +} + +} // namespace view_manager
diff --git a/services/ui/view_manager/view_registry.h b/services/ui/view_manager/view_registry.h new file mode 100644 index 0000000..52f6782 --- /dev/null +++ b/services/ui/view_manager/view_registry.h
@@ -0,0 +1,161 @@ +// Copyright 2015 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. + +#ifndef SERVICES_UI_VIEW_MANAGER_VIEW_REGISTRY_H_ +#define SERVICES_UI_VIEW_MANAGER_VIEW_REGISTRY_H_ + +#include <unordered_map> +#include <vector> + +#include "base/macros.h" +#include "mojo/services/ui/views/interfaces/view_trees.mojom.h" +#include "mojo/services/ui/views/interfaces/views.mojom.h" +#include "services/ui/view_manager/view_layout_request.h" +#include "services/ui/view_manager/view_state.h" +#include "services/ui/view_manager/view_tree_state.h" + +namespace view_manager { + +class SurfaceManager; + +// Maintains a registry of the state of all views. +// All ViewState objects are owned by the registry. +class ViewRegistry { + public: + explicit ViewRegistry(SurfaceManager* surface_manager); + ~ViewRegistry(); + + // VIEW MANAGER REQUESTS + + // Registers a view and returns its ViewToken. + mojo::ui::ViewTokenPtr RegisterView( + mojo::ui::ViewPtr view, + mojo::InterfaceRequest<mojo::ui::ViewHost> view_host_request); + + // Registers a view tree. + void RegisterViewTree( + mojo::ui::ViewTreePtr view_tree, + mojo::InterfaceRequest<mojo::ui::ViewTreeHost> view_tree_host_request); + + // VIEW HOST REQUESTS + + // Requests layout. + // Destroys |view_state| if an error occurs. + void RequestLayout(ViewState* view_state); + + // Adds a child, reparenting it if necessary. + // Destroys |parent_state| if an error occurs. + void AddChild(ViewState* parent_state, + uint32_t child_key, + mojo::ui::ViewTokenPtr child_view_token); + + // Removes a child. + // Destroys |parent_state| if an error occurs. + void RemoveChild(ViewState* parent_state, uint32_t child_key); + + // Lays out a child and optionally provides its size. + // Destroys |parent_state| if an error occurs. + void LayoutChild(ViewState* parent_state, + uint32_t child_key, + mojo::ui::ViewLayoutParamsPtr child_layout_params, + const ViewLayoutCallback& callback); + + // VIEW TREE HOST REQUESTS + + // Requests layout. + // Destroys |tree_state| if an error occurs. + void RequestLayout(ViewTreeState* tree_state); + + // Sets the root of the view tree. + // Destroys |tree_state| if an error occurs. + void SetRoot(ViewTreeState* tree_state, + uint32_t root_key, + mojo::ui::ViewTokenPtr root_view_token); + + // Resets the root of the view tree. + // Destroys |tree_state| if an error occurs. + void ResetRoot(ViewTreeState* tree_state); + + // Lays out a view tree's root and optionally provides its size. + // Destroys |tree_state| if an error occurs. + void LayoutRoot(ViewTreeState* tree_state, + mojo::ui::ViewLayoutParamsPtr root_layout_params, + const ViewLayoutCallback& callback); + + private: + // LIFETIME + + void OnViewConnectionError(ViewState* view_state); + void UnregisterView(ViewState* view_state); + void OnViewTreeConnectionError(ViewTreeState* tree_state); + void UnregisterViewTree(ViewTreeState* tree_state); + + // TREE MANIPULATION + + ViewState* FindView(uint32_t view_token); + void LinkChild(ViewState* parent_state, + uint32_t child_key, + ViewState* child_state); + void LinkChildAsUnavailable(ViewState* parent_state, uint32_t child_key); + void MarkChildAsUnavailable(ViewState* parent_state, uint32_t child_key); + void UnlinkChild(ViewState* parent_state, + ViewState::ChildrenMap::iterator child_it); + void LinkRoot(ViewTreeState* tree_state, + ViewState* root_state, + uint32_t root_key); + void UnlinkRoot(ViewTreeState* tree_state); + void HijackView(ViewState* view_state); + + // Must be called before the view is actually unlinked from the tree. + // Caller is still responsible for actually unlinking the view. + void ResetStateWhenUnlinking(ViewState* view_state); + + // LAYOUT + + void InvalidateLayout(ViewState* view_state); + void InvalidateLayoutForChild(ViewState* parent_state, uint32_t child_key); + void InvalidateLayoutForRoot(ViewTreeState* tree_state); + void SetLayout(ViewState* view_state, + mojo::ui::ViewLayoutParamsPtr layout_params, + const ViewLayoutCallback& callback); + void EnqueueLayoutRequest(ViewState* view_state, + mojo::ui::ViewLayoutParamsPtr layout_params); + void IssueNextViewLayoutRequest(ViewState* view_state); + void IssueNextViewTreeLayoutRequest(ViewTreeState* tree_state); + + // SIGNALLING + + void SendChildUnavailable(ViewState* parent_state, uint32_t child_key); + void SendRootUnavailable(ViewTreeState* tree_state, uint32_t root_key); + void SendViewLayoutRequest(ViewState* view_state); + void SendViewTreeLayoutRequest(ViewTreeState* tree_state); + void OnViewLayoutResult(base::WeakPtr<ViewState> view_state_weak, + mojo::ui::ViewLayoutInfoPtr info); + void OnViewTreeLayoutResult(base::WeakPtr<ViewTreeState> tree_state_weak); + +#if DCHECK_IS_ON() + bool IsViewStateRegisteredDebug(ViewState* view_state) { + return view_state && FindView(view_state->view_token_value()); + } + + bool IsViewTreeStateRegisteredDebug(ViewTreeState* tree_state) { + return tree_state && std::any_of(view_trees_.begin(), view_trees_.end(), + [tree_state](ViewTreeState* other) { + return tree_state == other; + }); + } +#endif + + SurfaceManager* surface_manager_; + + uint32_t next_view_token_value_; + std::unordered_map<uint32_t, ViewState*> views_by_token_; + std::vector<ViewTreeState*> view_trees_; + + DISALLOW_COPY_AND_ASSIGN(ViewRegistry); +}; + +} // namespace view_manager + +#endif // SERVICES_UI_VIEW_MANAGER_VIEW_REGISTRY_H_
diff --git a/services/ui/view_manager/view_state.cc b/services/ui/view_manager/view_state.cc new file mode 100644 index 0000000..cfccc56 --- /dev/null +++ b/services/ui/view_manager/view_state.cc
@@ -0,0 +1,55 @@ +// Copyright 2015 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 "base/logging.h" +#include "services/ui/view_manager/view_state.h" +#include "services/ui/view_manager/view_tree_state.h" + +namespace view_manager { + +ViewState::ViewState(mojo::ui::ViewPtr view, uint32_t view_token_value) + : view_(view.Pass()), + view_token_value_(view_token_value), + tree_(nullptr), + parent_(nullptr), + key_(0), + weak_factory_(this) { + DCHECK(view_); +} + +ViewState::~ViewState() {} + +void ViewState::SetTree(ViewTreeState* tree, uint32_t key) { + DCHECK(tree); + DCHECK(!parent_); // must be the root + if (tree_ != tree) { + SetTreeUnchecked(tree); + } + key_ = key; +} + +void ViewState::SetTreeUnchecked(ViewTreeState* tree) { + tree_ = tree; + for (const auto& pair : children_) { + pair.second->SetTreeUnchecked(tree); + } +} + +void ViewState::SetParent(ViewState* parent, uint32_t key) { + DCHECK(parent); + parent_ = parent; + key_ = key; + SetTreeUnchecked(parent->tree_); +} + +void ViewState::ResetContainer() { + parent_ = nullptr; + key_ = 0; + SetTreeUnchecked(nullptr); +} + +void ViewState::GetServiceProvider( + mojo::InterfaceRequest<mojo::ServiceProvider> service_provider) {} + +} // namespace view_manager
diff --git a/services/ui/view_manager/view_state.h b/services/ui/view_manager/view_state.h new file mode 100644 index 0000000..1f1d2a8 --- /dev/null +++ b/services/ui/view_manager/view_state.h
@@ -0,0 +1,128 @@ +// Copyright 2015 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. + +#ifndef SERVICES_UI_VIEW_MANAGER_VIEW_STATE_H_ +#define SERVICES_UI_VIEW_MANAGER_VIEW_STATE_H_ + +#include <memory> +#include <set> +#include <unordered_map> + +#include "base/callback.h" +#include "base/macros.h" +#include "base/memory/weak_ptr.h" +#include "mojo/services/ui/views/interfaces/views.mojom.h" +#include "services/ui/view_manager/view_layout_request.h" + +namespace view_manager { + +class ViewTreeState; + +// Describes the state of a particular view. +// This object is owned by the ViewRegistry that created it. +class ViewState { + public: + using ChildrenMap = std::unordered_map<uint32_t, ViewState*>; + + ViewState(mojo::ui::ViewPtr view, uint32_t view_token_value); + ~ViewState(); + + base::WeakPtr<ViewState> GetWeakPtr() { return weak_factory_.GetWeakPtr(); } + + // Gets the view interface, never null. + // Caller does not obtain ownership of the view. + mojo::ui::View* view() const { return view_.get(); } + + // Gets the view token value used to refer to this view globally. + uint32_t view_token_value() const { return view_token_value_; } + + // Sets the associated host implementation and takes ownership of it. + void set_view_host(mojo::ui::ViewHost* host) { view_host_.reset(host); } + + // Sets the connection error handler for the view. + void set_view_connection_error_handler(const base::Closure& handler) { + view_.set_connection_error_handler(handler); + } + + // Gets the view tree to which this view belongs, or null if none. + ViewTreeState* tree() const { return tree_; } + + // Gets the parent view state, or null if none. + ViewState* parent() const { return parent_; } + + // Gets the key that this child has in its container, or 0 if none. + uint32_t key() const { return key_; } + + // Recursively sets the view tree to which this view and all of its + // descendents belongs. Must not be null. This method must only be called + // on root views. + void SetTree(ViewTreeState* tree, uint32_t key); + + // Sets the parent view state pointer, the child's key in its parent, + // and set its view tree to that of its parent. Must not be null. + void SetParent(ViewState* parent, uint32_t key); + + // Resets the parent view state and tree pointers to null. + void ResetContainer(); + + // Gets the view's service provider. + void GetServiceProvider( + mojo::InterfaceRequest<mojo::ServiceProvider> service_provider); + + // The map of children, indexed by child key. + // Child view state may be null if the child with the given key has + // become unavailable but not yet removed. + ChildrenMap& children() { return children_; } + + // The set of children needing layout. + // This set must never contain non-existent or unavailable children. + std::set<uint32_t>& children_needing_layout() { + return children_needing_layout_; + } + + // The list of pending layout requests. + std::vector<std::unique_ptr<ViewLayoutRequest>>& pending_layout_requests() { + return pending_layout_requests_; + } + + // The layout parameters most recently processed by the view, + // or null if none. These parameters are preserved across reparenting. + mojo::ui::ViewLayoutParamsPtr& layout_params() { return layout_params_; } + + // The layout information most recently provided by the view in + // response to the value of |layout_params|, or null if none. These + // results are preserved across reparenting. + mojo::ui::ViewLayoutInfoPtr& layout_info() { return layout_info_; } + + // The id of the Surface which the view manager itself created to wrap the + // view's own Surface, or null if none. The wrapped Surface is destroyed + // when the view is reparented so that the old parent can no longer embed + // the view's actual content. + mojo::SurfaceIdPtr& wrapped_surface() { return wrapped_surface_; } + + private: + void SetTreeUnchecked(ViewTreeState* tree); + + mojo::ui::ViewPtr view_; + const uint32_t view_token_value_; + + std::unique_ptr<mojo::ui::ViewHost> view_host_; + ViewTreeState* tree_; + ViewState* parent_; + uint32_t key_; + ChildrenMap children_; + std::set<uint32_t> children_needing_layout_; + std::vector<std::unique_ptr<ViewLayoutRequest>> pending_layout_requests_; + mojo::ui::ViewLayoutParamsPtr layout_params_; + mojo::ui::ViewLayoutInfoPtr layout_info_; + mojo::SurfaceIdPtr wrapped_surface_; + + base::WeakPtrFactory<ViewState> weak_factory_; // must be last + + DISALLOW_COPY_AND_ASSIGN(ViewState); +}; + +} // namespace view_manager + +#endif // SERVICES_UI_VIEW_MANAGER_VIEW_STATE_H_
diff --git a/services/ui/view_manager/view_tree_host_impl.cc b/services/ui/view_manager/view_tree_host_impl.cc new file mode 100644 index 0000000..e970a69 --- /dev/null +++ b/services/ui/view_manager/view_tree_host_impl.cc
@@ -0,0 +1,47 @@ +// Copyright 2015 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 "base/bind.h" +#include "base/bind_helpers.h" +#include "services/ui/view_manager/view_tree_host_impl.h" + +namespace view_manager { + +ViewTreeHostImpl::ViewTreeHostImpl( + ViewRegistry* registry, + ViewTreeState* state, + mojo::InterfaceRequest<mojo::ui::ViewTreeHost> view_tree_host_request) + : registry_(registry), + state_(state), + binding_(this, view_tree_host_request.Pass()) {} + +ViewTreeHostImpl::~ViewTreeHostImpl() {} + +void ViewTreeHostImpl::RequestLayout() { + registry_->RequestLayout(state_); +} + +void ViewTreeHostImpl::SetRoot(uint32_t root_key, + mojo::ui::ViewTokenPtr root_view_token) { + registry_->SetRoot(state_, root_key, root_view_token.Pass()); +} + +void ViewTreeHostImpl::ResetRoot() { + registry_->ResetRoot(state_); +} + +static void RunLayoutRootCallback( + const ViewTreeHostImpl::LayoutRootCallback& callback, + mojo::ui::ViewLayoutInfoPtr info) { + callback.Run(info.Pass()); +} + +void ViewTreeHostImpl::LayoutRoot( + mojo::ui::ViewLayoutParamsPtr root_layout_params, + const LayoutRootCallback& callback) { + registry_->LayoutRoot(state_, root_layout_params.Pass(), + base::Bind(&RunLayoutRootCallback, callback)); +} + +} // namespace view_manager
diff --git a/services/ui/view_manager/view_tree_host_impl.h b/services/ui/view_manager/view_tree_host_impl.h new file mode 100644 index 0000000..9677d81 --- /dev/null +++ b/services/ui/view_manager/view_tree_host_impl.h
@@ -0,0 +1,49 @@ +// Copyright 2015 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. + +#ifndef SERVICES_UI_VIEW_MANAGER_VIEW_TREE_HOST_IMPL_H_ +#define SERVICES_UI_VIEW_MANAGER_VIEW_TREE_HOST_IMPL_H_ + +#include "base/macros.h" +#include "mojo/public/cpp/bindings/binding.h" +#include "mojo/services/ui/views/interfaces/view_trees.mojom.h" +#include "services/ui/view_manager/view_registry.h" +#include "services/ui/view_manager/view_tree_state.h" + +namespace view_manager { + +// ViewTreeHost interface implementation. +// This object is owned by its associated ViewTreeState. +class ViewTreeHostImpl : public mojo::ui::ViewTreeHost { + public: + ViewTreeHostImpl( + ViewRegistry* registry, + ViewTreeState* state, + mojo::InterfaceRequest<mojo::ui::ViewTreeHost> view_tree_host_request); + ~ViewTreeHostImpl() override; + + void set_view_tree_host_connection_error_handler( + const base::Closure& handler) { + binding_.set_connection_error_handler(handler); + } + + private: + // |ViewTreeHost|: + void RequestLayout() override; + void SetRoot(uint32_t root_key, + mojo::ui::ViewTokenPtr root_view_token) override; + void ResetRoot() override; + void LayoutRoot(mojo::ui::ViewLayoutParamsPtr root_layout_params, + const LayoutRootCallback& callback) override; + + ViewRegistry* const registry_; + ViewTreeState* const state_; + mojo::Binding<mojo::ui::ViewTreeHost> binding_; + + DISALLOW_COPY_AND_ASSIGN(ViewTreeHostImpl); +}; + +} // namespace view_manager + +#endif // SERVICES_UI_VIEW_MANAGER_VIEW_TREE_HOST_IMPL_H_
diff --git a/services/ui/view_manager/view_tree_state.cc b/services/ui/view_manager/view_tree_state.cc new file mode 100644 index 0000000..e236c08 --- /dev/null +++ b/services/ui/view_manager/view_tree_state.cc
@@ -0,0 +1,38 @@ +// Copyright 2015 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 "base/logging.h" +#include "services/ui/view_manager/view_tree_state.h" + +namespace view_manager { + +ViewTreeState::ViewTreeState(mojo::ui::ViewTreePtr view_tree) + : view_tree_(view_tree.Pass()), + root_(nullptr), + explicit_root_(false), + layout_request_pending_(false), + layout_request_issued_(false), + weak_factory_(this) { + DCHECK(view_tree_); +} + +ViewTreeState::~ViewTreeState() {} + +void ViewTreeState::SetRoot(ViewState* root, uint32_t key) { + DCHECK(root); + if (root_ != root) { + ResetRoot(); + root->SetTree(this, key); + root_ = root; + } +} + +void ViewTreeState::ResetRoot() { + if (root_) { + root_->ResetContainer(); + } + root_ = nullptr; +} + +} // namespace view_manager
diff --git a/services/ui/view_manager/view_tree_state.h b/services/ui/view_manager/view_tree_state.h new file mode 100644 index 0000000..abcbad9 --- /dev/null +++ b/services/ui/view_manager/view_tree_state.h
@@ -0,0 +1,87 @@ +// Copyright 2015 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. + +#ifndef SERVICES_UI_VIEW_MANAGER_VIEW_TREE_STATE_H_ +#define SERVICES_UI_VIEW_MANAGER_VIEW_TREE_STATE_H_ + +#include <memory> +#include <set> +#include <unordered_map> + +#include "base/callback.h" +#include "base/macros.h" +#include "base/memory/weak_ptr.h" +#include "mojo/public/cpp/bindings/binding.h" +#include "mojo/services/ui/views/interfaces/view_trees.mojom.h" +#include "services/ui/view_manager/view_state.h" + +namespace view_manager { + +// Describes the state of a particular view tree. +// This object is owned by the ViewRegistry that created it. +class ViewTreeState { + public: + explicit ViewTreeState(mojo::ui::ViewTreePtr view_tree); + ~ViewTreeState(); + + base::WeakPtr<ViewTreeState> GetWeakPtr() { + return weak_factory_.GetWeakPtr(); + } + + // Gets the view tree interface, never null. + // Caller does not obtain ownership of the view. + mojo::ui::ViewTree* view_tree() const { return view_tree_.get(); } + + // Sets the associated host implementation and takes ownership of it. + void set_view_tree_host(mojo::ui::ViewTreeHost* host) { + view_tree_host_.reset(host); + } + + // Sets the connection error handler for the view. + void set_view_tree_connection_error_handler(const base::Closure& handler) { + view_tree_.set_connection_error_handler(handler); + } + + // Gets the root of the view tree, or null if it is unavailable. + ViewState* root() const { return root_; } + + // Sets the root of the view tree. Must not be null. + // The view specified as the new root must not have any parents. + void SetRoot(ViewState* root, uint32_t key); + + // Resets the root view to null. + void ResetRoot(); + + // True if the client previously set but has not yet explicitly unset + // the root, independent of whether it is currently available. + bool explicit_root() const { return explicit_root_; } + void set_explicit_root(bool value) { explicit_root_ = value; } + + // True if there is a pending layout request. + bool layout_request_pending() const { return layout_request_pending_; } + void set_layout_request_pending(bool value) { + layout_request_pending_ = value; + } + + // True if a layout request has been issued. + bool layout_request_issued() const { return layout_request_issued_; } + void set_layout_request_issued(bool value) { layout_request_issued_ = value; } + + private: + mojo::ui::ViewTreePtr view_tree_; + + std::unique_ptr<mojo::ui::ViewTreeHost> view_tree_host_; + ViewState* root_; + bool explicit_root_; + bool layout_request_pending_; + bool layout_request_issued_; + + base::WeakPtrFactory<ViewTreeState> weak_factory_; // must be last + + DISALLOW_COPY_AND_ASSIGN(ViewTreeState); +}; + +} // namespace view_manager + +#endif // SERVICES_UI_VIEW_MANAGER_VIEW_TREE_STATE_H_