Create a RegisterViewAssociate method in ViewManager Add tests for ViewManager and Views #703 R=jeffbrown@google.com Review URL: https://codereview.chromium.org/1949233002 .
diff --git a/mojo/dart/packages/mojo_services/lib/mojo/ui/view_associates.mojom.dart b/mojo/dart/packages/mojo_services/lib/mojo/ui/view_associates.mojom.dart index eeed7cf..96f3d2a 100644 --- a/mojo/dart/packages/mojo_services/lib/mojo/ui/view_associates.mojom.dart +++ b/mojo/dart/packages/mojo_services/lib/mojo/ui/view_associates.mojom.dart
@@ -1088,6 +1088,183 @@ } } + +class _ViewAssociateOwnerServiceDescription implements service_describer.ServiceDescription { + dynamic getTopLevelInterface([Function responseFactory]) => + responseFactory(null); + + dynamic getTypeDefinition(String typeKey, [Function responseFactory]) => + responseFactory(null); + + dynamic getAllTypeDefinitions([Function responseFactory]) => + responseFactory(null); +} + +abstract class ViewAssociateOwner { + static const String serviceName = "mojo::ui::ViewAssociateOwner"; +} + +class _ViewAssociateOwnerProxyControl + extends bindings.ProxyMessageHandler + implements bindings.ProxyControl { + _ViewAssociateOwnerProxyControl.fromEndpoint( + core.MojoMessagePipeEndpoint endpoint) : super.fromEndpoint(endpoint); + + _ViewAssociateOwnerProxyControl.fromHandle( + core.MojoHandle handle) : super.fromHandle(handle); + + _ViewAssociateOwnerProxyControl.unbound() : super.unbound(); + + service_describer.ServiceDescription get serviceDescription => + new _ViewAssociateOwnerServiceDescription(); + + String get serviceName => ViewAssociateOwner.serviceName; + + void handleResponse(bindings.ServiceMessage message) { + switch (message.header.type) { + default: + proxyError("Unexpected message type: ${message.header.type}"); + close(immediate: true); + break; + } + } + + @override + String toString() { + var superString = super.toString(); + return "_ViewAssociateOwnerProxyControl($superString)"; + } +} + +class ViewAssociateOwnerProxy + extends bindings.Proxy + implements ViewAssociateOwner { + ViewAssociateOwnerProxy.fromEndpoint( + core.MojoMessagePipeEndpoint endpoint) + : super(new _ViewAssociateOwnerProxyControl.fromEndpoint(endpoint)); + + ViewAssociateOwnerProxy.fromHandle(core.MojoHandle handle) + : super(new _ViewAssociateOwnerProxyControl.fromHandle(handle)); + + ViewAssociateOwnerProxy.unbound() + : super(new _ViewAssociateOwnerProxyControl.unbound()); + + static ViewAssociateOwnerProxy newFromEndpoint( + core.MojoMessagePipeEndpoint endpoint) { + assert(endpoint.setDescription("For ViewAssociateOwnerProxy")); + return new ViewAssociateOwnerProxy.fromEndpoint(endpoint); + } + + factory ViewAssociateOwnerProxy.connectToService( + bindings.ServiceConnector s, String url, [String serviceName]) { + ViewAssociateOwnerProxy p = new ViewAssociateOwnerProxy.unbound(); + s.connectToService(url, p, serviceName); + return p; + } + + +} + +class _ViewAssociateOwnerStubControl + extends bindings.StubMessageHandler + implements bindings.StubControl<ViewAssociateOwner> { + ViewAssociateOwner _impl; + + _ViewAssociateOwnerStubControl.fromEndpoint( + core.MojoMessagePipeEndpoint endpoint, [ViewAssociateOwner impl]) + : super.fromEndpoint(endpoint, autoBegin: impl != null) { + _impl = impl; + } + + _ViewAssociateOwnerStubControl.fromHandle( + core.MojoHandle handle, [ViewAssociateOwner impl]) + : super.fromHandle(handle, autoBegin: impl != null) { + _impl = impl; + } + + _ViewAssociateOwnerStubControl.unbound([this._impl]) : super.unbound(); + + + + dynamic handleMessage(bindings.ServiceMessage message) { + if (bindings.ControlMessageHandler.isControlMessage(message)) { + return bindings.ControlMessageHandler.handleMessage(this, + 0, + message); + } + if (_impl == null) { + throw new core.MojoApiError("$this has no implementation set"); + } + switch (message.header.type) { + default: + throw new bindings.MojoCodecError("Unexpected message name"); + break; + } + return null; + } + + ViewAssociateOwner get impl => _impl; + set impl(ViewAssociateOwner d) { + if (d == null) { + throw new core.MojoApiError("$this: Cannot set a null implementation"); + } + if (isBound && (_impl == null)) { + beginHandlingEvents(); + } + _impl = d; + } + + @override + void bind(core.MojoMessagePipeEndpoint endpoint) { + super.bind(endpoint); + if (!isOpen && (_impl != null)) { + beginHandlingEvents(); + } + } + + @override + String toString() { + var superString = super.toString(); + return "_ViewAssociateOwnerStubControl($superString)"; + } + + int get version => 0; + + static service_describer.ServiceDescription _cachedServiceDescription; + static service_describer.ServiceDescription get serviceDescription { + if (_cachedServiceDescription == null) { + _cachedServiceDescription = new _ViewAssociateOwnerServiceDescription(); + } + return _cachedServiceDescription; + } +} + +class ViewAssociateOwnerStub + extends bindings.Stub<ViewAssociateOwner> + implements ViewAssociateOwner { + ViewAssociateOwnerStub.fromEndpoint( + core.MojoMessagePipeEndpoint endpoint, [ViewAssociateOwner impl]) + : super(new _ViewAssociateOwnerStubControl.fromEndpoint(endpoint, impl)); + + ViewAssociateOwnerStub.fromHandle( + core.MojoHandle handle, [ViewAssociateOwner impl]) + : super(new _ViewAssociateOwnerStubControl.fromHandle(handle, impl)); + + ViewAssociateOwnerStub.unbound([ViewAssociateOwner impl]) + : super(new _ViewAssociateOwnerStubControl.unbound(impl)); + + static ViewAssociateOwnerStub newFromEndpoint( + core.MojoMessagePipeEndpoint endpoint) { + assert(endpoint.setDescription("For ViewAssociateOwnerStub")); + return new ViewAssociateOwnerStub.fromEndpoint(endpoint); + } + + static service_describer.ServiceDescription get serviceDescription => + _ViewAssociateOwnerStubControl.serviceDescription; + + +} + const int _viewInspectorMethodGetHitTesterName = 0; const int _viewInspectorMethodResolveScenesName = 1;
diff --git a/mojo/dart/packages/mojo_services/lib/mojo/ui/view_manager.mojom.dart b/mojo/dart/packages/mojo_services/lib/mojo/ui/view_manager.mojom.dart index a8dc87f..a970dfa 100644 --- a/mojo/dart/packages/mojo_services/lib/mojo/ui/view_manager.mojom.dart +++ b/mojo/dart/packages/mojo_services/lib/mojo/ui/view_manager.mojom.dart
@@ -7,6 +7,7 @@ import 'package:mojo/bindings.dart' as bindings; import 'package:mojo/core.dart' as core; import 'package:mojo/mojo/bindings/types/service_describer.mojom.dart' as service_describer; +import 'package:mojo_services/mojo/ui/view_associates.mojom.dart' as view_associates_mojom; import 'package:mojo_services/mojo/ui/view_token.mojom.dart' as view_token_mojom; import 'package:mojo_services/mojo/ui/view_trees.mojom.dart' as view_trees_mojom; import 'package:mojo_services/mojo/ui/views.mojom.dart' as views_mojom; @@ -220,8 +221,165 @@ } } + +class _ViewManagerRegisterViewAssociateParams extends bindings.Struct { + static const List<bindings.StructDataHeader> kVersions = const [ + const bindings.StructDataHeader(32, 0) + ]; + Object viewAssociate = null; + Object viewAssociateOwner = null; + String label = null; + + _ViewManagerRegisterViewAssociateParams() : super(kVersions.last.size); + + static _ViewManagerRegisterViewAssociateParams deserialize(bindings.Message message) { + var decoder = new bindings.Decoder(message); + var result = decode(decoder); + if (decoder.excessHandles != null) { + decoder.excessHandles.forEach((h) => h.close()); + } + return result; + } + + static _ViewManagerRegisterViewAssociateParams decode(bindings.Decoder decoder0) { + if (decoder0 == null) { + return null; + } + _ViewManagerRegisterViewAssociateParams result = new _ViewManagerRegisterViewAssociateParams(); + + var mainDataHeader = decoder0.decodeStructDataHeader(); + if (mainDataHeader.version <= kVersions.last.version) { + // Scan in reverse order to optimize for more recent versions. + for (int i = kVersions.length - 1; i >= 0; --i) { + if (mainDataHeader.version >= kVersions[i].version) { + if (mainDataHeader.size == kVersions[i].size) { + // Found a match. + break; + } + throw new bindings.MojoCodecError( + 'Header size doesn\'t correspond to known version size.'); + } + } + } else if (mainDataHeader.size < kVersions.last.size) { + throw new bindings.MojoCodecError( + 'Message newer than the last known version cannot be shorter than ' + 'required by the last known version.'); + } + if (mainDataHeader.version >= 0) { + + result.viewAssociate = decoder0.decodeServiceInterface(8, false, view_associates_mojom.ViewAssociateProxy.newFromEndpoint); + } + if (mainDataHeader.version >= 0) { + + result.viewAssociateOwner = decoder0.decodeInterfaceRequest(16, false, view_associates_mojom.ViewAssociateOwnerStub.newFromEndpoint); + } + if (mainDataHeader.version >= 0) { + + result.label = decoder0.decodeString(24, true); + } + return result; + } + + void encode(bindings.Encoder encoder) { + var encoder0 = encoder.getStructEncoderAtOffset(kVersions.last); + try { + encoder0.encodeInterface(viewAssociate, 8, false); + } on bindings.MojoCodecError catch(e) { + e.message = "Error encountered while encoding field " + "viewAssociate of struct _ViewManagerRegisterViewAssociateParams: $e"; + rethrow; + } + try { + encoder0.encodeInterfaceRequest(viewAssociateOwner, 16, false); + } on bindings.MojoCodecError catch(e) { + e.message = "Error encountered while encoding field " + "viewAssociateOwner of struct _ViewManagerRegisterViewAssociateParams: $e"; + rethrow; + } + try { + encoder0.encodeString(label, 24, true); + } on bindings.MojoCodecError catch(e) { + e.message = "Error encountered while encoding field " + "label of struct _ViewManagerRegisterViewAssociateParams: $e"; + rethrow; + } + } + + String toString() { + return "_ViewManagerRegisterViewAssociateParams(" + "viewAssociate: $viewAssociate" ", " + "viewAssociateOwner: $viewAssociateOwner" ", " + "label: $label" ")"; + } + + Map toJson() { + throw new bindings.MojoCodecError( + 'Object containing handles cannot be encoded to JSON.'); + } +} + + +class _ViewManagerFinishedRegisteringViewAssociatesParams extends bindings.Struct { + static const List<bindings.StructDataHeader> kVersions = const [ + const bindings.StructDataHeader(8, 0) + ]; + + _ViewManagerFinishedRegisteringViewAssociatesParams() : super(kVersions.last.size); + + static _ViewManagerFinishedRegisteringViewAssociatesParams deserialize(bindings.Message message) { + var decoder = new bindings.Decoder(message); + var result = decode(decoder); + if (decoder.excessHandles != null) { + decoder.excessHandles.forEach((h) => h.close()); + } + return result; + } + + static _ViewManagerFinishedRegisteringViewAssociatesParams decode(bindings.Decoder decoder0) { + if (decoder0 == null) { + return null; + } + _ViewManagerFinishedRegisteringViewAssociatesParams result = new _ViewManagerFinishedRegisteringViewAssociatesParams(); + + var mainDataHeader = decoder0.decodeStructDataHeader(); + if (mainDataHeader.version <= kVersions.last.version) { + // Scan in reverse order to optimize for more recent versions. + for (int i = kVersions.length - 1; i >= 0; --i) { + if (mainDataHeader.version >= kVersions[i].version) { + if (mainDataHeader.size == kVersions[i].size) { + // Found a match. + break; + } + throw new bindings.MojoCodecError( + 'Header size doesn\'t correspond to known version size.'); + } + } + } else if (mainDataHeader.size < kVersions.last.size) { + throw new bindings.MojoCodecError( + 'Message newer than the last known version cannot be shorter than ' + 'required by the last known version.'); + } + return result; + } + + void encode(bindings.Encoder encoder) { + encoder.getStructEncoderAtOffset(kVersions.last); + } + + String toString() { + return "_ViewManagerFinishedRegisteringViewAssociatesParams("")"; + } + + Map toJson() { + Map map = new Map(); + return map; + } +} + const int _viewManagerMethodCreateViewName = 0; const int _viewManagerMethodCreateViewTreeName = 1; +const int _viewManagerMethodRegisterViewAssociateName = 2; +const int _viewManagerMethodFinishedRegisteringViewAssociatesName = 3; class _ViewManagerServiceDescription implements service_describer.ServiceDescription { dynamic getTopLevelInterface([Function responseFactory]) => @@ -238,6 +396,8 @@ static const String serviceName = "mojo::ui::ViewManager"; void createView(Object view, Object viewOwner, Object viewListener, String label); void createViewTree(Object viewTree, Object viewTreeListener, String label); + void registerViewAssociate(Object viewAssociate, Object viewAssociateOwner, String label); + void finishedRegisteringViewAssociates(); } class _ViewManagerProxyControl @@ -324,6 +484,27 @@ ctrl.sendMessage(params, _viewManagerMethodCreateViewTreeName); } + void registerViewAssociate(Object viewAssociate, Object viewAssociateOwner, String label) { + if (!ctrl.isBound) { + ctrl.proxyError("The Proxy is closed."); + return; + } + var params = new _ViewManagerRegisterViewAssociateParams(); + params.viewAssociate = viewAssociate; + params.viewAssociateOwner = viewAssociateOwner; + params.label = label; + ctrl.sendMessage(params, + _viewManagerMethodRegisterViewAssociateName); + } + void finishedRegisteringViewAssociates() { + if (!ctrl.isBound) { + ctrl.proxyError("The Proxy is closed."); + return; + } + var params = new _ViewManagerFinishedRegisteringViewAssociatesParams(); + ctrl.sendMessage(params, + _viewManagerMethodFinishedRegisteringViewAssociatesName); + } } class _ViewManagerStubControl @@ -367,6 +548,14 @@ message.payload); _impl.createViewTree(params.viewTree, params.viewTreeListener, params.label); break; + case _viewManagerMethodRegisterViewAssociateName: + var params = _ViewManagerRegisterViewAssociateParams.deserialize( + message.payload); + _impl.registerViewAssociate(params.viewAssociate, params.viewAssociateOwner, params.label); + break; + case _viewManagerMethodFinishedRegisteringViewAssociatesName: + _impl.finishedRegisteringViewAssociates(); + break; default: throw new bindings.MojoCodecError("Unexpected message name"); break; @@ -440,6 +629,12 @@ void createViewTree(Object viewTree, Object viewTreeListener, String label) { return impl.createViewTree(viewTree, viewTreeListener, label); } + void registerViewAssociate(Object viewAssociate, Object viewAssociateOwner, String label) { + return impl.registerViewAssociate(viewAssociate, viewAssociateOwner, label); + } + void finishedRegisteringViewAssociates() { + return impl.finishedRegisteringViewAssociates(); + } }
diff --git a/mojo/services/ui/views/interfaces/view_associates.mojom b/mojo/services/ui/views/interfaces/view_associates.mojom index 8fcac9c..a1d575e 100644 --- a/mojo/services/ui/views/interfaces/view_associates.mojom +++ b/mojo/services/ui/views/interfaces/view_associates.mojom
@@ -27,11 +27,6 @@ // This mechanism is designed to avoid a potential explosion in complexity // if all features which depend on the state of views were implemented // in one place. -// -// TODO(jeffbrown): In the current implementation, the view manager binds -// to a hard coded set of associates at start up time which can be overridden -// from the command-line. We should find a better way to register associates -// once we decide how the system as a whole should be initialized. [ServiceName="mojo::ui::ViewAssociate"] interface ViewAssociate { // Connects to the associate. @@ -65,6 +60,15 @@ handle<message_pipe> pipe); }; +// ViewAssociateOwner is used as a transferable reference which can +// be passed to a ViewAssociate's intended container as part of a request to +// register the ViewAssociate with the ViewManager. The ViewAssociateOwner will +// get a connection error callback if the corresponding ViewAssociate is +// destroyed, and similarly the ViewAssociate that it owns will be killed if +// this object is destroyed. +[ServiceName="mojo::ui::ViewAssociateOwner"] +interface ViewAssociateOwner {}; + // Provides information about the services offered by an associate. struct ViewAssociateInfo { // The names of view services offered by the associate.
diff --git a/mojo/services/ui/views/interfaces/view_manager.mojom b/mojo/services/ui/views/interfaces/view_manager.mojom index 6592ea9..68487bc 100644 --- a/mojo/services/ui/views/interfaces/view_manager.mojom +++ b/mojo/services/ui/views/interfaces/view_manager.mojom
@@ -67,4 +67,30 @@ CreateViewTree(mojo.ui.ViewTree& view_tree, mojo.ui.ViewTreeListener view_tree_listener, string? label); + + // Registers a view associate with the view manager. + // + // The services provided by |view_associate| will be made available to views. + // + // You receive a |view_associate_owner| which will receive a connection error + // callback if |view_associate| is closed. Conversely, the service for + // |view_associate| will be destroyed if |view_associate_owner| is destroyed. + // + // |label| is used to identify the ViewAssociate for debug purposes. + // + // TODO(mikejurka): This method should only be made accessible to trusted + // services, once we have a security story + RegisterViewAssociate(mojo.ui.ViewAssociate view_associate, + mojo.ui.ViewAssociateOwner& view_associate_owner, string? label); + + // Tells the view manager we've registered all our view associates. + // + // This allows it to prevent views from requesting services from ViewAssociate + // before all the ViewAssociates have been registered. + // + // TODO(mikejurka): This method should only be made accessible to trusted + // services, once we have a security story + // TODO(mikejurka): We should remove this method once we have a way of + // dynamically registering and unregistering ViewAssociates + FinishedRegisteringViewAssociates(); };
diff --git a/mojo/tools/data/apptests b/mojo/tools/data/apptests index b91dae3..9a8af7c 100644 --- a/mojo/tools/data/apptests +++ b/mojo/tools/data/apptests
@@ -64,6 +64,9 @@ "test": "mojo:ui_associates_apptests", }, { + "test": "mojo:view_manager_apptests", + }, + { "test": "mojo:url_response_disk_cache_apptests", }, {
diff --git a/services/BUILD.gn b/services/BUILD.gn index 6d4e00b..bfd560a 100644 --- a/services/BUILD.gn +++ b/services/BUILD.gn
@@ -85,6 +85,7 @@ "//services/media/common:apptests", "//services/native_support:apptests", "//services/prediction:apptests", + "//services/ui/view_manager:view_manager_apptests", ] # TODO: fix ffmpeg build
diff --git a/services/gfx/compositor/compositor_engine.cc b/services/gfx/compositor/compositor_engine.cc index 1e18866..8ad5b0f 100644 --- a/services/gfx/compositor/compositor_engine.cc +++ b/services/gfx/compositor/compositor_engine.cc
@@ -83,14 +83,18 @@ } // Destroy any renderers using this scene. + std::vector<RendererState*> renderers_to_destroy; for (auto& renderer : renderers_) { if (renderer->root_scene() == scene_state) { - LOG(ERROR) << "Destroying renderer whose root scene has become " - "unavailable: renderer=" - << renderer; - DestroyRenderer(renderer); + renderers_to_destroy.emplace_back(renderer); } } + for (auto& renderer : renderers_to_destroy) { + LOG(ERROR) << "Destroying renderer whose root scene has become " + "unavailable: renderer=" + << renderer; + DestroyRenderer(renderer); + } // Consider all dependent rendering to be invalidated. universe_.RemoveScene(scene_state->scene_token());
diff --git a/services/ui/launcher/launch_instance.cc b/services/ui/launcher/launch_instance.cc index c2aa2cb..82169cb 100644 --- a/services/ui/launcher/launch_instance.cc +++ b/services/ui/launcher/launch_instance.cc
@@ -4,6 +4,7 @@ #include "services/ui/launcher/launch_instance.h" +#include "base/bind.h" #include "base/command_line.h" #include "base/logging.h" #include "base/trace_event/trace_event.h" @@ -18,9 +19,13 @@ LaunchInstance::LaunchInstance(mojo::ApplicationImpl* app_impl, const std::string& app_url, + mojo::gfx::composition::Compositor* compositor, + mojo::ui::ViewManager* view_manager, const base::Closure& shutdown_callback) : app_impl_(app_impl), app_url_(app_url), + compositor_(compositor), + view_manager_(view_manager), shutdown_callback_(shutdown_callback), viewport_event_dispatcher_binding_(this) {} @@ -30,16 +35,6 @@ DVLOG(1) << "Launching " << app_url_; TRACE_EVENT0("launcher", __func__); - mojo::ConnectToService(app_impl_->shell(), "mojo:compositor_service", - GetProxy(&compositor_)); - compositor_.set_connection_error_handler(base::Bind( - &LaunchInstance::OnCompositorConnectionError, base::Unretained(this))); - - mojo::ConnectToService(app_impl_->shell(), "mojo:view_manager_service", - GetProxy(&view_manager_)); - view_manager_.set_connection_error_handler(base::Bind( - &LaunchInstance::OnViewManagerConnectionError, base::Unretained(this))); - InitViewport(); mojo::ui::ViewProviderPtr client_view_provider; @@ -50,16 +45,6 @@ nullptr); } -void LaunchInstance::OnCompositorConnectionError() { - LOG(ERROR) << "Exiting due to compositor connection error."; - shutdown_callback_.Run(); -} - -void LaunchInstance::OnViewManagerConnectionError() { - LOG(ERROR) << "Exiting due to view manager connection error."; - shutdown_callback_.Run(); -} - void LaunchInstance::InitViewport() { mojo::ConnectToService(app_impl_->shell(), "mojo:native_viewport_service", GetProxy(&viewport_)); @@ -92,7 +77,7 @@ mojo::ContextProviderPtr context_provider; viewport_->GetContextProvider(GetProxy(&context_provider)); - view_tree_.reset(new LauncherViewTree(compositor_.get(), view_manager_.get(), + view_tree_.reset(new LauncherViewTree(compositor_, view_manager_, context_provider.Pass(), metrics.Pass(), shutdown_callback_)); view_tree_->SetRoot(client_view_owner_.Pass());
diff --git a/services/ui/launcher/launch_instance.h b/services/ui/launcher/launch_instance.h index 644f31d..6a9c71b 100644 --- a/services/ui/launcher/launch_instance.h +++ b/services/ui/launcher/launch_instance.h
@@ -24,6 +24,8 @@ public: LaunchInstance(mojo::ApplicationImpl* app_impl, const std::string& app_url, + mojo::gfx::composition::Compositor* compositor, + mojo::ui::ViewManager* view_manager, const base::Closure& shutdown_callback); ~LaunchInstance() override; @@ -34,9 +36,6 @@ void OnEvent(mojo::EventPtr event, const mojo::Callback<void()>& callback) override; - void OnCompositorConnectionError(); - void OnViewManagerConnectionError(); - void InitViewport(); void OnViewportConnectionError(); void OnViewportCreated(mojo::ViewportMetricsPtr metrics); @@ -45,10 +44,10 @@ mojo::ApplicationImpl* app_impl_; std::string app_url_; - base::Closure shutdown_callback_; - mojo::gfx::composition::CompositorPtr compositor_; - mojo::ui::ViewManagerPtr view_manager_; + mojo::gfx::composition::Compositor* compositor_; + mojo::ui::ViewManager* view_manager_; + base::Closure shutdown_callback_; mojo::NativeViewportPtr viewport_; mojo::Binding<NativeViewportEventDispatcher>
diff --git a/services/ui/launcher/launcher_app.cc b/services/ui/launcher/launcher_app.cc index cc140a7..d301cc1 100644 --- a/services/ui/launcher/launcher_app.cc +++ b/services/ui/launcher/launcher_app.cc
@@ -6,11 +6,13 @@ #include "base/command_line.h" #include "base/logging.h" +#include "base/strings/string_split.h" #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_impl.h" +#include "mojo/public/cpp/application/connect.h" #include "mojo/public/cpp/application/service_provider_impl.h" namespace launcher { @@ -31,11 +33,64 @@ tracing_.Initialize(app_impl_); TRACE_EVENT0("launcher", __func__); + InitCompositor(); + InitViewManager(); + InitViewAssociates(command_line->GetSwitchValueASCII("view_associate_urls")); + for (size_t i = 0; i < command_line->GetArgs().size(); ++i) { Launch(command_line->GetArgs()[i]); } } +void LauncherApp::InitCompositor() { + mojo::ConnectToService(app_impl_->shell(), "mojo:compositor_service", + GetProxy(&compositor_)); + compositor_.set_connection_error_handler(base::Bind( + &LauncherApp::OnCompositorConnectionError, base::Unretained(this))); +} + +void LauncherApp::InitViewManager() { + mojo::ConnectToService(app_impl_->shell(), "mojo:view_manager_service", + GetProxy(&view_manager_)); + view_manager_.set_connection_error_handler(base::Bind( + &LauncherApp::OnViewManagerConnectionError, base::Unretained(this))); +} + +void LauncherApp::InitViewAssociates( + const std::string& associate_urls_command_line_param) { + // Build up the list of ViewAssociates we are going to start + auto associate_urls = + SplitString(associate_urls_command_line_param, ",", base::KEEP_WHITESPACE, + base::SPLIT_WANT_ALL); + + // If there's nothing we got from the command line, use our own list + if (associate_urls.empty()) { + // TODO(jeffbrown): Replace this hardcoded list. + associate_urls.push_back("mojo:input_manager_service"); + } + + view_associate_owners_.reserve(associate_urls.size()); + + // Connect to ViewAssociates. + for (const auto& url : associate_urls) { + // Connect to the ViewAssociate. + DVLOG(2) << "Connecting to ViewAssociate " << url; + mojo::ui::ViewAssociatePtr view_associate; + mojo::ConnectToService(app_impl_->shell(), url, GetProxy(&view_associate)); + + // Wire up the associate to the ViewManager. + mojo::ui::ViewAssociateOwnerPtr view_associate_owner; + view_manager_->RegisterViewAssociate(view_associate.Pass(), + GetProxy(&view_associate_owner), url); + + view_associate_owner.set_connection_error_handler(base::Bind( + &LauncherApp::OnViewAssociateConnectionError, base::Unretained(this))); + + view_associate_owners_.push_back(view_associate_owner.Pass()); + } + view_manager_->FinishedRegisteringViewAssociates(); +} + bool LauncherApp::ConfigureIncomingConnection( mojo::ServiceProviderImpl* service_provider_impl) { // Only present the launcher interface to the shell. @@ -52,8 +107,9 @@ void LauncherApp::Launch(const mojo::String& application_url) { uint32_t next_id = next_id_++; std::unique_ptr<LaunchInstance> instance(new LaunchInstance( - app_impl_, application_url, base::Bind(&LauncherApp::OnLaunchTermination, - base::Unretained(this), next_id))); + app_impl_, application_url, compositor_.get(), view_manager_.get(), + base::Bind(&LauncherApp::OnLaunchTermination, base::Unretained(this), + next_id))); instance->Launch(); launch_instances_.emplace(next_id, std::move(instance)); } @@ -65,4 +121,19 @@ } } +void LauncherApp::OnCompositorConnectionError() { + LOG(ERROR) << "Exiting due to compositor connection error."; + app_impl_->Terminate(); +} + +void LauncherApp::OnViewManagerConnectionError() { + LOG(ERROR) << "Exiting due to view manager connection error."; + app_impl_->Terminate(); +} + +void LauncherApp::OnViewAssociateConnectionError() { + LOG(ERROR) << "Exiting due to view associate connection error."; + app_impl_->Terminate(); +}; + } // namespace launcher
diff --git a/services/ui/launcher/launcher_app.h b/services/ui/launcher/launcher_app.h index dcf0d48..f04e645 100644 --- a/services/ui/launcher/launcher_app.h +++ b/services/ui/launcher/launcher_app.h
@@ -24,6 +24,9 @@ private: // |ApplicationDelegate|: void Initialize(mojo::ApplicationImpl* app_impl) override; + void InitCompositor(); + void InitViewManager(); + void InitViewAssociates(const std::string& associate_urls_command_line_param); bool ConfigureIncomingConnection( mojo::ServiceProviderImpl* service_provider_impl) override; @@ -32,14 +35,23 @@ void OnLaunchTermination(uint32_t id); + void OnCompositorConnectionError(); + void OnViewManagerConnectionError(); + void OnViewAssociateConnectionError(); + mojo::ApplicationImpl* app_impl_; mojo::TracingImpl tracing_; mojo::BindingSet<Launcher> bindings_; std::unordered_map<uint32_t, std::unique_ptr<LaunchInstance>> launch_instances_; + uint32_t next_id_; + mojo::gfx::composition::CompositorPtr compositor_; + mojo::ui::ViewManagerPtr view_manager_; + std::vector<mojo::ui::ViewAssociateOwnerPtr> view_associate_owners_; + DISALLOW_COPY_AND_ASSIGN(LauncherApp); };
diff --git a/services/ui/view_manager/BUILD.gn b/services/ui/view_manager/BUILD.gn index b55b429..6b892ce 100644 --- a/services/ui/view_manager/BUILD.gn +++ b/services/ui/view_manager/BUILD.gn
@@ -5,11 +5,8 @@ import("//mojo/public/mojo_application.gni") import("//testing/test.gni") -mojo_native_application("view_manager") { - output_name = "view_manager_service" - +source_set("common") { sources = [ - "main.cc", "view_associate_table.cc", "view_associate_table.h", "view_container_state.cc", @@ -32,13 +29,12 @@ "view_tree_state.h", ] - deps = [ + public_deps = [ "//base", "//mojo/application", "//mojo/common", "//mojo/common:tracing_impl", "//mojo/converters/geometry", - "//mojo/environment:chromium", "//mojo/public/cpp/bindings:bindings", "//mojo/services/geometry/cpp", "//mojo/services/gfx/composition/cpp", @@ -47,3 +43,40 @@ "//mojo/services/ui/views/interfaces", ] } + +mojo_native_application("view_manager") { + output_name = "view_manager_service" + + sources = [ + "main.cc", + ] + + deps = [ + ":common", + "//mojo/environment:chromium", + ] +} + +mojo_native_application("view_manager_apptests") { + output_name = "view_manager_apptests" + + testonly = true + + sources = [ + "tests/mock_view_associate.cc", + "tests/mock_view_associate.h", + "tests/view_associate_table_test.cc", + "tests/view_manager_test.cc", + "tests/view_manager_test_base.cc", + "tests/view_manager_test_base.h", + ] + + deps = [ + ":common", + "//base", + "//mojo/application:test_support", + "//mojo/public/cpp/bindings", + "//mojo/public/cpp/bindings:bindings", + "//mojo/ui/associates:mocks", + ] +}
diff --git a/services/ui/view_manager/tests/mock_view_associate.cc b/services/ui/view_manager/tests/mock_view_associate.cc new file mode 100644 index 0000000..4c464bf --- /dev/null +++ b/services/ui/view_manager/tests/mock_view_associate.cc
@@ -0,0 +1,33 @@ +// Copyright 2016 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/tests/mock_view_associate.h" + +namespace view_manager { +namespace test { + +MockViewAssociate::MockViewAssociate() {} +MockViewAssociate::~MockViewAssociate() {} + +void MockViewAssociate::Connect( + mojo::InterfaceHandle<mojo::ui::ViewInspector> inspector, + const ConnectCallback& callback) { + connect_invokecount++; + + auto info = mojo::ui::ViewAssociateInfo::New(); + callback.Run(info.Pass()); +} + +void MockViewAssociate::ConnectToViewService( + mojo::ui::ViewTokenPtr view_token, + const mojo::String& service_name, + mojo::ScopedMessagePipeHandle client_handle) {} + +void MockViewAssociate::ConnectToViewTreeService( + mojo::ui::ViewTreeTokenPtr view_tree_token, + const mojo::String& service_name, + mojo::ScopedMessagePipeHandle client_handle) {} + +} // namespace test +} // namespace view_manager
diff --git a/services/ui/view_manager/tests/mock_view_associate.h b/services/ui/view_manager/tests/mock_view_associate.h new file mode 100644 index 0000000..337348c --- /dev/null +++ b/services/ui/view_manager/tests/mock_view_associate.h
@@ -0,0 +1,33 @@ +// Copyright 2016 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/services/ui/views/interfaces/view_manager.mojom.h" +#include "mojo/services/ui/views/interfaces/views.mojom.h" + +namespace view_manager { +namespace test { + +class MockViewAssociate : public mojo::ui::ViewAssociate { + public: + MockViewAssociate(); + ~MockViewAssociate() override; + + void Connect(mojo::InterfaceHandle<mojo::ui::ViewInspector> inspector, + const ConnectCallback& callback) override; + + void ConnectToViewService( + mojo::ui::ViewTokenPtr view_token, + const mojo::String& service_name, + mojo::ScopedMessagePipeHandle client_handle) override; + + void ConnectToViewTreeService( + mojo::ui::ViewTreeTokenPtr view_tree_token, + const mojo::String& service_name, + mojo::ScopedMessagePipeHandle client_handle) override; + + int connect_invokecount = 0; +}; + +} // namespace test +} // namespace view_manager
diff --git a/services/ui/view_manager/tests/view_associate_table_test.cc b/services/ui/view_manager/tests/view_associate_table_test.cc new file mode 100644 index 0000000..01eb4b3 --- /dev/null +++ b/services/ui/view_manager/tests/view_associate_table_test.cc
@@ -0,0 +1,58 @@ +// Copyright 2016 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/services/ui/views/interfaces/view_manager.mojom.h" +#include "mojo/services/ui/views/interfaces/views.mojom.h" +#include "mojo/ui/associates/mock_view_inspector.h" +#include "services/ui/view_manager/tests/mock_view_associate.h" +#include "services/ui/view_manager/tests/view_manager_test_base.h" +#include "services/ui/view_manager/view_associate_table.h" + +namespace view_manager { +namespace test { + +class ViewAssociateTableTest : public ViewManagerTestBase { + public: + ViewAssociateTableTest() {} + ~ViewAssociateTableTest() override {} + + void SetUp() override { ViewManagerTestBase::SetUp(); } + + private: + DISALLOW_COPY_AND_ASSIGN(ViewAssociateTableTest); +}; + +TEST_F(ViewAssociateTableTest, RegisterViewAssociateThenCloseIt) { + // Create a mock view registry + mojo::ui::MockViewInspector mock_view_inspector; + + // Create a view associate table + ViewAssociateTable view_associate_table; + + { + // Create and bind a MockViewAssociate + mojo::ui::ViewAssociatePtr associate; + MockViewAssociate mock_view_associate; + mojo::Binding<mojo::ui::ViewAssociate> view_associate_binding( + &mock_view_associate, mojo::GetProxy(&associate)); + + // call ViewAssociateTable::RegisterViewAssociate + EXPECT_EQ((size_t)0, view_associate_table.associate_count()); + + mojo::ui::ViewAssociateOwnerPtr view_associate_owner; + view_associate_table.RegisterViewAssociate( + &mock_view_inspector, associate.Pass(), + mojo::GetProxy(&view_associate_owner), "test_view_associate"); + KICK_MESSAGE_LOOP_WHILE(view_associate_table.associate_count() != 1); + EXPECT_EQ((size_t)1, view_associate_table.associate_count()); + } + + // ViewAssociate has been destroyed (since it's out of scope now) + // Make sure it's been removed + KICK_MESSAGE_LOOP_WHILE(view_associate_table.associate_count() != 0); + EXPECT_EQ((size_t)0, view_associate_table.associate_count()); +} + +} // namespace test +} // namespace view_manager
diff --git a/services/ui/view_manager/tests/view_manager_test.cc b/services/ui/view_manager/tests/view_manager_test.cc new file mode 100644 index 0000000..13e30c6 --- /dev/null +++ b/services/ui/view_manager/tests/view_manager_test.cc
@@ -0,0 +1,174 @@ +// Copyright 2016 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/public/cpp/application/application_impl.h" +#include "mojo/public/cpp/application/connect.h" +#include "mojo/services/ui/views/interfaces/view_manager.mojom.h" +#include "mojo/services/ui/views/interfaces/views.mojom.h" +#include "services/ui/view_manager/tests/mock_view_associate.h" +#include "services/ui/view_manager/tests/view_manager_test_base.h" + +namespace view_manager { +namespace test { + +class MockViewListener : public mojo::ui::ViewListener { + public: + MockViewListener() {} + ~MockViewListener() override {} + + void OnPropertiesChanged( + uint32_t scene_version, + mojo::ui::ViewPropertiesPtr properties, + const OnPropertiesChangedCallback& callback) override {} +}; + +class ViewManagerTest : public ViewManagerTestBase { + public: + ViewManagerTest() {} + ~ViewManagerTest() override {} + + void SetUp() override { + ViewManagerTestBase::SetUp(); + + // Connect to view manager + mojo::ConnectToService(shell(), "mojo:view_manager_service", + mojo::GetProxy(&view_manager_)); + } + + protected: + mojo::ui::ViewManagerPtr view_manager_; + + private: + DISALLOW_COPY_AND_ASSIGN(ViewManagerTest); +}; + +TEST_F(ViewManagerTest, CreateAViewManagerAndView) { + // Create and bind a mock view listener + mojo::ui::ViewListenerPtr view_listener; + MockViewListener mock_view_listener; + mojo::Binding<mojo::ui::ViewListener> view_listener_binding( + &mock_view_listener, mojo::GetProxy(&view_listener)); + + // Create a view + mojo::ui::ViewPtr view; + mojo::ui::ViewOwnerPtr client_view_owner; + view_manager_->CreateView(mojo::GetProxy(&view), + mojo::GetProxy(&client_view_owner), + view_listener.Pass(), "test_view"); + + // Call View::GetToken. Check that you get the callback. + int view_token_callback_invokecount = 0; + auto view_token_callback = [&view_token_callback_invokecount]( + mojo::ui::ViewTokenPtr token) { view_token_callback_invokecount++; }; + + EXPECT_EQ(0, view_token_callback_invokecount); + view->GetToken(view_token_callback); + + KICK_MESSAGE_LOOP_WHILE(view_token_callback_invokecount != 1); + + EXPECT_EQ(1, view_token_callback_invokecount); +} + +TEST_F(ViewManagerTest, ConnectAMockViewAssociate) { + // Create and bind a MockViewAssociate + mojo::InterfaceHandle<mojo::ui::ViewAssociate> associate; + MockViewAssociate mock_view_associate; + mojo::Binding<mojo::ui::ViewAssociate> view_associate_binding( + &mock_view_associate, mojo::GetProxy(&associate)); + + // Call ViewManager::RegisterViewAssociate. MockViewAssociate::Connect + // should be called back + EXPECT_EQ(0, mock_view_associate.connect_invokecount); + mojo::ui::ViewAssociateOwnerPtr view_associate_owner; + view_manager_->RegisterViewAssociate(associate.Pass(), + mojo::GetProxy(&view_associate_owner), + "test_view_associate"); + + KICK_MESSAGE_LOOP_WHILE(mock_view_associate.connect_invokecount != 1); + + EXPECT_EQ(1, mock_view_associate.connect_invokecount); +} + +TEST_F(ViewManagerTest, DisconnectAMockViewAssociate) { + mojo::ui::ViewAssociateOwnerPtr view_associate_owner; + int owner_connection_error_callback_invokecount = 0; + + { + // Create and bind a MockViewAssociate + mojo::InterfaceHandle<mojo::ui::ViewAssociate> associate; + MockViewAssociate mock_view_associate; + mojo::Binding<mojo::ui::ViewAssociate> view_associate_binding( + &mock_view_associate, mojo::GetProxy(&associate)); + + // Call ViewManager::RegisterViewAssociate. MockViewAssociate::Connect + // should be called back + EXPECT_EQ(0, mock_view_associate.connect_invokecount); + + view_manager_->RegisterViewAssociate(associate.Pass(), + mojo::GetProxy(&view_associate_owner), + "test_view_associate_xyz"); + + // set a callback for errors + view_associate_owner.set_connection_error_handler( + // use lambda function as callback + [&owner_connection_error_callback_invokecount]() { + owner_connection_error_callback_invokecount++; + }); + + KICK_MESSAGE_LOOP_WHILE(mock_view_associate.connect_invokecount != 1); + + EXPECT_EQ(1, mock_view_associate.connect_invokecount); + + EXPECT_EQ(0, owner_connection_error_callback_invokecount); + } + + // mock_view_associate is out of scope, should be destroyed + // we expect to get a connection error from the owner + KICK_MESSAGE_LOOP_WHILE(owner_connection_error_callback_invokecount != 1) + + EXPECT_EQ(1, owner_connection_error_callback_invokecount); +} + +TEST_F(ViewManagerTest, DisconnectAViewAssociateOwner) { + // Create and bind a MockViewAssociate + mojo::InterfaceHandle<mojo::ui::ViewAssociate> associate; + MockViewAssociate mock_view_associate; + mojo::Binding<mojo::ui::ViewAssociate> view_associate_binding( + &mock_view_associate, mojo::GetProxy(&associate)); + + // set a callback for errors + int connection_error_callback_invokecount = 0; + view_associate_binding.set_connection_error_handler( + // use lambda function as callback + [&connection_error_callback_invokecount]() { + connection_error_callback_invokecount++; + }); + + { + mojo::ui::ViewAssociateOwnerPtr view_associate_owner; + + // Call ViewManager::RegisterViewAssociate. MockViewAssociate::Connect + // should be called back + EXPECT_EQ(0, mock_view_associate.connect_invokecount); + + view_manager_->RegisterViewAssociate(associate.Pass(), + mojo::GetProxy(&view_associate_owner), + "test_view_associate_xyz"); + + KICK_MESSAGE_LOOP_WHILE(mock_view_associate.connect_invokecount != 1); + + EXPECT_EQ(1, mock_view_associate.connect_invokecount); + + EXPECT_EQ(0, connection_error_callback_invokecount); + } + + // view_associate_owner is out of scope, should be destroyed + // we expect to get a connection error from the view associate + KICK_MESSAGE_LOOP_WHILE(connection_error_callback_invokecount != 1) + + EXPECT_EQ(1, connection_error_callback_invokecount); +} + +} // namespace test +} // namespace view_manager
diff --git a/services/ui/view_manager/tests/view_manager_test_base.cc b/services/ui/view_manager/tests/view_manager_test_base.cc new file mode 100644 index 0000000..e123720 --- /dev/null +++ b/services/ui/view_manager/tests/view_manager_test_base.cc
@@ -0,0 +1,38 @@ +// Copyright 2016 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/tests/view_manager_test_base.h" + +#include "base/run_loop.h" +#include "mojo/public/cpp/application/application_test_base.h" +#include "mojo/public/cpp/application/connect.h" +#include "mojo/services/ui/views/interfaces/view_manager.mojom.h" +#include "mojo/services/ui/views/interfaces/views.mojom.h" + +namespace view_manager { +namespace test { + +ViewManagerTestBase::ViewManagerTestBase() : weak_factory_(this) {} + +ViewManagerTestBase::~ViewManagerTestBase() {} + +void ViewManagerTestBase::SetUp() { + mojo::test::ApplicationTestBase::SetUp(); + quit_message_loop_callback_ = + base::Bind(&ViewManagerTestBase::QuitMessageLoopCallback, + weak_factory_.GetWeakPtr()); +} + +void ViewManagerTestBase::QuitMessageLoopCallback() { + base::MessageLoop::current()->Quit(); +} + +void ViewManagerTestBase::KickMessageLoop() { + base::MessageLoop::current()->PostDelayedTask( + FROM_HERE, quit_message_loop_callback_, kDefaultMessageDelay); + base::MessageLoop::current()->Run(); +} + +} // namespace test +} // namespace view_manager
diff --git a/services/ui/view_manager/tests/view_manager_test_base.h b/services/ui/view_manager/tests/view_manager_test_base.h new file mode 100644 index 0000000..4198be4 --- /dev/null +++ b/services/ui/view_manager/tests/view_manager_test_base.h
@@ -0,0 +1,39 @@ +// Copyright 2016 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 "mojo/public/cpp/application/application_test_base.h" + +namespace view_manager { +namespace test { + +const base::TimeDelta kDefaultMessageDelay = + base::TimeDelta::FromMilliseconds(10); + +// Run message loop until condition is true (timeout after 400*10ms = 4000ms) +#define KICK_MESSAGE_LOOP_WHILE(x) \ + for (int i = 0; x && i < 400; i++) { \ + KickMessageLoop(); \ + } + +class ViewManagerTestBase : public mojo::test::ApplicationTestBase { + public: + ViewManagerTestBase(); + ~ViewManagerTestBase() override; + + void SetUp() override; + void KickMessageLoop(); + + protected: + base::Closure quit_message_loop_callback_; + base::WeakPtrFactory<ViewManagerTestBase> weak_factory_; + + private: + void QuitMessageLoopCallback(); + + DISALLOW_COPY_AND_ASSIGN(ViewManagerTestBase); +}; + +} // namespace test +} // namespace view_manager
diff --git a/services/ui/view_manager/view_associate_table.cc b/services/ui/view_manager/view_associate_table.cc index 2088696..4acb471 100644 --- a/services/ui/view_manager/view_associate_table.cc +++ b/services/ui/view_manager/view_associate_table.cc
@@ -23,39 +23,56 @@ ViewAssociateTable::~ViewAssociateTable() {} -void ViewAssociateTable::ConnectAssociates( - mojo::ApplicationImpl* app_impl, +void ViewAssociateTable::RegisterViewAssociate( mojo::ui::ViewInspector* inspector, - const std::vector<std::string>& urls, - const AssociateConnectionErrorCallback& connection_error_callback) { - DCHECK(app_impl); + mojo::ui::ViewAssociatePtr associate, + mojo::InterfaceRequest<mojo::ui::ViewAssociateOwner> + view_associate_owner_request, + const mojo::String& label) { DCHECK(inspector); + DCHECK(associate.is_bound()); - for (auto& url : urls) { - DVLOG(1) << "Connecting to view associate: url=" << url; - associates_.emplace_back(new AssociateData(url, inspector)); - AssociateData* data = associates_.back().get(); + std::string sanitized_label = + label.get().substr(0, mojo::ui::kLabelMaxLength); + associates_.emplace_back( + new AssociateData(sanitized_label, associate.Pass(), this, inspector)); + AssociateData* data = associates_.back().get(); - mojo::ConnectToService(app_impl->shell(), url, GetProxy(&data->associate)); - data->associate.set_connection_error_handler( - base::Bind(connection_error_callback, url)); + data->BindOwner(view_associate_owner_request.Pass()); - mojo::ui::ViewInspectorPtr inspector; - data->inspector_binding.Bind(GetProxy(&inspector)); - data->associate->Connect( - inspector.Pass(), - base::Bind(&ViewAssociateTable::OnConnected, base::Unretained(this), - pending_connection_count_)); + // Set it to use our error handler. + data->associate.set_connection_error_handler( + base::Bind(&ViewAssociateTable::OnAssociateConnectionError, + base::Unretained(this), data)); - pending_connection_count_++; - } + data->associate_owner.set_connection_error_handler( + base::Bind(&ViewAssociateTable::OnAssociateOwnerConnectionError, + base::Unretained(this), data)); + + // Connect the associate to our view inspector. + mojo::ui::ViewInspectorPtr inspector_ptr; + data->inspector_binding.Bind(GetProxy(&inspector_ptr)); + data->associate->Connect( + inspector_ptr.Pass(), + base::Bind(&ViewAssociateTable::OnConnected, base::Unretained(this), + pending_connection_count_)); + + // Wait for the associate to connect to our view inspector. + pending_connection_count_++; +} + +void ViewAssociateTable::FinishedRegisteringViewAssociates() { + waiting_to_register_associates_ = false; + + // If no more pending connections, kick off deferred work + CompleteDeferredWorkIfReady(); } void ViewAssociateTable::ConnectToViewService( mojo::ui::ViewTokenPtr view_token, const mojo::String& service_name, mojo::ScopedMessagePipeHandle client_handle) { - if (pending_connection_count_) { + if (waiting_to_register_associates_ || pending_connection_count_) { deferred_work_.push_back( base::Bind(&ViewAssociateTable::ConnectToViewService, base::Unretained(this), base::Passed(view_token.Pass()), @@ -68,7 +85,7 @@ if (Contains(data->info->view_service_names, service_name)) { DVLOG(2) << "Connecting to view service: view_token=" << view_token << ", service_name=" << service_name - << ", associate_url=" << data->url; + << ", associate_label=" << data->label; DCHECK(data->associate); data->associate->ConnectToViewService(view_token.Pass(), service_name, client_handle.Pass()); @@ -81,11 +98,42 @@ // Allow pipe to be closed as an indication of failure. } +bool ViewAssociateTable::RemoveAssociateData(AssociateData* associate_data, + std::string& label) { + for (auto it = associates_.begin(); it != associates_.end(); it++) { + AssociateData* data = it->get(); + if (associate_data == data) { + label = data->label; + associates_.erase(it); + return true; + } + } + return false; +} + +void ViewAssociateTable::OnAssociateConnectionError( + AssociateData* associate_data) { + std::string label; + bool removed = RemoveAssociateData(associate_data, label); + DCHECK(removed); + DVLOG(2) << "ViewAssociate disconnected, removing from table" + << ", associate_label=" << label; +} + +void ViewAssociateTable::OnAssociateOwnerConnectionError( + AssociateData* associate_data) { + std::string label; + bool removed = RemoveAssociateData(associate_data, label); + DCHECK(removed); + DVLOG(2) << "ViewAssociateOwner disconnected, removing from table" + << ", associate_label=" << label; +} + void ViewAssociateTable::ConnectToViewTreeService( mojo::ui::ViewTreeTokenPtr view_tree_token, const mojo::String& service_name, mojo::ScopedMessagePipeHandle client_handle) { - if (pending_connection_count_) { + if (waiting_to_register_associates_ || pending_connection_count_) { deferred_work_.push_back( base::Bind(&ViewAssociateTable::ConnectToViewTreeService, base::Unretained(this), base::Passed(view_tree_token.Pass()), @@ -98,7 +146,7 @@ if (Contains(data->info->view_tree_service_names, service_name)) { DVLOG(2) << "Connecting to view tree service: view_tree_token=" << view_tree_token << ", service_name=" << service_name - << ", associate_url=" << data->url; + << ", associate_label=" << data->label; DCHECK(data->associate); data->associate->ConnectToViewTreeService( view_tree_token.Pass(), service_name, client_handle.Pass()); @@ -117,28 +165,44 @@ DCHECK(pending_connection_count_); DCHECK(!associates_[index]->info); - DVLOG(1) << "Connected to view associate: url=" << associates_[index]->url + DVLOG(1) << "Connected to view associate: label=" << associates_[index]->label << ", info=" << info; associates_[index]->info = info.Pass(); pending_connection_count_--; - if (!pending_connection_count_) - CompleteDeferredWork(); + CompleteDeferredWorkIfReady(); } -void ViewAssociateTable::CompleteDeferredWork() { - DCHECK(!pending_connection_count_); +void ViewAssociateTable::CompleteDeferredWorkIfReady() { + // We check to see if all the ViewAssociates have been registered, and if + // they connected to us. Otherwise, we keep the work deferred. + if (!waiting_to_register_associates_ && !pending_connection_count_) { + for (auto& work : deferred_work_) + work.Run(); + deferred_work_.clear(); + } +} - for (auto& work : deferred_work_) - work.Run(); - deferred_work_.clear(); +size_t ViewAssociateTable::associate_count() { + return associates_.size(); } ViewAssociateTable::AssociateData::AssociateData( - const std::string& url, + const std::string& label, + mojo::ui::ViewAssociatePtr associate, + mojo::ui::ViewAssociateOwner* associate_owner_impl, mojo::ui::ViewInspector* inspector) - : url(url), inspector_binding(inspector) {} + : label(label), + associate(associate.Pass()), + associate_owner(associate_owner_impl), + inspector_binding(inspector) {} ViewAssociateTable::AssociateData::~AssociateData() {} +void ViewAssociateTable::AssociateData::BindOwner( + mojo::InterfaceRequest<mojo::ui::ViewAssociateOwner> + view_associate_owner_request) { + associate_owner.Bind(view_associate_owner_request.Pass()); +} + } // namespace view_manager
diff --git a/services/ui/view_manager/view_associate_table.h b/services/ui/view_manager/view_associate_table.h index 9cdc71b..4952498 100644 --- a/services/ui/view_manager/view_associate_table.h +++ b/services/ui/view_manager/view_associate_table.h
@@ -14,26 +14,26 @@ #include "mojo/common/binding_set.h" #include "mojo/public/cpp/application/application_impl.h" #include "mojo/services/ui/views/interfaces/view_associates.mojom.h" +#include "mojo/services/ui/views/interfaces/view_manager.mojom.h" namespace view_manager { // Maintains a table of all connected view associates. -class ViewAssociateTable { +class ViewAssociateTable : public mojo::ui::ViewAssociateOwner { public: using AssociateConnectionErrorCallback = base::Callback<void(const std::string&)>; ViewAssociateTable(); - ~ViewAssociateTable(); + ~ViewAssociateTable() override; - // Begins connecting to the view associates. - // Invokes |connection_error_callback| if an associate connection fails - // and provides the associate's url. - void ConnectAssociates( - mojo::ApplicationImpl* app_impl, + void RegisterViewAssociate( mojo::ui::ViewInspector* inspector, - const std::vector<std::string>& urls, - const AssociateConnectionErrorCallback& connection_error_callback); + mojo::ui::ViewAssociatePtr associate, + mojo::InterfaceRequest<mojo::ui::ViewAssociateOwner> view_associate_owner, + const mojo::String& label); + + void FinishedRegisteringViewAssociates(); // Connects to services offered by the view associates. void ConnectToViewService(mojo::ui::ViewTokenPtr view_token, @@ -45,22 +45,35 @@ void OnConnected(uint32_t index, mojo::ui::ViewAssociateInfoPtr info); - void CompleteDeferredWork(); + size_t associate_count(); private: struct AssociateData { - AssociateData(const std::string& url, mojo::ui::ViewInspector* inspector); + AssociateData(const std::string& label, + mojo::ui::ViewAssociatePtr associate, + mojo::ui::ViewAssociateOwner* associate_owner_impl, + mojo::ui::ViewInspector* inspector); ~AssociateData(); - const std::string url; + void BindOwner(mojo::InterfaceRequest<mojo::ui::ViewAssociateOwner> + view_associate_owner_request); + + const std::string label; mojo::ui::ViewAssociatePtr associate; + mojo::Binding<mojo::ui::ViewAssociateOwner> associate_owner; mojo::ui::ViewAssociateInfoPtr info; mojo::Binding<mojo::ui::ViewInspector> inspector_binding; }; + bool RemoveAssociateData(AssociateData* associate_data, std::string& label); + void CompleteDeferredWorkIfReady(); + void OnAssociateOwnerConnectionError(AssociateData* associate_data); + void OnAssociateConnectionError(AssociateData* associate_data); + std::vector<std::unique_ptr<AssociateData>> associates_; uint32_t pending_connection_count_ = 0u; + bool waiting_to_register_associates_ = true; std::vector<base::Closure> deferred_work_; DISALLOW_COPY_AND_ASSIGN(ViewAssociateTable);
diff --git a/services/ui/view_manager/view_manager_app.cc b/services/ui/view_manager/view_manager_app.cc index 171d8bc..3ea2d69 100644 --- a/services/ui/view_manager/view_manager_app.cc +++ b/services/ui/view_manager/view_manager_app.cc
@@ -44,19 +44,6 @@ // Create the registry. registry_.reset(new ViewRegistry(compositor.Pass())); - - // Connect to associates. - // TODO(jeffbrown): Consider making the launcher register associates - // with the view manager or perhaps per view tree. - std::vector<std::string> associate_urls = command_line->GetArgs(); - if (associate_urls.empty()) { - // TODO(jeffbrown): Replace this hardcoded list. - associate_urls.push_back("mojo:input_manager_service"); - } - registry_->ConnectAssociates( - app_impl_, associate_urls, - base::Bind(&ViewManagerApp::OnAssociateConnectionError, - base::Unretained(this))); } bool ViewManagerApp::ConfigureIncomingConnection( @@ -76,11 +63,6 @@ Shutdown(); } -void ViewManagerApp::OnAssociateConnectionError(const std::string& url) { - LOG(ERROR) << "Exiting due to view associate connection error: url=" << url; - Shutdown(); -} - void ViewManagerApp::Shutdown() { app_impl_->Terminate(); }
diff --git a/services/ui/view_manager/view_manager_app.h b/services/ui/view_manager/view_manager_app.h index 2c83451..05e0844 100644 --- a/services/ui/view_manager/view_manager_app.h +++ b/services/ui/view_manager/view_manager_app.h
@@ -30,7 +30,6 @@ mojo::ServiceProviderImpl* service_provider_impl) override; void OnCompositorConnectionError(); - void OnAssociateConnectionError(const std::string& url); void Shutdown();
diff --git a/services/ui/view_manager/view_manager_impl.cc b/services/ui/view_manager/view_manager_impl.cc index 7e443b2..975bc40 100644 --- a/services/ui/view_manager/view_manager_impl.cc +++ b/services/ui/view_manager/view_manager_impl.cc
@@ -6,6 +6,7 @@ #include <utility> +#include "base/bind.h" #include "services/ui/view_manager/view_impl.h" #include "services/ui/view_manager/view_tree_impl.h" @@ -36,4 +37,19 @@ label); } +// TODO(mikejurka): This should only be called by trusted code (ie launcher), +// once we have a security story. +void ViewManagerImpl::RegisterViewAssociate( + mojo::InterfaceHandle<mojo::ui::ViewAssociate> view_associate, + mojo::InterfaceRequest<mojo::ui::ViewAssociateOwner> view_associate_owner, + const mojo::String& label) { + registry_->RegisterViewAssociate( + registry_, mojo::ui::ViewAssociatePtr::Create(std::move(view_associate)), + view_associate_owner.Pass(), label); +} + +void ViewManagerImpl::FinishedRegisteringViewAssociates() { + registry_->FinishedRegisteringViewAssociates(); +} + } // namespace view_manager
diff --git a/services/ui/view_manager/view_manager_impl.h b/services/ui/view_manager/view_manager_impl.h index a84e933..1b4ca09 100644 --- a/services/ui/view_manager/view_manager_impl.h +++ b/services/ui/view_manager/view_manager_impl.h
@@ -28,6 +28,11 @@ mojo::InterfaceRequest<mojo::ui::ViewTree> view_tree_request, mojo::InterfaceHandle<mojo::ui::ViewTreeListener> view_tree_listener, const mojo::String& label) override; + void RegisterViewAssociate( + mojo::InterfaceHandle<mojo::ui::ViewAssociate> view_associate, + mojo::InterfaceRequest<mojo::ui::ViewAssociateOwner> view_associate_owner, + const mojo::String& label) override; + void FinishedRegisteringViewAssociates() override; ViewRegistry* registry_;
diff --git a/services/ui/view_manager/view_registry.cc b/services/ui/view_manager/view_registry.cc index aee3a46..1a70f73 100644 --- a/services/ui/view_manager/view_registry.cc +++ b/services/ui/view_manager/view_registry.cc
@@ -59,14 +59,21 @@ ViewRegistry::~ViewRegistry() {} -void ViewRegistry::ConnectAssociates( - mojo::ApplicationImpl* app_impl, - const std::vector<std::string>& urls, - const AssociateConnectionErrorCallback& connection_error_callback) { - associate_table_.ConnectAssociates(app_impl, this, urls, - connection_error_callback); +// REGISTERING ASSOCIATES + +void ViewRegistry::RegisterViewAssociate( + mojo::ui::ViewInspector* view_inspector, + mojo::ui::ViewAssociatePtr view_associate, + mojo::InterfaceRequest<mojo::ui::ViewAssociateOwner> view_associate_owner, + const mojo::String& label) { + associate_table_.RegisterViewAssociate(view_inspector, view_associate.Pass(), + view_associate_owner.Pass(), label); } +void ViewRegistry::FinishedRegisteringViewAssociates() { + associate_table_.FinishedRegisteringViewAssociates(); +}; + // CREATE / DESTROY VIEWS void ViewRegistry::CreateView(
diff --git a/services/ui/view_manager/view_registry.h b/services/ui/view_manager/view_registry.h index 9550e29..1b1e17c 100644 --- a/services/ui/view_manager/view_registry.h +++ b/services/ui/view_manager/view_registry.h
@@ -31,14 +31,6 @@ explicit ViewRegistry(mojo::gfx::composition::CompositorPtr compositor); ~ViewRegistry() override; - // Begins connecting to the view associates. - // Invokes |connection_error_callback| if an associate connection fails - // and provides the associate's url. - void ConnectAssociates( - mojo::ApplicationImpl* app_impl, - const std::vector<std::string>& urls, - const AssociateConnectionErrorCallback& connection_error_callback); - // VIEW MANAGER REQUESTS // Creates a view and returns its ViewToken. @@ -54,6 +46,14 @@ mojo::ui::ViewTreeListenerPtr view_tree_listener, const mojo::String& label); + void RegisterViewAssociate( + mojo::ui::ViewInspector* view_inspector, + mojo::ui::ViewAssociatePtr view_associate, + mojo::InterfaceRequest<mojo::ui::ViewAssociateOwner> view_associate_owner, + const mojo::String& label); + + void FinishedRegisteringViewAssociates(); + // VIEW STUB REQUESTS void OnViewResolved(ViewStub* view_stub, mojo::ui::ViewTokenPtr view_token);