diff options
author | ben <ben@chromium.org> | 2016-01-11 18:44:08 -0800 |
---|---|---|
committer | Commit bot <commit-bot@chromium.org> | 2016-01-12 02:44:59 +0000 |
commit | 273c1e30241a8a5509ad9b307380ed8600ba82d8 (patch) | |
tree | 25da3f1a02f1f9d5f129ba8d794d20b51355a07b /mojo/shell/public | |
parent | 9c1122c7163411be938195ba546460ba54d33b00 (diff) | |
download | chromium_src-273c1e30241a8a5509ad9b307380ed8600ba82d8.zip chromium_src-273c1e30241a8a5509ad9b307380ed8600ba82d8.tar.gz chromium_src-273c1e30241a8a5509ad9b307380ed8600ba82d8.tar.bz2 |
Move mojo/application/public -> mojo/shell/public
TBR=sky@chromium.org
http://crbug.com/575308
Review URL: https://codereview.chromium.org/1565343003
Cr-Commit-Position: refs/heads/master@{#368776}
Diffstat (limited to 'mojo/shell/public')
44 files changed, 2920 insertions, 0 deletions
diff --git a/mojo/shell/public/cpp/BUILD.gn b/mojo/shell/public/cpp/BUILD.gn new file mode 100644 index 0000000..55cd782 --- /dev/null +++ b/mojo/shell/public/cpp/BUILD.gn @@ -0,0 +1,111 @@ +# Copyright 2014 The Chromium Authors. All rights reserved. +# Use of this source code is governed by a BSD-style license that can be +# found in the LICENSE file. + +# GYP version: mojo/mojo_base.gyp:mojo_application_base +source_set("cpp") { + public_deps = [ + ":init_commandline", + ":sources", + ] +} + +# Like the target above, but without special commandline initialization that +# apps use. +source_set("cpp_for_chromium") { + public_deps = [ + ":sources", + ] +} + +source_set("sources") { + sources = [ + "app_lifetime_helper.h", + "application_connection.h", + "application_delegate.h", + "application_impl.h", + "application_runner.h", + "connect.h", + "initialize_base_and_icu.cc", + "interface_factory.h", + "interface_factory_impl.h", + "lib/app_lifetime_helper.cc", + "lib/application_delegate.cc", + "lib/application_impl.cc", + "lib/application_runner.cc", + "lib/interface_factory_connector.h", + "lib/service_connector_registry.cc", + "lib/service_connector_registry.h", + "lib/service_provider_impl.cc", + "lib/service_registry.cc", + "lib/service_registry.h", + "service_connector.h", + "service_provider_impl.h", + ] + + deps = [ + "//base:i18n", + "//mojo/common", + "//mojo/converters/network", + "//mojo/environment:chromium", + "//mojo/message_pump", + ] + + public_deps = [ + "//base", + "//mojo/public/cpp/bindings", + "//mojo/public/cpp/system", + "//mojo/shell/public/interfaces", + ] +} + +source_set("init_commandline") { + sources = [ + "lib/init_commandline.cc", + ] +} + +source_set("content_handler") { + sources = [ + "content_handler_factory.h", + "lib/content_handler_factory.cc", + ] + deps = [ + ":cpp", + + # TODO: this code should not depend on base. + "//base", + "//mojo/message_pump", + "//mojo/services/network/public/interfaces", + "//mojo/shell/public/interfaces:interfaces_cpp_sources", + ] +} + +source_set("test_support") { + testonly = true + sources = [ + "application_test_base.h", + "lib/application_test_base.cc", + "lib/application_test_main.cc", + ] + + public_deps = [ + ":cpp", + "//testing/gtest", + ] + + deps = [ + "//base", + "//base/test:test_support", + "//mojo/logging", + "//mojo/public/cpp/bindings", + "//mojo/public/cpp/environment", + "//mojo/public/cpp/system", + "//mojo/shell/public/interfaces:interfaces_cpp_sources", + ] + + data_deps = [] + if (is_android) { + data_deps += [ "//mojo/android" ] + } +} diff --git a/mojo/shell/public/cpp/app_lifetime_helper.h b/mojo/shell/public/cpp/app_lifetime_helper.h new file mode 100644 index 0000000..ae2503f --- /dev/null +++ b/mojo/shell/public/cpp/app_lifetime_helper.h @@ -0,0 +1,81 @@ +// 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 MOJO_SHELL_PUBLIC_CPP_APP_LIFETIME_HELPER_H_ +#define MOJO_SHELL_PUBLIC_CPP_APP_LIFETIME_HELPER_H_ + +#include "base/macros.h" +#include "base/memory/scoped_ptr.h" +#include "base/single_thread_task_runner.h" + +namespace mojo { + +class ApplicationImpl; +class AppLifetimeHelper; + +// A service implementation should keep this object as a member variable to hold +// a reference on the application. +// Since services can live on different threads than the app, this class is +// safe to use on any thread. However, each instance should only be used on one +// thread at a time (otherwise there'll be races between the addref resulting +// from cloning and destruction). +class AppRefCount { + public: + ~AppRefCount(); + + // When a service creates another object that is held by the client, it should + // also vend to it a refcount using this method. That way if the caller stops + // holding on to the service but keeps the reference to the object, the app is + // still alive. + scoped_ptr<AppRefCount> Clone(); + + private: + friend AppLifetimeHelper; + + AppRefCount(AppLifetimeHelper* app_lifetime_helper, + scoped_refptr<base::SingleThreadTaskRunner> app_task_runner); + + // Don't need to use weak ptr because if the app thread is alive that means + // app is. + AppLifetimeHelper* app_lifetime_helper_; + scoped_refptr<base::SingleThreadTaskRunner> app_task_runner_; + +#ifndef NDEBUG + scoped_refptr<base::SingleThreadTaskRunner> clone_task_runner_; +#endif + + DISALLOW_COPY_AND_ASSIGN(AppRefCount); +}; + +// This is a helper class for apps to manage their lifetime, specifically so +// apps can quit when they're not used anymore. +// An app can contain an object of this class as a member variable. Each time it +// creates an instance of a service, it gives it a refcount using +// CreateAppRefCount. The service implementation then keeps that object as a +// member variable. When all the service implemenations go away, the app will be +// quit with a call to mojo::ApplicationImpl::Terminate(). +class AppLifetimeHelper { + public: + explicit AppLifetimeHelper(ApplicationImpl* app); + ~AppLifetimeHelper(); + + scoped_ptr<AppRefCount> CreateAppRefCount(); + + private: + friend AppRefCount; + void AddRef(); + void Release(); + + friend ApplicationImpl; + void OnQuit(); + + ApplicationImpl* app_; + int ref_count_; + + DISALLOW_COPY_AND_ASSIGN(AppLifetimeHelper); +}; + +} // namespace mojo + +#endif // MOJO_SHELL_PUBLIC_CPP_APP_LIFETIME_HELPER_H_ diff --git a/mojo/shell/public/cpp/application_connection.h b/mojo/shell/public/cpp/application_connection.h new file mode 100644 index 0000000..64d613d --- /dev/null +++ b/mojo/shell/public/cpp/application_connection.h @@ -0,0 +1,144 @@ +// Copyright 2014 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef MOJO_SHELL_PUBLIC_CPP_APPLICATION_CONNECTION_H_ +#define MOJO_SHELL_PUBLIC_CPP_APPLICATION_CONNECTION_H_ + +#include <stdint.h> + +#include <string> +#include <utility> + +#include "base/memory/weak_ptr.h" +#include "mojo/shell/public/cpp/lib/interface_factory_connector.h" +#include "mojo/shell/public/interfaces/service_provider.mojom.h" + +namespace mojo { + +class ServiceConnector; + +// Represents a connection to another application. An instance of this class is +// passed to ApplicationDelegate's ConfigureIncomingConnection() method each +// time a connection is made to this app, and to ApplicationDelegate's +// ConfigureOutgoingConnection() method when the app connects to another. +// +// To use, define a class that implements your specific service API (e.g., +// FooImpl to implement a service named Foo). Then implement an +// InterfaceFactory<Foo> that binds instances of FooImpl to +// InterfaceRequest<Foo>s and register that on the connection like this: +// +// connection->AddService(&factory); +// +// Or, if you have multiple factories implemented by the same type, explicitly +// specify the interface to register the factory for: +// +// connection->AddService<Foo>(&my_foo_and_bar_factory_); +// connection->AddService<Bar>(&my_foo_and_bar_factory_); +// +// The InterfaceFactory must outlive the ApplicationConnection. +// +// Additionally you specify a ServiceConnector. If a ServiceConnector has +// been set and an InterfaceFactory has not been registered for the interface +// request, than the interface request is sent to the ServiceConnector. +// +// Just as with InterfaceFactory, ServiceConnector must outlive +// ApplicationConnection. +// +// An ApplicationConnection's lifetime is managed by an ApplicationImpl. To +// close a connection, call CloseConnection which will destroy this object. +class ApplicationConnection { + public: + virtual ~ApplicationConnection() {} + + class TestApi { + public: + explicit TestApi(ApplicationConnection* connection) + : connection_(connection) { + } + base::WeakPtr<ApplicationConnection> GetWeakPtr() { + return connection_->GetWeakPtr(); + } + + private: + ApplicationConnection* connection_; + }; + + // See class description for details. + virtual void SetServiceConnector(ServiceConnector* connector) = 0; + + // Makes Interface available as a service to the remote application. + // |factory| will create implementations of Interface on demand. + // Returns true if the service was exposed, false if capability filtering + // from the shell prevented the service from being exposed. + template <typename Interface> + bool AddService(InterfaceFactory<Interface>* factory) { + return SetServiceConnectorForName( + new internal::InterfaceFactoryConnector<Interface>(factory), + Interface::Name_); + } + + // Binds |ptr| to an implemention of Interface in the remote application. + // |ptr| can immediately be used to start sending requests to the remote + // service. + template <typename Interface> + void ConnectToService(InterfacePtr<Interface>* ptr) { + if (ServiceProvider* sp = GetServiceProvider()) { + MessagePipe pipe; + ptr->Bind(InterfacePtrInfo<Interface>(std::move(pipe.handle0), 0u)); + sp->ConnectToService(Interface::Name_, std::move(pipe.handle1)); + } + } + + // Returns the URL that was used by the source application to establish a + // connection to the destination application. + // + // When ApplicationConnection is representing an incoming connection this can + // be different than the URL the application was initially loaded from, if the + // application handles multiple URLs. Note that this is the URL after all + // URL rewriting and HTTP redirects have been performed. + // + // When ApplicationConnection is representing and outgoing connection, this + // will be the same as the value returned by GetRemoveApplicationURL(). + virtual const std::string& GetConnectionURL() = 0; + + // Returns the URL identifying the remote application on this connection. + virtual const std::string& GetRemoteApplicationURL() = 0; + + // Returns the raw proxy to the remote application's ServiceProvider + // interface. Most applications will just use ConnectToService() instead. + // Caller does not take ownership. + virtual ServiceProvider* GetServiceProvider() = 0; + + // Returns the local application's ServiceProvider interface. The return + // value is owned by this connection. + virtual ServiceProvider* GetLocalServiceProvider() = 0; + + // Register a handler to receive an error notification on the pipe to the + // remote application's service provider. + virtual void SetRemoteServiceProviderConnectionErrorHandler( + const Closure& handler) = 0; + + // Returns the id of the deepest content handler used in connecting to + // the application. If the content handler id has not yet been determined + // this returns false, use AddContentHandlerIDCallback() to schedule a + // callback when the content handler is has been obtained. A value of 0 + // indicates no content handler was used in connecting to the application. + virtual bool GetContentHandlerID(uint32_t* content_handler_id) = 0; + + // See description in GetTargetID(). If the id of the content handler has + // been obtained |callback| is run immediately. + virtual void AddContentHandlerIDCallback(const Closure& callback) = 0; + + protected: + // Returns true if the connector was set, false if it was not set (e.g. by + // some filtering policy preventing this interface from being exposed). + virtual bool SetServiceConnectorForName(ServiceConnector* service_connector, + const std::string& name) = 0; + + virtual base::WeakPtr<ApplicationConnection> GetWeakPtr() = 0; +}; + +} // namespace mojo + +#endif // MOJO_SHELL_PUBLIC_CPP_APPLICATION_CONNECTION_H_ diff --git a/mojo/shell/public/cpp/application_delegate.h b/mojo/shell/public/cpp/application_delegate.h new file mode 100644 index 0000000..219091f --- /dev/null +++ b/mojo/shell/public/cpp/application_delegate.h @@ -0,0 +1,54 @@ +// Copyright 2014 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef MOJO_SHELL_PUBLIC_CPP_APPLICATION_DELEGATE_H_ +#define MOJO_SHELL_PUBLIC_CPP_APPLICATION_DELEGATE_H_ + +#include <string> + +#include "mojo/public/cpp/system/macros.h" + +namespace mojo { + +class ApplicationConnection; +class ApplicationImpl; + +// An abstract class that the application may subclass to control various +// behaviors of ApplicationImpl. +class ApplicationDelegate { + public: + ApplicationDelegate(); + virtual ~ApplicationDelegate(); + + // Called exactly once before any other method. + virtual void Initialize(ApplicationImpl* app); + + // Override this method to configure what services a connection supports when + // being connected to from an app. + // Return false to reject the connection entirely. + virtual bool ConfigureIncomingConnection(ApplicationConnection* connection); + + // Override this method to configure what services a connection supports when + // connecting to another app. + // Return false to reject the connection entirely. + virtual bool ConfigureOutgoingConnection(ApplicationConnection* connection); + + // Called when the shell connection has a connection error. + // + // Return true to shutdown the application. Return false to skip shutting + // down the connection, but user is then required to call + // ApplicationImpl::QuitNow() when done. + virtual bool OnShellConnectionError(); + + // Called before ApplicationImpl::Terminate(). After returning from this call + // the delegate can no longer rely on the main run loop still running. + virtual void Quit(); + + private: + MOJO_DISALLOW_COPY_AND_ASSIGN(ApplicationDelegate); +}; + +} // namespace mojo + +#endif // MOJO_SHELL_PUBLIC_CPP_APPLICATION_DELEGATE_H_ diff --git a/mojo/shell/public/cpp/application_impl.h b/mojo/shell/public/cpp/application_impl.h new file mode 100644 index 0000000..3853bab --- /dev/null +++ b/mojo/shell/public/cpp/application_impl.h @@ -0,0 +1,186 @@ +// Copyright 2014 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef MOJO_SHELL_PUBLIC_CPP_APPLICATION_IMPL_H_ +#define MOJO_SHELL_PUBLIC_CPP_APPLICATION_IMPL_H_ + +#include <utility> +#include <vector> + +#include "base/macros.h" +#include "base/memory/scoped_vector.h" +#include "base/memory/weak_ptr.h" +#include "mojo/public/cpp/bindings/binding.h" +#include "mojo/public/cpp/bindings/callback.h" +#include "mojo/public/cpp/system/core.h" +#include "mojo/shell/public/cpp/app_lifetime_helper.h" +#include "mojo/shell/public/cpp/application_connection.h" +#include "mojo/shell/public/cpp/application_delegate.h" +#include "mojo/shell/public/cpp/lib/service_registry.h" +#include "mojo/shell/public/interfaces/application.mojom.h" +#include "mojo/shell/public/interfaces/shell.mojom.h" + +namespace mojo { + +CapabilityFilterPtr CreatePermissiveCapabilityFilter(); + +// TODO(beng): This comment is hilariously out of date. +// Utility class for communicating with the Shell, and providing Services +// to clients. +// +// To use define a class that implements your specific server api, e.g. FooImpl +// to implement a service named Foo. +// That class must subclass an InterfaceImpl specialization. +// +// If there is context that is to be shared amongst all instances, define a +// constructor with that class as its only argument, otherwise define an empty +// constructor. +// +// class FooImpl : public InterfaceImpl<Foo> { +// public: +// FooImpl(ApplicationContext* app_context) {} +// }; +// +// or +// +// class BarImpl : public InterfaceImpl<Bar> { +// public: +// // contexts will remain valid for the lifetime of BarImpl. +// BarImpl(ApplicationContext* app_context, BarContext* service_context) +// : app_context_(app_context), servicecontext_(context) {} +// +// Create an ApplicationImpl instance that collects any service implementations. +// +// ApplicationImpl app(service_provider_handle); +// app.AddService<FooImpl>(); +// +// BarContext context; +// app.AddService<BarImpl>(&context); +// +// +class ApplicationImpl : public Application { + public: + class ConnectParams { + public: + explicit ConnectParams(const std::string& url); + explicit ConnectParams(URLRequestPtr request); + ~ConnectParams(); + + URLRequestPtr TakeRequest() { return std::move(request_); } + CapabilityFilterPtr TakeFilter() { return std::move(filter_); } + void set_filter(CapabilityFilterPtr filter) { filter_ = std::move(filter); } + + private: + URLRequestPtr request_; + CapabilityFilterPtr filter_; + + DISALLOW_COPY_AND_ASSIGN(ConnectParams); + }; + + class TestApi { + public: + explicit TestApi(ApplicationImpl* application) + : application_(application) {} + + void UnbindConnections(InterfaceRequest<Application>* application_request, + ShellPtr* shell) { + application_->UnbindConnections(application_request, shell); + } + + private: + ApplicationImpl* application_; + }; + + // Does not take ownership of |delegate|, which must remain valid for the + // lifetime of ApplicationImpl. + ApplicationImpl(ApplicationDelegate* delegate, + InterfaceRequest<Application> request); + // Constructs an ApplicationImpl with a custom termination closure. This + // closure is invoked on Quit() instead of the default behavior of quitting + // the current base::MessageLoop. + ApplicationImpl(ApplicationDelegate* delegate, + InterfaceRequest<Application> request, + const Closure& termination_closure); + ~ApplicationImpl() override; + + // The Mojo shell. This will return a valid pointer after Initialize() has + // been invoked. It will remain valid until UnbindConnections() is invoked or + // the ApplicationImpl is destroyed. + Shell* shell() const { return shell_.get(); } + + const std::string& url() const { return url_; } + + AppLifetimeHelper* app_lifetime_helper() { return &app_lifetime_helper_; } + + // Requests a new connection to an application. Returns a pointer to the + // connection if the connection is permitted by this application's delegate, + // or nullptr otherwise. Caller takes ownership. + scoped_ptr<ApplicationConnection> ConnectToApplication( + const std::string& url); + scoped_ptr<ApplicationConnection> ConnectToApplication(ConnectParams* params); + + // Connect to application identified by |request->url| and connect to the + // service implementation of the interface identified by |Interface|. + template <typename Interface> + void ConnectToService(ConnectParams* params, InterfacePtr<Interface>* ptr) { + scoped_ptr<ApplicationConnection> connection = ConnectToApplication(params); + if (!connection.get()) + return; + connection->ConnectToService(ptr); + } + template <typename Interface> + void ConnectToService(const std::string& url, InterfacePtr<Interface>* ptr) { + ConnectParams params(url); + params.set_filter(CreatePermissiveCapabilityFilter()); + return ConnectToService(¶ms, ptr); + } + + // Block the calling thread until the Initialize() method is called by the + // shell. + void WaitForInitialize(); + + // Initiate shutdown of this application. This may involve a round trip to the + // Shell to ensure there are no inbound service requests. + void Quit(); + + private: + // Application implementation. + void Initialize(ShellPtr shell, const mojo::String& url) override; + void AcceptConnection(const String& requestor_url, + InterfaceRequest<ServiceProvider> services, + ServiceProviderPtr exposed_services, + Array<String> allowed_interfaces, + const String& url) override; + void OnQuitRequested(const Callback<void(bool)>& callback) override; + + void OnConnectionError(); + + // Called from Quit() when there is no Shell connection, or asynchronously + // from Quit() once the Shell has OK'ed shutdown. + void QuitNow(); + + // Unbinds the Shell and Application connections. Can be used to re-bind the + // handles to another implementation of ApplicationImpl, for instance when + // running apptests. + void UnbindConnections(InterfaceRequest<Application>* application_request, + ShellPtr* shell); + + // We track the lifetime of incoming connection registries as it more + // convenient for the client. + ScopedVector<ApplicationConnection> incoming_connections_; + ApplicationDelegate* delegate_; + Binding<Application> binding_; + ShellPtr shell_; + std::string url_; + Closure termination_closure_; + AppLifetimeHelper app_lifetime_helper_; + bool quit_requested_; + base::WeakPtrFactory<ApplicationImpl> weak_factory_; + + MOJO_DISALLOW_COPY_AND_ASSIGN(ApplicationImpl); +}; + +} // namespace mojo + +#endif // MOJO_SHELL_PUBLIC_CPP_APPLICATION_IMPL_H_ diff --git a/mojo/shell/public/cpp/application_runner.h b/mojo/shell/public/cpp/application_runner.h new file mode 100644 index 0000000..10eb0a7 --- /dev/null +++ b/mojo/shell/public/cpp/application_runner.h @@ -0,0 +1,63 @@ +// Copyright 2014 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef MOJO_SHELL_PUBLIC_CPP_APPLICATION_RUNNER_H_ +#define MOJO_SHELL_PUBLIC_CPP_APPLICATION_RUNNER_H_ + +#include "base/memory/scoped_ptr.h" +#include "base/message_loop/message_loop.h" +#include "mojo/public/cpp/system/core.h" + +namespace mojo { + +class ApplicationDelegate; + +// A utility for running a chromium based mojo Application. The typical use +// case is to use when writing your MojoMain: +// +// MojoResult MojoMain(MojoHandle shell_handle) { +// mojo::ApplicationRunner runner(new MyDelegate()); +// return runner.Run(shell_handle); +// } +// +// ApplicationRunner takes care of chromium environment initialization and +// shutdown, and starting a MessageLoop from which your application can run and +// ultimately Quit(). +class ApplicationRunner { + public: + // Takes ownership of |delegate|. + explicit ApplicationRunner(ApplicationDelegate* delegate); + ~ApplicationRunner(); + + static void InitBaseCommandLine(); + + void set_message_loop_type(base::MessageLoop::Type type); + + // Once the various parameters have been set above, use Run to initialize an + // ApplicationImpl wired to the provided delegate, and run a MessageLoop until + // the application exits. + // + // Iff |init_base| is true, the runner will perform some initialization of + // base globals (e.g. CommandLine and AtExitManager) before starting the + // application. + MojoResult Run(MojoHandle shell_handle, bool init_base); + + // Calls Run above with |init_base| set to |true|. + MojoResult Run(MojoHandle shell_handle); + + private: + scoped_ptr<ApplicationDelegate> delegate_; + + // MessageLoop type. TYPE_CUSTOM is default (MessagePumpMojo will be used as + // the underlying message pump). + base::MessageLoop::Type message_loop_type_; + // Whether Run() has been called. + bool has_run_; + + MOJO_DISALLOW_COPY_AND_ASSIGN(ApplicationRunner); +}; + +} // namespace mojo + +#endif // MOJO_SHELL_PUBLIC_CPP_APPLICATION_RUNNER_H_ diff --git a/mojo/shell/public/cpp/application_test_base.h b/mojo/shell/public/cpp/application_test_base.h new file mode 100644 index 0000000..55b9ea9 --- /dev/null +++ b/mojo/shell/public/cpp/application_test_base.h @@ -0,0 +1,59 @@ +// Copyright 2014 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef MOJO_SHELL_PUBLIC_CPP_APPLICATION_TEST_BASE_H_ +#define MOJO_SHELL_PUBLIC_CPP_APPLICATION_TEST_BASE_H_ + +#include "mojo/public/cpp/bindings/array.h" +#include "mojo/public/cpp/bindings/string.h" +#include "mojo/public/cpp/system/macros.h" +#include "mojo/shell/public/cpp/application_delegate.h" +#include "mojo/shell/public/interfaces/application.mojom.h" +#include "testing/gtest/include/gtest/gtest.h" + +namespace mojo { + +class ApplicationImpl; + +namespace test { + +// Run all application tests. This must be called after the environment is +// initialized, to support construction of a default run loop. +MojoResult RunAllTests(MojoHandle application_request_handle); + +// A GTEST base class for application testing executed in mojo_shell. +class ApplicationTestBase : public testing::Test { + public: + ApplicationTestBase(); + ~ApplicationTestBase() override; + + protected: + ApplicationImpl* application_impl() { return application_impl_; } + + // Get the ApplicationDelegate for the application to be tested. + virtual ApplicationDelegate* GetApplicationDelegate(); + + // testing::Test: + void SetUp() override; + void TearDown() override; + + // True by default, which indicates a MessageLoop will automatically be + // created for the application. Tests may override this function to prevent + // a default loop from being created. + virtual bool ShouldCreateDefaultRunLoop(); + + private: + // The application implementation instance, reconstructed for each test. + ApplicationImpl* application_impl_; + // The application delegate used if GetApplicationDelegate is not overridden. + ApplicationDelegate default_application_delegate_; + + MOJO_DISALLOW_COPY_AND_ASSIGN(ApplicationTestBase); +}; + +} // namespace test + +} // namespace mojo + +#endif // MOJO_SHELL_PUBLIC_CPP_APPLICATION_TEST_BASE_H_ diff --git a/mojo/shell/public/cpp/connect.h b/mojo/shell/public/cpp/connect.h new file mode 100644 index 0000000..acc496c --- /dev/null +++ b/mojo/shell/public/cpp/connect.h @@ -0,0 +1,25 @@ +// Copyright 2014 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef MOJO_SHELL_PUBLIC_CPP_CONNECT_H_ +#define MOJO_SHELL_PUBLIC_CPP_CONNECT_H_ + +#include <utility> + +#include "mojo/shell/public/interfaces/service_provider.mojom.h" + +namespace mojo { + +// Binds |ptr| to a remote implementation of Interface from |service_provider|. +template <typename Interface> +inline void ConnectToService(ServiceProvider* service_provider, + InterfacePtr<Interface>* ptr) { + MessagePipe pipe; + ptr->Bind(InterfacePtrInfo<Interface>(std::move(pipe.handle0), 0u)); + service_provider->ConnectToService(Interface::Name_, std::move(pipe.handle1)); +} + +} // namespace mojo + +#endif // MOJO_SHELL_PUBLIC_CPP_CONNECT_H_ diff --git a/mojo/shell/public/cpp/content_handler_factory.h b/mojo/shell/public/cpp/content_handler_factory.h new file mode 100644 index 0000000..4bcb81e --- /dev/null +++ b/mojo/shell/public/cpp/content_handler_factory.h @@ -0,0 +1,81 @@ +// Copyright 2014 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef MOJO_SHELL_PUBLIC_CPP_CONTENT_HANDLER_FACTORY_H_ +#define MOJO_SHELL_PUBLIC_CPP_CONTENT_HANDLER_FACTORY_H_ + +#include "base/macros.h" +#include "base/memory/scoped_ptr.h" +#include "mojo/services/network/public/interfaces/url_loader.mojom.h" +#include "mojo/shell/public/cpp/interface_factory.h" +#include "mojo/shell/public/interfaces/content_handler.mojom.h" +#include "mojo/shell/public/interfaces/shell.mojom.h" + +namespace mojo { + +class ContentHandlerFactory : public InterfaceFactory<ContentHandler> { + public: + class HandledApplicationHolder { + public: + virtual ~HandledApplicationHolder() {} + }; + + class Delegate { + public: + virtual ~Delegate() {} + // Implement this method to create the Application. This method will be + // called on a new thread. Leaving this method will quit the application. + virtual void RunApplication( + InterfaceRequest<Application> application_request, + URLResponsePtr response) = 0; + }; + + class ManagedDelegate : public Delegate { + public: + ~ManagedDelegate() override {} + // Implement this method to create the Application for the given content. + // This method will be called on a new thread. The application will be run + // on this new thread, and the returned value will be kept alive until the + // application ends. + virtual scoped_ptr<HandledApplicationHolder> CreateApplication( + InterfaceRequest<Application> application_request, + URLResponsePtr response) = 0; + + private: + void RunApplication(InterfaceRequest<Application> application_request, + URLResponsePtr response) override; + }; + + explicit ContentHandlerFactory(Delegate* delegate); + ~ContentHandlerFactory() override; + + private: + // From InterfaceFactory: + void Create(ApplicationConnection* connection, + InterfaceRequest<ContentHandler> request) override; + + Delegate* delegate_; + + DISALLOW_COPY_AND_ASSIGN(ContentHandlerFactory); +}; + +template <class A> +class HandledApplicationHolderImpl + : public ContentHandlerFactory::HandledApplicationHolder { + public: + explicit HandledApplicationHolderImpl(A* value) : value_(value) {} + + private: + scoped_ptr<A> value_; +}; + +template <class A> +scoped_ptr<ContentHandlerFactory::HandledApplicationHolder> +make_handled_factory_holder(A* value) { + return make_scoped_ptr(new HandledApplicationHolderImpl<A>(value)); +} + +} // namespace mojo + +#endif // MOJO_SHELL_PUBLIC_CPP_CONTENT_HANDLER_FACTORY_H_ diff --git a/mojo/shell/public/cpp/initialize_base_and_icu.cc b/mojo/shell/public/cpp/initialize_base_and_icu.cc new file mode 100644 index 0000000..2ec37df --- /dev/null +++ b/mojo/shell/public/cpp/initialize_base_and_icu.cc @@ -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. + +// This file declares a raw symbol and should be included only once in a +// certain binary target. This needs to be run before we raise the sandbox, +// which means that it can't use mojo. Our runners will dig around in the +// symbol table and run this before the mojo system is initialized. + +#include <stdint.h> + +#include "base/files/file.h" +#include "base/i18n/icu_util.h" +#include "base/rand_util.h" +#include "base/sys_info.h" +#include "mojo/public/c/system/types.h" + +#if !defined(OS_ANDROID) +#include "third_party/icu/source/i18n/unicode/timezone.h" +#endif + +extern "C" { +#if defined(WIN32) +__declspec(dllexport) void __cdecl +#else +void __attribute__((visibility("default"))) +#endif +InitializeBase(const uint8_t* icu_data) { + base::RandUint64(); + base::SysInfo::AmountOfPhysicalMemory(); + base::SysInfo::NumberOfProcessors(); +#if defined(OS_POSIX) && !defined(OS_MACOSX) + base::SysInfo::MaxSharedMemorySize(); +#endif + + // Initialize core ICU. We must perform the full initialization before we + // initialize icu::TimeZone subsystem because otherwise ICU gets in a state + // where the timezone data is disconnected from the locale data which can + // cause crashes. + CHECK(base::i18n::InitializeICUFromRawMemory(icu_data)); + +#if !defined(OS_ANDROID) + // ICU DateFormat class (used in base/time_format.cc) needs to get the + // Olson timezone ID by accessing the zoneinfo files on disk. After + // TimeZone::createDefault is called once here, the timezone ID is + // cached and there's no more need to access the file system. + scoped_ptr<icu::TimeZone> zone(icu::TimeZone::createDefault()); +#endif +} +} diff --git a/mojo/shell/public/cpp/interface_factory.h b/mojo/shell/public/cpp/interface_factory.h new file mode 100644 index 0000000..7f8f26e --- /dev/null +++ b/mojo/shell/public/cpp/interface_factory.h @@ -0,0 +1,30 @@ +// Copyright 2014 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef MOJO_SHELL_PUBLIC_CPP_INTERFACE_FACTORY_H_ +#define MOJO_SHELL_PUBLIC_CPP_INTERFACE_FACTORY_H_ + +#include "mojo/public/cpp/bindings/interface_request.h" + +namespace mojo { + +class ApplicationConnection; +template <typename Interface> +class InterfaceRequest; + +// Implement this class to provide implementations of a given interface and +// bind them to incoming requests. The implementation of this class is +// responsible for managing the lifetime of the implementations of the +// interface. +template <typename Interface> +class InterfaceFactory { + public: + virtual ~InterfaceFactory() {} + virtual void Create(ApplicationConnection* connection, + InterfaceRequest<Interface> request) = 0; +}; + +} // namespace mojo + +#endif // MOJO_SHELL_PUBLIC_CPP_INTERFACE_FACTORY_H_ diff --git a/mojo/shell/public/cpp/interface_factory_impl.h b/mojo/shell/public/cpp/interface_factory_impl.h new file mode 100644 index 0000000..7de2449 --- /dev/null +++ b/mojo/shell/public/cpp/interface_factory_impl.h @@ -0,0 +1,49 @@ +// Copyright 2014 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef MOJO_SHELL_PUBLIC_CPP_INTERFACE_FACTORY_IMPL_H_ +#define MOJO_SHELL_PUBLIC_CPP_INTERFACE_FACTORY_IMPL_H_ + +#include "mojo/shell/public/cpp/interface_factory.h" + +namespace mojo { + +// Use this class to allocate and bind instances of Impl to interface requests. +// The lifetime of the constructed Impl is bound to the pipe. +template <typename Impl, + typename Interface = typename Impl::ImplementedInterface> +class InterfaceFactoryImpl : public InterfaceFactory<Interface> { + public: + virtual ~InterfaceFactoryImpl() {} + + virtual void Create(ApplicationConnection* connection, + InterfaceRequest<Interface> request) override { + BindToRequest(new Impl(), &request); + } +}; + +// Use this class to allocate and bind instances of Impl constructed with a +// context parameter to interface requests. The lifetime of the constructed +// Impl is bound to the pipe. +template <typename Impl, + typename Context, + typename Interface = typename Impl::ImplementedInterface> +class InterfaceFactoryImplWithContext : public InterfaceFactory<Interface> { + public: + explicit InterfaceFactoryImplWithContext(Context* context) + : context_(context) {} + virtual ~InterfaceFactoryImplWithContext() {} + + virtual void Create(ApplicationConnection* connection, + InterfaceRequest<Interface> request) override { + BindToRequest(new Impl(context_), &request); + } + + private: + Context* context_; +}; + +} // namespace mojo + +#endif // MOJO_SHELL_PUBLIC_CPP_INTERFACE_FACTORY_IMPL_H_ diff --git a/mojo/shell/public/cpp/lazy_interface_ptr.h b/mojo/shell/public/cpp/lazy_interface_ptr.h new file mode 100644 index 0000000..0497dc2 --- /dev/null +++ b/mojo/shell/public/cpp/lazy_interface_ptr.h @@ -0,0 +1,46 @@ +// Copyright 2014 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef MOJO_SHELL_PUBLIC_CPP_LAZY_INTERFACE_PTR_H_ +#define MOJO_SHELL_PUBLIC_CPP_LAZY_INTERFACE_PTR_H_ + +#include "mojo/shell/public/cpp/connect.h" +#include "mojo/shell/public/interfaces/service_provider.mojom.h" + +namespace mojo { + +// An InterfacePtr that will request an implementation from a specified +// ServiceProvider when it is first accessed with the get() method. +template <typename Interface> +class LazyInterfacePtr : public InterfacePtr<Interface> { + public: + LazyInterfacePtr() : service_provider_(nullptr) {} + + explicit LazyInterfacePtr(ServiceProvider* service_provider) + : service_provider_(service_provider) {} + + void set_service_provider(ServiceProvider* service_provider) { + if (service_provider != service_provider_) { + InterfacePtr<Interface>::reset(); + } + service_provider_ = service_provider; + } + + Interface* get() const { + if (!InterfacePtr<Interface>::get() && service_provider_) { + mojo::ConnectToService<Interface>( + service_provider_, const_cast<LazyInterfacePtr<Interface>*>(this)); + } + return InterfacePtr<Interface>::get(); + } + Interface* operator->() const { return get(); } + Interface& operator*() const { return *get(); } + + private: + ServiceProvider* service_provider_; +}; + +} // namespace mojo + +#endif // MOJO_SHELL_PUBLIC_CPP_LAZY_INTERFACE_PTR_H_ diff --git a/mojo/shell/public/cpp/lib/app_lifetime_helper.cc b/mojo/shell/public/cpp/lib/app_lifetime_helper.cc new file mode 100644 index 0000000..0772412a --- /dev/null +++ b/mojo/shell/public/cpp/lib/app_lifetime_helper.cc @@ -0,0 +1,94 @@ +// 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/shell/public/cpp/app_lifetime_helper.h" + +#include "base/bind.h" +#include "base/message_loop/message_loop.h" +#include "mojo/shell/public/cpp/application_impl.h" + +namespace mojo { + +AppRefCount::AppRefCount( + AppLifetimeHelper* app_lifetime_helper, + scoped_refptr<base::SingleThreadTaskRunner> app_task_runner) + : app_lifetime_helper_(app_lifetime_helper), + app_task_runner_(app_task_runner) { +} + +AppRefCount::~AppRefCount() { +#ifndef NDEBUG + // Ensure that this object is used on only one thread at a time, or else there + // could be races where the object is being reset on one thread and cloned on + // another. + if (clone_task_runner_) { + DCHECK(clone_task_runner_->BelongsToCurrentThread()); + } +#endif + + if (app_task_runner_->BelongsToCurrentThread()) { + app_lifetime_helper_->Release(); + return; + } + + app_task_runner_->PostTask( + FROM_HERE, + base::Bind(&AppLifetimeHelper::Release, + base::Unretained(app_lifetime_helper_))); +} + +scoped_ptr<AppRefCount> AppRefCount::Clone() { + if (app_task_runner_->BelongsToCurrentThread()) { + app_lifetime_helper_->AddRef(); + } else { + app_task_runner_->PostTask( + FROM_HERE, + base::Bind(&AppLifetimeHelper::AddRef, + base::Unretained(app_lifetime_helper_))); + } + +#ifndef NDEBUG + // Ensure that this object is used on only one thread at a time, or else there + // could be races where the object is being reset on one thread and cloned on + // another. + if (clone_task_runner_) { + DCHECK(clone_task_runner_->BelongsToCurrentThread()); + } else { + clone_task_runner_ = base::MessageLoop::current()->task_runner(); + } +#endif + + return make_scoped_ptr(new AppRefCount( + app_lifetime_helper_, app_task_runner_)); +} + +AppLifetimeHelper::AppLifetimeHelper(ApplicationImpl* app) + : app_(app), ref_count_(0) { +} + +AppLifetimeHelper::~AppLifetimeHelper() { +} + +scoped_ptr<AppRefCount> AppLifetimeHelper::CreateAppRefCount() { + AddRef(); + return make_scoped_ptr(new AppRefCount( + this, base::MessageLoop::current()->task_runner())); +} + +void AppLifetimeHelper::AddRef() { + ref_count_++; +} + +void AppLifetimeHelper::Release() { + if (!--ref_count_) { + if (app_) + app_->Quit(); + } +} + +void AppLifetimeHelper::OnQuit() { + app_ = nullptr; +} + +} // namespace mojo diff --git a/mojo/shell/public/cpp/lib/application_delegate.cc b/mojo/shell/public/cpp/lib/application_delegate.cc new file mode 100644 index 0000000..41ba51c --- /dev/null +++ b/mojo/shell/public/cpp/lib/application_delegate.cc @@ -0,0 +1,34 @@ +// Copyright 2014 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "mojo/shell/public/cpp/application_delegate.h" + +namespace mojo { + +ApplicationDelegate::ApplicationDelegate() { +} +ApplicationDelegate::~ApplicationDelegate() { +} + +void ApplicationDelegate::Initialize(ApplicationImpl* app) { +} + +bool ApplicationDelegate::ConfigureIncomingConnection( + ApplicationConnection* connection) { + return true; +} + +bool ApplicationDelegate::ConfigureOutgoingConnection( + ApplicationConnection* connection) { + return true; +} + +bool ApplicationDelegate::OnShellConnectionError() { + return true; +} + +void ApplicationDelegate::Quit() { +} + +} // namespace mojo diff --git a/mojo/shell/public/cpp/lib/application_impl.cc b/mojo/shell/public/cpp/lib/application_impl.cc new file mode 100644 index 0000000..e48483f --- /dev/null +++ b/mojo/shell/public/cpp/lib/application_impl.cc @@ -0,0 +1,181 @@ +// Copyright 2014 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include <algorithm> +#include <utility> + +#include "base/bind.h" +#include "base/message_loop/message_loop.h" +#include "mojo/converters/network/network_type_converters.h" +#include "mojo/public/cpp/bindings/interface_ptr.h" +#include "mojo/public/cpp/environment/logging.h" +#include "mojo/shell/public/cpp/application_delegate.h" +#include "mojo/shell/public/cpp/application_impl.h" +#include "mojo/shell/public/cpp/lib/service_registry.h" + +namespace mojo { + +namespace { + +void DefaultTerminationClosure() { + if (base::MessageLoop::current() && + base::MessageLoop::current()->is_running()) + base::MessageLoop::current()->QuitWhenIdle(); +} + +} // namespace + +ApplicationImpl::ConnectParams::ConnectParams(const std::string& url) + : ConnectParams(URLRequest::From(url)) {} +ApplicationImpl::ConnectParams::ConnectParams(URLRequestPtr request) + : request_(std::move(request)), filter_(CapabilityFilter::New()) { + filter_->filter.mark_non_null(); +} +ApplicationImpl::ConnectParams::~ConnectParams() {} + +ApplicationImpl::ApplicationImpl(ApplicationDelegate* delegate, + InterfaceRequest<Application> request) + : ApplicationImpl(delegate, + std::move(request), + base::Bind(&DefaultTerminationClosure)) {} + +ApplicationImpl::ApplicationImpl(ApplicationDelegate* delegate, + InterfaceRequest<Application> request, + const Closure& termination_closure) + : delegate_(delegate), + binding_(this, std::move(request)), + termination_closure_(termination_closure), + app_lifetime_helper_(this), + quit_requested_(false), + weak_factory_(this) {} + +ApplicationImpl::~ApplicationImpl() { + app_lifetime_helper_.OnQuit(); +} + +scoped_ptr<ApplicationConnection> ApplicationImpl::ConnectToApplication( + const std::string& url) { + ConnectParams params(url); + params.set_filter(CreatePermissiveCapabilityFilter()); + return ConnectToApplication(¶ms); +} + +scoped_ptr<ApplicationConnection> + ApplicationImpl::ConnectToApplication(ConnectParams* params) { + if (!shell_) + return nullptr; + DCHECK(params); + URLRequestPtr request = params->TakeRequest(); + ServiceProviderPtr local_services; + InterfaceRequest<ServiceProvider> local_request = GetProxy(&local_services); + ServiceProviderPtr remote_services; + std::string application_url = request->url.To<std::string>(); + // We allow all interfaces on outgoing connections since we are presumably in + // a position to know who we're talking to. + // TODO(beng): is this a valid assumption or do we need to figure some way to + // filter here too? + std::set<std::string> allowed; + allowed.insert("*"); + InterfaceRequest<ServiceProvider> remote_services_proxy = + GetProxy(&remote_services); + scoped_ptr<internal::ServiceRegistry> registry(new internal::ServiceRegistry( + application_url, application_url, std::move(remote_services), + std::move(local_request), allowed)); + shell_->ConnectToApplication(std::move(request), + std::move(remote_services_proxy), + std::move(local_services), params->TakeFilter(), + registry->GetConnectToApplicationCallback()); + if (!delegate_->ConfigureOutgoingConnection(registry.get())) + return nullptr; + return std::move(registry); +} + +void ApplicationImpl::WaitForInitialize() { + DCHECK(!shell_.is_bound()); + binding_.WaitForIncomingMethodCall(); +} + +void ApplicationImpl::Quit() { + // We can't quit immediately, since there could be in-flight requests from the + // shell. So check with it first. + if (shell_) { + quit_requested_ = true; + shell_->QuitApplication(); + } else { + QuitNow(); + } +} + +void ApplicationImpl::Initialize(ShellPtr shell, const mojo::String& url) { + shell_ = std::move(shell); + shell_.set_connection_error_handler([this]() { OnConnectionError(); }); + url_ = url; + delegate_->Initialize(this); +} + +void ApplicationImpl::AcceptConnection( + const String& requestor_url, + InterfaceRequest<ServiceProvider> services, + ServiceProviderPtr exposed_services, + Array<String> allowed_interfaces, + const String& url) { + scoped_ptr<ApplicationConnection> registry(new internal::ServiceRegistry( + url, requestor_url, std::move(exposed_services), std::move(services), + allowed_interfaces.To<std::set<std::string>>())); + if (!delegate_->ConfigureIncomingConnection(registry.get())) + return; + + // If we were quitting because we thought there were no more services for this + // app in use, then that has changed so cancel the quit request. + if (quit_requested_) + quit_requested_ = false; + + incoming_connections_.push_back(std::move(registry)); +} + +void ApplicationImpl::OnQuitRequested(const Callback<void(bool)>& callback) { + // If by the time we got the reply from the shell, more requests had come in + // then we don't want to quit the app anymore so we return false. Otherwise + // |quit_requested_| is true so we tell the shell to proceed with the quit. + callback.Run(quit_requested_); + if (quit_requested_) + QuitNow(); +} + +void ApplicationImpl::OnConnectionError() { + base::WeakPtr<ApplicationImpl> ptr(weak_factory_.GetWeakPtr()); + + // We give the delegate notice first, since it might want to do something on + // shell connection errors other than immediate termination of the run + // loop. The application might want to continue servicing connections other + // than the one to the shell. + bool quit_now = delegate_->OnShellConnectionError(); + if (quit_now) + QuitNow(); + if (!ptr) + return; + shell_ = nullptr; +} + +void ApplicationImpl::QuitNow() { + delegate_->Quit(); + termination_closure_.Run(); +} + +void ApplicationImpl::UnbindConnections( + InterfaceRequest<Application>* application_request, + ShellPtr* shell) { + *application_request = binding_.Unbind(); + shell->Bind(shell_.PassInterface()); +} + +CapabilityFilterPtr CreatePermissiveCapabilityFilter() { + CapabilityFilterPtr filter(CapabilityFilter::New()); + Array<String> all_interfaces; + all_interfaces.push_back("*"); + filter->filter.insert("*", std::move(all_interfaces)); + return filter; +} + +} // namespace mojo diff --git a/mojo/shell/public/cpp/lib/application_runner.cc b/mojo/shell/public/cpp/lib/application_runner.cc new file mode 100644 index 0000000..02380c6 --- /dev/null +++ b/mojo/shell/public/cpp/lib/application_runner.cc @@ -0,0 +1,77 @@ +// Copyright 2014 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "mojo/shell/public/cpp/application_runner.h" + +#include "base/at_exit.h" +#include "base/command_line.h" +#include "base/memory/scoped_ptr.h" +#include "base/message_loop/message_loop.h" +#include "base/process/launch.h" +#include "mojo/message_pump/message_pump_mojo.h" +#include "mojo/shell/public/cpp/application_delegate.h" +#include "mojo/shell/public/cpp/application_impl.h" + +namespace mojo { + +int g_application_runner_argc; +const char* const* g_application_runner_argv; + +ApplicationRunner::ApplicationRunner(ApplicationDelegate* delegate) + : delegate_(scoped_ptr<ApplicationDelegate>(delegate)), + message_loop_type_(base::MessageLoop::TYPE_CUSTOM), + has_run_(false) {} + +ApplicationRunner::~ApplicationRunner() {} + +void ApplicationRunner::InitBaseCommandLine() { + base::CommandLine::Init(g_application_runner_argc, g_application_runner_argv); +} + +void ApplicationRunner::set_message_loop_type(base::MessageLoop::Type type) { + DCHECK_NE(base::MessageLoop::TYPE_CUSTOM, type); + DCHECK(!has_run_); + + message_loop_type_ = type; +} + +MojoResult ApplicationRunner::Run(MojoHandle application_request_handle, + bool init_base) { + DCHECK(!has_run_); + has_run_ = true; + + scoped_ptr<base::AtExitManager> at_exit; + if (init_base) { + InitBaseCommandLine(); + at_exit.reset(new base::AtExitManager); + } + + { + scoped_ptr<base::MessageLoop> loop; + if (message_loop_type_ == base::MessageLoop::TYPE_CUSTOM) + loop.reset(new base::MessageLoop(common::MessagePumpMojo::Create())); + else + loop.reset(new base::MessageLoop(message_loop_type_)); + + ApplicationImpl impl(delegate_.get(), + MakeRequest<Application>(MakeScopedHandle( + MessagePipeHandle(application_request_handle)))); + loop->Run(); + // It's very common for the delegate to cache the app and terminate on + // errors. If we don't delete the delegate before the app we run the risk + // of the delegate having a stale reference to the app and trying to use it. + // Note that we destruct the message loop first because that might trigger + // connection error handlers and they might access objects created by the + // delegate. + loop.reset(); + delegate_.reset(); + } + return MOJO_RESULT_OK; +} + +MojoResult ApplicationRunner::Run(MojoHandle application_request_handle) { + return Run(application_request_handle, true); +} + +} // namespace mojo diff --git a/mojo/shell/public/cpp/lib/application_test_base.cc b/mojo/shell/public/cpp/lib/application_test_base.cc new file mode 100644 index 0000000..d1b2cfa --- /dev/null +++ b/mojo/shell/public/cpp/lib/application_test_base.cc @@ -0,0 +1,158 @@ +// Copyright 2014 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include <utility> + +#include "base/command_line.h" +#include "base/strings/utf_string_conversions.h" +#include "mojo/public/cpp/bindings/binding.h" +#include "mojo/public/cpp/environment/environment.h" +#include "mojo/public/cpp/system/message_pipe.h" +#include "mojo/shell/public/cpp/application_impl.h" +#include "mojo/shell/public/cpp/application_test_base.h" +#include "mojo/shell/public/interfaces/application.mojom.h" + +namespace mojo { +namespace test { + +namespace { +// Share the application URL with multiple application tests. +String g_url; + +// Application request handle passed from the shell in MojoMain, stored in +// between SetUp()/TearDown() so we can (re-)intialize new ApplicationImpls. +InterfaceRequest<Application> g_application_request; + +// Shell pointer passed in the initial mojo.Application.Initialize() call, +// stored in between initial setup and the first test and between SetUp/TearDown +// calls so we can (re-)initialize new ApplicationImpls. +ShellPtr g_shell; + +class ShellGrabber : public Application { + public: + explicit ShellGrabber(InterfaceRequest<Application> application_request) + : binding_(this, std::move(application_request)) {} + + void WaitForInitialize() { + // Initialize is always the first call made on Application. + MOJO_CHECK(binding_.WaitForIncomingMethodCall()); + } + + private: + // Application implementation. + void Initialize(ShellPtr shell, const mojo::String& url) override { + g_url = url; + g_application_request = binding_.Unbind(); + g_shell = std::move(shell); + } + + void AcceptConnection(const String& requestor_url, + InterfaceRequest<ServiceProvider> services, + ServiceProviderPtr exposed_services, + Array<String> allowed_interfaces, + const String& url) override { + MOJO_CHECK(false); + } + + void OnQuitRequested(const Callback<void(bool)>& callback) override { + MOJO_CHECK(false); + } + + Binding<Application> binding_; +}; + +} // namespace + +MojoResult RunAllTests(MojoHandle application_request_handle) { + { + // This loop is used for init, and then destroyed before running tests. + Environment::InstantiateDefaultRunLoop(); + + // Grab the shell handle. + ShellGrabber grabber( + MakeRequest<Application>(MakeScopedHandle( + MessagePipeHandle(application_request_handle)))); + grabber.WaitForInitialize(); + MOJO_CHECK(g_shell); + MOJO_CHECK(g_application_request.is_pending()); + + int argc = 0; + base::CommandLine* cmd_line = base::CommandLine::ForCurrentProcess(); + const char** argv = new const char* [cmd_line->argv().size() + 1]; +#if defined(OS_WIN) + std::vector<std::string> local_strings; +#endif + for (auto& arg : cmd_line->argv()) { +#if defined(OS_WIN) + local_strings.push_back(base::WideToUTF8(arg)); + argv[argc++] = local_strings.back().c_str(); +#else + argv[argc++] = arg.c_str(); +#endif + } + argv[argc] = nullptr; + + testing::InitGoogleTest(&argc, const_cast<char**>(&(argv[0]))); + + Environment::DestroyDefaultRunLoop(); + } + + int result = RUN_ALL_TESTS(); + + // Shut down our message pipes before exiting. + (void)g_application_request.PassMessagePipe(); + g_shell.reset(); + + return (result == 0) ? MOJO_RESULT_OK : MOJO_RESULT_UNKNOWN; +} + +ApplicationTestBase::ApplicationTestBase() : application_impl_(nullptr) { +} + +ApplicationTestBase::~ApplicationTestBase() { +} + +ApplicationDelegate* ApplicationTestBase::GetApplicationDelegate() { + return &default_application_delegate_; +} + +void ApplicationTestBase::SetUp() { + // A run loop is recommended for ApplicationImpl initialization and + // communication. + if (ShouldCreateDefaultRunLoop()) + Environment::InstantiateDefaultRunLoop(); + + MOJO_CHECK(g_application_request.is_pending()); + MOJO_CHECK(g_shell); + + // New applications are constructed for each test to avoid persisting state. + application_impl_ = new ApplicationImpl(GetApplicationDelegate(), + std::move(g_application_request)); + + // Fake application initialization. + Application* application = application_impl_; + application->Initialize(std::move(g_shell), g_url); +} + +void ApplicationTestBase::TearDown() { + MOJO_CHECK(!g_application_request.is_pending()); + MOJO_CHECK(!g_shell); + + // TODO: commented out until http://crbug.com/533107 is solved. + // { + // ApplicationImpl::TestApi test_api(application_impl_); + // test_api.UnbindConnections(&g_application_request, &g_shell); + // } + delete application_impl_; + if (ShouldCreateDefaultRunLoop()) + Environment::DestroyDefaultRunLoop(); +} + +bool ApplicationTestBase::ShouldCreateDefaultRunLoop() { + return true; +} + + +} // namespace test +} // namespace mojo diff --git a/mojo/shell/public/cpp/lib/application_test_main.cc b/mojo/shell/public/cpp/lib/application_test_main.cc new file mode 100644 index 0000000..470846c --- /dev/null +++ b/mojo/shell/public/cpp/lib/application_test_main.cc @@ -0,0 +1,23 @@ +// Copyright 2014 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "base/at_exit.h" +#include "base/command_line.h" +#include "base/test/test_timeouts.h" +#include "mojo/logging/init_logging.h" +#include "mojo/public/c/system/main.h" +#include "mojo/shell/public/cpp/application_runner.h" +#include "mojo/shell/public/cpp/application_test_base.h" + +MojoResult MojoMain(MojoHandle handle) { + // An AtExitManager instance is needed to construct message loops. + base::AtExitManager at_exit; + + // Initialize the current process Commandline and test timeouts. + mojo::ApplicationRunner::InitBaseCommandLine(); + mojo::InitLogging(); + TestTimeouts::Initialize(); + + return mojo::test::RunAllTests(handle); +} diff --git a/mojo/shell/public/cpp/lib/content_handler_factory.cc b/mojo/shell/public/cpp/lib/content_handler_factory.cc new file mode 100644 index 0000000..c810354 --- /dev/null +++ b/mojo/shell/public/cpp/lib/content_handler_factory.cc @@ -0,0 +1,143 @@ +// Copyright 2014 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include <set> +#include <utility> + +#include "base/bind.h" +#include "base/callback.h" +#include "base/macros.h" +#include "base/memory/weak_ptr.h" +#include "base/single_thread_task_runner.h" +#include "base/thread_task_runner_handle.h" +#include "base/threading/platform_thread.h" +#include "mojo/message_pump/message_pump_mojo.h" +#include "mojo/public/cpp/bindings/strong_binding.h" +#include "mojo/shell/public/cpp/application_connection.h" +#include "mojo/shell/public/cpp/application_delegate.h" +#include "mojo/shell/public/cpp/application_impl.h" +#include "mojo/shell/public/cpp/application_runner.h" +#include "mojo/shell/public/cpp/content_handler_factory.h" +#include "mojo/shell/public/cpp/interface_factory_impl.h" + +namespace mojo { + +namespace { + +class ApplicationThread : public base::PlatformThread::Delegate { + public: + ApplicationThread( + scoped_refptr<base::SingleThreadTaskRunner> handler_thread, + const base::Callback<void(ApplicationThread*)>& termination_callback, + ContentHandlerFactory::Delegate* handler_delegate, + InterfaceRequest<Application> application_request, + URLResponsePtr response, + const Callback<void()>& destruct_callback) + : handler_thread_(handler_thread), + termination_callback_(termination_callback), + handler_delegate_(handler_delegate), + application_request_(std::move(application_request)), + response_(std::move(response)), + destruct_callback_(destruct_callback) {} + + ~ApplicationThread() override { + destruct_callback_.Run(); + } + + private: + void ThreadMain() override { + handler_delegate_->RunApplication(std::move(application_request_), + std::move(response_)); + handler_thread_->PostTask(FROM_HERE, + base::Bind(termination_callback_, this)); + } + + scoped_refptr<base::SingleThreadTaskRunner> handler_thread_; + base::Callback<void(ApplicationThread*)> termination_callback_; + ContentHandlerFactory::Delegate* handler_delegate_; + InterfaceRequest<Application> application_request_; + URLResponsePtr response_; + Callback<void()> destruct_callback_; + + DISALLOW_COPY_AND_ASSIGN(ApplicationThread); +}; + +class ContentHandlerImpl : public ContentHandler { + public: + ContentHandlerImpl(ContentHandlerFactory::Delegate* delegate, + InterfaceRequest<ContentHandler> request) + : delegate_(delegate), + binding_(this, std::move(request)), + weak_factory_(this) {} + ~ContentHandlerImpl() override { + // We're shutting down and doing cleanup. Cleanup may trigger calls back to + // OnThreadEnd(). As we're doing the cleanup here we don't want to do it in + // OnThreadEnd() as well. InvalidateWeakPtrs() ensures we don't get any + // calls to OnThreadEnd(). + weak_factory_.InvalidateWeakPtrs(); + for (auto thread : active_threads_) { + base::PlatformThread::Join(thread.second); + delete thread.first; + } + } + + private: + // Overridden from ContentHandler: + void StartApplication( + InterfaceRequest<Application> application_request, + URLResponsePtr response, + const Callback<void()>& destruct_callback) override { + ApplicationThread* thread = + new ApplicationThread(base::ThreadTaskRunnerHandle::Get(), + base::Bind(&ContentHandlerImpl::OnThreadEnd, + weak_factory_.GetWeakPtr()), + delegate_, std::move(application_request), + std::move(response), destruct_callback); + base::PlatformThreadHandle handle; + bool launched = base::PlatformThread::Create(0, thread, &handle); + DCHECK(launched); + active_threads_[thread] = handle; + } + + void OnThreadEnd(ApplicationThread* thread) { + DCHECK(active_threads_.find(thread) != active_threads_.end()); + base::PlatformThreadHandle handle = active_threads_[thread]; + active_threads_.erase(thread); + base::PlatformThread::Join(handle); + delete thread; + } + + ContentHandlerFactory::Delegate* delegate_; + std::map<ApplicationThread*, base::PlatformThreadHandle> active_threads_; + StrongBinding<ContentHandler> binding_; + base::WeakPtrFactory<ContentHandlerImpl> weak_factory_; + + DISALLOW_COPY_AND_ASSIGN(ContentHandlerImpl); +}; + +} // namespace + +ContentHandlerFactory::ContentHandlerFactory(Delegate* delegate) + : delegate_(delegate) { +} + +ContentHandlerFactory::~ContentHandlerFactory() { +} + +void ContentHandlerFactory::ManagedDelegate::RunApplication( + InterfaceRequest<Application> application_request, + URLResponsePtr response) { + base::MessageLoop loop(common::MessagePumpMojo::Create()); + auto application = this->CreateApplication(std::move(application_request), + std::move(response)); + if (application) + loop.Run(); +} + +void ContentHandlerFactory::Create(ApplicationConnection* connection, + InterfaceRequest<ContentHandler> request) { + new ContentHandlerImpl(delegate_, std::move(request)); +} + +} // namespace mojo diff --git a/mojo/shell/public/cpp/lib/init_commandline.cc b/mojo/shell/public/cpp/lib/init_commandline.cc new file mode 100644 index 0000000..a61797b --- /dev/null +++ b/mojo/shell/public/cpp/lib/init_commandline.cc @@ -0,0 +1,22 @@ +// Copyright 2014 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "build/build_config.h" + +namespace mojo { + +extern int g_application_runner_argc; +extern const char* const* g_application_runner_argv; + +} + +#if !defined(OS_WIN) +extern "C" { +__attribute__((visibility("default"))) void InitCommandLineArgs( + int argc, const char* const* argv) { + mojo::g_application_runner_argc = argc; + mojo::g_application_runner_argv = argv; +} +} +#endif diff --git a/mojo/shell/public/cpp/lib/interface_factory_connector.h b/mojo/shell/public/cpp/lib/interface_factory_connector.h new file mode 100644 index 0000000..5ef7eaa --- /dev/null +++ b/mojo/shell/public/cpp/lib/interface_factory_connector.h @@ -0,0 +1,39 @@ +// Copyright 2014 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef MOJO_SHELL_PUBLIC_CPP_LIB_INTERFACE_FACTORY_CONNECTOR_H_ +#define MOJO_SHELL_PUBLIC_CPP_LIB_INTERFACE_FACTORY_CONNECTOR_H_ + +#include <utility> + +#include "mojo/public/cpp/bindings/interface_request.h" +#include "mojo/shell/public/cpp/interface_factory.h" +#include "mojo/shell/public/cpp/service_connector.h" + +namespace mojo { +namespace internal { + +template <typename Interface> +class InterfaceFactoryConnector : public ServiceConnector { + public: + explicit InterfaceFactoryConnector(InterfaceFactory<Interface>* factory) + : factory_(factory) {} + ~InterfaceFactoryConnector() override {} + + void ConnectToService(ApplicationConnection* application_connection, + const std::string& interface_name, + ScopedMessagePipeHandle client_handle) override { + factory_->Create(application_connection, + MakeRequest<Interface>(std::move(client_handle))); + } + + private: + InterfaceFactory<Interface>* factory_; + MOJO_DISALLOW_COPY_AND_ASSIGN(InterfaceFactoryConnector); +}; + +} // namespace internal +} // namespace mojo + +#endif // MOJO_SHELL_PUBLIC_CPP_LIB_INTERFACE_FACTORY_CONNECTOR_H_ diff --git a/mojo/shell/public/cpp/lib/service_connector_registry.cc b/mojo/shell/public/cpp/lib/service_connector_registry.cc new file mode 100644 index 0000000..edc91e0 --- /dev/null +++ b/mojo/shell/public/cpp/lib/service_connector_registry.cc @@ -0,0 +1,61 @@ +// 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/shell/public/cpp/lib/service_connector_registry.h" + +#include <utility> + +#include "mojo/shell/public/cpp/service_connector.h" + +namespace mojo { +namespace internal { + +ServiceConnectorRegistry::ServiceConnectorRegistry() + : service_connector_(nullptr) { +} + +ServiceConnectorRegistry::~ServiceConnectorRegistry() { + for (NameToServiceConnectorMap::iterator i = + name_to_service_connector_.begin(); + i != name_to_service_connector_.end(); ++i) { + delete i->second; + } + name_to_service_connector_.clear(); +} + +void ServiceConnectorRegistry::SetServiceConnectorForName( + ServiceConnector* service_connector, + const std::string& interface_name) { + RemoveServiceConnectorForName(interface_name); + name_to_service_connector_[interface_name] = service_connector; +} + +void ServiceConnectorRegistry::RemoveServiceConnectorForName( + const std::string& interface_name) { + NameToServiceConnectorMap::iterator it = + name_to_service_connector_.find(interface_name); + if (it == name_to_service_connector_.end()) + return; + delete it->second; + name_to_service_connector_.erase(it); +} + +void ServiceConnectorRegistry::ConnectToService( + ApplicationConnection* application_connection, + const std::string& interface_name, + ScopedMessagePipeHandle client_handle) { + auto iter = name_to_service_connector_.find(interface_name); + if (iter != name_to_service_connector_.end()) { + iter->second->ConnectToService(application_connection, interface_name, + std::move(client_handle)); + return; + } + if (service_connector_) { + service_connector_->ConnectToService(application_connection, interface_name, + std::move(client_handle)); + } +} + +} // namespace internal +} // namespace mojo diff --git a/mojo/shell/public/cpp/lib/service_connector_registry.h b/mojo/shell/public/cpp/lib/service_connector_registry.h new file mode 100644 index 0000000..9475436 --- /dev/null +++ b/mojo/shell/public/cpp/lib/service_connector_registry.h @@ -0,0 +1,62 @@ +// 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 MOJO_SHELL_PUBLIC_CPP_LIB_SERVICE_CONNECTOR_REGISTRY_H_ +#define MOJO_SHELL_PUBLIC_CPP_LIB_SERVICE_CONNECTOR_REGISTRY_H_ + +#include <map> +#include <string> + +#include "mojo/public/cpp/system/message_pipe.h" + +namespace mojo { + +class ApplicationConnection; +class ServiceConnector; + +namespace internal { + +// ServiceConnectorRegistry maintains a default ServiceConnector as well as at +// most one ServiceConnector per interface name. When ConnectToService() is +// invoked the ServiceConnector registered by name is given the request. If +// a ServiceConnector has not been registered by name than the default +// ServiceConnector is given the request. +class ServiceConnectorRegistry { + public: + ServiceConnectorRegistry(); + ~ServiceConnectorRegistry(); + + // Sets the default ServiceConnector. ServiceConnectorRegistry does *not* + // take ownership of |service_connector|. + void set_service_connector(ServiceConnector* service_connector) { + service_connector_ = service_connector; + } + + // Returns true if non ServiceConnectors have been registered by name. + bool empty() const { return name_to_service_connector_.empty(); } + + // Sets a ServiceConnector by name. This deletes the existing ServiceConnector + // and takes ownership of |service_connector|. + void SetServiceConnectorForName(ServiceConnector* service_connector, + const std::string& interface_name); + void RemoveServiceConnectorForName(const std::string& interface_name); + + void ConnectToService(ApplicationConnection* application_connection, + const std::string& interface_name, + ScopedMessagePipeHandle client_handle); + + private: + using NameToServiceConnectorMap = std::map<std::string, ServiceConnector*>; + + ServiceConnector* service_connector_; + + NameToServiceConnectorMap name_to_service_connector_; + + MOJO_DISALLOW_COPY_AND_ASSIGN(ServiceConnectorRegistry); +}; + +} // namespace internal +} // namespace mojo + +#endif // MOJO_SHELL_PUBLIC_CPP_LIB_SERVICE_CONNECTOR_REGISTRY_H_ diff --git a/mojo/shell/public/cpp/lib/service_provider_impl.cc b/mojo/shell/public/cpp/lib/service_provider_impl.cc new file mode 100644 index 0000000..4a411a2 --- /dev/null +++ b/mojo/shell/public/cpp/lib/service_provider_impl.cc @@ -0,0 +1,43 @@ +// Copyright 2014 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include <utility> + +#include "mojo/public/cpp/environment/logging.h" +#include "mojo/shell/public/cpp/service_connector.h" +#include "mojo/shell/public/cpp/service_provider_impl.h" + +namespace mojo { + +ServiceProviderImpl::ServiceProviderImpl() : binding_(this) { +} + +ServiceProviderImpl::ServiceProviderImpl( + InterfaceRequest<ServiceProvider> request) + : binding_(this, std::move(request)) {} + +ServiceProviderImpl::~ServiceProviderImpl() { +} + +void ServiceProviderImpl::Bind(InterfaceRequest<ServiceProvider> request) { + binding_.Bind(std::move(request)); +} + +void ServiceProviderImpl::ConnectToService( + const String& service_name, + ScopedMessagePipeHandle client_handle) { + // TODO(beng): perhaps take app connection thru ctor so that we can pass + // ApplicationConnection through? + service_connector_registry_.ConnectToService(nullptr, service_name, + std::move(client_handle)); +} + +void ServiceProviderImpl::SetServiceConnectorForName( + ServiceConnector* service_connector, + const std::string& interface_name) { + service_connector_registry_.SetServiceConnectorForName(service_connector, + interface_name); +} + +} // namespace mojo diff --git a/mojo/shell/public/cpp/lib/service_registry.cc b/mojo/shell/public/cpp/lib/service_registry.cc new file mode 100644 index 0000000..09f71de --- /dev/null +++ b/mojo/shell/public/cpp/lib/service_registry.cc @@ -0,0 +1,138 @@ +// Copyright 2014 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "mojo/shell/public/cpp/lib/service_registry.h" + +#include <stdint.h> + +#include <utility> + +#include "base/bind.h" +#include "base/logging.h" +#include "mojo/shell/public/cpp/application_connection.h" +#include "mojo/shell/public/cpp/service_connector.h" + +namespace mojo { +namespace internal { + +ServiceRegistry::ServiceRegistry( + const std::string& connection_url, + const std::string& remote_url, + ServiceProviderPtr remote_services, + InterfaceRequest<ServiceProvider> local_services, + const std::set<std::string>& allowed_interfaces) + : connection_url_(connection_url), + remote_url_(remote_url), + local_binding_(this), + remote_service_provider_(std::move(remote_services)), + allowed_interfaces_(allowed_interfaces), + allow_all_interfaces_(allowed_interfaces_.size() == 1 && + allowed_interfaces_.count("*") == 1), + content_handler_id_(0u), + is_content_handler_id_valid_(false), + weak_factory_(this) { + if (local_services.is_pending()) + local_binding_.Bind(std::move(local_services)); +} + +ServiceRegistry::ServiceRegistry() + : local_binding_(this), + allow_all_interfaces_(true), + weak_factory_(this) { +} + +ServiceRegistry::~ServiceRegistry() { +} + +Shell::ConnectToApplicationCallback +ServiceRegistry::GetConnectToApplicationCallback() { + return base::Bind(&ServiceRegistry::OnGotContentHandlerID, + weak_factory_.GetWeakPtr()); +} + +void ServiceRegistry::SetServiceConnector(ServiceConnector* connector) { + service_connector_registry_.set_service_connector(connector); +} + +bool ServiceRegistry::SetServiceConnectorForName( + ServiceConnector* service_connector, + const std::string& interface_name) { + if (allow_all_interfaces_ || + allowed_interfaces_.count(interface_name)) { + service_connector_registry_.SetServiceConnectorForName(service_connector, + interface_name); + return true; + } + LOG(WARNING) << "CapabilityFilter prevented connection to interface: " + << interface_name << " connection_url:" << connection_url_ + << " remote_url:" << remote_url_; + return false; +} + +ServiceProvider* ServiceRegistry::GetLocalServiceProvider() { + return this; +} + +void ServiceRegistry::SetRemoteServiceProviderConnectionErrorHandler( + const Closure& handler) { + remote_service_provider_.set_connection_error_handler(handler); +} + +bool ServiceRegistry::GetContentHandlerID(uint32_t* content_handler_id) { + if (!is_content_handler_id_valid_) + return false; + + *content_handler_id = content_handler_id_; + return true; +} + +void ServiceRegistry::AddContentHandlerIDCallback(const Closure& callback) { + if (is_content_handler_id_valid_) { + callback.Run(); + return; + } + content_handler_id_callbacks_.push_back(callback); +} + +base::WeakPtr<ApplicationConnection> ServiceRegistry::GetWeakPtr() { + return weak_factory_.GetWeakPtr(); +} + +void ServiceRegistry::RemoveServiceConnectorForName( + const std::string& interface_name) { + service_connector_registry_.RemoveServiceConnectorForName(interface_name); + if (service_connector_registry_.empty()) + remote_service_provider_.reset(); +} + +const std::string& ServiceRegistry::GetConnectionURL() { + return connection_url_; +} + +const std::string& ServiceRegistry::GetRemoteApplicationURL() { + return remote_url_; +} + +ServiceProvider* ServiceRegistry::GetServiceProvider() { + return remote_service_provider_.get(); +} + +void ServiceRegistry::OnGotContentHandlerID(uint32_t content_handler_id) { + DCHECK(!is_content_handler_id_valid_); + is_content_handler_id_valid_ = true; + content_handler_id_ = content_handler_id; + std::vector<Closure> callbacks; + callbacks.swap(content_handler_id_callbacks_); + for (auto callback : callbacks) + callback.Run(); +} + +void ServiceRegistry::ConnectToService(const mojo::String& service_name, + ScopedMessagePipeHandle client_handle) { + service_connector_registry_.ConnectToService(this, service_name, + std::move(client_handle)); +} + +} // namespace internal +} // namespace mojo diff --git a/mojo/shell/public/cpp/lib/service_registry.h b/mojo/shell/public/cpp/lib/service_registry.h new file mode 100644 index 0000000..1c2abc9 --- /dev/null +++ b/mojo/shell/public/cpp/lib/service_registry.h @@ -0,0 +1,83 @@ +// Copyright 2014 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef MOJO_SHELL_PUBLIC_CPP_LIB_SERVICE_REGISTRY_H_ +#define MOJO_SHELL_PUBLIC_CPP_LIB_SERVICE_REGISTRY_H_ + +#include <stdint.h> + +#include <set> +#include <string> + +#include "mojo/public/cpp/bindings/binding.h" +#include "mojo/shell/public/cpp/application_connection.h" +#include "mojo/shell/public/cpp/lib/service_connector_registry.h" +#include "mojo/shell/public/interfaces/service_provider.mojom.h" +#include "mojo/shell/public/interfaces/shell.mojom.h" + +namespace mojo { +namespace internal { + +// A ServiceRegistry represents each half of a connection between two +// applications, allowing customization of which services are published to the +// other. +class ServiceRegistry : public ServiceProvider, public ApplicationConnection { + public: + ServiceRegistry(); + // |allowed_interfaces| are the set of interfaces that the shell has allowed + // an application to expose to another application. If this set contains only + // the string value "*" all interfaces may be exposed. + ServiceRegistry(const std::string& connection_url, + const std::string& remote_url, + ServiceProviderPtr remote_services, + InterfaceRequest<ServiceProvider> local_services, + const std::set<std::string>& allowed_interfaces); + ~ServiceRegistry() override; + + Shell::ConnectToApplicationCallback GetConnectToApplicationCallback(); + + // ApplicationConnection overrides. + void SetServiceConnector(ServiceConnector* service_connector) override; + bool SetServiceConnectorForName(ServiceConnector* service_connector, + const std::string& interface_name) override; + const std::string& GetConnectionURL() override; + const std::string& GetRemoteApplicationURL() override; + ServiceProvider* GetServiceProvider() override; + ServiceProvider* GetLocalServiceProvider() override; + void SetRemoteServiceProviderConnectionErrorHandler( + const Closure& handler) override; + bool GetContentHandlerID(uint32_t* target_id) override; + void AddContentHandlerIDCallback(const Closure& callback) override; + base::WeakPtr<ApplicationConnection> GetWeakPtr() override; + + void RemoveServiceConnectorForName(const std::string& interface_name); + + private: + void OnGotContentHandlerID(uint32_t content_handler_id); + + // ServiceProvider method. + void ConnectToService(const mojo::String& service_name, + ScopedMessagePipeHandle client_handle) override; + + const std::string connection_url_; + const std::string remote_url_; + Binding<ServiceProvider> local_binding_; + ServiceProviderPtr remote_service_provider_; + ServiceConnectorRegistry service_connector_registry_; + const std::set<std::string> allowed_interfaces_; + const bool allow_all_interfaces_; + // The id of the content_handler is only available once the callback from + // establishing the connection is made. + uint32_t content_handler_id_; + bool is_content_handler_id_valid_; + std::vector<Closure> content_handler_id_callbacks_; + base::WeakPtrFactory<ServiceRegistry> weak_factory_; + + MOJO_DISALLOW_COPY_AND_ASSIGN(ServiceRegistry); +}; + +} // namespace internal +} // namespace mojo + +#endif // MOJO_SHELL_PUBLIC_CPP_LIB_SERVICE_REGISTRY_H_ diff --git a/mojo/shell/public/cpp/service_connector.h b/mojo/shell/public/cpp/service_connector.h new file mode 100644 index 0000000..8856e3f --- /dev/null +++ b/mojo/shell/public/cpp/service_connector.h @@ -0,0 +1,30 @@ +// 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 MOJO_SHELL_PUBLIC_CPP_SERVICE_CONNECTOR_H_ +#define MOJO_SHELL_PUBLIC_CPP_SERVICE_CONNECTOR_H_ + +#include <string> + +#include "mojo/public/cpp/system/message_pipe.h" + +namespace mojo { + +class ApplicationConnection; + +class ServiceConnector { + public: + virtual ~ServiceConnector() {} + + // Asks the ServiceConnector to connect to the specified service. If the + // ServiceConnector connects to the service it should take ownership of + // the handle in |handle|. + virtual void ConnectToService(ApplicationConnection* application_connection, + const std::string& interface_name, + ScopedMessagePipeHandle handle) = 0; +}; + +} // namespace mojo + +#endif // MOJO_SHELL_PUBLIC_CPP_SERVICE_CONNECTOR_H_ diff --git a/mojo/shell/public/cpp/service_provider_impl.h b/mojo/shell/public/cpp/service_provider_impl.h new file mode 100644 index 0000000..2d5ed75 --- /dev/null +++ b/mojo/shell/public/cpp/service_provider_impl.h @@ -0,0 +1,50 @@ +// Copyright 2014 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef MOJO_SHELL_PUBLIC_CPP_SERVICE_PROVIDER_IMPL_H_ +#define MOJO_SHELL_PUBLIC_CPP_SERVICE_PROVIDER_IMPL_H_ + +#include <string> + +#include "mojo/public/cpp/bindings/strong_binding.h" +#include "mojo/shell/public/cpp/lib/interface_factory_connector.h" +#include "mojo/shell/public/cpp/lib/service_connector_registry.h" +#include "mojo/shell/public/interfaces/service_provider.mojom.h" + +namespace mojo { + +// Implements a registry that can be used to expose services to another app. +class ServiceProviderImpl : public ServiceProvider { + public: + ServiceProviderImpl(); + explicit ServiceProviderImpl(InterfaceRequest<ServiceProvider> request); + ~ServiceProviderImpl() override; + + void Bind(InterfaceRequest<ServiceProvider> request); + + template <typename Interface> + void AddService(InterfaceFactory<Interface>* factory) { + SetServiceConnectorForName( + new internal::InterfaceFactoryConnector<Interface>(factory), + Interface::Name_); + } + + private: + // Overridden from ServiceProvider: + void ConnectToService(const String& service_name, + ScopedMessagePipeHandle client_handle) override; + + void SetServiceConnectorForName(ServiceConnector* service_connector, + const std::string& interface_name); + + StrongBinding<ServiceProvider> binding_; + + internal::ServiceConnectorRegistry service_connector_registry_; + + MOJO_DISALLOW_COPY_AND_ASSIGN(ServiceProviderImpl); +}; + +} // namespace mojo + +#endif // MOJO_SHELL_PUBLIC_CPP_SERVICE_PROVIDER_IMPL_H_ diff --git a/mojo/shell/public/cpp/tests/BUILD.gn b/mojo/shell/public/cpp/tests/BUILD.gn new file mode 100644 index 0000000..51da838 --- /dev/null +++ b/mojo/shell/public/cpp/tests/BUILD.gn @@ -0,0 +1,18 @@ +# Copyright 2014 The Chromium Authors. All rights reserved. +# Use of this source code is governed by a BSD-style license that can be +# found in the LICENSE file. + +import("//testing/test.gni") + +test("mojo_public_application_unittests") { + sources = [ + "service_registry_unittest.cc", + ] + + deps = [ + "//base", + "//mojo/shell/public/cpp", + "//testing/gtest", + "//third_party/mojo/src/mojo/edk/test:run_all_unittests", + ] +} diff --git a/mojo/shell/public/cpp/tests/service_registry_unittest.cc b/mojo/shell/public/cpp/tests/service_registry_unittest.cc new file mode 100644 index 0000000..349e9aa --- /dev/null +++ b/mojo/shell/public/cpp/tests/service_registry_unittest.cc @@ -0,0 +1,72 @@ +// Copyright 2014 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "mojo/shell/public/cpp/lib/service_registry.h" + +#include "base/memory/scoped_ptr.h" +#include "mojo/shell/public/cpp/service_connector.h" +#include "testing/gtest/include/gtest/gtest.h" + +namespace mojo { +namespace internal { +namespace { + +class TestConnector : public ServiceConnector { + public: + explicit TestConnector(int* delete_count) : delete_count_(delete_count) {} + ~TestConnector() override { (*delete_count_)++; } + void ConnectToService(ApplicationConnection* application_connection, + const std::string& interface_name, + ScopedMessagePipeHandle client_handle) override {} + + private: + int* delete_count_; +}; + +TEST(ServiceRegistryTest, Ownership) { + int delete_count = 0; + + // Destruction. + { + ServiceRegistry registry; + registry.SetServiceConnectorForName(new TestConnector(&delete_count), + "TC1"); + } + EXPECT_EQ(1, delete_count); + + // Removal. + { + scoped_ptr<ServiceRegistry> registry(new ServiceRegistry); + ServiceConnector* c = new TestConnector(&delete_count); + registry->SetServiceConnectorForName(c, "TC1"); + registry->RemoveServiceConnectorForName("TC1"); + registry.reset(); + EXPECT_EQ(2, delete_count); + } + + // Multiple. + { + ServiceRegistry registry; + registry.SetServiceConnectorForName(new TestConnector(&delete_count), + "TC1"); + registry.SetServiceConnectorForName(new TestConnector(&delete_count), + "TC2"); + } + EXPECT_EQ(4, delete_count); + + // Re-addition. + { + ServiceRegistry registry; + registry.SetServiceConnectorForName(new TestConnector(&delete_count), + "TC1"); + registry.SetServiceConnectorForName(new TestConnector(&delete_count), + "TC1"); + EXPECT_EQ(5, delete_count); + } + EXPECT_EQ(6, delete_count); +} + +} // namespace +} // namespace internal +} // namespace mojo diff --git a/mojo/shell/public/interfaces/BUILD.gn b/mojo/shell/public/interfaces/BUILD.gn new file mode 100644 index 0000000..1aa7fc5 --- /dev/null +++ b/mojo/shell/public/interfaces/BUILD.gn @@ -0,0 +1,22 @@ +# Copyright 2014 The Chromium Authors. All rights reserved. +# Use of this source code is governed by a BSD-style license that can be +# found in the LICENSE file. + +import("//mojo/public/tools/bindings/mojom.gni") + +# GYP version: mojo/mojo_base.gyp:mojo_application_bindings +mojom("interfaces") { + sources = [ + "application.mojom", + "application_manager.mojom", + "content_handler.mojom", + "service_provider.mojom", + "shell.mojom", + ] + + import_dirs = [ "//mojo/services" ] + + deps = [ + "//mojo/services/network/public/interfaces", + ] +} diff --git a/mojo/shell/public/interfaces/application.mojom b/mojo/shell/public/interfaces/application.mojom new file mode 100644 index 0000000..f0a8724 --- /dev/null +++ b/mojo/shell/public/interfaces/application.mojom @@ -0,0 +1,70 @@ +// Copyright 2014 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +module mojo; + +import "mojo/shell/public/interfaces/service_provider.mojom"; +import "mojo/shell/public/interfaces/shell.mojom"; + +// This is the primary interface implemented by every Mojo application. It +// allows the application to receive its startup arguments from the shell, and +// to be notified of events that occur during its execution. +// +// TODO(aa): It would be good to reorder the parameters once we have interface +// versioning. +interface Application { + // Initializes the application with the specified arguments. This method is + // guaranteed to be called before any other method is called, and will only be + // called once. + // + // The |url| parameter is the identity of the application as far as the shell + // is concerned. This will be the URL the application was found at, after all + // mappings, resolution, and redirects. And it will not include the + // querystring, since the querystring is not part of an application's + // identity. + Initialize(Shell shell, string url); + + // Called when another application (identified by |requestor_url|) attempts to + // open a connection to this application. + // + // If the other application wants to request services from this application, + // it will have passed a valid interface request through the |services| + // parameter (i.e. one containing a valid message pipe endpoint). This + // application may then bind an implementation of |ServiceProvider| to that + // request in order to make services available to the other application. + // + // If the other application wants to offer services to this application, it + // will have passed a bound interface through the |exposed_services| + // parameter. This application may then request services through that + // interface. + // + // It is possible that both parameters will be valid/bound if the other + // application wants to both request services from and offer services to this + // application. + // + // This application is free to ignore the |services| or |exposed_services| + // parameters if it does not wish to offer or request services. + // + // |allowed_interfaces| is a set of interface names that the shell has + // determined can be exposed by this application to the connecting + // application. When this parameter is empty, this application should expose + // no services to the connecting application. When this parameter contains + // only the single string value "*" the application may expose all of its + // services to the connecting application. + // + // |resolved_url| is the URL that was requested to create this connection, + // after all mappings, resolutions, and redirects. This will include any + // querystring that was part of the request. + // + AcceptConnection(string requestor_url, + ServiceProvider&? services, + ServiceProvider? exposed_services, + array<string> allowed_interfaces, + string resolved_url); + + // Called by the shell in response to calling Shell's QuitApplication. The + // application should run the callback with true if shutdown can proceed. + // See Shell::QuitApplication for details about shutdown workflow. + OnQuitRequested() => (bool can_quit); +}; diff --git a/mojo/shell/public/interfaces/application_manager.mojom b/mojo/shell/public/interfaces/application_manager.mojom new file mode 100644 index 0000000..02d059a --- /dev/null +++ b/mojo/shell/public/interfaces/application_manager.mojom @@ -0,0 +1,67 @@ +// 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. + +module mojo.shell.mojom; + +import "mojo/shell/public/interfaces/shell.mojom"; + +struct ApplicationInfo { + int32 id; + string url; + string qualifier; + uint32 pid; +}; + +// Implemented by an application that wishes to be informed when the list of +// running applications changes. +interface ApplicationManagerListener { + // Called once when the listener is added via + // ApplicationManager::AddListener() to provide the initial list of running + // applications that the listener observes changes against. + SetRunningApplications(array<ApplicationInfo> applications); + + // Called when the application manager has started tracking an application. + // This happens when the application manager first handles a request to launch + // the application, and before any process or content handler is created for + // it. + ApplicationInstanceCreated(ApplicationInfo application); + + // Called when the application manager has stopped tracking an application. + // (i.e. when it has ended/quit). + ApplicationInstanceDestroyed(int32 id); + + // Called when a pid is available for the application. This could be because a + // process was created by the runner for it, or because an existing content + // handler process was assigned. + ApplicationPIDAvailable(int32 id, uint32 pid); +}; + +// Implemented by an object in the application manager associated with a +// specific instance. Tells it the PID for a process launched by the client. +// This interface is only available to callers of ApplicationManager:: +// CreateInstanceForHandle(). +interface PIDReceiver { + SetPID(uint32 pid); +}; + +interface ApplicationManager { + // Instructs the ApplicationManager to create an instance for an existing + // process at the other end of |channel|, and perform applicable + // initialization. This assumes the target process will bind the other end of + // channel to an implementation of ChildController and bind an Application + // request there. + CreateInstanceForHandle(handle channel, + string url, + mojo.CapabilityFilter filter, + PIDReceiver& pid_receiver); + + // Called by a child process every time it launches a process. This is needed + // so that the ChildBroker class in the grandchild process can talk to the one + // global BrokerState in the parent mojo_runner process. + RegisterProcessWithBroker(uint32 pid, + handle pipe); + + // The listener is removed when the pipe is closed. + AddListener(ApplicationManagerListener listener); +}; diff --git a/mojo/shell/public/interfaces/content_handler.mojom b/mojo/shell/public/interfaces/content_handler.mojom new file mode 100644 index 0000000..5fdb826 --- /dev/null +++ b/mojo/shell/public/interfaces/content_handler.mojom @@ -0,0 +1,20 @@ +// Copyright 2014 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +module mojo; + +import "mojo/shell/public/interfaces/application.mojom"; +import "network/public/interfaces/url_loader.mojom"; + +// Interface implemented by content handlers. To avoid race conditions with +// dropped requests, the implementation should keep a reference to the lifetime +// of the app (by holding on to AppRefCount). Each application started by +// StartApplication should call the callback given by that method on +// destruction. When the owner in the shell notices this, it will destroy the +// interface pointer, which should cause the strongly-bound ContentHandler +// implementation to self destruct and release the app reference. +interface ContentHandler { + // The callback should be called when the application is destructed. + StartApplication(Application& application, URLResponse response) => (); +}; diff --git a/mojo/shell/public/interfaces/service_provider.mojom b/mojo/shell/public/interfaces/service_provider.mojom new file mode 100644 index 0000000..8c81879 --- /dev/null +++ b/mojo/shell/public/interfaces/service_provider.mojom @@ -0,0 +1,17 @@ +// Copyright 2014 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +module mojo; + +// An interface through which a client may request services from a host. +// Instances of this interface are created within the context of an +// already-identified client and host pair, so there is no need to explicitly +// identify the client or host in the methods below. +interface ServiceProvider { + // Asks the host to provide the service identified by |interface_name| through + // the message |pipe| endpoint supplied by the caller. If the host is not + // willing or able to provide the requested service, it should close the + // |pipe|. + ConnectToService(string interface_name, handle<message_pipe> pipe); +}; diff --git a/mojo/shell/public/interfaces/shell.mojom b/mojo/shell/public/interfaces/shell.mojom new file mode 100644 index 0000000..b70b9fe --- /dev/null +++ b/mojo/shell/public/interfaces/shell.mojom @@ -0,0 +1,79 @@ +// Copyright 2014 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +module mojo; + +import "mojo/shell/public/interfaces/service_provider.mojom"; +import "network/public/interfaces/url_loader.mojom"; + +// Specifies a whitelist of applications and services an application can connect +// to. Connections to applications not explicitly specified here as a key are +// rejected. Connections to services not specified in an application's allowed +// interfaces value are not made. +// A "*" value as the only key in an otherwise empty map means the application +// may connect to any other application. +// A "*" value as the only string in an otherwise empty array of interface names +// means the application may connect to any service in that application. +// An empty interface name array means the application may not connect to any +// services exposed by the application it is connecting to. +struct CapabilityFilter { + map<string, array<string>> filter; +}; + +// An interface through which a Mojo application may communicate with the Mojo +// system and request connections to other applications. +interface Shell { + // Used to indicate the app was not launched by a content handler. + const uint32 kInvalidContentHandlerID = 0; + + // Establishes a connection with another application ("target application") + // (located at |request->url|) through which the calling application and the + // target application may request services from one another. + // |application_url| is a URLRequest in case this is called for an HTTP + // navigation, in which case HTTP specific information like POST data, + // referrer header etc... needed. + // + // If the calling application would like to request services from the target + // application, it should pass a valid interface request in the |services| + // parameter (i.e. one containing a valid message pipe endpoint). If the + // target application does not wish to offer services, it may either not bind + // an implementation to the interface request, or else bind an implementation + // that will reject some or all service requests. + // + // If the calling application would like to offer services to the target + // application, it should pass a bound interface through the + // |exposed_services| parameter. The target application may then request + // services through that interface. + // + // At least one of |services| or |exposed_services| should be valid/bound in + // the call. + // + // If the |application_url| does not contain a domain, but is of the form + // "mojo:{service}", it is up to the Mojo shell to select an appropriate + // application for the service. Currently, the shell does this based on the + // value of its --origin flag. + // + // |filter| is a whitelist of application URLs and services that the target + // application is permitted to connect to. See documentation for + // CapabilityFilter above. + // + // If the connection to |application_url| involves a content handler, then + // |content_handler_id| is the id of the deepest content handler used to + // establish the connection to |application_url|. If no content handler is + // used |content_handler_id| is kInvalidContentHandlerID. + // TODO(beng): determine if we need to expose the target application id also. + ConnectToApplication(URLRequest application_url, + ServiceProvider&? services, + ServiceProvider? exposed_services, + CapabilityFilter filter) => (uint32 content_handler_id); + + // When there are no more instantiated services in an application, it should + // start its shutdown process by calling this method. Additionally, it should + // keep track of any new service requests that come in. The shell will then + // call Application::OnQuitRequested and start queueing new service requests. + // If the application didn't get any new service requests in the meantime, it + // should call the callback with a true value. Otherwise it should call it + // with false. + QuitApplication(); +}; diff --git a/mojo/shell/public/java/BUILD.gn b/mojo/shell/public/java/BUILD.gn new file mode 100644 index 0000000..a89d0fc --- /dev/null +++ b/mojo/shell/public/java/BUILD.gn @@ -0,0 +1,21 @@ +# Copyright 2014 The Chromium Authors. All rights reserved. +# Use of this source code is governed by a BSD-style license that can be +# found in the LICENSE file. + +import("//build/config/android/rules.gni") + +android_library("application") { + java_files = [ + "src/org/chromium/mojo/application/ApplicationConnection.java", + "src/org/chromium/mojo/application/ApplicationDelegate.java", + "src/org/chromium/mojo/application/ApplicationImpl.java", + "src/org/chromium/mojo/application/ApplicationRunner.java", + "src/org/chromium/mojo/application/ServiceFactoryBinder.java", + "src/org/chromium/mojo/application/ShellHelper.java", + ] + deps = [ + "//mojo/public/java:bindings", + "//mojo/public/java:system", + "//mojo/shell/public/interfaces:interfaces_java", + ] +} diff --git a/mojo/shell/public/java/src/org/chromium/mojo/application/ApplicationConnection.java b/mojo/shell/public/java/src/org/chromium/mojo/application/ApplicationConnection.java new file mode 100644 index 0000000..6b0e548 --- /dev/null +++ b/mojo/shell/public/java/src/org/chromium/mojo/application/ApplicationConnection.java @@ -0,0 +1,107 @@ +// 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. + +package org.chromium.mojo.application; + +import org.chromium.mojo.bindings.Interface; +import org.chromium.mojo.system.MessagePipeHandle; +import org.chromium.mojo.system.MojoException; +import org.chromium.mojom.mojo.ServiceProvider; + +import java.io.Closeable; +import java.util.HashMap; +import java.util.Map; + +/** + * Represents a connection to another application. + */ +public class ApplicationConnection implements Closeable { + private final String mConnectionUrl; + private final ServiceProvider mExposedServices; + private final String mRequestorUrl; + private final ServiceProviderImpl mServiceProviderImpl; + + /** + * @param requestorUrl URL of the application requesting this connection. + * @param exposedServices ServiceProvider for services exposed by the remote application. + */ + public ApplicationConnection( + String requestorUrl, ServiceProvider exposedServices, String connectionUrl) { + mRequestorUrl = requestorUrl; + mExposedServices = exposedServices; + mConnectionUrl = connectionUrl; + mServiceProviderImpl = new ServiceProviderImpl(); + } + + /** + * @return URL of the application requesting this connection. + */ + public String getRequestorUrl() { + return mRequestorUrl; + } + + /** + * @return URL that was used by the source application to establish this connection. + */ + public String connectionUrl() { + return mConnectionUrl; + } + + /** + * @return ServiceProvider for services exposed by the remote application. + */ + public ServiceProvider getRemoteServiceProvider() { + return mExposedServices; + } + + /** + * Add a new service for this application. + * + * @param binder Handle to a ServiceFactoryBinder which contains a service implementation. + */ + public void addService(ServiceFactoryBinder<? extends Interface> binder) { + mServiceProviderImpl.addService(binder); + } + + /** + * @return ServiceProvider for this application. + */ + public ServiceProvider getLocalServiceProvider() { + return mServiceProviderImpl; + } + + @Override + public void close() { + mServiceProviderImpl.close(); + if (mExposedServices != null) { + mExposedServices.close(); + } + } +} + +class ServiceProviderImpl implements ServiceProvider { + private final Map<String, ServiceFactoryBinder<? extends Interface>> mNameToServiceMap = + new HashMap<String, ServiceFactoryBinder<? extends Interface>>(); + + ServiceProviderImpl() {} + + public void addService(ServiceFactoryBinder<? extends Interface> binder) { + mNameToServiceMap.put(binder.getInterfaceName(), binder); + } + + @Override + public void connectToService(String interfaceName, MessagePipeHandle pipe) { + if (mNameToServiceMap.containsKey(interfaceName)) { + mNameToServiceMap.get(interfaceName).bindNewInstanceToMessagePipe(pipe); + } else { + pipe.close(); + } + } + + @Override + public void close() {} + + @Override + public void onConnectionError(MojoException e) {} +}
\ No newline at end of file diff --git a/mojo/shell/public/java/src/org/chromium/mojo/application/ApplicationDelegate.java b/mojo/shell/public/java/src/org/chromium/mojo/application/ApplicationDelegate.java new file mode 100644 index 0000000..72b1c62 --- /dev/null +++ b/mojo/shell/public/java/src/org/chromium/mojo/application/ApplicationDelegate.java @@ -0,0 +1,37 @@ +// 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. + +package org.chromium.mojo.application; + +import org.chromium.mojom.mojo.Shell; + +/** + * Applications should implement this interface to control various behaviors of Mojo application + * interface. + */ +public interface ApplicationDelegate { + /** + * Called exactly once before any other method. + * + * @param shell A handle to the shell interface. + * @param args Arguments used for this application. + * @param url URL of this application. + */ + public void initialize(Shell shell, String[] args, String url); + + /** + * This method is used to configure what services a connection supports when being connected to. + * Return false to reject the connection entirely. + * + * @param connection A handle to the connection. + * @return If this application accepts any incoming connection. + */ + public boolean configureIncomingConnection(ApplicationConnection connection); + + /** + * Called before exiting. After returning from this call, the delegate cannot expect RunLoop to + * still be running. + */ + public void quit(); +} diff --git a/mojo/shell/public/java/src/org/chromium/mojo/application/ApplicationImpl.java b/mojo/shell/public/java/src/org/chromium/mojo/application/ApplicationImpl.java new file mode 100644 index 0000000..7768b8d --- /dev/null +++ b/mojo/shell/public/java/src/org/chromium/mojo/application/ApplicationImpl.java @@ -0,0 +1,71 @@ +// 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. + +package org.chromium.mojo.application; + +import org.chromium.mojo.bindings.InterfaceRequest; +import org.chromium.mojo.system.Core; +import org.chromium.mojo.system.MessagePipeHandle; +import org.chromium.mojo.system.MojoException; +import org.chromium.mojom.mojo.Application; +import org.chromium.mojom.mojo.ServiceProvider; +import org.chromium.mojom.mojo.Shell; + +import java.util.ArrayList; + +/** + * Utility class for communicating with the Shell, and provide Services to clients. + */ +class ApplicationImpl implements Application { + private final ApplicationDelegate mApplicationDelegate; + private final ArrayList<ApplicationConnection> mIncomingConnections = + new ArrayList<ApplicationConnection>(); + private final Core mCore; + private Shell mShell; + + public ApplicationImpl( + ApplicationDelegate delegate, Core core, MessagePipeHandle applicationRequest) { + mApplicationDelegate = delegate; + mCore = core; + ApplicationImpl.MANAGER.bind(this, applicationRequest); + } + + @Override + public void initialize(Shell shell, String[] args, String url) { + mShell = shell; + mApplicationDelegate.initialize(shell, args, url); + } + + @Override + public void acceptConnection(String requestorUrl, InterfaceRequest<ServiceProvider> services, + ServiceProvider exposedServices, String connectionUrl) { + ApplicationConnection connection = + new ApplicationConnection(requestorUrl, exposedServices, connectionUrl); + if (services != null && mApplicationDelegate.configureIncomingConnection(connection)) { + ServiceProvider.MANAGER.bind(connection.getLocalServiceProvider(), services); + mIncomingConnections.add(connection); + } else { + connection.close(); + } + } + + @Override + public void requestQuit() { + mApplicationDelegate.quit(); + for (ApplicationConnection connection : mIncomingConnections) { + connection.close(); + } + mCore.getCurrentRunLoop().quit(); + } + + @Override + public void close() { + if (mShell != null) { + mShell.close(); + } + } + + @Override + public void onConnectionError(MojoException e) {} +} diff --git a/mojo/shell/public/java/src/org/chromium/mojo/application/ApplicationRunner.java b/mojo/shell/public/java/src/org/chromium/mojo/application/ApplicationRunner.java new file mode 100644 index 0000000..1a903b4 --- /dev/null +++ b/mojo/shell/public/java/src/org/chromium/mojo/application/ApplicationRunner.java @@ -0,0 +1,32 @@ +// 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. + +package org.chromium.mojo.application; + +import org.chromium.mojo.system.Core; +import org.chromium.mojo.system.MessagePipeHandle; +import org.chromium.mojo.system.RunLoop; + +/** + * A utility for running an Application. + * + */ +public class ApplicationRunner { + /** + * Runs the delegate in a RunLoop. + * + * @param delegate Application specific functionality. + * @param core Core mojo interface. + * @param applicationRequest Handle for the application request. + */ + public static void run( + ApplicationDelegate delegate, Core core, MessagePipeHandle applicationRequest) { + try (RunLoop runLoop = core.createDefaultRunLoop()) { + try (ApplicationImpl application = + new ApplicationImpl(delegate, core, applicationRequest)) { + runLoop.run(); + } + } + } +} diff --git a/mojo/shell/public/java/src/org/chromium/mojo/application/ServiceFactoryBinder.java b/mojo/shell/public/java/src/org/chromium/mojo/application/ServiceFactoryBinder.java new file mode 100644 index 0000000..462d74e --- /dev/null +++ b/mojo/shell/public/java/src/org/chromium/mojo/application/ServiceFactoryBinder.java @@ -0,0 +1,30 @@ +// 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. + +package org.chromium.mojo.application; + +import org.chromium.mojo.bindings.Interface; +import org.chromium.mojo.system.MessagePipeHandle; + +/** + * ServiceFactoryBinder holds the necessary information to bind a service interface to a message + * pipe. + * + * @param <T> A mojo service interface. + */ +public interface ServiceFactoryBinder<T extends Interface> { + /** + * An application implements to bind a service implementation to |pipe|. + * + * @param pipe A handle to the incoming connection pipe. + */ + public void bindNewInstanceToMessagePipe(MessagePipeHandle pipe); + + /** + * Name of the service interface being implemented. + * + * @return Service interface name. + */ + public String getInterfaceName(); +} diff --git a/mojo/shell/public/java/src/org/chromium/mojo/application/ShellHelper.java b/mojo/shell/public/java/src/org/chromium/mojo/application/ShellHelper.java new file mode 100644 index 0000000..705e22e --- /dev/null +++ b/mojo/shell/public/java/src/org/chromium/mojo/application/ShellHelper.java @@ -0,0 +1,40 @@ +// 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. + +package org.chromium.mojo.application; + +import org.chromium.mojo.bindings.Interface; +import org.chromium.mojo.bindings.Interface.Proxy; +import org.chromium.mojo.bindings.InterfaceRequest; +import org.chromium.mojo.system.Core; +import org.chromium.mojo.system.Pair; +import org.chromium.mojom.mojo.ServiceProvider; +import org.chromium.mojom.mojo.Shell; + +/** + * Helper class to help connecting to other application through the shell. + */ +public class ShellHelper { + /** + * Connects to a service in another application. + * + * @param core Implementation of the {@link Core} api. + * @param shell Instance of the shell. + * @param application URL to the application to use. + * @param manager {@link org.chromium.mojo.bindings.Interface.Manager} for the service to + * connect to. + * @return a proxy to the service. + */ + public static <I extends Interface, P extends Proxy> P connectToService( + Core core, Shell shell, String application, Interface.Manager<I, P> manager) { + Pair<ServiceProvider.Proxy, InterfaceRequest<ServiceProvider>> providerRequest = + ServiceProvider.MANAGER.getInterfaceRequest(core); + try (ServiceProvider.Proxy provider = providerRequest.first) { + shell.connectToApplication(application, providerRequest.second, null); + Pair<P, InterfaceRequest<I>> serviceRequest = manager.getInterfaceRequest(core); + provider.connectToService(manager.getName(), serviceRequest.second.passHandle()); + return serviceRequest.first; + } + } +} |