diff options
author | blundell <blundell@chromium.org> | 2015-01-29 09:36:11 -0800 |
---|---|---|
committer | Commit bot <commit-bot@chromium.org> | 2015-01-29 17:37:14 +0000 |
commit | 29cbd973babcdcddc9df43f492088fa69795874e (patch) | |
tree | 022390ea53b2af5e2d8cd0bbe1d45d674ab1de32 | |
parent | 931d092bc54acfd92322f4d28a3d1c401f76c181 (diff) | |
download | chromium_src-29cbd973babcdcddc9df43f492088fa69795874e.zip chromium_src-29cbd973babcdcddc9df43f492088fa69795874e.tar.gz chromium_src-29cbd973babcdcddc9df43f492088fa69795874e.tar.bz2 |
Update mojo sdk to rev 126532ce21c5c3c55a1e1693731411cb60169efd
Update HTMLViewer for de-clienting of Shell.
Update ServiceRegistryTest.java for de-clienting of MathCalculator.
NOPRESUBMIT=true
Review URL: https://codereview.chromium.org/883843002
Cr-Commit-Position: refs/heads/master@{#313729}
99 files changed, 4219 insertions, 717 deletions
diff --git a/content/public/android/javatests/src/org/chromium/content/browser/ServiceRegistryTest.java b/content/public/android/javatests/src/org/chromium/content/browser/ServiceRegistryTest.java index b0746b5..d076896 100644 --- a/content/public/android/javatests/src/org/chromium/content/browser/ServiceRegistryTest.java +++ b/content/public/android/javatests/src/org/chromium/content/browser/ServiceRegistryTest.java @@ -10,9 +10,9 @@ import org.chromium.base.library_loader.LibraryLoader; import org.chromium.content.browser.ServiceRegistry.ImplementationFactory; import org.chromium.content_shell.ShellMojoTestUtils; import org.chromium.content_shell_apk.ContentShellTestBase; +import org.chromium.mojo.bindings.ConnectionErrorHandler; import org.chromium.mojo.bindings.InterfaceRequest; import org.chromium.mojo.bindings.test.mojom.math.Calculator; -import org.chromium.mojo.bindings.test.mojom.math.CalculatorUi; import org.chromium.mojo.system.Core; import org.chromium.mojo.system.MojoException; import org.chromium.mojo.system.Pair; @@ -33,10 +33,33 @@ public class ServiceRegistryTest extends ContentShellTestBase { private final Core mCore = CoreImpl.getInstance(); private long mNativeTestEnvironment; + static class CalcConnectionErrorHandler implements ConnectionErrorHandler { + + MojoException mLastMojoException; + + @Override + public void onConnectionError(MojoException e) { + mLastMojoException = e; + } + } + + static class CalcCallback implements Calculator.AddResponse, Calculator.MultiplyResponse { + + double mResult = 0.0; + + @Override + public void call(Double result) { + mResult = result; + } + + public double getResult() { + return mResult; + } + } + static class CalculatorImpl implements Calculator { double mResult = 0.0; - CalculatorUi mClient; @Override public void close() {} @@ -45,26 +68,21 @@ public class ServiceRegistryTest extends ContentShellTestBase { public void onConnectionError(MojoException e) {} @Override - public void clear() { + public void clear(ClearResponse callback) { mResult = 0.0; - mClient.output(mResult); + callback.call(mResult); } @Override - public void add(double value) { + public void add(double value, AddResponse callback) { mResult += value; - mClient.output(mResult); + callback.call(mResult); } @Override - public void multiply(double value) { + public void multiply(double value, MultiplyResponse callback) { mResult *= value; - mClient.output(mResult); - } - - @Override - public void setClient(CalculatorUi client) { - mClient = client; + callback.call(mResult); } } @@ -76,24 +94,6 @@ public class ServiceRegistryTest extends ContentShellTestBase { } } - static class CalculatorUiImpl implements CalculatorUi { - - double mOutput = 0.0; - MojoException mLastMojoException; - - @Override - public void close() {} - - @Override - public void onConnectionError(MojoException e) { - mLastMojoException = e; - } - - @Override - public void output(double value) { - mOutput = value; - } - } @Override protected void setUp() throws Exception { @@ -126,22 +126,25 @@ public class ServiceRegistryTest extends ContentShellTestBase { // Add the Calculator service. serviceRegistryA.addService(Calculator.MANAGER, new CalculatorFactory()); - // Create an instance of CalculatorUi and request a Calculator service for it. - CalculatorUiImpl calculatorUi = new CalculatorUiImpl(); Pair<Calculator.Proxy, InterfaceRequest<Calculator>> requestPair = - Calculator.MANAGER.getInterfaceRequest(mCore, calculatorUi); + Calculator.MANAGER.getInterfaceRequest(mCore); + mCloseablesToClose.add(requestPair.first); serviceRegistryB.connectToRemoteService(Calculator.MANAGER, requestPair.second); // Perform a few operations on the Calculator. Calculator.Proxy calculator = requestPair.first; - calculator.add(21); - calculator.multiply(2); + CalcConnectionErrorHandler errorHandler = new CalcConnectionErrorHandler(); + calculator.setErrorHandler(errorHandler); + CalcCallback callback = new CalcCallback(); + + calculator.add(21, callback); + ShellMojoTestUtils.runLoop(RUN_LOOP_TIMEOUT_MS); + assertEquals(21.0, callback.getResult()); - // Spin the message loop and verify the results. - assertEquals(0.0, calculatorUi.mOutput); + calculator.multiply(2, callback); ShellMojoTestUtils.runLoop(RUN_LOOP_TIMEOUT_MS); - assertEquals(42.0, calculatorUi.mOutput); + assertEquals(42.0, callback.getResult()); } /** @@ -155,39 +158,46 @@ public class ServiceRegistryTest extends ContentShellTestBase { ServiceRegistry serviceRegistryB = registryPair.second; // Request the Calculator service before it is added. - CalculatorUiImpl calculatorUi = new CalculatorUiImpl(); Pair<Calculator.Proxy, InterfaceRequest<Calculator>> requestPair = - Calculator.MANAGER.getInterfaceRequest(mCore, calculatorUi); - mCloseablesToClose.add(requestPair.first); + Calculator.MANAGER.getInterfaceRequest(mCore); + Calculator.Proxy calculator = requestPair.first; + CalcConnectionErrorHandler errorHandler = new CalcConnectionErrorHandler(); + calculator.setErrorHandler(errorHandler); + mCloseablesToClose.add(calculator); serviceRegistryB.connectToRemoteService(Calculator.MANAGER, requestPair.second); // Spin the message loop and verify that an error occured. - assertNull(calculatorUi.mLastMojoException); + assertNull(errorHandler.mLastMojoException); ShellMojoTestUtils.runLoop(RUN_LOOP_TIMEOUT_MS); - assertNotNull(calculatorUi.mLastMojoException); + assertNotNull(errorHandler.mLastMojoException); // Add the Calculator service and request it again. - calculatorUi.mLastMojoException = null; + errorHandler.mLastMojoException = null; serviceRegistryA.addService(Calculator.MANAGER, new CalculatorFactory()); - requestPair = Calculator.MANAGER.getInterfaceRequest(mCore, calculatorUi); - mCloseablesToClose.add(requestPair.first); + requestPair = Calculator.MANAGER.getInterfaceRequest(mCore); + calculator = requestPair.first; + errorHandler = new CalcConnectionErrorHandler(); + mCloseablesToClose.add(calculator); serviceRegistryB.connectToRemoteService(Calculator.MANAGER, requestPair.second); // Spin the message loop and verify that no error occured. - assertNull(calculatorUi.mLastMojoException); + assertNull(errorHandler.mLastMojoException); ShellMojoTestUtils.runLoop(RUN_LOOP_TIMEOUT_MS); - assertNull(calculatorUi.mLastMojoException); + assertNull(errorHandler.mLastMojoException); // Remove the Calculator service and request it again. - calculatorUi.mLastMojoException = null; + errorHandler.mLastMojoException = null; serviceRegistryA.removeService(Calculator.MANAGER); - requestPair = Calculator.MANAGER.getInterfaceRequest(mCore, calculatorUi); - mCloseablesToClose.add(requestPair.first); + requestPair = Calculator.MANAGER.getInterfaceRequest(mCore); + calculator = requestPair.first; + errorHandler = new CalcConnectionErrorHandler(); + calculator.setErrorHandler(errorHandler); + mCloseablesToClose.add(calculator); serviceRegistryB.connectToRemoteService(Calculator.MANAGER, requestPair.second); // Spin the message loop and verify that an error occured. - assertNull(calculatorUi.mLastMojoException); + assertNull(errorHandler.mLastMojoException); ShellMojoTestUtils.runLoop(RUN_LOOP_TIMEOUT_MS); - assertNotNull(calculatorUi.mLastMojoException); + assertNotNull(errorHandler.mLastMojoException); } } diff --git a/mojo/application/application_runner_chromium.cc b/mojo/application/application_runner_chromium.cc index ed6d013..069ff90 100644 --- a/mojo/application/application_runner_chromium.cc +++ b/mojo/application/application_runner_chromium.cc @@ -10,8 +10,8 @@ #include "base/memory/scoped_ptr.h" #include "base/message_loop/message_loop.h" #include "mojo/common/message_pump_mojo.h" -#include "third_party/mojo/src/mojo/public/cpp/application/application_delegate.h" -#include "third_party/mojo/src/mojo/public/cpp/application/application_impl.h" +#include "mojo/public/cpp/application/application_delegate.h" +#include "mojo/public/cpp/application/application_impl.h" namespace mojo { @@ -37,14 +37,13 @@ void ApplicationRunnerChromium::set_message_loop_type( message_loop_type_ = type; } -MojoResult ApplicationRunnerChromium::Run(MojoHandle shell_handle) { +MojoResult ApplicationRunnerChromium::Run( + MojoHandle application_request_handle) { DCHECK(!has_run_); has_run_ = true; base::CommandLine::Init(0, NULL); -#if !defined(COMPONENT_BUILD) base::AtExitManager at_exit; -#endif #ifndef NDEBUG base::debug::EnableInProcessStackDumping(); @@ -58,7 +57,8 @@ MojoResult ApplicationRunnerChromium::Run(MojoHandle shell_handle) { loop.reset(new base::MessageLoop(message_loop_type_)); ApplicationImpl impl(delegate_.get(), - MakeScopedHandle(MessagePipeHandle(shell_handle))); + MakeRequest<Application>(MakeScopedHandle( + MessagePipeHandle(application_request_handle)))); loop->Run(); } delegate_.reset(); diff --git a/mojo/application/application_test_main_chromium.cc b/mojo/application/application_test_main_chromium.cc index 33c4084..e6df84f 100644 --- a/mojo/application/application_test_main_chromium.cc +++ b/mojo/application/application_test_main_chromium.cc @@ -3,12 +3,12 @@ // found in the LICENSE file. #include "base/at_exit.h" -#include "third_party/mojo/src/mojo/public/c/system/main.h" -#include "third_party/mojo/src/mojo/public/cpp/application/application_test_base.h" +#include "mojo/public/c/system/main.h" +#include "mojo/public/cpp/application/application_test_base.h" -MojoResult MojoMain(MojoHandle shell_handle) { +MojoResult MojoMain(MojoHandle handle) { // An AtExitManager instance is needed to construct message loops. base::AtExitManager at_exit; - return mojo::test::RunAllTests(shell_handle); + return mojo::test::RunAllTests(handle); } diff --git a/mojo/services/content_handler/public/interfaces/BUILD.gn b/mojo/services/content_handler/public/interfaces/BUILD.gn index cb38d83..5016c65 100644 --- a/mojo/services/content_handler/public/interfaces/BUILD.gn +++ b/mojo/services/content_handler/public/interfaces/BUILD.gn @@ -12,8 +12,14 @@ mojom("interfaces") { import_dirs = [ get_path_info("../../../", "abspath") ] + if (defined(network_service_root)) { + import_dirs += [ network_service_root ] + } else { + network_service_root = "../../.." + } + deps = [ - "../../../network/public/interfaces", + "$network_service_root/network/public/interfaces", ] mojo_sdk_deps = [ "mojo/public/interfaces/application" ] diff --git a/mojo/services/content_handler/public/interfaces/content_handler.mojom b/mojo/services/content_handler/public/interfaces/content_handler.mojom index c56410e..39ddfea 100644 --- a/mojo/services/content_handler/public/interfaces/content_handler.mojom +++ b/mojo/services/content_handler/public/interfaces/content_handler.mojom @@ -4,9 +4,9 @@ module mojo; -import "mojo/public/interfaces/application/shell.mojom"; +import "mojo/public/interfaces/application/application.mojom"; import "network/public/interfaces/url_loader.mojom"; interface ContentHandler { - StartApplication(Shell shell, URLResponse response); + StartApplication(Application& application, URLResponse response); }; diff --git a/mojo/services/html_viewer/html_viewer.cc b/mojo/services/html_viewer/html_viewer.cc index 06df351..73f3045 100644 --- a/mojo/services/html_viewer/html_viewer.cc +++ b/mojo/services/html_viewer/html_viewer.cc @@ -23,6 +23,7 @@ #include "third_party/mojo/src/mojo/public/cpp/application/application_impl.h" #include "third_party/mojo/src/mojo/public/cpp/application/connect.h" #include "third_party/mojo/src/mojo/public/cpp/application/interface_factory_impl.h" +#include "third_party/mojo/src/mojo/public/cpp/bindings/strong_binding.h" #if !defined(COMPONENT_BUILD) #include "base/i18n/icu_util.h" @@ -59,24 +60,24 @@ class HTMLViewer; class HTMLViewerApplication : public mojo::Application { public: - HTMLViewerApplication(ShellPtr shell, + HTMLViewerApplication(InterfaceRequest<Application> request, URLResponsePtr response, scoped_refptr<base::MessageLoopProxy> compositor_thread, WebMediaPlayerFactory* web_media_player_factory) : url_(response->url), - shell_(shell.Pass()), + binding_(this, request.Pass()), initial_response_(response.Pass()), compositor_thread_(compositor_thread), - web_media_player_factory_(web_media_player_factory) { - shell_.set_client(this); + web_media_player_factory_(web_media_player_factory) {} + + void Initialize(ShellPtr shell, Array<String> args) override { ServiceProviderPtr service_provider; + shell_ = shell.Pass(); shell_->ConnectToApplication("mojo:network_service", GetProxy(&service_provider), nullptr); ConnectToService(service_provider.get(), &network_service_); } - void Initialize(Array<String> args) override {} - void AcceptConnection(const String& requestor_url, InterfaceRequest<ServiceProvider> services, ServiceProviderPtr exposed_services) override { @@ -113,6 +114,7 @@ class HTMLViewerApplication : public mojo::Application { } String url_; + mojo::StrongBinding<mojo::Application> binding_; ShellPtr shell_; mojo::NetworkServicePtr network_service_; URLResponsePtr initial_response_; @@ -130,8 +132,11 @@ class ContentHandlerImpl : public mojo::InterfaceImpl<ContentHandler> { private: // Overridden from ContentHandler: - void StartApplication(ShellPtr shell, URLResponsePtr response) override { - new HTMLViewerApplication(shell.Pass(), response.Pass(), compositor_thread_, + void StartApplication(InterfaceRequest<mojo::Application> request, + URLResponsePtr response) override { + new HTMLViewerApplication(request.Pass(), + response.Pass(), + compositor_thread_, web_media_player_factory_); } diff --git a/mojo/services/native_viewport/public/interfaces/native_viewport.mojom b/mojo/services/native_viewport/public/interfaces/native_viewport.mojom index 72d3c9d..75757c2 100644 --- a/mojo/services/native_viewport/public/interfaces/native_viewport.mojom +++ b/mojo/services/native_viewport/public/interfaces/native_viewport.mojom @@ -14,26 +14,26 @@ struct ViewportMetrics { float device_pixel_ratio = 1.0; }; -[Client=NativeViewportClient] interface NativeViewport { // TODO(sky): having a create function is awkward. Should there be a factory // to create the NativeViewport that takes the size? - Create(Size size) => (uint64 native_viewport_id); + Create(Size size) => (uint64 native_viewport_id, ViewportMetrics metrics); + Show(); Hide(); Close(); SetSize(Size size); SubmittedFrame(SurfaceId surface_id); SetEventDispatcher(NativeViewportEventDispatcher dispatcher); + + // The initial viewport metrics will be sent in the reply to the Create + // method. Call RequestMetrics() to receive updates when the viewport metrics + // change. The reply will be sent when the viewport metrics are different from + // the values last sent, so to receive continuous updates call this method + // again after receiving the callback. + RequestMetrics() => (ViewportMetrics metrics); }; interface NativeViewportEventDispatcher { OnEvent(Event event) => (); }; - -interface NativeViewportClient { - // OnMetricsAvailable() is sent at least once after the callback from Create() - // is called. - OnMetricsChanged(ViewportMetrics metrics); - OnDestroyed(); -}; diff --git a/mojo/services/navigation/public/interfaces/BUILD.gn b/mojo/services/navigation/public/interfaces/BUILD.gn index 9b396e7..da9fc70 100644 --- a/mojo/services/navigation/public/interfaces/BUILD.gn +++ b/mojo/services/navigation/public/interfaces/BUILD.gn @@ -12,7 +12,13 @@ mojom("interfaces") { import_dirs = [ get_path_info("../../../", "abspath") ] + if (defined(network_service_root)) { + import_dirs += [ network_service_root ] + } else { + network_service_root = "../../.." + } + deps = [ - "../../../network/public/interfaces", + "$network_service_root/network/public/interfaces", ] } diff --git a/mojo/services/public/js/application.js b/mojo/services/public/js/application.js index ed41628..d4aa1e6 100644 --- a/mojo/services/public/js/application.js +++ b/mojo/services/public/js/application.js @@ -4,30 +4,41 @@ define("mojo/services/public/js/application", [ "mojo/public/js/bindings", + "mojo/public/js/core", + "mojo/public/js/connection", "mojo/public/js/threading", + "mojo/public/interfaces/application/application.mojom", "mojo/services/public/js/service_provider", "mojo/services/public/js/shell", -], function(bindings, threading, serviceProvider, shell) { +], function(bindings, core, connection, threading, applicationMojom, serviceProvider, shell) { + const ApplicationInterface = applicationMojom.Application; const ProxyBindings = bindings.ProxyBindings; const ServiceProvider = serviceProvider.ServiceProvider; const Shell = shell.Shell; class Application { - constructor(shellHandle, url) { + constructor(appRequestHandle, url) { this.url = url; this.serviceProviders = []; this.exposedServiceProviders = []; - this.shellHandle_ = shellHandle; - this.shell = new Shell(shellHandle, { - initialize: this.initialize.bind(this), - acceptConnection: this.doAcceptConnection.bind(this), - }); + this.appRequestHandle_ = appRequestHandle; + this.appStub_ = + connection.bindHandleToStub(appRequestHandle, ApplicationInterface); + bindings.StubBindings(this.appStub_).delegate = { + initialize: this.doInitialize.bind(this), + acceptConnection: this.doAcceptConnection.bind(this), + }; } - initialize(args) { + doInitialize(shellProxy, args) { + this.shellProxy_ = shellProxy; + this.shell = new Shell(shellProxy); + this.initialize(args); } + initialize(args) {} + // The mojom signature of this function is: // AcceptConnection(string requestor_url, // ServiceProvider&? services, @@ -39,23 +50,20 @@ define("mojo/services/public/js/application", [ doAcceptConnection(requestorUrl, servicesRequest, exposedServicesProxy) { // Construct a new js ServiceProvider that can make outgoing calls on // exposedServicesProxy. - var serviceProvider = new ServiceProvider(exposedServicesProxy); + var serviceProvider = + new ServiceProvider(servicesRequest, exposedServicesProxy); this.serviceProviders.push(serviceProvider); - - // Then associate incoming calls with the serviceProvider. - ProxyBindings(servicesRequest).setLocalDelegate(serviceProvider); - this.acceptConnection(requestorUrl, serviceProvider); } - acceptConnection(requestorUrl, serviceProvider) { - } + acceptConnection(requestorUrl, serviceProvider) {} quit() { this.serviceProviders.forEach(function(sp) { sp.close(); }); this.shell.close(); + core.close(this.appRequestHandle_); threading.quit(); } } diff --git a/mojo/services/public/js/service_provider.js b/mojo/services/public/js/service_provider.js index 9566583..a6a81ca 100644 --- a/mojo/services/public/js/service_provider.js +++ b/mojo/services/public/js/service_provider.js @@ -18,10 +18,12 @@ define("mojo/services/public/js/service_provider", [ } class ServiceProvider { - constructor(service) { - this.proxy = service; + constructor(servicesRequest, exposedServicesProxy) { + this.proxy = exposedServicesProxy; this.providers_ = new Map(); // serviceName => see provideService() below this.pendingRequests_ = new Map(); // serviceName => serviceHandle + if (servicesRequest) + StubBindings(servicesRequest).delegate = this; } // Incoming requests @@ -34,11 +36,10 @@ define("mojo/services/public/js/service_provider", [ this.pendingRequests_.set(serviceName, serviceHandle); return; } - var proxy = connection.bindProxyHandle( - serviceHandle, provider.service, provider.service.client); - if (ProxyBindings(proxy).local) - ProxyBindings(proxy).setLocalDelegate(new provider.factory(proxy)); - provider.connections.push(ProxyBindings(proxy).connection); + + var stub = connection.bindHandleToStub(serviceHandle, provider.service); + StubBindings(stub).delegate = new provider.factory(); + provider.connections.push(StubBindings(stub).connection); } provideService(service, factory) { @@ -66,12 +67,11 @@ define("mojo/services/public/js/service_provider", [ if (!clientImpl && interfaceObject.client) throw new Error("Client implementation must be provided"); - var remoteProxy; - var clientFactory = function(x) {remoteProxy = x; return clientImpl;}; - var messagePipeHandle = connection.bindProxyClient( - clientFactory, interfaceObject.client, interfaceObject); - this.proxy.connectToService(interfaceObject.name, messagePipeHandle); - return remoteProxy; + var serviceProxy; + var serviceHandle = connection.bindProxy( + function(sp) {serviceProxy = sp;}, interfaceObject); + this.proxy.connectToService(interfaceObject.name, serviceHandle); + return serviceProxy; }; close() { diff --git a/mojo/services/public/js/shell.js b/mojo/services/public/js/shell.js index 9fc8552..e6c2dee 100644 --- a/mojo/services/public/js/shell.js +++ b/mojo/services/public/js/shell.js @@ -8,8 +8,8 @@ define("mojo/services/public/js/shell", [ "mojo/public/js/connection", "mojo/public/interfaces/application/shell.mojom", "mojo/public/interfaces/application/service_provider.mojom", - "mojo/services/public/js/service_provider", -], function(bindings, core, connection, shellMojom, spMojom, sp) { + "mojo/services/public/js/service_provider","console", +], function(bindings, core, connection, shellMojom, spMojom, sp, console) { const ProxyBindings = bindings.ProxyBindings; const StubBindings = bindings.StubBindings; @@ -18,13 +18,8 @@ define("mojo/services/public/js/shell", [ const ShellInterface = shellMojom.Shell; class Shell { - constructor(shellHandle, app) { - this.shellHandle = shellHandle; - this.proxy = connection.bindProxyHandle( - shellHandle, ShellInterface.client, ShellInterface); - - ProxyBindings(this.proxy).setLocalDelegate(app); - // TODO: call this serviceProviders_ + constructor(shellProxy) { + this.shellProxy = shellProxy; this.applications_ = new Map(); } @@ -33,11 +28,12 @@ define("mojo/services/public/js/shell", [ if (application) return application; - this.proxy.connectToApplication(url, function(services) { - application = new ServiceProvider(services); - }, function() { - return application; - }); + var application = new ServiceProvider(); + this.shellProxy.connectToApplication(url, + function(services) { + application.proxy = services; + }, + application); this.applications_.set(url, application); return application; } @@ -50,8 +46,8 @@ define("mojo/services/public/js/shell", [ this.applications_.forEach(function(application, url) { application.close(); }); + ProxyBindings(this.shellProxy).close(); this.applications_.clear(); - core.close(this.shellHandle); } } diff --git a/mojo/services/view_manager/public/cpp/DEPS b/mojo/services/view_manager/public/cpp/DEPS index 84b6bb7..c08c2c8 100644 --- a/mojo/services/view_manager/public/cpp/DEPS +++ b/mojo/services/view_manager/public/cpp/DEPS @@ -1,8 +1,14 @@ include_rules = [ - "!base", "+geometry/public", "+input_events/public", "+surfaces/public", "+view_manager/public", "+window_manager/public", + + # TODO(blundell): Eliminate these dependencies. crbug.com/451403 + "!base/basictypes.h", + "!base/bind.h", + "!base/compiler_specific.h", + "!base/memory/scoped_ptr.h", + "!base/observer_list.h", ] diff --git a/mojo/services/view_manager/public/cpp/lib/BUILD.gn b/mojo/services/view_manager/public/cpp/lib/BUILD.gn deleted file mode 100644 index 2090455..0000000 --- a/mojo/services/view_manager/public/cpp/lib/BUILD.gn +++ /dev/null @@ -1,23 +0,0 @@ -# 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/ui.gni") - -source_set("run_unittests") { - testonly = true - sources = [ - "view_manager_test_suite.cc", - "view_manager_test_suite.h", - "view_manager_unittests.cc", - ] - - deps = [ - "//base", - "//base/test:test_support", - ] - - if (use_x11) { - deps += [ "//ui/gfx/x" ] - } -} diff --git a/mojo/services/view_manager/public/cpp/lib/DEPS b/mojo/services/view_manager/public/cpp/lib/DEPS index d4de534..f36ebc7 100644 --- a/mojo/services/view_manager/public/cpp/lib/DEPS +++ b/mojo/services/view_manager/public/cpp/lib/DEPS @@ -1,11 +1,10 @@ include_rules = [ "+mojo/services/window_manager/public", -] -specific_include_rules = { - "view_manager_test_suite.cc": [ - "!mojo/services/native_viewport/native_viewport.h", - "!ui/gfx", - "!ui/gl", - ], -} + # TODO(blundell): Eliminate these dependencies. crbug.com/451403 + "!base/callback.h", + "!base/memory/scoped_vector.h", + "!base/memory/weak_ptr.h", + "!base/message_loop/message_loop.h", + "!base/stl_util.h", +] diff --git a/mojo/services/view_manager/public/cpp/lib/view.cc b/mojo/services/view_manager/public/cpp/lib/view.cc index 929eec4..f7e7784 100644 --- a/mojo/services/view_manager/public/cpp/lib/view.cc +++ b/mojo/services/view_manager/public/cpp/lib/view.cc @@ -5,6 +5,7 @@ #include "view_manager/public/cpp/view.h" #include <set> +#include <string> #include "mojo/public/cpp/application/service_provider_impl.h" #include "view_manager/public/cpp/lib/view_manager_client_impl.h" @@ -247,6 +248,18 @@ void View::SetSharedProperty(const std::string& name, properties_.erase(it); } + // TODO: add test coverage of this (450303). + if (manager_) { + Array<uint8_t> transport_value; + if (value) { + transport_value.resize(value->size()); + if (value->size()) + memcpy(&transport_value.front(), &(value->front()), value->size()); + } + static_cast<ViewManagerClientImpl*>(manager_)->SetProperty( + id_, name, transport_value.Pass()); + } + FOR_EACH_OBSERVER( ViewObserver, observers_, OnViewSharedPropertyChanged(this, name, old_value_ptr, value)); @@ -369,10 +382,20 @@ void View::Embed(const String& url, //////////////////////////////////////////////////////////////////////////////// // View, protected: +namespace { + +ViewportMetricsPtr CreateEmptyViewportMetrics() { + ViewportMetricsPtr metrics = ViewportMetrics::New(); + metrics->size = Size::New(); + return metrics; +} +} + View::View() : manager_(NULL), id_(static_cast<Id>(-1)), parent_(NULL), + viewport_metrics_(CreateEmptyViewportMetrics()), visible_(true), drawn_(false) { } @@ -412,6 +435,7 @@ View::View(ViewManager* manager, Id id) : manager_(manager), id_(id), parent_(nullptr), + viewport_metrics_(CreateEmptyViewportMetrics()), visible_(false), drawn_(false) { } @@ -476,6 +500,15 @@ void View::LocalSetBounds(const Rect& old_bounds, bounds_ = new_bounds; } +void View::LocalSetViewportMetrics(const ViewportMetrics& old_metrics, + const ViewportMetrics& new_metrics) { + // TODO(eseidel): We could check old_metrics against viewport_metrics_. + viewport_metrics_ = new_metrics.Clone(); + FOR_EACH_OBSERVER( + ViewObserver, observers_, + OnViewViewportMetricsChanged(this, old_metrics, new_metrics)); +} + void View::LocalSetDrawn(bool value) { if (drawn_ == value) return; diff --git a/mojo/services/view_manager/public/cpp/lib/view_manager_client_impl.cc b/mojo/services/view_manager/public/cpp/lib/view_manager_client_impl.cc index 78daeb3..595e039 100644 --- a/mojo/services/view_manager/public/cpp/lib/view_manager_client_impl.cc +++ b/mojo/services/view_manager/public/cpp/lib/view_manager_client_impl.cc @@ -36,7 +36,8 @@ View* AddViewToViewManager(ViewManagerClientImpl* client, private_view.set_id(view_data->view_id); private_view.set_visible(view_data->visible); private_view.set_drawn(view_data->drawn); - private_view.set_viewport_metrics(view_data->viewport_metrics.Pass()); + private_view.LocalSetViewportMetrics(ViewportMetrics(), + *view_data->viewport_metrics); private_view.set_properties( view_data->properties.To<std::map<std::string, std::vector<uint8_t>>>()); client->AddView(view); @@ -296,6 +297,26 @@ void ViewManagerClientImpl::OnViewBoundsChanged(Id view_id, ViewPrivate(view).LocalSetBounds(*old_bounds, *new_bounds); } +namespace { + +void SetViewportMetricsOnDecendants(View* root, + const ViewportMetrics& old_metrics, + const ViewportMetrics& new_metrics) { + ViewPrivate(root).LocalSetViewportMetrics(old_metrics, new_metrics); + const View::Children& children = root->children(); + for (size_t i = 0; i < children.size(); ++i) + SetViewportMetricsOnDecendants(children[i], old_metrics, new_metrics); +} +} + +void ViewManagerClientImpl::OnViewViewportMetricsChanged( + ViewportMetricsPtr old_metrics, + ViewportMetricsPtr new_metrics) { + View* view = GetRoot(); + if (view) + SetViewportMetricsOnDecendants(view, *old_metrics, *new_metrics); +} + void ViewManagerClientImpl::OnViewHierarchyChanged( Id view_id, Id new_parent_id, diff --git a/mojo/services/view_manager/public/cpp/lib/view_manager_client_impl.h b/mojo/services/view_manager/public/cpp/lib/view_manager_client_impl.h index c8ae5c4..2405229 100644 --- a/mojo/services/view_manager/public/cpp/lib/view_manager_client_impl.h +++ b/mojo/services/view_manager/public/cpp/lib/view_manager_client_impl.h @@ -103,6 +103,8 @@ class ViewManagerClientImpl : public ViewManager, void OnViewBoundsChanged(Id view_id, RectPtr old_bounds, RectPtr new_bounds) override; + void OnViewViewportMetricsChanged(ViewportMetricsPtr old_metrics, + ViewportMetricsPtr new_metrics) override; void OnViewHierarchyChanged(Id view_id, Id new_parent_id, Id old_parent_id, diff --git a/mojo/services/view_manager/public/cpp/lib/view_private.cc b/mojo/services/view_manager/public/cpp/lib/view_private.cc index 395aacf..20232e8 100644 --- a/mojo/services/view_manager/public/cpp/lib/view_private.cc +++ b/mojo/services/view_manager/public/cpp/lib/view_private.cc @@ -8,6 +8,7 @@ namespace mojo { ViewPrivate::ViewPrivate(View* view) : view_(view) { + CHECK(view); } ViewPrivate::~ViewPrivate() { diff --git a/mojo/services/view_manager/public/cpp/lib/view_private.h b/mojo/services/view_manager/public/cpp/lib/view_private.h index 964e6e5..1c54567 100644 --- a/mojo/services/view_manager/public/cpp/lib/view_private.h +++ b/mojo/services/view_manager/public/cpp/lib/view_private.h @@ -39,8 +39,9 @@ class ViewPrivate { view_->properties_ = data; } - void set_viewport_metrics(ViewportMetricsPtr viewport_metrics) { - view_->viewport_metrics_ = viewport_metrics.Pass(); + void LocalSetViewportMetrics(const ViewportMetrics& old_metrics, + const ViewportMetrics& new_metrics) { + view_->LocalSetViewportMetrics(new_metrics, new_metrics); } void LocalDestroy() { diff --git a/mojo/services/view_manager/public/cpp/tests/BUILD.gn b/mojo/services/view_manager/public/cpp/tests/BUILD.gn index 2cbcf43..e912084 100644 --- a/mojo/services/view_manager/public/cpp/tests/BUILD.gn +++ b/mojo/services/view_manager/public/cpp/tests/BUILD.gn @@ -7,23 +7,27 @@ import("//testing/test.gni") test("mojo_view_manager_lib_unittests") { sources = [ - "view_unittest.cc", + "view_manager_test_suite.cc", + "view_manager_test_suite.h", "view_manager_unittest.cc", + "view_manager_unittests.cc", + "view_unittest.cc", ] deps = [ "//base", "//base/test:test_support", - "//mojo/converters/geometry", - "//mojo/environment:chromium", "//mojo/public/cpp/application", "//mojo/services/geometry/public/cpp", "//mojo/services/geometry/public/interfaces", "//mojo/services/view_manager/public/cpp", - "//mojo/services/view_manager/public/cpp/lib:run_unittests", "//mojo/services/view_manager/public/interfaces", "//shell/application_manager", "//shell:test_support", "//testing/gtest", ] + + if (use_x11) { + deps += [ "//ui/gfx/x" ] + } } diff --git a/mojo/services/view_manager/public/cpp/tests/DEPS b/mojo/services/view_manager/public/cpp/tests/DEPS index af27b9a..0f9ddb4 100644 --- a/mojo/services/view_manager/public/cpp/tests/DEPS +++ b/mojo/services/view_manager/public/cpp/tests/DEPS @@ -1,5 +1,16 @@ include_rules = [ + "+mojo/services/geometry/public", + + # TODO(blundell): Determine whether to eliminate these dependencies or move + # the tests outside of view_manager's public code. crbug.com/451403 + "!base/auto_reset.h", + "!base/bind.h", + "!base/i18n/icu_util.h", + "!base/logging.h", + "!base/strings/stringprintf.h", + "!base/test/launcher/unit_test_launcher.h", + "!base/test/test_suite.h", "!shell/application_manager", "!shell/shell_test_helper.h", - "+mojo/services/geometry/public", + "!ui/gfx/x/x11_connection.h", ] diff --git a/mojo/services/view_manager/public/cpp/lib/view_manager_test_suite.cc b/mojo/services/view_manager/public/cpp/tests/view_manager_test_suite.cc index 93a221e..2bfdd1e 100644 --- a/mojo/services/view_manager/public/cpp/lib/view_manager_test_suite.cc +++ b/mojo/services/view_manager/public/cpp/tests/view_manager_test_suite.cc @@ -2,7 +2,7 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -#include "mojo/services/view_manager/public/cpp/lib/view_manager_test_suite.h" +#include "mojo/services/view_manager/public/cpp/tests/view_manager_test_suite.h" #include "base/i18n/icu_util.h" diff --git a/mojo/services/view_manager/public/cpp/lib/view_manager_test_suite.h b/mojo/services/view_manager/public/cpp/tests/view_manager_test_suite.h index ec5bb88..ef81661 100644 --- a/mojo/services/view_manager/public/cpp/lib/view_manager_test_suite.h +++ b/mojo/services/view_manager/public/cpp/tests/view_manager_test_suite.h @@ -2,8 +2,8 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -#ifndef MOJO_SERVICES_VIEW_MANAGER_PUBLIC_CPP_LIB_VIEW_MANAGER_TEST_SUITE_H_ -#define MOJO_SERVICES_VIEW_MANAGER_PUBLIC_CPP_LIB_VIEW_MANAGER_TEST_SUITE_H_ +#ifndef MOJO_SERVICES_VIEW_MANAGER_PUBLIC_CPP_TESTS_VIEW_MANAGER_TEST_SUITE_H_ +#define MOJO_SERVICES_VIEW_MANAGER_PUBLIC_CPP_TESTS_VIEW_MANAGER_TEST_SUITE_H_ #include "base/test/test_suite.h" @@ -23,4 +23,4 @@ class ViewManagerTestSuite : public base::TestSuite { } // namespace mojo -#endif // MOJO_SERVICES_VIEW_MANAGER_PUBLIC_CPP_LIB_VIEW_MANAGER_TEST_SUITE_H_ +#endif // MOJO_SERVICES_VIEW_MANAGER_PUBLIC_CPP_TESTS_VIEW_MANAGER_TEST_SUITE_H_ diff --git a/mojo/services/view_manager/public/cpp/tests/view_manager_unittest.cc b/mojo/services/view_manager/public/cpp/tests/view_manager_unittest.cc index d9e4db8..bc69464 100644 --- a/mojo/services/view_manager/public/cpp/tests/view_manager_unittest.cc +++ b/mojo/services/view_manager/public/cpp/tests/view_manager_unittest.cc @@ -63,11 +63,11 @@ class ConnectApplicationLoader : public ApplicationLoader, // Overridden from ApplicationLoader: void Load(ApplicationManager* manager, const GURL& url, - ScopedMessagePipeHandle shell_handle, + InterfaceRequest<Application> application_request, LoadCallback callback) override { - ASSERT_TRUE(shell_handle.is_valid()); + ASSERT_TRUE(application_request.is_pending()); scoped_ptr<ApplicationImpl> app( - new ApplicationImpl(this, shell_handle.Pass())); + new ApplicationImpl(this, application_request.Pass())); apps_.push_back(app.release()); } diff --git a/mojo/services/view_manager/public/cpp/lib/view_manager_unittests.cc b/mojo/services/view_manager/public/cpp/tests/view_manager_unittests.cc index 1d8bd98..95a7b5c 100644 --- a/mojo/services/view_manager/public/cpp/lib/view_manager_unittests.cc +++ b/mojo/services/view_manager/public/cpp/tests/view_manager_unittests.cc @@ -4,7 +4,7 @@ #include "base/bind.h" #include "base/test/launcher/unit_test_launcher.h" -#include "mojo/services/view_manager/public/cpp/lib/view_manager_test_suite.h" +#include "mojo/services/view_manager/public/cpp/tests/view_manager_test_suite.h" int main(int argc, char** argv) { mojo::ViewManagerTestSuite test_suite(argc, argv); diff --git a/mojo/services/view_manager/public/cpp/view.h b/mojo/services/view_manager/public/cpp/view.h index 1215a3b..07bc5b0 100644 --- a/mojo/services/view_manager/public/cpp/view.h +++ b/mojo/services/view_manager/public/cpp/view.h @@ -153,6 +153,8 @@ class View { // Returns true if the order actually changed. bool LocalReorder(View* relative, OrderDirection direction); void LocalSetBounds(const Rect& old_bounds, const Rect& new_bounds); + void LocalSetViewportMetrics(const ViewportMetrics& old_metrics, + const ViewportMetrics& new_metrics); void LocalSetDrawn(bool drawn); // Methods implementing visibility change notifications. See ViewObserver diff --git a/mojo/services/view_manager/public/cpp/view_observer.h b/mojo/services/view_manager/public/cpp/view_observer.h index 4c0ba87..7430566 100644 --- a/mojo/services/view_manager/public/cpp/view_observer.h +++ b/mojo/services/view_manager/public/cpp/view_observer.h @@ -56,6 +56,11 @@ class ViewObserver { const Rect& old_bounds, const Rect& new_bounds) {} + virtual void OnViewViewportMetricsChanged(View* view, + const ViewportMetrics& old_bounds, + const ViewportMetrics& new_bounds) { + } + virtual void OnCaptureChanged(View* gained_capture, View* lost_capture) {} virtual void OnViewFocusChanged(View* gained_focus, View* lost_focus) {} virtual void OnViewActivationChanged(View* gained_active, View* lost_active) { diff --git a/mojo/services/view_manager/public/interfaces/view_manager.mojom b/mojo/services/view_manager/public/interfaces/view_manager.mojom index 55552f8..23238d5 100644 --- a/mojo/services/view_manager/public/interfaces/view_manager.mojom +++ b/mojo/services/view_manager/public/interfaces/view_manager.mojom @@ -159,6 +159,11 @@ interface ViewManagerClient { mojo.Rect old_bounds, mojo.Rect new_bounds); + // Invoked when the viewport metrics for the view have changed. + // Clients are expected to propagate this to the view tree. + OnViewViewportMetricsChanged(mojo.ViewportMetrics old_metrics, + mojo.ViewportMetrics new_metrics); + // Invoked when a change is done to the hierarchy. A value of 0 is used to // identify a null view. For example, if the old_parent is NULL, 0 is // supplied. @@ -173,7 +178,7 @@ interface ViewManagerClient { // Invoked when the order of views within a parent changes. OnViewReordered(uint32 view_id, uint32 relative_view_id, - OrderDirection direction); + OrderDirection direction); // Invoked when a view is deleted. OnViewDeleted(uint32 view); diff --git a/third_party/mojo/src/mojo/edk/embedder/BUILD.gn b/third_party/mojo/src/mojo/edk/embedder/BUILD.gn index d9a6e69..5eb77d7 100644 --- a/third_party/mojo/src/mojo/edk/embedder/BUILD.gn +++ b/third_party/mojo/src/mojo/edk/embedder/BUILD.gn @@ -35,6 +35,7 @@ mojo_edk_source_set("embedder") { mojo_edk_configs = [ "mojo/edk/system:system_config" ] public_deps = [ + ":delegates", ":platform", ] @@ -90,6 +91,27 @@ mojo_edk_source_set("platform") { } } +mojo_edk_source_set("delegates") { + # This isn't really a standalone target; it must be linked into the + # mojo_system_impl component. + visibility = [ ":embedder" ] + + mojo_edk_visibility = [ "mojo/edk/system" ] + + sources = [ + "master_process_delegate.h", + "slave_process_delegate.h", + ] + + defines = [ "MOJO_SYSTEM_IMPL_IMPLEMENTATION" ] + + mojo_edk_configs = [ "mojo/edk/system:system_config" ] + + deps = [ + "//base", + ] +} + mojo_edk_source_set("embedder_unittests") { testonly = true mojo_edk_visibility = [ "mojo/edk/system:mojo_system_unittests" ] diff --git a/third_party/mojo/src/mojo/edk/embedder/master_process_delegate.h b/third_party/mojo/src/mojo/edk/embedder/master_process_delegate.h new file mode 100644 index 0000000..13791f4 --- /dev/null +++ b/third_party/mojo/src/mojo/edk/embedder/master_process_delegate.h @@ -0,0 +1,47 @@ +// Copyright 2015 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef MOJO_EDK_EMBEDDER_MASTER_PROCESS_DELEGATE_H_ +#define MOJO_EDK_EMBEDDER_MASTER_PROCESS_DELEGATE_H_ + +#include "base/macros.h" +#include "base/memory/scoped_ptr.h" +#include "mojo/edk/system/system_impl_export.h" + +namespace mojo { +namespace embedder { + +// An interface for containers of slave process information, to be used by +// |MasterProcessDelegate| (below). +class MOJO_SYSTEM_IMPL_EXPORT SlaveInfo { + public: + SlaveInfo() {} + virtual ~SlaveInfo() {} + + private: + DISALLOW_COPY_AND_ASSIGN(SlaveInfo); +}; + +// An interface for the master process delegate (which lives in the master +// process). +class MOJO_SYSTEM_IMPL_EXPORT MasterProcessDelegate { + public: + // Called when contact with the slave process specified by |slave_info| has + // been lost. + // TODO(vtl): Obviously, there needs to be a suitable embedder API for + // connecting to a process. What will it be? Mention that here once it exists. + virtual void OnSlaveDisconnect(scoped_ptr<SlaveInfo> slave_info) = 0; + + protected: + MasterProcessDelegate() {} + virtual ~MasterProcessDelegate() {} + + private: + DISALLOW_COPY_AND_ASSIGN(MasterProcessDelegate); +}; + +} // namespace embedder +} // namespace mojo + +#endif // MOJO_EDK_EMBEDDER_MASTER_PROCESS_DELEGATE_H_ diff --git a/third_party/mojo/src/mojo/edk/embedder/slave_process_delegate.h b/third_party/mojo/src/mojo/edk/embedder/slave_process_delegate.h new file mode 100644 index 0000000..1c55a7a --- /dev/null +++ b/third_party/mojo/src/mojo/edk/embedder/slave_process_delegate.h @@ -0,0 +1,36 @@ +// 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_EDK_EMBEDDER_SLAVE_PROCESS_DELEGATE_H_ +#define MOJO_EDK_EMBEDDER_SLAVE_PROCESS_DELEGATE_H_ + +#include "base/macros.h" +#include "base/memory/scoped_ptr.h" +#include "mojo/edk/system/system_impl_export.h" + +namespace mojo { +namespace embedder { + +// An interface for the slave process delegate (which lives in each slave +// process). +class MOJO_SYSTEM_IMPL_EXPORT SlaveProcessDelegate { + public: + // Called when contact with the master process has been lost. + // TODO(vtl): Obviously, there needs to be a suitable embedder API for + // connecting to the master process. What will it be? Mention that here once + // it exists. + virtual void OnMasterDisconnect() = 0; + + protected: + SlaveProcessDelegate() {} + virtual ~SlaveProcessDelegate() {} + + private: + DISALLOW_COPY_AND_ASSIGN(SlaveProcessDelegate); +}; + +} // namespace embedder +} // namespace mojo + +#endif // MOJO_EDK_EMBEDDER_SLAVE_PROCESS_DELEGATE_H_ diff --git a/third_party/mojo/src/mojo/edk/system/BUILD.gn b/third_party/mojo/src/mojo/edk/system/BUILD.gn index 67310c8..12fa225 100644 --- a/third_party/mojo/src/mojo/edk/system/BUILD.gn +++ b/third_party/mojo/src/mojo/edk/system/BUILD.gn @@ -39,6 +39,7 @@ component("system") { "channel_manager.h", "configuration.cc", "configuration.h", + "connection_manager.h", "core.cc", "core.h", "data_pipe.cc", @@ -62,6 +63,8 @@ component("system") { "local_message_pipe_endpoint.h", "mapping_table.cc", "mapping_table.h", + "master_connection_manager.cc", + "master_connection_manager.h", "memory.cc", "memory.h", "message_in_transit.cc", @@ -87,6 +90,8 @@ component("system") { "shared_buffer_dispatcher.h", "simple_dispatcher.cc", "simple_dispatcher.h", + "slave_connection_manager.cc", + "slave_connection_manager.h", "transport_data.cc", "transport_data.h", "unique_identifier.cc", @@ -104,6 +109,7 @@ component("system") { public_deps = [ "../embedder", + "../embedder:delegates", "../embedder:platform", "../../public/c/system", ] @@ -139,6 +145,7 @@ test("mojo_system_unittests") { "channel_endpoint_id_unittest.cc", "channel_manager_unittest.cc", "channel_unittest.cc", + "connection_manager_unittest.cc", "core_test_base.cc", "core_test_base.h", "core_unittest.cc", diff --git a/third_party/mojo/src/mojo/edk/system/connection_manager.h b/third_party/mojo/src/mojo/edk/system/connection_manager.h new file mode 100644 index 0000000..fede7a5 --- /dev/null +++ b/third_party/mojo/src/mojo/edk/system/connection_manager.h @@ -0,0 +1,105 @@ +// 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_EDK_SYSTEM_CONNECTION_MANAGER_H_ +#define MOJO_EDK_SYSTEM_CONNECTION_MANAGER_H_ + +#include "base/macros.h" +#include "mojo/edk/system/unique_identifier.h" + +namespace mojo { + +namespace embedder { +class ScopedPlatformHandle; +} // namespace embedder + +namespace system { + +// (Temporary, unique) identifiers for connections, used as they are being +// brought up: +typedef UniqueIdentifier ConnectionIdentifier; + +// Identifiers for processes (note that these are not OS process IDs): +typedef uint64_t ProcessIdentifier; +const ProcessIdentifier kInvalidProcessIdentifier = 0; + +// |ConnectionManager| is an interface for the system that allows "connections" +// (i.e., native "pipes") to be established between different processes. +// +// The starting point for establishing such a connection is that the two +// processes (not necessarily distinct) are provided with a common +// |ConnectionIdentifier|, and also some (probably indirect, temporary) way of +// communicating. +// +// (The usual case for this are processes A, B, C, with connections A <-> B <-> +// C, with the goal being to establish a direct connection A <-> C. Process B +// generates a |ConnectionIdentifier| that it transmits to A and C, and serves +// as an intermediary until A and C are directly connected.) +// +// To establish such a connection, each process calls |AllowConnect()| with the +// common |ConnectionIdentifier|. Each process then informs the other process +// that it has done so. Once a process knows that both processes have called +// |AllowConnect()|, it proceeds to call |Connect()|. +// +// On success, if the two processes are in fact distinct, |Connect()| provides a +// native (platform) handle for a "pipe" that connects/will connect the two +// processes. (If they are in fact the same process, success will simply yield +// no valid handle, to indicate this case.) +// +// Additionally, on success |Connect()| also provides a unique identifier for +// the peer process. In this way, processes may recognize when they already have +// a direct connection and reuse that, disposing of the new one provided by +// |Connect()|. (TODO(vtl): This is somewhat wasteful, but removes the need to +// handle various race conditions, and for the "master" process -- see below -- +// to track connection teardowns.) +// +// Implementation notes: We implement this using a "star topology", with a +// single trusted "master" (broker) process and an arbitrary number of untrusted +// "slave" (client) processes. The former is implemented by +// |MasterConnectionManager| (master_connection_manager.*) and the latter by +// |SlaveConnectionManager| (slave_connection_manager.*). Each slave is +// connected to the master by a special dedicated |RawChannel|, on which it does +// synchronous IPC (note, however, that the master should never block on any +// slave). +class ConnectionManager { + public: + // All of these methods return true on success or false on failure. Failure is + // obviously fatal for the establishment of a particular connection, but + // should not be treated as fatal to the process. Failure may, e.g., be caused + // by a misbehaving (malicious) untrusted peer process. + + // TODO(vtl): Add a "get my own process identifier" method? + + // Allows a process who makes the identical call (with equal |connection_id|) + // to connect to the calling process. (On success, there will be a "pending + // connection" for the given |connection_id| for the calling process.) + virtual bool AllowConnect(const ConnectionIdentifier& connection_id) = 0; + + // Cancels a pending connection for the calling process. (Note that this may + // fail even if |AllowConnect()| succeeded; regardless, |Connect()| should not + // be called.) + virtual bool CancelConnect(const ConnectionIdentifier& connection_id) = 0; + + // Connects a pending connection; to be called only after both parties have + // called |AllowConnect()|. On success, |peer_process_identifier| is set to an + // unique identifier for the peer process, and if the peer process is not the + // same as the calling process then |*platform_handle| is set to a suitable + // native handle connecting the two parties (if the two parties are the same + // process, then |*platform_handle| is reset to be invalid). + virtual bool Connect(const ConnectionIdentifier& connection_id, + ProcessIdentifier* peer_process_identifier, + embedder::ScopedPlatformHandle* platform_handle) = 0; + + protected: + ConnectionManager() {} + virtual ~ConnectionManager() {} + + private: + DISALLOW_COPY_AND_ASSIGN(ConnectionManager); +}; + +} // namespace system +} // namespace mojo + +#endif // MOJO_EDK_SYSTEM_CONNECTION_MANAGER_H_ diff --git a/third_party/mojo/src/mojo/edk/system/connection_manager_unittest.cc b/third_party/mojo/src/mojo/edk/system/connection_manager_unittest.cc new file mode 100644 index 0000000..f46c386 --- /dev/null +++ b/third_party/mojo/src/mojo/edk/system/connection_manager_unittest.cc @@ -0,0 +1,501 @@ +// 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 is really a unit test for |MasterConnectionManager| and +// |SlaveConnectionManager| (since they need to be tested together). + +#include "mojo/edk/system/connection_manager.h" + +#include <stdint.h> + +#include <string> + +#include "base/macros.h" +#include "base/message_loop/message_loop.h" +#include "base/run_loop.h" +#include "base/threading/thread_checker.h" +#include "mojo/edk/embedder/master_process_delegate.h" +#include "mojo/edk/embedder/platform_channel_pair.h" +#include "mojo/edk/embedder/slave_process_delegate.h" +#include "mojo/edk/system/master_connection_manager.h" +#include "mojo/edk/system/slave_connection_manager.h" +#include "mojo/edk/test/test_utils.h" +#include "testing/gtest/include/gtest/gtest.h" + +namespace mojo { +namespace system { +namespace { + +bool ArePlatformHandlesConnected(const embedder::PlatformHandle& h1, + const embedder::PlatformHandle& h2) { + const uint32_t w1 = 0xdeadbeef; + size_t num_bytes = 0; + if (!mojo::test::BlockingWrite(h1, &w1, sizeof(w1), &num_bytes) || + num_bytes != sizeof(w1)) + return false; + uint32_t r = 0; + num_bytes = 0; + if (!mojo::test::BlockingRead(h2, &r, sizeof(r), &num_bytes) || + num_bytes != sizeof(r)) + return false; + if (r != w1) + return false; + + const uint32_t w2 = 0xfeedface; + num_bytes = 0; + if (!mojo::test::BlockingWrite(h1, &w2, sizeof(w2), &num_bytes) || + num_bytes != sizeof(w2)) + return false; + r = 0; + num_bytes = 0; + if (!mojo::test::BlockingRead(h2, &r, sizeof(r), &num_bytes) || + num_bytes != sizeof(r)) + return false; + if (r != w2) + return false; + + return true; +} + +class TestSlaveInfo : public embedder::SlaveInfo { + public: + explicit TestSlaveInfo(const std::string& name) : name_(name) {} + ~TestSlaveInfo() override { CHECK(thread_checker_.CalledOnValidThread()); } + + const std::string& name() const { return name_; } + + private: + base::ThreadChecker thread_checker_; + std::string name_; + + DISALLOW_COPY_AND_ASSIGN(TestSlaveInfo); +}; + +// Connects the given |slave| (with the given |slave_process_delegate| to the +// given master, creating and using a |TestSlaveInfo| with the given +// |slave_name|. +void ConnectSlave(MasterConnectionManager* master, + embedder::SlaveProcessDelegate* slave_process_delegate, + SlaveConnectionManager* slave, + const std::string& slave_name) { + embedder::PlatformChannelPair platform_channel_pair; + master->AddSlave(make_scoped_ptr(new TestSlaveInfo(slave_name)), + platform_channel_pair.PassServerHandle()); + slave->Init(slave_process_delegate, platform_channel_pair.PassClientHandle()); +} + +class MockMasterProcessDelegate : public embedder::MasterProcessDelegate { + public: + MockMasterProcessDelegate() + : current_run_loop_(), on_slave_disconnect_calls_(0) {} + ~MockMasterProcessDelegate() override {} + + void RunUntilNotified() { + CHECK(!current_run_loop_); + base::RunLoop run_loop; + current_run_loop_ = &run_loop; + run_loop.Run(); + current_run_loop_ = nullptr; + } + + unsigned on_slave_disconnect_calls() const { + return on_slave_disconnect_calls_; + } + const std::string& last_slave_disconnect_name() const { + return last_slave_disconnect_name_; + } + + // |embedder::MasterProcessDelegate| implementation: + void OnSlaveDisconnect(scoped_ptr<embedder::SlaveInfo> slave_info) override { + CHECK(thread_checker_.CalledOnValidThread()); + on_slave_disconnect_calls_++; + last_slave_disconnect_name_ = + static_cast<TestSlaveInfo*>(slave_info.get())->name(); + DVLOG(1) << "Disconnected from slave process " + << last_slave_disconnect_name_; + slave_info.reset(); + + if (current_run_loop_) + current_run_loop_->Quit(); + } + + private: + base::ThreadChecker thread_checker_; + base::RunLoop* current_run_loop_; + + unsigned on_slave_disconnect_calls_; + std::string last_slave_disconnect_name_; + + DISALLOW_COPY_AND_ASSIGN(MockMasterProcessDelegate); +}; + +class MockSlaveProcessDelegate : public embedder::SlaveProcessDelegate { + public: + MockSlaveProcessDelegate() + : current_run_loop_(), on_master_disconnect_calls_(0) {} + ~MockSlaveProcessDelegate() override {} + + void RunUntilNotified() { + CHECK(!current_run_loop_); + base::RunLoop run_loop; + current_run_loop_ = &run_loop; + run_loop.Run(); + current_run_loop_ = nullptr; + } + + unsigned on_master_disconnect_calls() const { + return on_master_disconnect_calls_; + } + + // |embedder::SlaveProcessDelegate| implementation: + void OnMasterDisconnect() override { + CHECK(thread_checker_.CalledOnValidThread()); + on_master_disconnect_calls_++; + DVLOG(1) << "Disconnected from master process"; + + if (current_run_loop_) + current_run_loop_->Quit(); + } + + private: + base::ThreadChecker thread_checker_; + base::RunLoop* current_run_loop_; + + unsigned on_master_disconnect_calls_; + + DISALLOW_COPY_AND_ASSIGN(MockSlaveProcessDelegate); +}; + +class ConnectionManagerTest : public testing::Test { + protected: + ConnectionManagerTest() {} + ~ConnectionManagerTest() override {} + + base::MessageLoop& message_loop() { return message_loop_; } + MockMasterProcessDelegate& master_process_delegate() { + return master_process_delegate_; + } + + private: + base::MessageLoop message_loop_; + MockMasterProcessDelegate master_process_delegate_; + + DISALLOW_COPY_AND_ASSIGN(ConnectionManagerTest); +}; + +TEST_F(ConnectionManagerTest, BasicConnectSlaves) { + MasterConnectionManager master; + master.Init(&master_process_delegate()); + + MockSlaveProcessDelegate slave1_process_delegate; + SlaveConnectionManager slave1; + ConnectSlave(&master, &slave1_process_delegate, &slave1, "slave1"); + + MockSlaveProcessDelegate slave2_process_delegate; + SlaveConnectionManager slave2; + ConnectSlave(&master, &slave2_process_delegate, &slave2, "slave2"); + + ConnectionIdentifier connection_id = ConnectionIdentifier::Generate(); + EXPECT_TRUE(slave1.AllowConnect(connection_id)); + EXPECT_TRUE(slave2.AllowConnect(connection_id)); + + ProcessIdentifier peer1; + embedder::ScopedPlatformHandle h1; + EXPECT_TRUE(slave1.Connect(connection_id, &peer1, &h1)); + EXPECT_TRUE(h1.is_valid()); + ProcessIdentifier peer2; + embedder::ScopedPlatformHandle h2; + EXPECT_TRUE(slave2.Connect(connection_id, &peer2, &h2)); + EXPECT_TRUE(h2.is_valid()); + + // TODO(vtl): If/when I add the ability to get one's own process identifier, + // there'll be more we can check. + EXPECT_NE(peer1, peer2); + EXPECT_TRUE(ArePlatformHandlesConnected(h1.get(), h2.get())); + + // The process manager shouldn't have gotten any notifications yet. (Spin the + // message loop to make sure none were enqueued.) + base::RunLoop().RunUntilIdle(); + EXPECT_EQ(0u, master_process_delegate().on_slave_disconnect_calls()); + + slave1.Shutdown(); + + // |OnSlaveDisconnect()| should be called once. + master_process_delegate().RunUntilNotified(); + EXPECT_EQ(1u, master_process_delegate().on_slave_disconnect_calls()); + EXPECT_EQ("slave1", master_process_delegate().last_slave_disconnect_name()); + + slave2.Shutdown(); + + // |OnSlaveDisconnect()| should be called again. + master_process_delegate().RunUntilNotified(); + EXPECT_EQ(2u, master_process_delegate().on_slave_disconnect_calls()); + EXPECT_EQ("slave2", master_process_delegate().last_slave_disconnect_name()); + + master.Shutdown(); + + // None of the above should result in |OnMasterDisconnect()| being called. + base::RunLoop().RunUntilIdle(); + EXPECT_EQ(0u, slave1_process_delegate.on_master_disconnect_calls()); + EXPECT_EQ(0u, slave2_process_delegate.on_master_disconnect_calls()); +} + +TEST_F(ConnectionManagerTest, ShutdownMasterBeforeSlave) { + MasterConnectionManager master; + master.Init(&master_process_delegate()); + + MockSlaveProcessDelegate slave_process_delegate; + SlaveConnectionManager slave; + ConnectSlave(&master, &slave_process_delegate, &slave, "slave"); + + // The process manager shouldn't have gotten any notifications yet. (Spin the + // message loop to make sure none were enqueued.) + base::RunLoop().RunUntilIdle(); + EXPECT_EQ(0u, master_process_delegate().on_slave_disconnect_calls()); + + master.Shutdown(); + + // |OnSlaveDisconnect()| should be called. + master_process_delegate().RunUntilNotified(); + EXPECT_EQ(1u, master_process_delegate().on_slave_disconnect_calls()); + EXPECT_EQ("slave", master_process_delegate().last_slave_disconnect_name()); + + // |OnMasterDisconnect()| should also be (or have been) called. + slave_process_delegate.RunUntilNotified(); + EXPECT_EQ(1u, slave_process_delegate.on_master_disconnect_calls()); + + slave.Shutdown(); +} + +TEST_F(ConnectionManagerTest, SlaveCancelConnect) { + MasterConnectionManager master; + master.Init(&master_process_delegate()); + + MockSlaveProcessDelegate slave1_process_delegate; + SlaveConnectionManager slave1; + ConnectSlave(&master, &slave1_process_delegate, &slave1, "slave1"); + + MockSlaveProcessDelegate slave2_process_delegate; + SlaveConnectionManager slave2; + ConnectSlave(&master, &slave2_process_delegate, &slave2, "slave2"); + + ConnectionIdentifier connection_id = ConnectionIdentifier::Generate(); + EXPECT_TRUE(slave1.AllowConnect(connection_id)); + EXPECT_TRUE(slave2.AllowConnect(connection_id)); + + EXPECT_TRUE(slave1.CancelConnect(connection_id)); + ProcessIdentifier peer2; + embedder::ScopedPlatformHandle h2; + EXPECT_FALSE(slave2.Connect(connection_id, &peer2, &h2)); + EXPECT_FALSE(h2.is_valid()); + + slave1.Shutdown(); + slave2.Shutdown(); + master.Shutdown(); +} + +// Tests that pending connections are removed on error. +TEST_F(ConnectionManagerTest, ErrorRemovePending) { + MasterConnectionManager master; + master.Init(&master_process_delegate()); + + MockSlaveProcessDelegate slave1_process_delegate; + SlaveConnectionManager slave1; + ConnectSlave(&master, &slave1_process_delegate, &slave1, "slave1"); + + MockSlaveProcessDelegate slave2_process_delegate; + SlaveConnectionManager slave2; + ConnectSlave(&master, &slave2_process_delegate, &slave2, "slave2"); + + ConnectionIdentifier connection_id = ConnectionIdentifier::Generate(); + EXPECT_TRUE(slave1.AllowConnect(connection_id)); + EXPECT_TRUE(slave2.AllowConnect(connection_id)); + + slave1.Shutdown(); + + // |OnSlaveDisconnect()| should be called. After it's called, this means that + // the disconnect has been detected and handled, including the removal of the + // pending connection. + master_process_delegate().RunUntilNotified(); + EXPECT_EQ(1u, master_process_delegate().on_slave_disconnect_calls()); + + ProcessIdentifier peer2; + embedder::ScopedPlatformHandle h2; + EXPECT_FALSE(slave2.Connect(connection_id, &peer2, &h2)); + EXPECT_FALSE(h2.is_valid()); + + slave2.Shutdown(); + master.Shutdown(); +} + +TEST_F(ConnectionManagerTest, ConnectSlaveToSelf) { + MasterConnectionManager master; + master.Init(&master_process_delegate()); + + MockSlaveProcessDelegate slave_process_delegate; + SlaveConnectionManager slave; + ConnectSlave(&master, &slave_process_delegate, &slave, "slave"); + + ConnectionIdentifier connection_id = ConnectionIdentifier::Generate(); + EXPECT_TRUE(slave.AllowConnect(connection_id)); + EXPECT_TRUE(slave.AllowConnect(connection_id)); + + // Currently, the connect-to-self case is signalled by the master not sending + // back a handle. + ProcessIdentifier peer1; + embedder::ScopedPlatformHandle h1; + EXPECT_TRUE(slave.Connect(connection_id, &peer1, &h1)); + EXPECT_FALSE(h1.is_valid()); + ProcessIdentifier peer2; + embedder::ScopedPlatformHandle h2; + EXPECT_TRUE(slave.Connect(connection_id, &peer2, &h2)); + EXPECT_FALSE(h2.is_valid()); + + EXPECT_EQ(peer1, peer2); + + slave.Shutdown(); + master.Shutdown(); +} + +TEST_F(ConnectionManagerTest, ConnectSlavesTwice) { + MasterConnectionManager master; + master.Init(&master_process_delegate()); + + MockSlaveProcessDelegate slave1_process_delegate; + SlaveConnectionManager slave1; + ConnectSlave(&master, &slave1_process_delegate, &slave1, "slave1"); + + MockSlaveProcessDelegate slave2_process_delegate; + SlaveConnectionManager slave2; + ConnectSlave(&master, &slave2_process_delegate, &slave2, "slave2"); + + ConnectionIdentifier connection_id = ConnectionIdentifier::Generate(); + EXPECT_TRUE(slave1.AllowConnect(connection_id)); + EXPECT_TRUE(slave2.AllowConnect(connection_id)); + + ProcessIdentifier peer1; + embedder::ScopedPlatformHandle h1; + EXPECT_TRUE(slave1.Connect(connection_id, &peer1, &h1)); + ProcessIdentifier peer2; + embedder::ScopedPlatformHandle h2; + EXPECT_TRUE(slave2.Connect(connection_id, &peer2, &h2)); + + EXPECT_NE(peer1, peer2); + EXPECT_TRUE(ArePlatformHandlesConnected(h1.get(), h2.get())); + + // Currently, the master doesn't detect the case of connecting a pair of + // slaves that are already connected. (Doing so would require more careful + // tracking and is prone to races -- especially if we want slaves to be able + // to tear down no-longer-needed connections.) But the slaves should be able + // to do the tracking themselves (using the peer process identifiers). + connection_id = ConnectionIdentifier::Generate(); + EXPECT_TRUE(slave1.AllowConnect(connection_id)); + EXPECT_TRUE(slave2.AllowConnect(connection_id)); + + h1.reset(); + h2.reset(); + ProcessIdentifier second_peer2; + EXPECT_TRUE(slave2.Connect(connection_id, &second_peer2, &h2)); + ProcessIdentifier second_peer1; + EXPECT_TRUE(slave1.Connect(connection_id, &second_peer1, &h1)); + + EXPECT_EQ(peer1, second_peer1); + EXPECT_EQ(peer2, second_peer2); + EXPECT_TRUE(ArePlatformHandlesConnected(h1.get(), h2.get())); + + slave2.Shutdown(); + slave1.Shutdown(); + master.Shutdown(); +} + +TEST_F(ConnectionManagerTest, ConnectMasterToSlave) { + MasterConnectionManager master; + master.Init(&master_process_delegate()); + + MockSlaveProcessDelegate slave_process_delegate; + SlaveConnectionManager slave; + ConnectSlave(&master, &slave_process_delegate, &slave, "slave"); + + ConnectionIdentifier connection_id = ConnectionIdentifier::Generate(); + EXPECT_TRUE(master.AllowConnect(connection_id)); + EXPECT_TRUE(slave.AllowConnect(connection_id)); + + ProcessIdentifier master_peer; + embedder::ScopedPlatformHandle master_h; + EXPECT_TRUE(master.Connect(connection_id, &master_peer, &master_h)); + EXPECT_TRUE(master_h.is_valid()); + ProcessIdentifier slave_peer; + embedder::ScopedPlatformHandle slave_h; + EXPECT_TRUE(slave.Connect(connection_id, &slave_peer, &slave_h)); + EXPECT_TRUE(slave_h.is_valid()); + + EXPECT_NE(master_peer, slave_peer); + EXPECT_TRUE(ArePlatformHandlesConnected(master_h.get(), slave_h.get())); + + slave.Shutdown(); + master.Shutdown(); +} + +TEST_F(ConnectionManagerTest, ConnectMasterToSelf) { + MasterConnectionManager master; + master.Init(&master_process_delegate()); + + ConnectionIdentifier connection_id = ConnectionIdentifier::Generate(); + EXPECT_TRUE(master.AllowConnect(connection_id)); + EXPECT_TRUE(master.AllowConnect(connection_id)); + + // Currently, the connect-to-self case is signalled by the master not sending + // back a handle. + ProcessIdentifier peer1; + embedder::ScopedPlatformHandle h1; + EXPECT_TRUE(master.Connect(connection_id, &peer1, &h1)); + EXPECT_FALSE(h1.is_valid()); + ProcessIdentifier peer2; + embedder::ScopedPlatformHandle h2; + EXPECT_TRUE(master.Connect(connection_id, &peer2, &h2)); + EXPECT_FALSE(h2.is_valid()); + + EXPECT_EQ(peer1, peer2); + + master.Shutdown(); +} + +TEST_F(ConnectionManagerTest, MasterCancelConnect) { + MasterConnectionManager master; + master.Init(&master_process_delegate()); + + MockSlaveProcessDelegate slave_process_delegate; + SlaveConnectionManager slave; + ConnectSlave(&master, &slave_process_delegate, &slave, "slave"); + + ConnectionIdentifier connection_id = ConnectionIdentifier::Generate(); + EXPECT_TRUE(master.AllowConnect(connection_id)); + EXPECT_TRUE(slave.AllowConnect(connection_id)); + + EXPECT_TRUE(master.CancelConnect(connection_id)); + ProcessIdentifier peer; + embedder::ScopedPlatformHandle h; + EXPECT_FALSE(slave.Connect(connection_id, &peer, &h)); + EXPECT_FALSE(h.is_valid()); + + slave.Shutdown(); + master.Shutdown(); +} + +TEST_F(ConnectionManagerTest, AddSlaveThenImmediateShutdown) { + MasterConnectionManager master; + master.Init(&master_process_delegate()); + + MockSlaveProcessDelegate slave_process_delegate; + SlaveConnectionManager slave; + embedder::PlatformChannelPair platform_channel_pair; + master.AddSlave(make_scoped_ptr(new TestSlaveInfo("slave")), + platform_channel_pair.PassServerHandle()); + master.Shutdown(); + // Since we never initialized |slave|, we don't have to shut it down. +} + +} // namespace +} // namespace system +} // namespace mojo diff --git a/third_party/mojo/src/mojo/edk/system/master_connection_manager.cc b/third_party/mojo/src/mojo/edk/system/master_connection_manager.cc new file mode 100644 index 0000000..b78dfb6 --- /dev/null +++ b/third_party/mojo/src/mojo/edk/system/master_connection_manager.cc @@ -0,0 +1,572 @@ +// 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/edk/system/master_connection_manager.h" + +#include "base/bind.h" +#include "base/bind_helpers.h" +#include "base/location.h" +#include "base/logging.h" +#include "base/message_loop/message_loop.h" +#include "base/synchronization/waitable_event.h" +#include "mojo/edk/embedder/master_process_delegate.h" +#include "mojo/edk/embedder/platform_channel_pair.h" +#include "mojo/edk/embedder/platform_handle_vector.h" +#include "mojo/edk/system/message_in_transit.h" +#include "mojo/edk/system/raw_channel.h" +#include "mojo/edk/system/transport_data.h" + +namespace mojo { +namespace system { + +const ProcessIdentifier kFirstProcessIdentifier = 1; +const ProcessIdentifier kMasterProcessIdentifier = + static_cast<ProcessIdentifier>(-1); + +// MasterConnectionManager::Helper --------------------------------------------- + +// |MasterConnectionManager::Helper| is not thread-safe, and must only be used +// on its |owner_|'s private thread. +class MasterConnectionManager::Helper : public RawChannel::Delegate { + public: + Helper(MasterConnectionManager* owner, + ProcessIdentifier process_identifier, + scoped_ptr<embedder::SlaveInfo> slave_info, + embedder::ScopedPlatformHandle platform_handle); + ~Helper() override; + + void Init(); + scoped_ptr<embedder::SlaveInfo> Shutdown(); + + private: + // |RawChannel::Delegate| methods: + void OnReadMessage( + const MessageInTransit::View& message_view, + embedder::ScopedPlatformHandleVectorPtr platform_handles) override; + void OnError(Error error) override; + + // Handles an error that's fatal to this object. Note that this probably + // results in |Shutdown()| being called (in the nested context) and then this + // object being destroyed. + void FatalError(); + + MasterConnectionManager* const owner_; + const ProcessIdentifier process_identifier_; + scoped_ptr<embedder::SlaveInfo> slave_info_; + scoped_ptr<RawChannel> raw_channel_; + + DISALLOW_COPY_AND_ASSIGN(Helper); +}; + +MasterConnectionManager::Helper::Helper( + MasterConnectionManager* owner, + ProcessIdentifier process_identifier, + scoped_ptr<embedder::SlaveInfo> slave_info, + embedder::ScopedPlatformHandle platform_handle) + : owner_(owner), + process_identifier_(process_identifier), + slave_info_(slave_info.Pass()), + raw_channel_(RawChannel::Create(platform_handle.Pass())) { +} + +MasterConnectionManager::Helper::~Helper() { + DCHECK(!slave_info_); +} + +void MasterConnectionManager::Helper::Init() { + raw_channel_->Init(this); +} + +scoped_ptr<embedder::SlaveInfo> MasterConnectionManager::Helper::Shutdown() { + raw_channel_->Shutdown(); + raw_channel_.reset(); + return slave_info_.Pass(); +} + +void MasterConnectionManager::Helper::OnReadMessage( + const MessageInTransit::View& message_view, + embedder::ScopedPlatformHandleVectorPtr platform_handles) { + if (message_view.type() != MessageInTransit::kTypeConnectionManager) { + LOG(ERROR) << "Invalid message type " << message_view.type(); + FatalError(); // WARNING: This destroys us. + return; + } + + // Currently, all the messages simply have a |ConnectionIdentifier| as data. + if (message_view.num_bytes() != sizeof(ConnectionIdentifier)) { + LOG(ERROR) << "Invalid message size " << message_view.num_bytes(); + FatalError(); // WARNING: This destroys us. + return; + } + + // And none of them should have any platform handles attached. + if (message_view.transport_data_buffer()) { + LOG(ERROR) << "Invalid message with transport data"; + FatalError(); // WARNING: This destroys us. + return; + } + + const ConnectionIdentifier* connection_id = + reinterpret_cast<const ConnectionIdentifier*>(message_view.bytes()); + bool result; + ProcessIdentifier peer_process_identifier = kInvalidProcessIdentifier; + embedder::ScopedPlatformHandle platform_handle; + uint32_t num_bytes = 0; + const void* bytes = nullptr; + switch (message_view.subtype()) { + case MessageInTransit::kSubtypeConnectionManagerAllowConnect: + result = owner_->AllowConnectImpl(process_identifier_, *connection_id); + break; + case MessageInTransit::kSubtypeConnectionManagerCancelConnect: + result = owner_->CancelConnectImpl(process_identifier_, *connection_id); + break; + case MessageInTransit::kSubtypeConnectionManagerConnect: + result = owner_->ConnectImpl(process_identifier_, *connection_id, + &peer_process_identifier, &platform_handle); + // Success acks for "connect" have the peer process identifier as data + // (and maybe also a platform handle). + if (result) { + num_bytes = static_cast<uint32_t>(sizeof(peer_process_identifier)); + bytes = &peer_process_identifier; + } + break; + default: + LOG(ERROR) << "Invalid message subtype " << message_view.subtype(); + FatalError(); // WARNING: This destroys us. + return; + } + + scoped_ptr<MessageInTransit> response(new MessageInTransit( + MessageInTransit::kTypeConnectionManagerAck, + result ? MessageInTransit::kSubtypeConnectionManagerAckSuccess + : MessageInTransit::kSubtypeConnectionManagerAckFailure, + num_bytes, bytes)); + + if (platform_handle.is_valid()) { + // Only success acks for "connect" *may* have a platform handle attached. + DCHECK(result); + DCHECK_EQ(message_view.subtype(), + MessageInTransit::kSubtypeConnectionManagerConnect); + + embedder::ScopedPlatformHandleVectorPtr platform_handles( + new embedder::PlatformHandleVector()); + platform_handles->push_back(platform_handle.release()); + response->SetTransportData( + make_scoped_ptr(new TransportData(platform_handles.Pass()))); + } + + if (!raw_channel_->WriteMessage(response.Pass())) { + LOG(ERROR) << "WriteMessage failed"; + FatalError(); // WARNING: This destroys us. + return; + } +} + +void MasterConnectionManager::Helper::OnError(Error /*error*/) { + // Every error (read or write) is fatal (for that particular connection). Read + // errors are fatal since no more commands will be received from that + // connection. Write errors are fatal since it is no longer possible to send + // responses. + FatalError(); // WARNING: This destroys us. +} + +void MasterConnectionManager::Helper::FatalError() { + owner_->OnError(process_identifier_); // WARNING: This destroys us. +} + +// MasterConnectionManager::PendingConnectionInfo ------------------------------ + +struct MasterConnectionManager::PendingConnectionInfo { + // States: + // - This is created upon a first "allow connect" (with |first| set + // immediately). We then wait for a second "allow connect". + // - After the second "allow connect" (and |second| is set), we wait for + // "connects" from both |first| and |second|. + // - We may then receive "connect" from either |first| or |second|, at which + // which point it remains to wait for "connect" from the other. + // I.e., the valid state transitions are: + // AWAITING_SECOND_ALLOW_CONNECT -> AWAITING_CONNECTS_FROM_BOTH + // -> {AWAITING_CONNECT_FROM_FIRST,AWAITING_CONNECT_FROM_SECOND} + enum State { + AWAITING_SECOND_ALLOW_CONNECT, + AWAITING_CONNECTS_FROM_BOTH, + AWAITING_CONNECT_FROM_FIRST, + AWAITING_CONNECT_FROM_SECOND + }; + + explicit PendingConnectionInfo(ProcessIdentifier first) + : state(AWAITING_SECOND_ALLOW_CONNECT), + first(first), + second(kInvalidProcessIdentifier) { + DCHECK_NE(first, kInvalidProcessIdentifier); + } + ~PendingConnectionInfo() {} + + State state; + + ProcessIdentifier first; + ProcessIdentifier second; + + // Valid in AWAITING_CONNECT_FROM_{FIRST, SECOND} states. + embedder::ScopedPlatformHandle remaining_handle; +}; + +// MasterConnectionManager ----------------------------------------------------- + +MasterConnectionManager::MasterConnectionManager() + : creation_thread_task_runner_(base::MessageLoop::current()->task_runner()), + master_process_delegate_(), + private_thread_("MasterConnectionManagerPrivateThread"), + next_process_identifier_(kFirstProcessIdentifier) { + DCHECK(creation_thread_task_runner_); + AssertOnCreationThread(); // Just make sure this assertion works correctly. +} + +MasterConnectionManager::~MasterConnectionManager() { + AssertOnCreationThread(); + DCHECK(!master_process_delegate_); + DCHECK(!private_thread_.message_loop()); + DCHECK(helpers_.empty()); + DCHECK(pending_connections_.empty()); +} + +void MasterConnectionManager::Init( + embedder::MasterProcessDelegate* master_process_delegate) { + AssertOnCreationThread(); + DCHECK(master_process_delegate); + DCHECK(!master_process_delegate_); + DCHECK(!private_thread_.message_loop()); + + master_process_delegate_ = master_process_delegate; + CHECK(private_thread_.StartWithOptions( + base::Thread::Options(base::MessageLoop::TYPE_IO, 0))); +} + +void MasterConnectionManager::Shutdown() { + AssertOnCreationThread(); + DCHECK(master_process_delegate_); + DCHECK(private_thread_.message_loop()); + + // The |Stop()| will actually finish all posted tasks. + private_thread_.message_loop()->PostTask( + FROM_HERE, base::Bind(&MasterConnectionManager::ShutdownOnPrivateThread, + base::Unretained(this))); + private_thread_.Stop(); + DCHECK(helpers_.empty()); + DCHECK(pending_connections_.empty()); + master_process_delegate_ = nullptr; +} + +void MasterConnectionManager::AddSlave( + scoped_ptr<embedder::SlaveInfo> slave_info, + embedder::ScopedPlatformHandle platform_handle) { + // We don't really care if |slave_info| is non-null or not. + DCHECK(platform_handle.is_valid()); + AssertNotOnPrivateThread(); + + // We have to wait for the task to be executed, in case someone calls + // |AddSlave()| followed immediately by |Shutdown()|. + base::WaitableEvent event(false, false); + private_thread_.message_loop()->PostTask( + FROM_HERE, + base::Bind(&MasterConnectionManager::AddSlaveOnPrivateThread, + base::Unretained(this), base::Passed(&slave_info), + base::Passed(&platform_handle), base::Unretained(&event))); + event.Wait(); +} + +bool MasterConnectionManager::AllowConnect( + const ConnectionIdentifier& connection_id) { + AssertNotOnPrivateThread(); + return AllowConnectImpl(kMasterProcessIdentifier, connection_id); +} + +bool MasterConnectionManager::CancelConnect( + const ConnectionIdentifier& connection_id) { + AssertNotOnPrivateThread(); + return CancelConnectImpl(kMasterProcessIdentifier, connection_id); +} + +bool MasterConnectionManager::Connect( + const ConnectionIdentifier& connection_id, + ProcessIdentifier* peer_process_identifier, + embedder::ScopedPlatformHandle* platform_handle) { + AssertNotOnPrivateThread(); + return ConnectImpl(kMasterProcessIdentifier, connection_id, + peer_process_identifier, platform_handle); +} + +bool MasterConnectionManager::AllowConnectImpl( + ProcessIdentifier process_identifier, + const ConnectionIdentifier& connection_id) { + DCHECK_NE(process_identifier, kInvalidProcessIdentifier); + + base::AutoLock locker(lock_); + + auto it = pending_connections_.find(connection_id); + if (it == pending_connections_.end()) { + pending_connections_[connection_id] = + new PendingConnectionInfo(process_identifier); + // TODO(vtl): Track process identifier -> pending connections also (so these + // can be removed efficiently if that process disconnects). + DVLOG(1) << "New pending connection ID " << connection_id + << ": AllowConnect() from first process identifier " + << process_identifier; + return true; + } + + PendingConnectionInfo* info = it->second; + if (info->state == PendingConnectionInfo::AWAITING_SECOND_ALLOW_CONNECT) { + info->state = PendingConnectionInfo::AWAITING_CONNECTS_FROM_BOTH; + info->second = process_identifier; + DVLOG(1) << "Pending connection ID " << connection_id + << ": AllowConnect() from second process identifier " + << process_identifier; + return true; + } + + // Someone's behaving badly, but we don't know who (it might not be the + // caller). + LOG(ERROR) << "AllowConnect() from process " << process_identifier + << " for connection ID " << connection_id << " already in state " + << info->state; + pending_connections_.erase(it); + delete info; + return false; +} + +bool MasterConnectionManager::CancelConnectImpl( + ProcessIdentifier process_identifier, + const ConnectionIdentifier& connection_id) { + DCHECK_NE(process_identifier, kInvalidProcessIdentifier); + + base::AutoLock locker(lock_); + + auto it = pending_connections_.find(connection_id); + if (it == pending_connections_.end()) { + // Not necessarily the caller's fault, and not necessarily an error. + DVLOG(1) << "CancelConnect() from process " << process_identifier + << " for connection ID " << connection_id + << " which is not (or no longer) pending"; + return true; + } + + PendingConnectionInfo* info = it->second; + if (process_identifier != info->first && process_identifier != info->second) { + LOG(ERROR) << "CancelConnect() from process " << process_identifier + << " for connection ID " << connection_id + << " which is neither connectee"; + return false; + } + + // Just erase it. The other side may also try to cancel, in which case it'll + // "fail" in the first if statement above (we assume that connection IDs never + // collide, so there's no need to carefully track both sides). + pending_connections_.erase(it); + delete info; + return true; +} + +bool MasterConnectionManager::ConnectImpl( + ProcessIdentifier process_identifier, + const ConnectionIdentifier& connection_id, + ProcessIdentifier* peer_process_identifier, + embedder::ScopedPlatformHandle* platform_handle) { + DCHECK_NE(process_identifier, kInvalidProcessIdentifier); + DCHECK(peer_process_identifier); + DCHECK(platform_handle); + DCHECK(!platform_handle->is_valid()); // Not technically wrong, but unlikely. + + base::AutoLock locker(lock_); + + auto it = pending_connections_.find(connection_id); + if (it == pending_connections_.end()) { + // Not necessarily the caller's fault. + LOG(ERROR) << "Connect() from process " << process_identifier + << " for connection ID " << connection_id + << " which is not pending"; + return false; + } + + PendingConnectionInfo* info = it->second; + if (info->state == PendingConnectionInfo::AWAITING_CONNECTS_FROM_BOTH) { + DCHECK(!info->remaining_handle.is_valid()); + + if (process_identifier == info->first) { + info->state = PendingConnectionInfo::AWAITING_CONNECT_FROM_SECOND; + *peer_process_identifier = info->second; + } else if (process_identifier == info->second) { + info->state = PendingConnectionInfo::AWAITING_CONNECT_FROM_FIRST; + *peer_process_identifier = info->first; + } else { + LOG(ERROR) << "Connect() from process " << process_identifier + << " for connection ID " << connection_id + << " which is neither connectee"; + return false; + } + + if (info->first == info->second) { + platform_handle->reset(); + DCHECK(!info->remaining_handle.is_valid()); + } else { + embedder::PlatformChannelPair platform_channel_pair; + *platform_handle = platform_channel_pair.PassServerHandle(); + DCHECK(platform_handle->is_valid()); + info->remaining_handle = platform_channel_pair.PassClientHandle(); + DCHECK(info->remaining_handle.is_valid()); + } + DVLOG(1) << "Connection ID " << connection_id + << ": first Connect() from process identifier " + << process_identifier; + return true; + } + + ProcessIdentifier remaining_connectee; + ProcessIdentifier peer; + if (info->state == PendingConnectionInfo::AWAITING_CONNECT_FROM_FIRST) { + remaining_connectee = info->first; + peer = info->second; + } else if (info->state == + PendingConnectionInfo::AWAITING_CONNECT_FROM_SECOND) { + remaining_connectee = info->second; + peer = info->first; + } else { + // Someone's behaving badly, but we don't know who (it might not be the + // caller). + LOG(ERROR) << "Connect() from process " << process_identifier + << " for connection ID " << connection_id << " in state " + << info->state; + pending_connections_.erase(it); + delete info; + return false; + } + + if (process_identifier != remaining_connectee) { + LOG(ERROR) << "Connect() from process " << process_identifier + << " for connection ID " << connection_id + << " which is not the remaining connectee"; + pending_connections_.erase(it); + delete info; + return false; + } + + *peer_process_identifier = peer; + *platform_handle = info->remaining_handle.Pass(); + DCHECK((info->first == info->second) ^ platform_handle->is_valid()); + pending_connections_.erase(it); + delete info; + DVLOG(1) << "Connection ID " << connection_id + << ": second Connect() from process identifier " + << process_identifier; + return true; +} + +void MasterConnectionManager::ShutdownOnPrivateThread() { + AssertOnPrivateThread(); + + if (!pending_connections_.empty()) { + DVLOG(1) << "Shutting down with connections pending"; + for (auto& p : pending_connections_) + delete p.second; + pending_connections_.clear(); + } + + if (!helpers_.empty()) { + DVLOG(1) << "Shutting down with slaves still connected"; + for (auto& p : helpers_) { + scoped_ptr<embedder::SlaveInfo> slave_info = p.second->Shutdown(); + delete p.second; + CallOnSlaveDisconnect(slave_info.Pass()); + } + helpers_.clear(); + } +} + +void MasterConnectionManager::AddSlaveOnPrivateThread( + scoped_ptr<embedder::SlaveInfo> slave_info, + embedder::ScopedPlatformHandle platform_handle, + base::WaitableEvent* event) { + DCHECK(platform_handle.is_valid()); + DCHECK(event); + AssertOnPrivateThread(); + + CHECK_NE(next_process_identifier_, kMasterProcessIdentifier); + ProcessIdentifier process_identifier = next_process_identifier_; + next_process_identifier_++; + + scoped_ptr<Helper> helper(new Helper( + this, process_identifier, slave_info.Pass(), platform_handle.Pass())); + helper->Init(); + + DCHECK(helpers_.find(process_identifier) == helpers_.end()); + helpers_[process_identifier] = helper.release(); + + DVLOG(1) << "Added process identifier " << process_identifier; + event->Signal(); +} + +void MasterConnectionManager::OnError(ProcessIdentifier process_identifier) { + DCHECK_NE(process_identifier, kInvalidProcessIdentifier); + AssertOnPrivateThread(); + + auto it = helpers_.find(process_identifier); + DCHECK(it != helpers_.end()); + Helper* helper = it->second; + scoped_ptr<embedder::SlaveInfo> slave_info = helper->Shutdown(); + helpers_.erase(it); + delete helper; + + { + base::AutoLock locker(lock_); + + // TODO(vtl): This isn't very efficient. + for (auto it = pending_connections_.begin(); + it != pending_connections_.end();) { + if (it->second->first == process_identifier || + it->second->second == process_identifier) { + auto it_to_erase = it; + ++it; + delete it_to_erase->second; + pending_connections_.erase(it_to_erase); + } else { + ++it; + } + } + } + + CallOnSlaveDisconnect(slave_info.Pass()); +} + +void MasterConnectionManager::CallOnSlaveDisconnect( + scoped_ptr<embedder::SlaveInfo> slave_info) { + AssertOnPrivateThread(); + DCHECK(master_process_delegate_); + creation_thread_task_runner_->PostTask( + FROM_HERE, base::Bind(&embedder::MasterProcessDelegate::OnSlaveDisconnect, + base::Unretained(master_process_delegate_), + base::Passed(&slave_info))); +} + +void MasterConnectionManager::AssertOnCreationThread() const { + DCHECK(base::MessageLoop::current()); + DCHECK_EQ(base::MessageLoop::current()->task_runner(), + creation_thread_task_runner_); +} + +void MasterConnectionManager::AssertNotOnPrivateThread() const { + // This should only be called after |Init()| and before |Shutdown()|. (If not, + // the subsequent |DCHECK_NE()| is invalid, since the current thread may not + // have a message loop.) + DCHECK(private_thread_.message_loop()); + DCHECK_NE(base::MessageLoop::current(), private_thread_.message_loop()); +} + +void MasterConnectionManager::AssertOnPrivateThread() const { + // This should only be called after |Init()| and before |Shutdown()|. + DCHECK(private_thread_.message_loop()); + DCHECK_EQ(base::MessageLoop::current(), private_thread_.message_loop()); +} + +} // namespace system +} // namespace mojo diff --git a/third_party/mojo/src/mojo/edk/system/master_connection_manager.h b/third_party/mojo/src/mojo/edk/system/master_connection_manager.h new file mode 100644 index 0000000..dcec9c2 --- /dev/null +++ b/third_party/mojo/src/mojo/edk/system/master_connection_manager.h @@ -0,0 +1,141 @@ +// 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_EDK_SYSTEM_MASTER_CONNECTION_MANAGER_H_ +#define MOJO_EDK_SYSTEM_MASTER_CONNECTION_MANAGER_H_ + +#include <stdint.h> + +#include "base/containers/hash_tables.h" +#include "base/macros.h" +#include "base/memory/ref_counted.h" +#include "base/synchronization/lock.h" +#include "base/threading/thread.h" +#include "mojo/edk/embedder/scoped_platform_handle.h" +#include "mojo/edk/system/connection_manager.h" +#include "mojo/edk/system/system_impl_export.h" + +namespace base { +class TaskRunner; +class WaitableEvent; +} + +namespace mojo { + +namespace embedder { +class MasterProcessDelegate; +class SlaveInfo; +} + +namespace system { + +// The |ConnectionManager| implementation for the master process. +// +// Objects of this class must be created, initialized (via |Init()|), shut down +// (via |Shutdown()|), and destroyed on the same thread (the "creation thread"). +// Otherwise, its public methods are thread-safe (except that they may not be +// called from its internal, private thread). +class MOJO_SYSTEM_IMPL_EXPORT MasterConnectionManager + : public ConnectionManager { + public: + // Note: None of the public methods may be called from |private_thread_|. + + MasterConnectionManager(); + ~MasterConnectionManager() override; + + // No other methods may be called until after this has been called. + // |master_process_delegate| must stay alive at least until after |Shutdown()| + // has been called; its methods will be called on this object's creation + // thread. + void Init(embedder::MasterProcessDelegate* master_process_delegate); + + // No other methods may be called after this is (or while it is being) called. + void Shutdown(); + + // Adds a slave process and sets up/tracks a connection to that slave (using + // |platform_handle|). (|slave_info| is used by the caller/implementation of + // |embedder::MasterProcessDelegate| to track this process; ownership of + // |slave_info| will be returned to the delegate via |OnSlaveDisconnect()|, + // which will always be called for each slave, assuming proper shutdown.) + void AddSlave(scoped_ptr<embedder::SlaveInfo> slave_info, + embedder::ScopedPlatformHandle platform_handle); + + // |ConnectionManager| methods: + bool AllowConnect(const ConnectionIdentifier& connection_id) override; + bool CancelConnect(const ConnectionIdentifier& connection_id) override; + bool Connect(const ConnectionIdentifier& connection_id, + ProcessIdentifier* peer_process_identifier, + embedder::ScopedPlatformHandle* platform_handle) override; + + private: + class Helper; + + // These should be thread-safe and may be called on any thread, including + // |private_thread_|: + bool AllowConnectImpl(ProcessIdentifier process_identifier, + const ConnectionIdentifier& connection_id); + bool CancelConnectImpl(ProcessIdentifier process_identifier, + const ConnectionIdentifier& connection_id); + bool ConnectImpl(ProcessIdentifier process_identifier, + const ConnectionIdentifier& connection_id, + ProcessIdentifier* peer_process_identifier, + embedder::ScopedPlatformHandle* platform_handle); + + // These should only be called on |private_thread_|: + void ShutdownOnPrivateThread(); + // Signals |*event| on completion. + void AddSlaveOnPrivateThread(scoped_ptr<embedder::SlaveInfo> slave_info, + embedder::ScopedPlatformHandle platform_handle, + base::WaitableEvent* event); + // Called by |Helper::OnError()|. + void OnError(ProcessIdentifier process_identifier); + // Posts a call to |master_process_delegate_->OnSlaveDisconnect()|. + void CallOnSlaveDisconnect(scoped_ptr<embedder::SlaveInfo> slave_info); + + // Asserts that the current thread is the creation thread. (This actually + // checks the current message loop, which is what we depend on, not the thread + // per se.) + void AssertOnCreationThread() const; + + // Asserts that the current thread is *not* |private_thread_| (no-op if + // DCHECKs are not enabled). This should only be called while + // |private_thread_| is alive (i.e., after |Init()| but before |Shutdown()|). + void AssertNotOnPrivateThread() const; + + // Asserts that the current thread is |private_thread_| (no-op if DCHECKs are + // not enabled). This should only be called while |private_thread_| is alive + // (i.e., after |Init()| but before |Shutdown()|). + void AssertOnPrivateThread() const; + + const scoped_refptr<base::TaskRunner> creation_thread_task_runner_; + + // This is set in |Init()| before |private_thread_| exists and only cleared in + // |Shutdown()| after |private_thread_| is dead. Thus it's safe to "use" on + // |private_thread_|. (Note that |master_process_delegate_| may only be called + // from the creation thread.) + embedder::MasterProcessDelegate* master_process_delegate_; + + // This is a private I/O thread on which this class does the bulk of its work. + // It is started in |Init()| and terminated in |Shutdown()|. + base::Thread private_thread_; + + // The following members are only accessed on |private_thread_|: + ProcessIdentifier next_process_identifier_; + base::hash_map<ProcessIdentifier, Helper*> helpers_; // Owns its values. + + // Protects the members below (except in the constructor, |Init()|, + // |Shutdown()|/|ShutdownOnPrivateThread()|, and the destructor). + base::Lock lock_; + + struct PendingConnectionInfo; + base::hash_map<ConnectionIdentifier, PendingConnectionInfo*> + pending_connections_; // Owns its values. + + DISALLOW_COPY_AND_ASSIGN(MasterConnectionManager); +}; + +} // namespace system +} // namespace mojo + +#endif // MOJO_EDK_SYSTEM_MASTER_CONNECTION_MANAGER_H_ diff --git a/third_party/mojo/src/mojo/edk/system/message_in_transit.cc b/third_party/mojo/src/mojo/edk/system/message_in_transit.cc index 68919f2..9e38064 100644 --- a/third_party/mojo/src/mojo/edk/system/message_in_transit.cc +++ b/third_party/mojo/src/mojo/edk/system/message_in_transit.cc @@ -20,6 +20,11 @@ STATIC_CONST_MEMBER_DEFINITION const MessageInTransit::Type MessageInTransit::kTypeChannel; STATIC_CONST_MEMBER_DEFINITION const MessageInTransit::Type MessageInTransit::kTypeRawChannel; +STATIC_CONST_MEMBER_DEFINITION const MessageInTransit::Type + MessageInTransit::kTypeConnectionManager; +STATIC_CONST_MEMBER_DEFINITION const MessageInTransit::Type + MessageInTransit::kTypeConnectionManagerAck; + STATIC_CONST_MEMBER_DEFINITION const MessageInTransit::Subtype MessageInTransit::kSubtypeEndpointData; STATIC_CONST_MEMBER_DEFINITION const MessageInTransit::Subtype @@ -30,6 +35,17 @@ STATIC_CONST_MEMBER_DEFINITION const MessageInTransit::Subtype MessageInTransit::kSubtypeChannelRemoveEndpointAck; STATIC_CONST_MEMBER_DEFINITION const MessageInTransit::Subtype MessageInTransit::kSubtypeRawChannelPosixExtraPlatformHandles; +STATIC_CONST_MEMBER_DEFINITION const MessageInTransit::Subtype + MessageInTransit::kSubtypeConnectionManagerAllowConnect; +STATIC_CONST_MEMBER_DEFINITION const MessageInTransit::Subtype + MessageInTransit::kSubtypeConnectionManagerCancelConnect; +STATIC_CONST_MEMBER_DEFINITION const MessageInTransit::Subtype + MessageInTransit::kSubtypeConnectionManagerConnect; +STATIC_CONST_MEMBER_DEFINITION const MessageInTransit::Subtype + MessageInTransit::kSubtypeConnectionManagerAckFailure; +STATIC_CONST_MEMBER_DEFINITION const MessageInTransit::Subtype + MessageInTransit::kSubtypeConnectionManagerAckSuccess; + STATIC_CONST_MEMBER_DEFINITION const size_t MessageInTransit::kMessageAlignment; struct MessageInTransit::PrivateStructForCompileAsserts { diff --git a/third_party/mojo/src/mojo/edk/system/message_in_transit.h b/third_party/mojo/src/mojo/edk/system/message_in_transit.h index 023362a..9f118f9 100644 --- a/third_party/mojo/src/mojo/edk/system/message_in_transit.h +++ b/third_party/mojo/src/mojo/edk/system/message_in_transit.h @@ -50,6 +50,11 @@ class MOJO_SYSTEM_IMPL_EXPORT MessageInTransit { static const Type kTypeChannel = 1; // Messages that are consumed by the |RawChannel| (implementation). static const Type kTypeRawChannel = 2; + // |ConnectionManager| implementations also use |RawChannel|s. + // Messages sent to a |MasterConnectionManager|. + static const Type kTypeConnectionManager = 3; + // Messages sent by a |MasterConnectionManager| (all responses). + static const Type kTypeConnectionManagerAck = 4; typedef uint16_t Subtype; // Subtypes for type |kTypeEndpoint|: @@ -58,9 +63,18 @@ class MOJO_SYSTEM_IMPL_EXPORT MessageInTransit { static const Subtype kSubtypeChannelAttachAndRunEndpoint = 0; static const Subtype kSubtypeChannelRemoveEndpoint = 1; static const Subtype kSubtypeChannelRemoveEndpointAck = 2; - // Subtypes for type |kTypeRawChannel|: static const Subtype kSubtypeRawChannelPosixExtraPlatformHandles = 0; + // Subtypes for type |kTypeConnectionManager| (the message data is always a + // buffer containing the connection ID): + static const Subtype kSubtypeConnectionManagerAllowConnect = 0; + static const Subtype kSubtypeConnectionManagerCancelConnect = 1; + static const Subtype kSubtypeConnectionManagerConnect = 2; + // Subtypes for type |kTypeConnectionManagerAck| (failure acks never have any + // message contents; success acks for "connect" always have a + // |ProcessIdentifier| as data and *may* have a platform handle attached): + static const Subtype kSubtypeConnectionManagerAckFailure = 0; + static const Subtype kSubtypeConnectionManagerAckSuccess = 1; // Messages (the header and data) must always be aligned to a multiple of this // quantity (which must be a power of 2). diff --git a/third_party/mojo/src/mojo/edk/system/message_pipe_dispatcher_unittest.cc b/third_party/mojo/src/mojo/edk/system/message_pipe_dispatcher_unittest.cc index 2f97b29..b5562b0 100644 --- a/third_party/mojo/src/mojo/edk/system/message_pipe_dispatcher_unittest.cc +++ b/third_party/mojo/src/mojo/edk/system/message_pipe_dispatcher_unittest.cc @@ -340,8 +340,13 @@ TEST(MessagePipeDispatcherTest, BasicClosed) { } } +#if defined(OS_WIN) // http://crbug.com/396386 -TEST(MessagePipeDispatcherTest, DISABLED_BasicThreaded) { +#define MAYBE_BasicThreaded DISABLED_BasicThreaded +#else +#define MAYBE_BasicThreaded BasicThreaded +#endif +TEST(MessagePipeDispatcherTest, MAYBE_BasicThreaded) { test::Stopwatch stopwatch; int32_t buffer[1]; const uint32_t kBufferSize = static_cast<uint32_t>(sizeof(buffer)); diff --git a/third_party/mojo/src/mojo/edk/system/simple_dispatcher_unittest.cc b/third_party/mojo/src/mojo/edk/system/simple_dispatcher_unittest.cc index 7d5edcb..b8e57e9 100644 --- a/third_party/mojo/src/mojo/edk/system/simple_dispatcher_unittest.cc +++ b/third_party/mojo/src/mojo/edk/system/simple_dispatcher_unittest.cc @@ -325,8 +325,13 @@ TEST(SimpleDispatcherTest, BasicClosed) { // Don't need to remove waiters from closed dispatchers. } +#if defined(OS_WIN) // http://crbug.com/396393 -TEST(SimpleDispatcherTest, DISABLED_BasicThreaded) { +#define MAYBE_BasicThreaded DISABLED_BasicThreaded +#else +#define MAYBE_BasicThreaded BasicThreaded +#endif +TEST(SimpleDispatcherTest, MAYBE_BasicThreaded) { test::Stopwatch stopwatch; bool did_wait; MojoResult result; diff --git a/third_party/mojo/src/mojo/edk/system/slave_connection_manager.cc b/third_party/mojo/src/mojo/edk/system/slave_connection_manager.cc new file mode 100644 index 0000000..2427823 --- /dev/null +++ b/third_party/mojo/src/mojo/edk/system/slave_connection_manager.cc @@ -0,0 +1,316 @@ +// 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/edk/system/slave_connection_manager.h" + +#include "base/bind.h" +#include "base/bind_helpers.h" +#include "base/location.h" +#include "base/logging.h" +#include "base/message_loop/message_loop.h" +#include "mojo/edk/system/message_in_transit.h" + +namespace mojo { +namespace system { + +// SlaveConnectionManager ------------------------------------------------------ + +SlaveConnectionManager::SlaveConnectionManager() + : creation_thread_task_runner_(base::MessageLoop::current()->task_runner()), + slave_process_delegate_(), + private_thread_("SlaveConnectionManagerPrivateThread"), + awaiting_ack_type_(NOT_AWAITING_ACK), + ack_result_(), + ack_peer_process_identifier_(), + ack_platform_handle_(), + event_(false, false) { // Auto-reset, not initially signalled. + DCHECK(creation_thread_task_runner_); + AssertOnCreationThread(); // Just make sure this assertion works correctly. +} + +SlaveConnectionManager::~SlaveConnectionManager() { + AssertOnCreationThread(); + DCHECK(!slave_process_delegate_); + DCHECK(!private_thread_.message_loop()); + DCHECK_EQ(awaiting_ack_type_, NOT_AWAITING_ACK); + DCHECK(!ack_result_); + DCHECK(!ack_peer_process_identifier_); + DCHECK(!ack_platform_handle_); +} + +void SlaveConnectionManager::Init( + embedder::SlaveProcessDelegate* slave_process_delegate, + embedder::ScopedPlatformHandle platform_handle) { + AssertOnCreationThread(); + DCHECK(slave_process_delegate); + DCHECK(platform_handle.is_valid()); + DCHECK(!slave_process_delegate_); + DCHECK(!private_thread_.message_loop()); + + slave_process_delegate_ = slave_process_delegate; + CHECK(private_thread_.StartWithOptions( + base::Thread::Options(base::MessageLoop::TYPE_IO, 0))); + private_thread_.message_loop()->PostTask( + FROM_HERE, + base::Bind(&SlaveConnectionManager::InitOnPrivateThread, + base::Unretained(this), base::Passed(&platform_handle))); + event_.Wait(); +} + +void SlaveConnectionManager::Shutdown() { + AssertOnCreationThread(); + DCHECK(slave_process_delegate_); + DCHECK(private_thread_.message_loop()); + + // The |Stop()| will actually finish all posted tasks. + private_thread_.message_loop()->PostTask( + FROM_HERE, base::Bind(&SlaveConnectionManager::ShutdownOnPrivateThread, + base::Unretained(this))); + private_thread_.Stop(); + slave_process_delegate_ = nullptr; +} + +bool SlaveConnectionManager::AllowConnect( + const ConnectionIdentifier& connection_id) { + AssertNotOnPrivateThread(); + + base::AutoLock locker(lock_); + bool result = false; + private_thread_.message_loop()->PostTask( + FROM_HERE, + base::Bind(&SlaveConnectionManager::AllowConnectOnPrivateThread, + base::Unretained(this), connection_id, &result)); + event_.Wait(); + return result; +} + +bool SlaveConnectionManager::CancelConnect( + const ConnectionIdentifier& connection_id) { + AssertNotOnPrivateThread(); + + base::AutoLock locker(lock_); + bool result = false; + private_thread_.message_loop()->PostTask( + FROM_HERE, + base::Bind(&SlaveConnectionManager::CancelConnectOnPrivateThread, + base::Unretained(this), connection_id, &result)); + event_.Wait(); + return result; +} + +bool SlaveConnectionManager::Connect( + const ConnectionIdentifier& connection_id, + ProcessIdentifier* peer_process_identifier, + embedder::ScopedPlatformHandle* platform_handle) { + AssertNotOnPrivateThread(); + + base::AutoLock locker(lock_); + bool result = false; + private_thread_.message_loop()->PostTask( + FROM_HERE, base::Bind(&SlaveConnectionManager::ConnectOnPrivateThread, + base::Unretained(this), connection_id, &result, + peer_process_identifier, platform_handle)); + event_.Wait(); + return result; +} + +void SlaveConnectionManager::InitOnPrivateThread( + embedder::ScopedPlatformHandle platform_handle) { + AssertOnPrivateThread(); + + raw_channel_ = RawChannel::Create(platform_handle.Pass()); + raw_channel_->Init(this); + event_.Signal(); +} + +void SlaveConnectionManager::ShutdownOnPrivateThread() { + AssertOnPrivateThread(); + + CHECK_EQ(awaiting_ack_type_, NOT_AWAITING_ACK); + if (raw_channel_) { + raw_channel_->Shutdown(); + raw_channel_.reset(); + } +} + +void SlaveConnectionManager::AllowConnectOnPrivateThread( + const ConnectionIdentifier& connection_id, + bool* result) { + DCHECK(result); + AssertOnPrivateThread(); + // This should only posted (from another thread, to |private_thread_|) with + // the lock held (until this thread triggers |event_|). + DCHECK(!lock_.Try()); + DCHECK_EQ(awaiting_ack_type_, NOT_AWAITING_ACK); + + DVLOG(1) << "Sending AllowConnect: connection ID " << connection_id; + if (!raw_channel_->WriteMessage(make_scoped_ptr(new MessageInTransit( + MessageInTransit::kTypeConnectionManager, + MessageInTransit::kSubtypeConnectionManagerAllowConnect, + sizeof(connection_id), &connection_id)))) { + // Don't tear things down; possibly we'll still read some messages. + *result = false; + event_.Signal(); + return; + } + awaiting_ack_type_ = AWAITING_ACCEPT_CONNECT_ACK; + ack_result_ = result; +} + +void SlaveConnectionManager::CancelConnectOnPrivateThread( + const ConnectionIdentifier& connection_id, + bool* result) { + DCHECK(result); + AssertOnPrivateThread(); + // This should only posted (from another thread, to |private_thread_|) with + // the lock held (until this thread triggers |event_|). + DCHECK(!lock_.Try()); + DCHECK_EQ(awaiting_ack_type_, NOT_AWAITING_ACK); + + DVLOG(1) << "Sending CancelConnect: connection ID " << connection_id; + if (!raw_channel_->WriteMessage(make_scoped_ptr(new MessageInTransit( + MessageInTransit::kTypeConnectionManager, + MessageInTransit::kSubtypeConnectionManagerCancelConnect, + sizeof(connection_id), &connection_id)))) { + // Don't tear things down; possibly we'll still read some messages. + *result = false; + event_.Signal(); + return; + } + awaiting_ack_type_ = AWAITING_CANCEL_CONNECT_ACK; + ack_result_ = result; +} + +void SlaveConnectionManager::ConnectOnPrivateThread( + const ConnectionIdentifier& connection_id, + bool* result, + ProcessIdentifier* peer_process_identifier, + embedder::ScopedPlatformHandle* platform_handle) { + DCHECK(result); + DCHECK(platform_handle); + DCHECK(!platform_handle->is_valid()); // Not technically wrong, but unlikely. + AssertOnPrivateThread(); + // This should only posted (from another thread, to |private_thread_|) with + // the lock held (until this thread triggers |event_|). + DCHECK(!lock_.Try()); + DCHECK_EQ(awaiting_ack_type_, NOT_AWAITING_ACK); + + DVLOG(1) << "Sending Connect: connection ID " << connection_id; + if (!raw_channel_->WriteMessage(make_scoped_ptr(new MessageInTransit( + MessageInTransit::kTypeConnectionManager, + MessageInTransit::kSubtypeConnectionManagerConnect, + sizeof(connection_id), &connection_id)))) { + // Don't tear things down; possibly we'll still read some messages. + *result = false; + platform_handle->reset(); + event_.Signal(); + return; + } + awaiting_ack_type_ = AWAITING_CONNECT_ACK; + ack_result_ = result; + ack_peer_process_identifier_ = peer_process_identifier; + ack_platform_handle_ = platform_handle; +} + +void SlaveConnectionManager::OnReadMessage( + const MessageInTransit::View& message_view, + embedder::ScopedPlatformHandleVectorPtr platform_handles) { + AssertOnPrivateThread(); + + // Set |*ack_result_| to false by default. + *ack_result_ = false; + + // Note: Since we should be able to trust the master, simply crash (i.e., + // |CHECK()|-fail) if it sends us something invalid. + + // Unsolicited message. + CHECK_NE(awaiting_ack_type_, NOT_AWAITING_ACK); + // Bad message type. + CHECK_EQ(message_view.type(), MessageInTransit::kTypeConnectionManagerAck); + + size_t num_bytes = message_view.num_bytes(); + size_t num_platform_handles = platform_handles ? platform_handles->size() : 0; + + if (message_view.subtype() == + MessageInTransit::kSubtypeConnectionManagerAckFailure) { + // Failure acks never have any contents. + CHECK_EQ(num_bytes, 0u); + CHECK_EQ(num_platform_handles, 0u); + // Leave |*ack_result_| false. + } else if (message_view.subtype() == + MessageInTransit::kSubtypeConnectionManagerAckSuccess) { + if (awaiting_ack_type_ == AWAITING_ACCEPT_CONNECT_ACK || + awaiting_ack_type_ == AWAITING_CANCEL_CONNECT_ACK) { + // Success acks for "accept/cancel connect" have no contents. + CHECK_EQ(num_bytes, 0u); + CHECK_EQ(num_platform_handles, 0u); + *ack_result_ = true; + DCHECK(!ack_peer_process_identifier_); + DCHECK(!ack_platform_handle_); + } else { + DCHECK_EQ(awaiting_ack_type_, AWAITING_CONNECT_ACK); + // Success acks for "connect" always have a |ProcessIdentifier| as data, + // and *maybe* one platform handle. + CHECK_EQ(num_bytes, sizeof(ProcessIdentifier)); + CHECK_LE(num_platform_handles, 1u); + *ack_result_ = true; + *ack_peer_process_identifier_ = + *reinterpret_cast<const ProcessIdentifier*>(message_view.bytes()); + if (num_platform_handles > 0) { + ack_platform_handle_->reset(platform_handles->at(0)); + platform_handles->at(0) = embedder::PlatformHandle(); + } else { + ack_platform_handle_->reset(); + } + } + } else { + // Bad message subtype. + CHECK(false); + } + + awaiting_ack_type_ = NOT_AWAITING_ACK; + ack_result_ = nullptr; + ack_peer_process_identifier_ = nullptr; + ack_platform_handle_ = nullptr; + event_.Signal(); +} + +void SlaveConnectionManager::OnError(Error error) { + AssertOnPrivateThread(); + + // Ignore write errors, since we may still have some messages to read. + if (error == RawChannel::Delegate::ERROR_WRITE) + return; + + raw_channel_->Shutdown(); + raw_channel_.reset(); + + DCHECK(slave_process_delegate_); + creation_thread_task_runner_->PostTask( + FROM_HERE, base::Bind(&embedder::SlaveProcessDelegate::OnMasterDisconnect, + base::Unretained(slave_process_delegate_))); +} + +void SlaveConnectionManager::AssertOnCreationThread() const { + DCHECK(base::MessageLoop::current()); + DCHECK_EQ(base::MessageLoop::current()->task_runner(), + creation_thread_task_runner_); +} + +void SlaveConnectionManager::AssertNotOnPrivateThread() const { + // This should only be called after |Init()| and before |Shutdown()|. (If not, + // the subsequent |DCHECK_NE()| is invalid, since the current thread may not + // have a message loop.) + DCHECK(private_thread_.message_loop()); + DCHECK_NE(base::MessageLoop::current(), private_thread_.message_loop()); +} + +void SlaveConnectionManager::AssertOnPrivateThread() const { + // This should only be called after |Init()| and before |Shutdown()|. + DCHECK(private_thread_.message_loop()); + DCHECK_EQ(base::MessageLoop::current(), private_thread_.message_loop()); +} + +} // namespace system +} // namespace mojo diff --git a/third_party/mojo/src/mojo/edk/system/slave_connection_manager.h b/third_party/mojo/src/mojo/edk/system/slave_connection_manager.h new file mode 100644 index 0000000..e773448 --- /dev/null +++ b/third_party/mojo/src/mojo/edk/system/slave_connection_manager.h @@ -0,0 +1,160 @@ +// 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_EDK_SYSTEM_SLAVE_CONNECTION_MANAGER_H_ +#define MOJO_EDK_SYSTEM_SLAVE_CONNECTION_MANAGER_H_ + +#include "base/macros.h" +#include "base/memory/ref_counted.h" +#include "base/memory/scoped_ptr.h" +#include "base/synchronization/lock.h" +#include "base/synchronization/waitable_event.h" +#include "base/threading/thread.h" +#include "mojo/edk/embedder/scoped_platform_handle.h" +#include "mojo/edk/embedder/slave_process_delegate.h" +#include "mojo/edk/system/connection_manager.h" +#include "mojo/edk/system/raw_channel.h" +#include "mojo/edk/system/system_impl_export.h" + +namespace base { +class TaskRunner; +} + +namespace mojo { + +namespace embedder { +class SlaveProcessDelegate; +} + +namespace system { + +// The |ConnectionManager| implementation for slave processes. +// +// Objects of this class must created, initialized (via |Init()|), shut down +// (via |Shutdown()|), and destroyed on the same thread (the "creation thread"). +// Otherwise, its public methods are thread-safe (except that they may not be +// called from its internal, private thread). +class MOJO_SYSTEM_IMPL_EXPORT SlaveConnectionManager + : public ConnectionManager, + public RawChannel::Delegate { + public: + // Note: None of the public methods may be called from |private_thread_|. + + SlaveConnectionManager(); + ~SlaveConnectionManager() override; + + // No other methods may be called until after this has been called. + // |slave_process_delegate| must stay alive at least until after |Shutdown()| + // has been called; its methods will be called on this object's creation + // thread. + void Init(embedder::SlaveProcessDelegate* slave_process_delegate, + embedder::ScopedPlatformHandle platform_handle); + + // No other methods may be called after this is (or while it is being) called. + void Shutdown(); + + // |ConnectionManager| methods: + bool AllowConnect(const ConnectionIdentifier& connection_id) override; + bool CancelConnect(const ConnectionIdentifier& connection_id) override; + bool Connect(const ConnectionIdentifier& connection_id, + ProcessIdentifier* peer_process_identifier, + embedder::ScopedPlatformHandle* platform_handle) override; + + private: + // These should only be called on |private_thread_|: + void InitOnPrivateThread(embedder::ScopedPlatformHandle platform_handle); + void ShutdownOnPrivateThread(); + void AllowConnectOnPrivateThread(const ConnectionIdentifier& connection_id, + bool* result); + void CancelConnectOnPrivateThread(const ConnectionIdentifier& connection_id, + bool* result); + void ConnectOnPrivateThread(const ConnectionIdentifier& connection_id, + bool* result, + ProcessIdentifier* peer_process_identifier, + embedder::ScopedPlatformHandle* platform_handle); + + // |RawChannel::Delegate| methods (only called on |private_thread_|): + void OnReadMessage( + const MessageInTransit::View& message_view, + embedder::ScopedPlatformHandleVectorPtr platform_handles) override; + void OnError(Error error) override; + + // Asserts that the current thread is the creation thread. (This actually + // checks the current message loop, which is what we depend on, not the thread + // per se.) + void AssertOnCreationThread() const; + + // Asserts that the current thread is *not* |private_thread_| (no-op if + // DCHECKs are not enabled). This should only be called while + // |private_thread_| is alive (i.e., after |Init()| but before |Shutdown()|). + void AssertNotOnPrivateThread() const; + + // Asserts that the current thread is |private_thread_| (no-op if DCHECKs are + // not enabled). This should only be called while |private_thread_| is alive + // (i.e., after |Init()| but before |Shutdown()|). + void AssertOnPrivateThread() const; + + const scoped_refptr<base::TaskRunner> creation_thread_task_runner_; + + // These is set in |Init()| before |private_thread_| exists and only cleared + // in |Shutdown()| after |private_thread_| is dead. Thus it's safe to "use" on + // |private_thread_|. (Note that |slave_process_delegate_| may only be called + // from the creation thread.) + embedder::SlaveProcessDelegate* slave_process_delegate_; + + // This is a private I/O thread on which this class does the bulk of its work. + // It is started in |Init()| and terminated in |Shutdown()|. + // TODO(vtl): This isn't really necessary. + base::Thread private_thread_; + + // Only accessed on |private_thread_|: + scoped_ptr<RawChannel> raw_channel_; + enum AwaitingAckType { + NOT_AWAITING_ACK, + AWAITING_ACCEPT_CONNECT_ACK, + AWAITING_CANCEL_CONNECT_ACK, + AWAITING_CONNECT_ACK + }; + AwaitingAckType awaiting_ack_type_; + bool* ack_result_; + // Used only when waiting for the ack to "connect": + ProcessIdentifier* ack_peer_process_identifier_; + embedder::ScopedPlatformHandle* ack_platform_handle_; + + // The (synchronous) |ConnectionManager| methods are implemented in the + // following way (T is any thread other than |private_thread_|): + // + // On thread T: + // 1. |F()| is called, where F is one of the |ConnectionManager| methods. + // 2. |lock_| is acquired. + // 3. |FImpl()| is posted to |private_thread_|. + // 4. |event_| is waited on (while holding |lock_|!). + // + // On |private_thread_| (all with |lock_| held!): + // 4.1. |FImpl()| is executed, writes an "F" message to |raw_channel_|, and + // sets |awaiting_ack_type_| appropriately (it must not be "set" + // before). + // 4.2. [Control returns to |private_thread_|'s message loop.] + // 4.3. Eventually, |raw_channel_| calls |OnReadMessage()| with a message, + // which must be response (|awaiting_ack_type_| must still be set). + // 4.4. |*ack_result_| and possibly |*ack_platform_handle_| are written to. + // |awaiting_ack_type_| is "unset". + // 4.5. |event_| is triggered. + // + // Back on thread T: + // 6. |lock_| is released. + // 7. [Return from |F()|.] + // + // TODO(vtl): This is all a hack. It'd really suffice to have a version of + // |RawChannel| with fully synchronous reading and writing. + base::Lock lock_; + base::WaitableEvent event_; + + DISALLOW_COPY_AND_ASSIGN(SlaveConnectionManager); +}; + +} // namespace system +} // namespace mojo + +#endif // MOJO_EDK_SYSTEM_SLAVE_CONNECTION_MANAGER_H_ diff --git a/third_party/mojo/src/mojo/public/VERSION b/third_party/mojo/src/mojo/public/VERSION index 928bc42..55ad309 100644 --- a/third_party/mojo/src/mojo/public/VERSION +++ b/third_party/mojo/src/mojo/public/VERSION @@ -1 +1 @@ -a85a2cea82d816de115e15253742b0f88a9924eb
\ No newline at end of file +126532ce21c5c3c55a1e1693731411cb60169efd
\ No newline at end of file diff --git a/third_party/mojo/src/mojo/public/cpp/application/BUILD.gn b/third_party/mojo/src/mojo/public/cpp/application/BUILD.gn index 8741c694..05708c7 100644 --- a/third_party/mojo/src/mojo/public/cpp/application/BUILD.gn +++ b/third_party/mojo/src/mojo/public/cpp/application/BUILD.gn @@ -64,6 +64,7 @@ mojo_sdk_source_set("test_support") { "mojo/public/cpp/bindings", "mojo/public/cpp/environment", "mojo/public/cpp/system", + "mojo/public/interfaces/application", ] } @@ -82,8 +83,10 @@ mojo_sdk_source_set("test_support_standalone") { ] mojo_sdk_deps = [ + "mojo/public/interfaces/application", "mojo/public/cpp/environment:standalone", "mojo/public/cpp/system", "mojo/public/cpp/utility", + "mojo/public/interfaces/application", ] } diff --git a/third_party/mojo/src/mojo/public/cpp/application/application_impl.h b/third_party/mojo/src/mojo/public/cpp/application/application_impl.h index 430a124..bb92307 100644 --- a/third_party/mojo/src/mojo/public/cpp/application/application_impl.h +++ b/third_party/mojo/src/mojo/public/cpp/application/application_impl.h @@ -50,11 +50,10 @@ class ApplicationDelegate; // app.AddService<BarImpl>(&context); // // -class ApplicationImpl : public InterfaceImpl<Application> { +class ApplicationImpl : public Application { public: ApplicationImpl(ApplicationDelegate* delegate, - ScopedMessagePipeHandle shell_handle); - ApplicationImpl(ApplicationDelegate* delegate, MojoHandle shell_handle); + InterfaceRequest<Application> request); ~ApplicationImpl() override; Shell* shell() const { return shell_.get(); } @@ -74,32 +73,36 @@ class ApplicationImpl : public InterfaceImpl<Application> { ConnectToApplication(application_url)->ConnectToService(ptr); } - // Wait for the ShellPtr's Initialize message. - bool WaitForInitialize(); + // Application implementation. + void Initialize(ShellPtr shell, Array<String> args) override; - // Unbind the shell from this application and return its handle. - ScopedMessagePipeHandle UnbindShell(); + // Block until the Application is initialized, if it is not already. + void WaitForInitialize(); - // Application implementation. - void Initialize(Array<String> args) override; + // Unbinds the Shell and Application connections. Must be called after + // Initialize. + void UnbindConnections(InterfaceRequest<Application>* application_request, + ShellPtr* shell); // Quits the main run loop for this application. static void Terminate(); + protected: + // Application implementation. + void AcceptConnection(const String& requestor_url, + InterfaceRequest<ServiceProvider> services, + ServiceProviderPtr exposed_services) override; + private: class ShellPtrWatcher; - void BindShell(ScopedMessagePipeHandle shell_handle); void ClearConnections(); + void OnShellError() { ClearConnections(); Terminate(); } - // Application implementation. - void AcceptConnection(const String& requestor_url, - InterfaceRequest<ServiceProvider> services, - ServiceProviderPtr exposed_services) override; void RequestQuit() override; typedef std::vector<internal::ServiceRegistry*> ServiceRegistryList; @@ -108,6 +111,7 @@ class ApplicationImpl : public InterfaceImpl<Application> { ServiceRegistryList incoming_service_registries_; ServiceRegistryList outgoing_service_registries_; ApplicationDelegate* delegate_; + Binding<Application> binding_; ShellPtr shell_; ShellPtrWatcher* shell_watch_; std::vector<std::string> args_; diff --git a/third_party/mojo/src/mojo/public/cpp/application/application_test_base.h b/third_party/mojo/src/mojo/public/cpp/application/application_test_base.h index bdf2f8b..10763be 100644 --- a/third_party/mojo/src/mojo/public/cpp/application/application_test_base.h +++ b/third_party/mojo/src/mojo/public/cpp/application/application_test_base.h @@ -9,6 +9,7 @@ #include "mojo/public/cpp/bindings/array.h" #include "mojo/public/cpp/bindings/string.h" #include "mojo/public/cpp/system/macros.h" +#include "mojo/public/interfaces/application/application.mojom.h" #include "testing/gtest/include/gtest/gtest.h" namespace mojo { @@ -22,7 +23,7 @@ const Array<String>& Args(); // Run all application tests. This must be called after the environment is // initialized, to support construction of a default run loop. -MojoResult RunAllTests(MojoHandle shell_handle); +MojoResult RunAllTests(MojoHandle application_request_handle); // A GTEST base class for application testing executed in mojo_shell. class ApplicationTestBase : public testing::Test { @@ -36,10 +37,6 @@ class ApplicationTestBase : public testing::Test { // Get the ApplicationDelegate for the application to be tested. virtual ApplicationDelegate* GetApplicationDelegate(); - // A testing::Test::SetUp helper to override the application command - // line arguments. - void SetUpWithArgs(const Array<String>& args); - // testing::Test: void SetUp() override; void TearDown() override; diff --git a/third_party/mojo/src/mojo/public/cpp/application/lib/application_impl.cc b/third_party/mojo/src/mojo/public/cpp/application/lib/application_impl.cc index 894060d..4d1f8dc 100644 --- a/third_party/mojo/src/mojo/public/cpp/application/lib/application_impl.cc +++ b/third_party/mojo/src/mojo/public/cpp/application/lib/application_impl.cc @@ -25,15 +25,11 @@ class ApplicationImpl::ShellPtrWatcher : public ErrorHandler { }; ApplicationImpl::ApplicationImpl(ApplicationDelegate* delegate, - ScopedMessagePipeHandle shell_handle) - : initialized_(false), delegate_(delegate), shell_watch_(nullptr) { - BindShell(shell_handle.Pass()); -} - -ApplicationImpl::ApplicationImpl(ApplicationDelegate* delegate, - MojoHandle shell_handle) - : initialized_(false), delegate_(delegate), shell_watch_(nullptr) { - BindShell(MakeScopedHandle(MessagePipeHandle(shell_handle))); + InterfaceRequest<Application> request) + : initialized_(false), + delegate_(delegate), + binding_(this, request.Pass()), + shell_watch_(nullptr) { } bool ApplicationImpl::HasArg(const std::string& arg) const { @@ -60,7 +56,7 @@ ApplicationImpl::~ApplicationImpl() { ApplicationConnection* ApplicationImpl::ConnectToApplication( const String& application_url) { - MOJO_CHECK(initialized_); + MOJO_CHECK(shell_); ServiceProviderPtr local_services; InterfaceRequest<ServiceProvider> local_request = GetProxy(&local_services); ServiceProviderPtr remote_services; @@ -76,29 +72,24 @@ ApplicationConnection* ApplicationImpl::ConnectToApplication( return registry; } -bool ApplicationImpl::WaitForInitialize() { - MOJO_CHECK(!initialized_); - bool result = shell_.WaitForIncomingMethodCall(); - MOJO_CHECK(initialized_ || !result); - return result; -} - -ScopedMessagePipeHandle ApplicationImpl::UnbindShell() { - return shell_.PassMessagePipe(); -} - -void ApplicationImpl::Initialize(Array<String> args) { - MOJO_CHECK(!initialized_); - initialized_ = true; +void ApplicationImpl::Initialize(ShellPtr shell, Array<String> args) { + shell_ = shell.Pass(); + shell_watch_ = new ShellPtrWatcher(this); + shell_.set_error_handler(shell_watch_); args_ = args.To<std::vector<std::string>>(); delegate_->Initialize(this); } -void ApplicationImpl::BindShell(ScopedMessagePipeHandle shell_handle) { - shell_watch_ = new ShellPtrWatcher(this); - shell_.Bind(shell_handle.Pass()); - shell_.set_client(this); - shell_.set_error_handler(shell_watch_); +void ApplicationImpl::WaitForInitialize() { + if (!shell_) + binding_.WaitForIncomingMethodCall(); +} + +void ApplicationImpl::UnbindConnections( + InterfaceRequest<Application>* application_request, + ShellPtr* shell) { + *application_request = binding_.Unbind(); + shell->Bind(shell_.PassMessagePipe()); } void ApplicationImpl::AcceptConnection( diff --git a/third_party/mojo/src/mojo/public/cpp/application/lib/application_runner.cc b/third_party/mojo/src/mojo/public/cpp/application/lib/application_runner.cc index dec74489..0737dc7 100644 --- a/third_party/mojo/src/mojo/public/cpp/application/lib/application_runner.cc +++ b/third_party/mojo/src/mojo/public/cpp/application/lib/application_runner.cc @@ -23,11 +23,12 @@ ApplicationRunner::~ApplicationRunner() { assert(!delegate_); } -MojoResult ApplicationRunner::Run(MojoHandle shell_handle) { +MojoResult ApplicationRunner::Run(MojoHandle app_request_handle) { Environment env; { RunLoop loop; - ApplicationImpl app(delegate_, shell_handle); + ApplicationImpl app(delegate_, MakeRequest<Application>(MakeScopedHandle( + MessagePipeHandle(app_request_handle)))); loop.Run(); } diff --git a/third_party/mojo/src/mojo/public/cpp/application/lib/application_test_base.cc b/third_party/mojo/src/mojo/public/cpp/application/lib/application_test_base.cc index 20a60a1..72058a4 100644 --- a/third_party/mojo/src/mojo/public/cpp/application/lib/application_test_base.cc +++ b/third_party/mojo/src/mojo/public/cpp/application/lib/application_test_base.cc @@ -4,33 +4,27 @@ #include "mojo/public/cpp/application/application_test_base.h" -#include "mojo/public/cpp/application/application_delegate.h" #include "mojo/public/cpp/application/application_impl.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/public/interfaces/application/application.mojom.h" namespace mojo { namespace test { namespace { - -// This shell handle is shared by multiple test application instances. -MessagePipeHandle g_shell_handle; // Share the application command-line arguments with multiple application tests. Array<String> g_args; -ScopedMessagePipeHandle PassShellHandle() { - MOJO_CHECK(g_shell_handle.is_valid()); - ScopedMessagePipeHandle scoped_handle(g_shell_handle); - g_shell_handle = MessagePipeHandle(); - return scoped_handle.Pass(); -} +// 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; -void SetShellHandle(ScopedMessagePipeHandle handle) { - MOJO_CHECK(handle.is_valid()); - MOJO_CHECK(!g_shell_handle.is_valid()); - g_shell_handle = handle.release(); -} +// 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; void InitializeArgs(int argc, std::vector<const char*> argv) { MOJO_CHECK(g_args.is_null()); @@ -40,38 +34,71 @@ void InitializeArgs(int argc, std::vector<const char*> argv) { } } +class ShellAndArgumentGrabber : public Application { + public: + ShellAndArgumentGrabber(Array<String>* args, + InterfaceRequest<Application> application_request) + : args_(args), binding_(this, application_request.Pass()) {} + + void WaitForInitialize() { + // Initialize is always the first call made on Application. + binding_.WaitForIncomingMethodCall(); + } + + private: + // Application implementation. + void Initialize(ShellPtr shell, Array<String> args) override { + *args_ = args.Pass(); + g_application_request = binding_.Unbind(); + g_shell = shell.Pass(); + } + + void AcceptConnection(const String& requestor_url, + InterfaceRequest<ServiceProvider> services, + ServiceProviderPtr exposed_services) override { + MOJO_CHECK(false); + } + + void RequestQuit() override { MOJO_CHECK(false); } + + Array<String>* args_; + Binding<Application> binding_; +}; + } // namespace const Array<String>& Args() { return g_args; } -MojoResult RunAllTests(MojoHandle shell_handle) { +MojoResult RunAllTests(MojoHandle application_request_handle) { { // This loop is used for init, and then destroyed before running tests. Environment::InstantiateDefaultRunLoop(); - // Construct an ApplicationImpl just for the GTEST commandline arguments. + // Grab the shell handle and GTEST commandline arguments. // GTEST command line arguments are supported amid application arguments: // $ mojo_shell mojo:example_apptests // --args-for='mojo:example_apptests arg1 --gtest_filter=foo arg2' - mojo::ApplicationDelegate dummy_application_delegate; - mojo::ApplicationImpl app(&dummy_application_delegate, shell_handle); - MOJO_CHECK(app.WaitForInitialize()); + Array<String> args; + ShellAndArgumentGrabber grabber( + &args, MakeRequest<Application>(MakeScopedHandle( + MessagePipeHandle(application_request_handle)))); + grabber.WaitForInitialize(); + MOJO_CHECK(g_shell); + MOJO_CHECK(g_application_request.is_pending()); // InitGoogleTest expects (argc + 1) elements, including a terminating null. // It also removes GTEST arguments from |argv| and updates the |argc| count. - const std::vector<std::string>& args = app.args(); MOJO_CHECK(args.size() < static_cast<size_t>(std::numeric_limits<int>::max())); int argc = static_cast<int>(args.size()); std::vector<const char*> argv(argc + 1); for (int i = 0; i < argc; ++i) - argv[i] = args[i].c_str(); + argv[i] = args[i].get().c_str(); argv[argc] = nullptr; testing::InitGoogleTest(&argc, const_cast<char**>(&(argv[0]))); - SetShellHandle(app.UnbindShell()); InitializeArgs(argc, argv); Environment::DestroyDefaultRunLoop(); @@ -79,9 +106,9 @@ MojoResult RunAllTests(MojoHandle shell_handle) { int result = RUN_ALL_TESTS(); - shell_handle = mojo::test::PassShellHandle().release().value(); - MojoResult close_result = MojoClose(shell_handle); - MOJO_CHECK(close_result == MOJO_RESULT_OK); + // Shut down our message pipes before exiting. + (void)g_application_request.PassMessagePipe(); + (void)g_shell.PassMessagePipe(); return (result == 0) ? MOJO_RESULT_OK : MOJO_RESULT_UNKNOWN; } @@ -96,26 +123,28 @@ ApplicationDelegate* ApplicationTestBase::GetApplicationDelegate() { return &default_application_delegate_; } -void ApplicationTestBase::SetUpWithArgs(const Array<String>& args) { +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(), - PassShellHandle()); + g_application_request.Pass()); // Fake application initialization with the given command line arguments. - application_impl_->Initialize(args.Clone()); -} - -void ApplicationTestBase::SetUp() { - SetUpWithArgs(Args()); + application_impl_->Initialize(g_shell.Pass(), g_args.Clone()); } void ApplicationTestBase::TearDown() { - SetShellHandle(application_impl_->UnbindShell()); + MOJO_CHECK(!g_application_request.is_pending()); + MOJO_CHECK(!g_shell); + + application_impl_->UnbindConnections(&g_application_request, &g_shell); delete application_impl_; if (ShouldCreateDefaultRunLoop()) Environment::DestroyDefaultRunLoop(); diff --git a/third_party/mojo/src/mojo/public/cpp/application/lib/application_test_main.cc b/third_party/mojo/src/mojo/public/cpp/application/lib/application_test_main.cc index 47f36e9..128d8ae 100644 --- a/third_party/mojo/src/mojo/public/cpp/application/lib/application_test_main.cc +++ b/third_party/mojo/src/mojo/public/cpp/application/lib/application_test_main.cc @@ -6,9 +6,9 @@ #include "mojo/public/cpp/application/application_test_base.h" #include "mojo/public/cpp/environment/environment.h" -MojoResult MojoMain(MojoHandle shell_handle) { +MojoResult MojoMain(MojoHandle handle) { // An Environment instance is needed to construct run loops. mojo::Environment environment; - return mojo::test::RunAllTests(shell_handle); + return mojo::test::RunAllTests(handle); } diff --git a/third_party/mojo/src/mojo/public/cpp/bindings/binding.h b/third_party/mojo/src/mojo/public/cpp/bindings/binding.h index 658b6a0..7b663c4 100644 --- a/third_party/mojo/src/mojo/public/cpp/bindings/binding.h +++ b/third_party/mojo/src/mojo/public/cpp/bindings/binding.h @@ -165,6 +165,13 @@ class Binding : public ErrorHandler { internal_router_->CloseMessagePipe(); } + // Unbinds the underlying pipe from this binding and returns it so it can be + // used in another context, such as on another thread or with a different + // implementation. + InterfaceRequest<Interface> Unbind() { + return MakeRequest<Interface>(internal_router_->PassMessagePipe()); + } + // Sets an error handler that will be called if a connection error occurs on // the bound message pipe. void set_error_handler(ErrorHandler* error_handler) { diff --git a/third_party/mojo/src/mojo/public/cpp/bindings/callback.h b/third_party/mojo/src/mojo/public/cpp/bindings/callback.h index d7bab16..b8bc423 100644 --- a/third_party/mojo/src/mojo/public/cpp/bindings/callback.h +++ b/third_party/mojo/src/mojo/public/cpp/bindings/callback.h @@ -14,27 +14,35 @@ namespace mojo { template <typename Sig> class Callback; +// Represents a callback with any number of parameters and no return value. The +// callback is executed by calling its Run() method. The callback may be "null", +// meaning it does nothing. template <typename... Args> class Callback<void(Args...)> { public: + // An interface that may be implemented to define the Run() method. struct Runnable { virtual ~Runnable() {} virtual void Run( + // ForwardType ensures String is passed as a const reference. typename internal::Callback_ParamTraits<Args>::ForwardType...) const = 0; }; + // Constructs a "null" callback that does nothing. Callback() {} - // The Callback assumes ownership of |runnable|. + // Constructs a callback that will run |runnable|. The callback takes + // ownership of |runnable|. explicit Callback(Runnable* runnable) : sink_(runnable) {} - // Any class that is copy-constructable and has a compatible Run method may - // be adapted to a Callback using this constructor. + // As above, but can take an object that isn't derived from Runnable, so long + // as it has a Run() method. template <typename Sink> Callback(const Sink& sink) : sink_(new Adapter<Sink>(sink)) {} + // Executes the callback function, invoking Pass() on move-only types. void Run(typename internal::Callback_ParamTraits<Args>::ForwardType... args) const { if (sink_.get()) @@ -43,9 +51,12 @@ class Callback<void(Args...)> { bool is_null() const { return !sink_.get(); } + // Resets the callback to the "null" state. void reset() { sink_.reset(); } private: + // Adapts a class that has a Run() method but is not derived from Runnable to + // be callable by Callback. template <typename Sink> struct Adapter : public Runnable { explicit Adapter(const Sink& sink) : sink(sink) {} @@ -60,6 +71,7 @@ class Callback<void(Args...)> { internal::SharedPtr<Runnable> sink_; }; +// A specialization of Callback which takes no parameters. typedef Callback<void()> Closure; } // namespace mojo diff --git a/third_party/mojo/src/mojo/public/cpp/bindings/interface_ptr.h b/third_party/mojo/src/mojo/public/cpp/bindings/interface_ptr.h index a2ede4d..b7f1a4e 100644 --- a/third_party/mojo/src/mojo/public/cpp/bindings/interface_ptr.h +++ b/third_party/mojo/src/mojo/public/cpp/bindings/interface_ptr.h @@ -15,84 +15,112 @@ namespace mojo { class ErrorHandler; -// InterfacePtr represents a proxy to a remote instance of an interface. +// A pointer to a local proxy of a remote Interface implementation. Uses a +// message pipe to communicate with the remote implementation, and automatically +// closes the pipe and deletes the proxy on destruction. The pointer must be +// bound to a message pipe before the interface methods can be called. +// +// Can also route incoming calls to a local implementation of the +// Interface::Client interface. To enable this, call the set_client() method. +// Calls to the client interface will originate from the same thread that owns +// this InterfacePtr. +// +// This class is thread hostile, as is the local proxy it manages. All calls to +// this class or the proxy should be from the same thread that created it. If +// you need to move the proxy to a different thread, extract the message pipe +// using PassMessagePipe(), pass it to a different thread, and create a new +// InterfacePtr from that thread. template <typename Interface> class InterfacePtr { MOJO_MOVE_ONLY_TYPE(InterfacePtr) public: + // Constructs an unbound InterfacePtr. InterfacePtr() {} InterfacePtr(decltype(nullptr)) {} + // Takes over the binding of another InterfacePtr. InterfacePtr(InterfacePtr&& other) { internal_state_.Swap(&other.internal_state_); } + + // Takes over the binding of another InterfacePtr, and closes any message pipe + // already bound to this pointer. InterfacePtr& operator=(InterfacePtr&& other) { reset(); internal_state_.Swap(&other.internal_state_); return *this; } + // Assigning nullptr to this class causes it to close the currently bound + // message pipe (if any) and returns the pointer to the unbound state. InterfacePtr& operator=(decltype(nullptr)) { reset(); return *this; } + // Closes the bound message pipe (if any) on destruction. ~InterfacePtr() {} + // Binds the InterfacePtr to a message pipe that is connected to a remote + // implementation of Interface. The |waiter| is used for receiving + // notifications when there is data to read from the message pipe. For most + // callers, the default |waiter| will be sufficient. + void Bind( + ScopedMessagePipeHandle handle, + const MojoAsyncWaiter* waiter = Environment::GetDefaultAsyncWaiter()) { + reset(); + internal_state_.Bind(handle.Pass(), waiter); + } + + // Returns a raw pointer to the local proxy. Caller does not take ownership. + // Note that the local proxy is thread hostile, as stated above. Interface* get() const { return internal_state_.instance(); } + + // Functions like a pointer to Interface. Must already be bound. Interface* operator->() const { return get(); } Interface& operator*() const { return *get(); } + // Closes the bound message pipe (if any) and returns the pointer to the + // unbound state. void reset() { State doomed; internal_state_.Swap(&doomed); } - // Blocks the current thread for the first incoming method call, i.e., either - // a call to a client method or a callback method. Returns |true| if a method - // has been called, |false| in case of error. It must only be called on a - // bound object. + // Blocks the current thread until the next incoming call to a client method + // or callback arrives, or until an error occurs. Returns |true| if a call + // arrived, or |false| in case of error. + // + // This method may only be called after the InterfacePtr has been bound to a + // message pipe. bool WaitForIncomingMethodCall() { return internal_state_.WaitForIncomingMethodCall(); } - // This method configures the InterfacePtr<..> to be a proxy to a remote - // object on the other end of the given pipe. - // - // The proxy is bound to the current thread, which means its methods may - // only be called on the current thread. - // - // To move a bound InterfacePtr<..> to another thread, call PassMessagePipe(). - // Then create a new InterfacePtr<..> on another thread, and bind the new - // InterfacePtr<..> to the message pipe on that thread. - void Bind( - ScopedMessagePipeHandle handle, - const MojoAsyncWaiter* waiter = Environment::GetDefaultAsyncWaiter()) { - reset(); - internal_state_.Bind(handle.Pass(), waiter); - } - - // The client interface may only be set after this InterfacePtr<..> is bound. + // Enables routing of incoming method calls to a local implementation of the + // Interface::Client interface. Calls to |client| will come from the thread + // that owns this InterfacePtr. void set_client(typename Interface::Client* client) { internal_state_.set_client(client); } - // This method may be called to query if the underlying pipe has encountered - // an error. If true, this means method calls made on this interface will be - // dropped (and may have already been dropped) on the floor. + // Indicates whether the message pipe has encountered an error. If true, + // method calls made on this interface will be dropped (and may already have + // been dropped). bool encountered_error() const { return internal_state_.encountered_error(); } - // This method may be called to register an ErrorHandler to observe a - // connection error on the underlying pipe. It must only be called on a bound - // object. - // The callback runs asynchronously from the current message loop. + // Registers a handler to receive error notifications. The handler will be + // called from the thread that owns this InterfacePtr. + // + // This method may only be called after the InterfacePtr has been bound to a + // message pipe. void set_error_handler(ErrorHandler* error_handler) { internal_state_.set_error_handler(error_handler); } - // Returns the underlying message pipe handle (if any) and resets the - // InterfacePtr<..> to its uninitialized state. This method is helpful if you - // need to move a proxy to another thread. See related notes for Bind. + // Unbinds the InterfacePtr and return the previously bound message pipe (if + // any). This method may be used to move the proxy to a different thread (see + // class comments for details). ScopedMessagePipeHandle PassMessagePipe() { State state; internal_state_.Swap(&state); @@ -120,11 +148,9 @@ class InterfacePtr { mutable State internal_state_; }; -// Takes a handle to the proxy end-point of a pipe. On the other end is -// presumed to be an interface implementation of type |Interface|. Returns a -// generated proxy to that interface, which may be used on the current thread. -// It is valid to call set_client on the returned InterfacePtr<..> to set an -// instance of Interface::Client. +// If the specified message pipe handle is valid, returns an InterfacePtr bound +// to it. Otherwise, returns an unbound InterfacePtr. The specified |waiter| +// will be used as in the InterfacePtr::Bind() method. template <typename Interface> InterfacePtr<Interface> MakeProxy( ScopedMessagePipeHandle handle, diff --git a/third_party/mojo/src/mojo/public/cpp/bindings/interface_request.h b/third_party/mojo/src/mojo/public/cpp/bindings/interface_request.h index 0b89103..488b679 100644 --- a/third_party/mojo/src/mojo/public/cpp/bindings/interface_request.h +++ b/third_party/mojo/src/mojo/public/cpp/bindings/interface_request.h @@ -9,35 +9,53 @@ namespace mojo { -// Used in methods that return instances of remote objects. +// Represents a request from a remote client for an implementation of Interface +// over a specified message pipe. The implementor of the interface should +// remove the message pipe by calling PassMessagePipe() and bind it to the +// implementation. If this is not done, the InterfaceRequest will automatically +// close the pipe on destruction. Can also represent the absence of a request +// if the client did not provide a message pipe. template <typename Interface> class InterfaceRequest { MOJO_MOVE_ONLY_TYPE(InterfaceRequest) public: + // Constructs an empty InterfaceRequest, representing that the client is not + // requesting an implementation of Interface. InterfaceRequest() {} - InterfaceRequest(decltype(nullptr)) {} + + // Takes the message pipe from another InterfaceRequest. InterfaceRequest(InterfaceRequest&& other) { handle_ = other.handle_.Pass(); } - InterfaceRequest& operator=(decltype(nullptr)) { - handle_.reset(); - return *this; - } InterfaceRequest& operator=(InterfaceRequest&& other) { handle_ = other.handle_.Pass(); return *this; } - // Returns true if the request has yet to be completed. - bool is_pending() const { return handle_.is_valid(); } + // Assigning to nullptr resets the InterfaceRequest to an empty state, + // closing the message pipe currently bound to it (if any). + InterfaceRequest& operator=(decltype(nullptr)) { + handle_.reset(); + return *this; + } + // Binds the request to a message pipe over which Interface is to be + // requested. If the request is already bound to a message pipe, the current + // message pipe will be closed. void Bind(ScopedMessagePipeHandle handle) { handle_ = handle.Pass(); } + // Indicates whether the request currently contains a valid message pipe. + bool is_pending() const { return handle_.is_valid(); } + + // Removes the message pipe from the request and returns it. ScopedMessagePipeHandle PassMessagePipe() { return handle_.Pass(); } private: ScopedMessagePipeHandle handle_; }; +// Makes an InterfaceRequest bound to the specified message pipe. If |handle| +// is empty or invalid, the resulting InterfaceRequest will represent the +// absence of a request. template <typename Interface> InterfaceRequest<Interface> MakeRequest(ScopedMessagePipeHandle handle) { InterfaceRequest<Interface> request; @@ -45,23 +63,48 @@ InterfaceRequest<Interface> MakeRequest(ScopedMessagePipeHandle handle) { return request.Pass(); } -// Used to construct a request that synchronously binds an InterfacePtr<..>, -// making it immediately usable upon return. The resulting request object may -// then be later bound to an InterfaceImpl<..> via BindToRequest. +// Creates a new message pipe over which Interface is to be served. Binds the +// specified InterfacePtr to one end of the message pipe, and returns an +// InterfaceRequest bound to the other. The InterfacePtr should be passed to +// the client, and the InterfaceRequest should be passed to whatever will +// provide the implementation. The implementation should typically be bound to +// the InterfaceRequest using the Binding or StrongBinding classes. The client +// may begin to issue calls even before an implementation has been bound, since +// messages sent over the pipe will just queue up until they are consumed by +// the implementation. +// +// Example #1: Requesting a remote implementation of an interface. +// =============================================================== // // Given the following interface: // -// interface Foo { -// CreateBar(Bar& bar); +// interface Database { +// OpenTable(Table& table); // } // -// The caller of CreateBar would have code similar to the following: +// The client would have code similar to the following: +// +// DatabasePtr database = ...; // Connect to database. +// TablePtr table; +// database->OpenTable(GetProxy(&table)); +// +// Upon return from GetProxy, |table| is ready to have methods called on it. +// +// Example #2: Registering a local implementation with a remote service. +// ===================================================================== +// +// Given the following interface +// interface Collector { +// RegisterSource(Source source); +// } // -// InterfacePtr<Foo> foo = ...; -// InterfacePtr<Bar> bar; -// foo->CreateBar(GetProxy(&bar)); +// The client would have code similar to the following: // -// Upon return from CreateBar, |bar| is ready to have methods called on it. +// CollectorPtr collector = ...; // Connect to Collector. +// SourcePtr source; +// InterfaceRequest<Source> source_request = GetProxy(&source); +// collector->RegisterSource(source.Pass()); +// CreateSource(source_request.Pass()); // Create implementation locally. // template <typename Interface> InterfaceRequest<Interface> GetProxy(InterfacePtr<Interface>* ptr) { diff --git a/third_party/mojo/src/mojo/public/cpp/bindings/lib/map_internal.h b/third_party/mojo/src/mojo/public/cpp/bindings/lib/map_internal.h index f006cd0..8afc18c 100644 --- a/third_party/mojo/src/mojo/public/cpp/bindings/lib/map_internal.h +++ b/third_party/mojo/src/mojo/public/cpp/bindings/lib/map_internal.h @@ -16,6 +16,7 @@ namespace internal { template <typename Key, typename Value, bool kValueIsMoveOnlyType> struct MapTraits {}; +// Defines traits of a map for which Value is not a move-only type. template <typename Key, typename Value> struct MapTraits<Key, Value, false> { // Map keys can't be move only types. @@ -105,6 +106,7 @@ struct MapTraits<Key, Value, false> { } }; +// Defines traits of a map for which Value is a move-only type. template <typename Key, typename Value> struct MapTraits<Key, Value, true> { // Map keys can't be move only types. diff --git a/third_party/mojo/src/mojo/public/cpp/bindings/lib/template_util.h b/third_party/mojo/src/mojo/public/cpp/bindings/lib/template_util.h index c221a54..2edbf5c 100644 --- a/third_party/mojo/src/mojo/public/cpp/bindings/lib/template_util.h +++ b/third_party/mojo/src/mojo/public/cpp/bindings/lib/template_util.h @@ -60,11 +60,13 @@ struct IsMoveOnlyType { sizeof(Test<T>(0)) == sizeof(YesType) && !IsConst<T>::value; }; +// Returns a reference to |t| when T is not a move-only type. template <typename T> typename EnableIf<!IsMoveOnlyType<T>::value, T>::type& Forward(T& t) { return t; } +// Returns the result of t.Pass() when T is a move-only type. template <typename T> typename EnableIf<IsMoveOnlyType<T>::value, T>::type Forward(T& t) { return t.Pass(); diff --git a/third_party/mojo/src/mojo/public/cpp/bindings/map.h b/third_party/mojo/src/mojo/public/cpp/bindings/map.h index 5149bb0..8ac183f 100644 --- a/third_party/mojo/src/mojo/public/cpp/bindings/map.h +++ b/third_party/mojo/src/mojo/public/cpp/bindings/map.h @@ -11,6 +11,15 @@ namespace mojo { +// A move-only map that can handle move-only values. Map has the following +// characteristics: +// - The map itself can be null, and this is distinct from empty. +// - Keys must not be move-only. +// - The Key-type's "<" operator is used to sort the entries, and also is +// used to determine equality of the key values. +// - There can only be one entry per unique key. +// - Values of move-only types will be moved into the Map when they are added +// using the insert() method. template <typename Key, typename Value> class Map { MOJO_MOVE_ONLY_TYPE(Map) @@ -39,6 +48,8 @@ class Map { Map() : is_null_(true) {} + // Constructs a non-null Map containing the specified |keys| mapped to the + // corresponding |values|. Map(mojo::Array<Key> keys, mojo::Array<Value> values) : is_null_(false) { MOJO_DCHECK(keys.size() == values.size()); Traits::InitializeFrom(&map_, keys.Pass(), values.Pass()); @@ -52,16 +63,21 @@ class Map { return *this; } + // Copies the contents of some other type of map into a new Map using a + // TypeConverter. A TypeConverter for std::map to Map is defined below. template <typename U> static Map From(const U& other) { return TypeConverter<Map, U>::Convert(other); } + // Copies the contents of the Map into some other type of map. A TypeConverter + // for Map to std::map is defined below. template <typename U> U To() const { return TypeConverter<U, Map>::Convert(*this); } + // Destroys the contents of the Map and leaves it in the null state. void reset() { if (!map_.empty()) { Traits::Finalize(&map_); @@ -72,40 +88,57 @@ class Map { bool is_null() const { return is_null_; } + // Indicates the number of keys in the map. size_t size() const { return map_.size(); } - // Used to mark an empty map as non-null for serialization purposes. void mark_non_null() { is_null_ = false; } - // Inserts a key-value pair into the map. Like std::map, this does not insert - // |value| if |key| is already a member of the map. + // Inserts a key-value pair into the map, moving the value by calling its + // Pass() method if it is a move-only type. Like std::map, this does not + // insert |value| if |key| is already a member of the map. void insert(KeyForwardType key, ValueForwardType value) { is_null_ = false; Traits::Insert(&map_, key, value); } + // Returns a reference to the value associated with the specified key, + // crashing the process if the key is not present in the map. ValueRefType at(KeyForwardType key) { return Traits::at(&map_, key); } ValueConstRefType at(KeyForwardType key) const { return Traits::at(&map_, key); } + // Returns a reference to the value associated with the specified key, + // creating a new entry if the key is not already present in the map. A + // newly-created value will be value-initialized (meaning that it will be + // initialized by the default constructor of the value type, if any, or else + // will be zero-initialized). ValueRefType operator[](KeyForwardType key) { is_null_ = false; return Traits::GetOrInsert(&map_, key); } + // Swaps the contents of this Map with another Map of the same type (including + // nullness). void Swap(Map<Key, Value>* other) { std::swap(is_null_, other->is_null_); map_.swap(other->map_); } + + // Swaps the contents of this Map with an std::map containing keys and values + // of the same type. Since std::map cannot represent the null state, the + // std::map will be empty if Map is null. The Map will always be left in a + // non-null state. void Swap(std::map<Key, Value>* other) { is_null_ = false; map_.swap(*other); } - // This moves all values in the map to a set of parallel arrays. This action - // is destructive because we can have move-only objects as values; therefore - // we can't have copy semantics here. + // Removes all contents from the Map and places them into parallel key/value + // arrays. Each key will be copied from the source to the destination, and + // values will be copied unless their type is designated move-only, in which + // case they will be passed by calling their Pass() method. Either way, the + // Map will be left in a null state. void DecomposeMapTo(mojo::Array<Key>* keys, mojo::Array<Value>* values) { Traits::Decompose(&map_, keys, values); Traits::Finalize(&map_); @@ -113,9 +146,11 @@ class Map { is_null_ = true; } - // Please note that calling this method will fail compilation if the value - // type cannot be cloned (which usually means that it is a Mojo handle type or - // a type contains Mojo handles). + // Returns a new Map that contains a copy of the contents of this map. If the + // values are of a type that is designated move-only, they will be cloned + // using the Clone() method of the type. Please note that calling this method + // will fail compilation if the value type cannot be cloned (which usually + // means that it is a Mojo handle type or a type that contains Mojo handles). Map Clone() const { Map result; result.is_null_ = is_null_; @@ -123,6 +158,13 @@ class Map { return result.Pass(); } + // Indicates whether the contents of this map are equal to those of another + // Map (including nullness). Keys are compared by the != operator. Values are + // compared as follows: + // - Map, Array, Struct, or StructPtr values are compared by their Equals() + // method. + // - ScopedHandleBase-derived types are compared by their handles. + // - Values of other types are compared by their "==" operator. bool Equals(const Map& other) const { if (is_null() != other.is_null()) return false; @@ -141,6 +183,7 @@ class Map { return true; } + // A read-only iterator for Map. class ConstMapIterator { public: ConstMapIterator( @@ -148,6 +191,7 @@ class Map { ValueStorageType>::const_iterator& it) : it_(it) {} + // Returns a const reference to the key and value. KeyConstRefType GetKey() { return Traits::GetKey(it_); } ValueConstRefType GetValue() { return Traits::GetValue(it_); } @@ -166,10 +210,13 @@ class Map { typename std::map<KeyStorageType, ValueStorageType>::const_iterator it_; }; - // Provide read-only iteration over map members. + // Provide read-only iteration over map members in a way similar to STL + // collections. ConstMapIterator begin() const { return ConstMapIterator(map_.begin()); } ConstMapIterator end() const { return ConstMapIterator(map_.end()); } + // Returns the iterator pointing to the entry for |key|, if present, or else + // returns end(). ConstMapIterator find(KeyForwardType key) const { return ConstMapIterator(map_.find(key)); } @@ -178,6 +225,9 @@ class Map { typedef std::map<KeyStorageType, ValueStorageType> Map::*Testable; public: + // The Map may be used in boolean expressions to determine if it is non-null, + // but is not implicitly convertible to an actual bool value (which would be + // dangerous). operator Testable() const { return is_null_ ? 0 : &Map::map_; } private: @@ -190,6 +240,8 @@ class Map { bool is_null_; }; +// Copies the contents of an std::map to a new Map, optionally changing the +// types of the keys and values along the way using TypeConverter. template <typename MojoKey, typename MojoValue, typename STLKey, @@ -207,6 +259,8 @@ struct TypeConverter<Map<MojoKey, MojoValue>, std::map<STLKey, STLValue>> { } }; +// Copies the contents of a Map to an std::map, optionally changing the types of +// the keys and values along the way using TypeConverter. template <typename MojoKey, typename MojoValue, typename STLKey, diff --git a/third_party/mojo/src/mojo/public/cpp/bindings/strong_binding.h b/third_party/mojo/src/mojo/public/cpp/bindings/strong_binding.h index 0cd4f03..7caf54a 100644 --- a/third_party/mojo/src/mojo/public/cpp/bindings/strong_binding.h +++ b/third_party/mojo/src/mojo/public/cpp/bindings/strong_binding.h @@ -45,6 +45,8 @@ namespace mojo { // }; template <typename Interface> class StrongBinding : public ErrorHandler { + MOJO_MOVE_ONLY_TYPE(StrongBinding) + public: explicit StrongBinding(Interface* impl) : binding_(impl) { binding_.set_error_handler(this); diff --git a/third_party/mojo/src/mojo/public/cpp/bindings/tests/interface_ptr_unittest.cc b/third_party/mojo/src/mojo/public/cpp/bindings/tests/interface_ptr_unittest.cc index f90dc3d..ded5888 100644 --- a/third_party/mojo/src/mojo/public/cpp/bindings/tests/interface_ptr_unittest.cc +++ b/third_party/mojo/src/mojo/public/cpp/bindings/tests/interface_ptr_unittest.cc @@ -27,34 +27,59 @@ class ErrorObserver : public ErrorHandler { bool encountered_error_; }; +template <typename Method, typename Class> +class RunnableImpl { + public: + RunnableImpl(Method method, Class instance) + : method_(method), instance_(instance) {} + template <typename... Args> + void Run(Args... args) const { + (instance_->*method_)(args...); + } + + private: + Method method_; + Class instance_; +}; + +template <typename Method, typename Class> +RunnableImpl<Method, Class> MakeRunnable(Method method, Class object) { + return RunnableImpl<Method, Class>(method, object); +} + +typedef mojo::Callback<void(double)> CalcCallback; + class MathCalculatorImpl : public InterfaceImpl<math::Calculator> { public: ~MathCalculatorImpl() override {} MathCalculatorImpl() : total_(0.0) {} - void Clear() override { client()->Output(total_); } + void Clear(const CalcCallback& callback) override { + total_ = 0.0; + callback.Run(total_); + } - void Add(double value) override { + void Add(double value, const CalcCallback& callback) override { total_ += value; - client()->Output(total_); + callback.Run(total_); } - void Multiply(double value) override { + void Multiply(double value, const CalcCallback& callback) override { total_ *= value; - client()->Output(total_); + callback.Run(total_); } private: double total_; }; -class MathCalculatorUIImpl : public math::CalculatorUI { +class MathCalculatorUI { public: - explicit MathCalculatorUIImpl(math::CalculatorPtr calculator) - : calculator_(calculator.Pass()), output_(0.0) { - calculator_.set_client(this); - } + explicit MathCalculatorUI(math::CalculatorPtr calculator) + : calculator_(calculator.Pass()), + output_(0.0), + callback_(MakeRunnable(&MathCalculatorUI::Output, this)) {} bool WaitForIncomingMethodCall() { return calculator_.WaitForIncomingMethodCall(); @@ -62,59 +87,60 @@ class MathCalculatorUIImpl : public math::CalculatorUI { bool encountered_error() const { return calculator_.encountered_error(); } - void Add(double value) { calculator_->Add(value); } + void Add(double value) { calculator_->Add(value, callback_); } - void Subtract(double value) { calculator_->Add(-value); } + void Subtract(double value) { calculator_->Add(-value, callback_); } - void Multiply(double value) { calculator_->Multiply(value); } + void Multiply(double value) { calculator_->Multiply(value, callback_); } - void Divide(double value) { calculator_->Multiply(1.0 / value); } + void Divide(double value) { calculator_->Multiply(1.0 / value, callback_); } double GetOutput() const { return output_; } private: - // math::CalculatorUI implementation: - void Output(double value) override { output_ = value; } + void Output(double output) { output_ = output; } math::CalculatorPtr calculator_; double output_; + Callback<void(double)> callback_; }; -class SelfDestructingMathCalculatorUIImpl : public math::CalculatorUI { +class SelfDestructingMathCalculatorUI { public: - explicit SelfDestructingMathCalculatorUIImpl(math::CalculatorPtr calculator) + explicit SelfDestructingMathCalculatorUI(math::CalculatorPtr calculator) : calculator_(calculator.Pass()), nesting_level_(0) { ++num_instances_; - calculator_.set_client(this); } void BeginTest(bool nested) { nesting_level_ = nested ? 2 : 1; - calculator_->Add(1.0); + calculator_->Add( + 1.0, MakeRunnable(&SelfDestructingMathCalculatorUI::Output, this)); } static int num_instances() { return num_instances_; } - private: - ~SelfDestructingMathCalculatorUIImpl() override { --num_instances_; } - - void Output(double value) override { + void Output(double value) { if (--nesting_level_ > 0) { // Add some more and wait for re-entrant call to Output! - calculator_->Add(1.0); + calculator_->Add( + 1.0, MakeRunnable(&SelfDestructingMathCalculatorUI::Output, this)); RunLoop::current()->RunUntilIdle(); } else { delete this; } } + private: + ~SelfDestructingMathCalculatorUI() { --num_instances_; } + math::CalculatorPtr calculator_; int nesting_level_; static int num_instances_; }; // static -int SelfDestructingMathCalculatorUIImpl::num_instances_ = 0; +int SelfDestructingMathCalculatorUI::num_instances_ = 0; class ReentrantServiceImpl : public InterfaceImpl<sample::Service> { public: @@ -157,7 +183,7 @@ TEST_F(InterfacePtrTest, EndToEnd) { BindToProxy(new MathCalculatorImpl(), &calc); // Suppose this is instantiated in a process that has pipe1_. - MathCalculatorUIImpl calculator_ui(calc.Pass()); + MathCalculatorUI calculator_ui(calc.Pass()); calculator_ui.Add(2.0); calculator_ui.Multiply(5.0); @@ -172,7 +198,7 @@ TEST_F(InterfacePtrTest, EndToEnd_Synchronous) { MathCalculatorImpl* impl = BindToProxy(new MathCalculatorImpl(), &calc); // Suppose this is instantiated in a process that has pipe1_. - MathCalculatorUIImpl calculator_ui(calc.Pass()); + MathCalculatorUI calculator_ui(calc.Pass()); EXPECT_EQ(0.0, calculator_ui.GetOutput()); @@ -230,7 +256,7 @@ TEST_F(InterfacePtrTest, EncounteredError) { math::CalculatorPtr proxy; MathCalculatorImpl* server = BindToProxy(new MathCalculatorImpl(), &proxy); - MathCalculatorUIImpl calculator_ui(proxy.Pass()); + MathCalculatorUI calculator_ui(proxy.Pass()); calculator_ui.Add(2.0); PumpMessages(); @@ -259,7 +285,7 @@ TEST_F(InterfacePtrTest, EncounteredErrorCallback) { ErrorObserver error_observer; proxy.set_error_handler(&error_observer); - MathCalculatorUIImpl calculator_ui(proxy.Pass()); + MathCalculatorUI calculator_ui(proxy.Pass()); calculator_ui.Add(2.0); PumpMessages(); @@ -297,30 +323,30 @@ TEST_F(InterfacePtrTest, DestroyInterfacePtrOnClientMethod) { math::CalculatorPtr proxy; BindToProxy(new MathCalculatorImpl(), &proxy); - EXPECT_EQ(0, SelfDestructingMathCalculatorUIImpl::num_instances()); + EXPECT_EQ(0, SelfDestructingMathCalculatorUI::num_instances()); - SelfDestructingMathCalculatorUIImpl* impl = - new SelfDestructingMathCalculatorUIImpl(proxy.Pass()); + SelfDestructingMathCalculatorUI* impl = + new SelfDestructingMathCalculatorUI(proxy.Pass()); impl->BeginTest(false); PumpMessages(); - EXPECT_EQ(0, SelfDestructingMathCalculatorUIImpl::num_instances()); + EXPECT_EQ(0, SelfDestructingMathCalculatorUI::num_instances()); } TEST_F(InterfacePtrTest, NestedDestroyInterfacePtrOnClientMethod) { math::CalculatorPtr proxy; BindToProxy(new MathCalculatorImpl(), &proxy); - EXPECT_EQ(0, SelfDestructingMathCalculatorUIImpl::num_instances()); + EXPECT_EQ(0, SelfDestructingMathCalculatorUI::num_instances()); - SelfDestructingMathCalculatorUIImpl* impl = - new SelfDestructingMathCalculatorUIImpl(proxy.Pass()); + SelfDestructingMathCalculatorUI* impl = + new SelfDestructingMathCalculatorUI(proxy.Pass()); impl->BeginTest(true); PumpMessages(); - EXPECT_EQ(0, SelfDestructingMathCalculatorUIImpl::num_instances()); + EXPECT_EQ(0, SelfDestructingMathCalculatorUI::num_instances()); } TEST_F(InterfacePtrTest, ReentrantWaitForIncomingMethodCall) { @@ -348,16 +374,16 @@ class StrongMathCalculatorImpl : public math::Calculator, public ErrorHandler { ~StrongMathCalculatorImpl() override { *destroyed_ = true; } // math::Calculator implementation. - void Clear() override { binding_.client()->Output(total_); } + void Clear(const CalcCallback& callback) override { callback.Run(total_); } - void Add(double value) override { + void Add(double value, const CalcCallback& callback) override { total_ += value; - binding_.client()->Output(total_); + callback.Run(total_); } - void Multiply(double value) override { + void Multiply(double value, const CalcCallback& callback) override { total_ *= value; - binding_.client()->Output(total_); + callback.Run(total_); } // ErrorHandler implementation. @@ -387,7 +413,7 @@ TEST(StrongConnectorTest, Math) { { // Suppose this is instantiated in a process that has the other end of the // message pipe. - MathCalculatorUIImpl calculator_ui(calc.Pass()); + MathCalculatorUI calculator_ui(calc.Pass()); calculator_ui.Add(2.0); calculator_ui.Multiply(5.0); @@ -419,16 +445,16 @@ class WeakMathCalculatorImpl : public math::Calculator, public ErrorHandler { } ~WeakMathCalculatorImpl() override { *destroyed_ = true; } - void Clear() override { binding_.client()->Output(total_); } + void Clear(const CalcCallback& callback) override { callback.Run(total_); } - void Add(double value) override { + void Add(double value, const CalcCallback& callback) override { total_ += value; - binding_.client()->Output(total_); + callback.Run(total_); } - void Multiply(double value) override { + void Multiply(double value, const CalcCallback& callback) override { total_ *= value; - binding_.client()->Output(total_); + callback.Run(total_); } // ErrorHandler implementation. @@ -457,7 +483,7 @@ TEST(WeakConnectorTest, Math) { { // Suppose this is instantiated in a process that has the other end of the // message pipe. - MathCalculatorUIImpl calculator_ui(calc.Pass()); + MathCalculatorUI calculator_ui(calc.Pass()); calculator_ui.Add(2.0); calculator_ui.Multiply(5.0); diff --git a/third_party/mojo/src/mojo/public/cpp/system/buffer.h b/third_party/mojo/src/mojo/public/cpp/system/buffer.h index 6817297..9458c0a 100644 --- a/third_party/mojo/src/mojo/public/cpp/system/buffer.h +++ b/third_party/mojo/src/mojo/public/cpp/system/buffer.h @@ -2,6 +2,13 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. +// This file provides a C++ wrapping around the Mojo C API for shared buffers, +// replacing the prefix of "Mojo" with a "mojo" namespace, and using more +// strongly-typed representations of |MojoHandle|s. +// +// Please see "mojo/public/c/system/buffer.h" for complete documentation of the +// API. + #ifndef MOJO_PUBLIC_CPP_SYSTEM_BUFFER_H_ #define MOJO_PUBLIC_CPP_SYSTEM_BUFFER_H_ @@ -13,8 +20,8 @@ namespace mojo { -// SharedBufferHandle ---------------------------------------------------------- - +// A strongly-typed representation of a |MojoHandle| referring to a shared +// buffer. class SharedBufferHandle : public Handle { public: SharedBufferHandle() {} @@ -30,6 +37,8 @@ typedef ScopedHandleBase<SharedBufferHandle> ScopedSharedBufferHandle; static_assert(sizeof(ScopedSharedBufferHandle) == sizeof(SharedBufferHandle), "Bad size for C++ ScopedSharedBufferHandle"); +// Creates a shared buffer. See |MojoCreateSharedBuffer()| for complete +// documentation. inline MojoResult CreateSharedBuffer( const MojoCreateSharedBufferOptions* options, uint64_t num_bytes, @@ -44,10 +53,17 @@ inline MojoResult CreateSharedBuffer( return rv; } +// Duplicates a handle to a buffer, most commonly so that the buffer can be +// shared with other applications. See |MojoDuplicateBufferHandle()| for +// complete documentation. +// +// TODO(ggowan): Rename this to DuplicateBufferHandle since it is making another +// handle to the same buffer, not duplicating the buffer itself. +// // TODO(vtl): This (and also the functions below) are templatized to allow for // future/other buffer types. A bit "safer" would be to overload this function -// manually. (The template enforces that the in and out handles to be of the -// same type.) +// manually. (The template enforces that the in and out handles be of the same +// type.) template <class BufferHandleType> inline MojoResult DuplicateBuffer( BufferHandleType buffer, @@ -63,6 +79,8 @@ inline MojoResult DuplicateBuffer( return rv; } +// Maps a part of a buffer (specified by |buffer|, |offset|, and |num_bytes|) +// into memory. See |MojoMapBuffer()| for complete documentation. template <class BufferHandleType> inline MojoResult MapBuffer(BufferHandleType buffer, uint64_t offset, @@ -73,6 +91,8 @@ inline MojoResult MapBuffer(BufferHandleType buffer, return MojoMapBuffer(buffer.value(), offset, num_bytes, pointer, flags); } +// Unmaps a part of a buffer that was previously mapped with |MapBuffer()|. +// See |MojoUnmapBuffer()| for complete documentation. inline MojoResult UnmapBuffer(void* pointer) { assert(pointer); return MojoUnmapBuffer(pointer); diff --git a/third_party/mojo/src/mojo/public/cpp/system/data_pipe.h b/third_party/mojo/src/mojo/public/cpp/system/data_pipe.h index 5d3396c..c451cff 100644 --- a/third_party/mojo/src/mojo/public/cpp/system/data_pipe.h +++ b/third_party/mojo/src/mojo/public/cpp/system/data_pipe.h @@ -2,6 +2,13 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. +// This file provides a C++ wrapping around the Mojo C API for data pipes, +// replacing the prefix of "Mojo" with a "mojo" namespace, and using more +// strongly-typed representations of |MojoHandle|s. +// +// Please see "mojo/public/c/system/data_pipe.h" for complete documentation of +// the API. + #ifndef MOJO_PUBLIC_CPP_SYSTEM_DATA_PIPE_H_ #define MOJO_PUBLIC_CPP_SYSTEM_DATA_PIPE_H_ @@ -13,8 +20,8 @@ namespace mojo { -// DataPipeProducerHandle and DataPipeConsumerHandle --------------------------- - +// A strongly-typed representation of a |MojoHandle| to the producer end of a +// data pipe. class DataPipeProducerHandle : public Handle { public: DataPipeProducerHandle() {} @@ -31,6 +38,8 @@ static_assert(sizeof(ScopedDataPipeProducerHandle) == sizeof(DataPipeProducerHandle), "Bad size for C++ ScopedDataPipeProducerHandle"); +// A strongly-typed representation of a |MojoHandle| to the consumer end of a +// data pipe. class DataPipeConsumerHandle : public Handle { public: DataPipeConsumerHandle() {} @@ -47,6 +56,8 @@ static_assert(sizeof(ScopedDataPipeConsumerHandle) == sizeof(DataPipeConsumerHandle), "Bad size for C++ ScopedDataPipeConsumerHandle"); +// Creates a new data pipe. See |MojoCreateDataPipe()| for complete +// documentation. inline MojoResult CreateDataPipe( const MojoCreateDataPipeOptions* options, ScopedDataPipeProducerHandle* data_pipe_producer, @@ -65,6 +76,7 @@ inline MojoResult CreateDataPipe( return rv; } +// Writes to a data pipe. See |MojoWriteData| for complete documentation. inline MojoResult WriteDataRaw(DataPipeProducerHandle data_pipe_producer, const void* elements, uint32_t* num_bytes, @@ -72,6 +84,8 @@ inline MojoResult WriteDataRaw(DataPipeProducerHandle data_pipe_producer, return MojoWriteData(data_pipe_producer.value(), elements, num_bytes, flags); } +// Begins a two-phase write to a data pipe. See |MojoBeginWriteData()| for +// complete documentation. inline MojoResult BeginWriteDataRaw(DataPipeProducerHandle data_pipe_producer, void** buffer, uint32_t* buffer_num_bytes, @@ -80,11 +94,14 @@ inline MojoResult BeginWriteDataRaw(DataPipeProducerHandle data_pipe_producer, data_pipe_producer.value(), buffer, buffer_num_bytes, flags); } +// Completes a two-phase write to a data pipe. See |MojoEndWriteData()| for +// complete documentation. inline MojoResult EndWriteDataRaw(DataPipeProducerHandle data_pipe_producer, uint32_t num_bytes_written) { return MojoEndWriteData(data_pipe_producer.value(), num_bytes_written); } +// Reads from a data pipe. See |MojoReadData()| for complete documentation. inline MojoResult ReadDataRaw(DataPipeConsumerHandle data_pipe_consumer, void* elements, uint32_t* num_bytes, @@ -92,6 +109,8 @@ inline MojoResult ReadDataRaw(DataPipeConsumerHandle data_pipe_consumer, return MojoReadData(data_pipe_consumer.value(), elements, num_bytes, flags); } +// Begins a two-phase read from a data pipe. See |MojoBeginReadData()| for +// complete documentation. inline MojoResult BeginReadDataRaw(DataPipeConsumerHandle data_pipe_consumer, const void** buffer, uint32_t* buffer_num_bytes, @@ -100,6 +119,8 @@ inline MojoResult BeginReadDataRaw(DataPipeConsumerHandle data_pipe_consumer, data_pipe_consumer.value(), buffer, buffer_num_bytes, flags); } +// Completes a two-phase read from a data pipe. See |MojoEndReadData()| for +// complete documentation. inline MojoResult EndReadDataRaw(DataPipeConsumerHandle data_pipe_consumer, uint32_t num_bytes_read) { return MojoEndReadData(data_pipe_consumer.value(), num_bytes_read); diff --git a/third_party/mojo/src/mojo/public/cpp/system/functions.h b/third_party/mojo/src/mojo/public/cpp/system/functions.h index d73d27a..9cfe316 100644 --- a/third_party/mojo/src/mojo/public/cpp/system/functions.h +++ b/third_party/mojo/src/mojo/public/cpp/system/functions.h @@ -2,6 +2,12 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. +// This file provides a C++ wrapping around the standalone functions of the Mojo +// C API, replacing the prefix of "Mojo" with a "mojo" namespace. +// +// Please see "mojo/public/c/system/functions.h" for complete documentation of +// the API. + #ifndef MOJO_PUBLIC_CPP_SYSTEM_FUNCTIONS_H_ #define MOJO_PUBLIC_CPP_SYSTEM_FUNCTIONS_H_ @@ -9,12 +15,18 @@ namespace mojo { -// Standalone functions -------------------------------------------------------- - +// Returns the current |MojoTimeTicks| value. See |MojoGetTimeTicksNow()| for +// complete documentation. inline MojoTimeTicks GetTimeTicksNow() { return MojoGetTimeTicksNow(); } +// The C++ wrappers for |MojoWait()| and |MojoWaitMany()| are defined in +// "handle.h". +// TODO(ggowan): Consider making the C and C++ APIs more consistent in the +// organization of the functions into different header files (since in the C +// API, those functions are defined in "functions.h"). + } // namespace mojo #endif // MOJO_PUBLIC_CPP_SYSTEM_FUNCTIONS_H_ diff --git a/third_party/mojo/src/mojo/public/cpp/system/handle.h b/third_party/mojo/src/mojo/public/cpp/system/handle.h index 0c5adc7..45624bc 100644 --- a/third_party/mojo/src/mojo/public/cpp/system/handle.h +++ b/third_party/mojo/src/mojo/public/cpp/system/handle.h @@ -61,23 +61,8 @@ namespace mojo { // |ScopedHandleBase<SharedBufferHandle>|) as an "out" parameter. // // An exception are some of the |...Raw()| functions. E.g., |CloseRaw()| takes a -// |Handle|, leaving the user to discard the handle. +// |Handle|, leaving the user to discard the wrapper. // -// More significantly, |WriteMessageRaw()| exposes the full API complexity of -// |MojoWriteMessage()| (but doesn't require any extra overhead). It takes a raw -// array of |Handle|s as input, and takes ownership of them (i.e., invalidates -// them) on *success* (but not on failure). There are a number of reasons for -// this. First, C++03 |std::vector|s cannot contain the move-only -// |Scoped...Handle|s. Second, |std::vector|s impose extra overhead -// (necessitating heap-allocation of the buffer). Third, |std::vector|s wouldn't -// provide the desired level of flexibility/safety: a vector of handles would -// have to be all of the same type (probably |Handle|/|ScopedHandle|). Fourth, -// it's expected to not be used directly, but instead be used by generated -// bindings. -// -// Other |...Raw()| functions expose similar rough edges, e.g., dealing with raw -// pointers (and lengths) instead of taking |std::vector|s or similar. - // ScopedHandleBase ------------------------------------------------------------ // Scoper for the actual handle types defined further below. It's move-only, diff --git a/third_party/mojo/src/mojo/public/cpp/system/macros.h b/third_party/mojo/src/mojo/public/cpp/system/macros.h index f2bd0bc..8c79989 100644 --- a/third_party/mojo/src/mojo/public/cpp/system/macros.h +++ b/third_party/mojo/src/mojo/public/cpp/system/macros.h @@ -2,15 +2,15 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -#ifndef MOJO_PUBLIC_CPP_SYSTEM_MACROS_H_ -#define MOJO_PUBLIC_CPP_SYSTEM_MACROS_H_ - -#include "mojo/public/c/system/macros.h" - // Define a set of C++ specific macros. // Mojo C++ API users can assume that mojo/public/cpp/system/macros.h // includes mojo/public/c/system/macros.h. +#ifndef MOJO_PUBLIC_CPP_SYSTEM_MACROS_H_ +#define MOJO_PUBLIC_CPP_SYSTEM_MACROS_H_ + +#include "mojo/public/c/system/macros.h" // Symbols exposed. + // A macro to disallow the copy constructor and operator= functions. // This should be used in the private: declarations for a class. #define MOJO_DISALLOW_COPY_AND_ASSIGN(TypeName) \ diff --git a/third_party/mojo/src/mojo/public/cpp/system/message_pipe.h b/third_party/mojo/src/mojo/public/cpp/system/message_pipe.h index b41469e..e7a1e35 100644 --- a/third_party/mojo/src/mojo/public/cpp/system/message_pipe.h +++ b/third_party/mojo/src/mojo/public/cpp/system/message_pipe.h @@ -2,6 +2,13 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. +// This file provides a C++ wrapping around the Mojo C API for message pipes, +// replacing the prefix of "Mojo" with a "mojo" namespace, and using more +// strongly-typed representations of |MojoHandle|s. +// +// Please see "mojo/public/c/system/message_pipe.h" for complete documentation +// of the API. + #ifndef MOJO_PUBLIC_CPP_SYSTEM_MESSAGE_PIPE_H_ #define MOJO_PUBLIC_CPP_SYSTEM_MESSAGE_PIPE_H_ @@ -13,8 +20,8 @@ namespace mojo { -// MessagePipeHandle ----------------------------------------------------------- - +// A strongly-typed representation of a |MojoHandle| to one end of a message +// pipe. class MessagePipeHandle : public Handle { public: MessagePipeHandle() {} @@ -30,6 +37,8 @@ typedef ScopedHandleBase<MessagePipeHandle> ScopedMessagePipeHandle; static_assert(sizeof(ScopedMessagePipeHandle) == sizeof(MessagePipeHandle), "Bad size for C++ ScopedMessagePipeHandle"); +// Creates a message pipe. See |MojoCreateMessagePipe()| for complete +// documentation. inline MojoResult CreateMessagePipe(const MojoCreateMessagePipeOptions* options, ScopedMessagePipeHandle* message_pipe0, ScopedMessagePipeHandle* message_pipe1) { @@ -46,9 +55,16 @@ inline MojoResult CreateMessagePipe(const MojoCreateMessagePipeOptions* options, return rv; } -// These "raw" versions fully expose the underlying API, but don't help with -// ownership of handles (especially when writing messages). -// TODO(vtl): Write "baked" versions. +// The following "...Raw" versions fully expose the underlying API, and don't +// help with ownership of handles (especially when writing messages). It is +// expected that in most cases these methods will be called through generated +// bindings anyway. +// TODO(vtl): Write friendlier versions of these functions (using scoped +// handles and/or vectors) if there is a demonstrated need for them. + +// Writes to a message pipe. If handles are attached, on success the handles +// will no longer be valid (the receiver will receive equivalent, but logically +// different, handles). See |MojoWriteMessage()| for complete documentation. inline MojoResult WriteMessageRaw(MessagePipeHandle message_pipe, const void* bytes, uint32_t num_bytes, @@ -59,6 +75,8 @@ inline MojoResult WriteMessageRaw(MessagePipeHandle message_pipe, message_pipe.value(), bytes, num_bytes, handles, num_handles, flags); } +// Reads from a message pipe. See |MojoReadMessage()| for complete +// documentation. inline MojoResult ReadMessageRaw(MessagePipeHandle message_pipe, void* bytes, uint32_t* num_bytes, diff --git a/third_party/mojo/src/mojo/public/dart/application.dart b/third_party/mojo/src/mojo/public/dart/application.dart new file mode 100644 index 0000000..80cc144 --- /dev/null +++ b/third_party/mojo/src/mojo/public/dart/application.dart @@ -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. + +library application; + +import 'dart:async'; +import 'dart:convert'; +import 'dart:mojo_bindings' as bindings; +import 'dart:mojo_core' as core; +import 'dart:typed_data'; + +import 'package:mojo/public/interfaces/application/application.mojom.dart' as application_mojom; +import 'package:mojo/public/interfaces/application/service_provider.mojom.dart' as service_provider; +import 'package:mojo/public/interfaces/application/shell.mojom.dart' as shell_mojom; + +part 'src/application.dart'; +part 'src/service_provider.dart'; diff --git a/third_party/mojo/src/mojo/public/dart/bindings.dart b/third_party/mojo/src/mojo/public/dart/bindings.dart index 0637737..ef481a1 100644 --- a/third_party/mojo/src/mojo/public/dart/bindings.dart +++ b/third_party/mojo/src/mojo/public/dart/bindings.dart @@ -9,8 +9,8 @@ import 'dart:convert'; import 'dart:mojo_core' as core; import 'dart:typed_data'; -part 'src/client.dart'; part 'src/codec.dart'; -part 'src/interface.dart'; part 'src/message.dart'; +part 'src/proxy.dart'; part 'src/struct.dart'; +part 'src/stub.dart'; diff --git a/third_party/mojo/src/mojo/public/dart/src/application.dart b/third_party/mojo/src/mojo/public/dart/src/application.dart new file mode 100644 index 0000000..55002f5e --- /dev/null +++ b/third_party/mojo/src/mojo/public/dart/src/application.dart @@ -0,0 +1,121 @@ +// 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. + +part of application; + +class _ApplicationImpl extends application_mojom.Application { + shell_mojom.ShellProxy shell; + Application _application; + + _ApplicationImpl( + Application application, core.MojoMessagePipeEndpoint endpoint) + : _application = application, super(endpoint) { + super.delegate = this; + } + + _ApplicationImpl.fromHandle(Application application, core.MojoHandle handle) + : _application = application, super.fromHandle(handle) { + super.delegate = this; + } + + void initialize(shell_mojom.ShellProxy shellProxy, List<String> args) { + assert(shell == null); + shell = shellProxy; + _application.initialize(args); + } + + void acceptConnection( + String requestorUrl, + service_provider.ServiceProviderStub services, + service_provider.ServiceProviderProxy exposedServices) => + _application._acceptConnection(requestorUrl, services, exposedServices); + + void requestQuit() => _application._requestQuitAndClose(); + + void close() => shell.close(); +} + +// TODO(zra): Better documentation and examples. +// To implement, do the following: +// - Optionally override acceptConnection() if services are to be provided. +// The override should assign a factory function to the passed in +// ServiceProvider's |factory| field, and then call listen on the +// ServiceProvider. The factory function should take a MojoMessagePipeEndpoint +// and return an object that implements the requested interface. +// - Optionally override initialize() where needed. +// - Optionally override requestClose() to clean up state specific to your +// application. +// To use an Application: +// - Call listen() on a newly created Application to begin providing services. +// - Call connectToService() to request services from the Shell. +// - Call close() to close connections to any requested ServiceProviders and the +// Shell. +abstract class Application { + _ApplicationImpl _applicationImpl; + List<service_provider.ServiceProviderProxy> _proxies; + List<ServiceProvider> _serviceProviders; + + Application(core.MojoMessagePipeEndpoint endpoint) { + _proxies = []; + _serviceProviders = []; + _applicationImpl = new _ApplicationImpl(this, endpoint); + } + + Application.fromHandle(core.MojoHandle appHandle) { + _proxies = []; + _serviceProviders = []; + _applicationImpl = new _ApplicationImpl.fromHandle(this, appHandle); + } + + void initialize(List<String> args) {} + + void connectToService(String url, bindings.Proxy proxy) { + assert(!proxy.isBound); + var endpoint = _connectToServiceHelper(url, proxy.name); + proxy.bind(endpoint); + } + + void requestQuit() {} + + listen() => _applicationImpl.listen(); + + void _requestQuitAndClose() { + requestQuit(); + close(); + } + + void close() { + assert(_applicationImpl != null); + _proxies.forEach((c) => c.close()); + _proxies.clear(); + _serviceProviders.forEach((sp) => sp.close()); + _serviceProviders.clear(); + _applicationImpl.close(); + } + + void _acceptConnection( + String requestorUrl, + service_provider.ServiceProviderStub services, + service_provider.ServiceProviderProxy exposedServices) { + var serviceProvider = new ServiceProvider(services, exposedServices); + _serviceProviders.add(serviceProvider); + acceptConnection(requestorUrl, serviceProvider); + } + + void acceptConnection(String requestorUrl, ServiceProvider serviceProvider) {} + + core.MojoMessagePipeEndpoint _connectToServiceHelper( + String url, String service) { + var applicationPipe = new core.MojoMessagePipe(); + var proxyEndpoint = applicationPipe.endpoints[0]; + var applicationEndpoint = applicationPipe.endpoints[1]; + var serviceProviderProxy = + new service_provider.ServiceProviderProxy.unbound(); + _applicationImpl.shell.connectToApplication( + url, serviceProviderProxy, null); + serviceProviderProxy.connectToService(service, applicationEndpoint); + _proxies.add(serviceProviderProxy); + return proxyEndpoint; + } +} diff --git a/third_party/mojo/src/mojo/public/dart/src/codec.dart b/third_party/mojo/src/mojo/public/dart/src/codec.dart index d5f5b90..7ad8370 100644 --- a/third_party/mojo/src/mojo/public/dart/src/codec.dart +++ b/third_party/mojo/src/mojo/public/dart/src/codec.dart @@ -56,7 +56,7 @@ class Encoder { Encoder._fromBuffer(_EncoderBuffer buffer) : _buffer = buffer, - _base = buffer.extent; + _base = buffer.extent; Encoder getEncoderAtOffset(DataHeader dataHeader) { var result = new Encoder._fromBuffer(_buffer); @@ -154,7 +154,7 @@ class Encoder { core.MojoSharedBuffer value, int offset, bool nullable) => encodeHandle(value != null ? value.handle : null, offset, nullable); - void encodeInterface(Interface interface, int offset, bool nullable) { + void encodeInterface(Stub interface, int offset, bool nullable) { if (interface == null) { encodeInvalideHandle(offset, nullable); return; @@ -164,7 +164,7 @@ class Encoder { encodeMessagePipeHandle(pipe.endpoints[1], offset, nullable); } - void encodeInterfaceRequest(Client client, int offset, bool nullable) { + void encodeInterfaceRequest(Proxy client, int offset, bool nullable) { if (client == null) { encodeInvalideHandle(offset, nullable); return; @@ -371,7 +371,7 @@ class Encoder { value, offset, nullability, expectedLength); void encodeInterfaceRequestArray( - List<Client> value, + List<Proxy> value, int offset, int nullability, int expectedLength) => @@ -380,7 +380,7 @@ class Encoder { value, offset, nullability, expectedLength); void encodeInterfaceArray( - List<Interface> value, + List<Stub> value, int offset, int nullability, int expectedLength) => @@ -507,13 +507,13 @@ class Decoder { core.MojoSharedBuffer decodeSharedBufferHandle(int offset, bool nullable) => new core.MojoSharedBuffer(decodeHandle(offset, nullable)); - Client decodeServiceInterface( + Proxy decodeServiceInterface( int offset, bool nullable, Function clientFactory) { var endpoint = decodeMessagePipeHandle(offset, nullable); return endpoint.handle.isValid ? clientFactory(endpoint) : null; } - Interface decodeInterfaceRequest( + Stub decodeInterfaceRequest( int offset, bool nullable, Function interfaceFactory) { var endpoint = decodeMessagePipeHandle(offset, nullable); return endpoint.handle.isValid ? interfaceFactory(endpoint) : null; @@ -699,7 +699,7 @@ class Decoder { _handleArrayDecodeHelper((d, o, n) => d.decodeSharedBufferHandle(o, n), offset, nullability, expectedLength); - List<Interface> decodeInterfaceRequestArray( + List<Stub> decodeInterfaceRequestArray( int offset, int nullability, int expectedLength, @@ -708,7 +708,7 @@ class Decoder { (d, o, n) => d.decodeInterfaceRequest(o, n, interfaceFactory), offset, nullability, expectedLength); - List<Client> decodeServiceInterfaceArray( + List<Proxy> decodeServiceInterfaceArray( int offset, int nullability, int expectedLength, diff --git a/third_party/mojo/src/mojo/public/dart/src/event_stream.dart b/third_party/mojo/src/mojo/public/dart/src/event_stream.dart index 7016b31..610285a 100644 --- a/third_party/mojo/src/mojo/public/dart/src/event_stream.dart +++ b/third_party/mojo/src/mojo/public/dart/src/event_stream.dart @@ -16,7 +16,7 @@ class MojoEventStream extends Stream<int> { // events. SendPort _sendPort; - // The receive port on which we listen and receive events from the handle + // The receive port on which we listen and receive events from the handle // watcher. ReceivePort _receivePort; @@ -118,8 +118,11 @@ class MojoEventStream extends Stream<int> { String toString() => "$_handle"; } +abstract class Listener { + StreamSubscription<List<int>> listen(); +} -class MojoEventStreamListener { +class MojoEventStreamListener implements Listener { MojoMessagePipeEndpoint _endpoint; MojoEventStream _eventStream; bool _isOpen = false; @@ -155,7 +158,7 @@ class MojoEventStreamListener { _isOpen = false; } - StreamSubscription<int> listen() { + StreamSubscription<List<int>> listen() { _isOpen = true; return _eventStream.listen((List<int> event) { var signalsWatched = new MojoHandleSignals(event[0]); diff --git a/third_party/mojo/src/mojo/public/dart/src/client.dart b/third_party/mojo/src/mojo/public/dart/src/proxy.dart index f8ec73c..f662f3a 100644 --- a/third_party/mojo/src/mojo/public/dart/src/client.dart +++ b/third_party/mojo/src/mojo/public/dart/src/proxy.dart @@ -4,19 +4,19 @@ part of bindings; -abstract class Client extends core.MojoEventStreamListener { +abstract class Proxy extends core.MojoEventStreamListener { Map<int, Completer> _completerMap; int _nextId = 0; - Client(core.MojoMessagePipeEndpoint endpoint) : + Proxy(core.MojoMessagePipeEndpoint endpoint) : _completerMap = {}, super(endpoint); - Client.fromHandle(core.MojoHandle handle) : + Proxy.fromHandle(core.MojoHandle handle) : _completerMap = {}, super.fromHandle(handle); - Client.unbound() : + Proxy.unbound() : _completerMap = {}, super.unbound(); @@ -37,7 +37,7 @@ abstract class Client extends core.MojoEventStreamListener { } void handleWrite() { - throw 'Unexpected write signal in client.'; + throw 'Unexpected write signal in proxy.'; } void sendMessage(Struct message, int name) { diff --git a/third_party/mojo/src/mojo/public/dart/src/service_provider.dart b/third_party/mojo/src/mojo/public/dart/src/service_provider.dart new file mode 100644 index 0000000..deac6bc --- /dev/null +++ b/third_party/mojo/src/mojo/public/dart/src/service_provider.dart @@ -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. + +part of application; + +typedef core.Listener ListenerFactory(core.MojoMessagePipeEndpoint endpoint); + +class ServiceProvider extends service_provider.ServiceProvider { + ListenerFactory factory; + + service_provider.ServiceProviderProxy _proxy; + + ServiceProvider( + service_provider.ServiceProviderStub services, + [service_provider.ServiceProviderProxy exposedServices = null]) + : _proxy = exposedServices, + super.fromStub(services) { + delegate = this; + } + + connectToService(String interfaceName, core.MojoMessagePipeEndpoint pipe) => + factory(pipe).listen(); + + requestService(String name, bindings.Proxy clientImpl) { + assert(_proxy != null); + assert(!clientImpl.isBound); + var pipe = new core.MojoMessagePipe(); + clientImpl.bind(pipe.endpoints[0]); + _proxy.connectToService(name, pipe.endpoints[1]); + } + + close() { + if (_proxy != null) { + _proxy.close(); + _proxy = null; + } + } +} diff --git a/third_party/mojo/src/mojo/public/dart/src/interface.dart b/third_party/mojo/src/mojo/public/dart/src/stub.dart index 0e73214..b4ab293 100644 --- a/third_party/mojo/src/mojo/public/dart/src/interface.dart +++ b/third_party/mojo/src/mojo/public/dart/src/stub.dart @@ -4,15 +4,15 @@ part of bindings; -abstract class Interface extends core.MojoEventStreamListener { +abstract class Stub extends core.MojoEventStreamListener { int _outstandingResponseFutures = 0; bool _isClosing = false; - Interface(core.MojoMessagePipeEndpoint endpoint) : super(endpoint); + Stub(core.MojoMessagePipeEndpoint endpoint) : super(endpoint); - Interface.fromHandle(core.MojoHandle handle) : super.fromHandle(handle); + Stub.fromHandle(core.MojoHandle handle) : super.fromHandle(handle); - Interface.unbound() : super.unbound(); + Stub.unbound() : super.unbound(); Future<Message> handleMessage(ServiceMessage message); @@ -85,19 +85,4 @@ abstract class Interface extends core.MojoEventStreamListener { var header = new MessageHeader.withRequestId(name, flags, id); return response.serializeWithHeader(header); } - - void sendMessage(Struct message, int name) { - var header = new MessageHeader(name); - var serviceMessage = message.serializeWithHeader(header); - endpoint.write(serviceMessage.buffer, - serviceMessage.buffer.lengthInBytes, - serviceMessage.handles); - if (!endpoint.status.isOk) { - throw "message pipe write failed: ${endpoint.status}"; - } - } - - Future sendMessageWithRequestId(Struct response, int name, int id) { - throw "The client interface should not expect a response"; - } } diff --git a/third_party/mojo/src/mojo/public/go/bindings/decoder.go b/third_party/mojo/src/mojo/public/go/bindings/decoder.go new file mode 100644 index 0000000..2dccd0a --- /dev/null +++ b/third_party/mojo/src/mojo/public/go/bindings/decoder.go @@ -0,0 +1,315 @@ +// 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 bindings + +import ( + "encoding/binary" + "fmt" + "math" + + "mojo/public/go/system" +) + +// Decoder is a helper to decode mojo complex elements from mojo archive format. +type Decoder struct { + // Buffer containing data to decode. + buf []byte + + // Index of the first unclaimed byte in buf. + end int + + // Array containing handles to decode. + handles []system.UntypedHandle + + // The first unclaimed handle index. + nextHandle int + + // A stack of encoding states matching current one-level value stack + // of the decoding data structure. + stateStack []encodingState +} + +// NewDecoder returns a decoder that will decode structured data from provided +// byte array and with handles. +func NewDecoder(bytes []byte, handles []system.UntypedHandle) *Decoder { + return &Decoder{buf: bytes, handles: handles} +} + +// claimData claims a block of |size| bytes for a one-level value. +func (d *Decoder) claimData(size int) error { + if d.end+size > len(d.buf) { + return fmt.Errorf("data buffer is too small") + } + d.end += size + return nil +} + +func (d *Decoder) claimHandle(index int) (system.UntypedHandle, error) { + if index >= len(d.handles) { + return nil, fmt.Errorf("trying to access non present handle") + } + if index < d.nextHandle { + return nil, fmt.Errorf("trying to access handle out of order") + } + d.nextHandle = index + 1 + return d.handles[index], nil +} + +func (d *Decoder) popState() { + if len(d.stateStack) != 0 { + d.stateStack = d.stateStack[:len(d.stateStack)-1] + } +} + +func (d *Decoder) pushState(header DataHeader, elementBitSize uint32) error { + oldEnd := d.end + if err := d.claimData(int(header.Size - dataHeaderSize)); err != nil { + return err + } + d.stateStack = append(d.stateStack, encodingState{ + offset: oldEnd, + limit: d.end, + elementBitSize: elementBitSize, + elements: header.Elements, + }) + return nil +} + +// state returns state of the top-level value. +func (d *Decoder) state() *encodingState { + if len(d.stateStack) == 0 { + return nil + } + return &d.stateStack[len(d.stateStack)-1] +} + +// StartArray starts decoding an array and reads its data header, +// returning number of elements declared in data header. +// Note: it doesn't read a pointer to the encoded array. +// Call |Finish()| after reading all array elements. +func (d *Decoder) StartArray(elementBitSize uint32) (uint32, error) { + header, err := d.readDataHeader() + if err != nil { + return 0, err + } + if got, want := int(header.Size), dataHeaderSize+bytesForBits(uint64(header.Elements)*uint64(elementBitSize)); got < want { + return 0, fmt.Errorf("data header size is too small: is %d, but should be at least %d", got, want) + } + if err := d.pushState(header, elementBitSize); err != nil { + return 0, err + } + return header.Elements, nil +} + +// StartMap starts decoding a map and reads its data header. +// Note: it doesn't read a pointer to the encoded map. +// Call |Finish()| after reading keys array and values array. +func (d *Decoder) StartMap() error { + header, err := d.readDataHeader() + if err != nil { + return err + } + if header != mapHeader { + return fmt.Errorf("invalid map header: %v", header) + } + if err := d.pushState(header, pointerBitSize); err != nil { + return err + } + return nil +} + +// StartArray starts decoding a struct and reads its data header, +// returning number of fields declared in data header. +// Note: it doesn't read a pointer to the encoded struct. +// Call |Finish()| after reading all fields. +func (d *Decoder) StartStruct() (uint32, error) { + header, err := d.readDataHeader() + if err != nil { + return 0, err + } + if header.Size < dataHeaderSize { + return 0, fmt.Errorf("data header size is too small: is %d, but should be at least %d", header.Size, dataHeaderSize) + } + if err := d.pushState(header, 0); err != nil { + return 0, err + } + return header.Elements, nil +} + +func (d *Decoder) readDataHeader() (DataHeader, error) { + if err := d.claimData(dataHeaderSize); err != nil { + return DataHeader{}, err + } + oldEnd := d.end - dataHeaderSize + header := DataHeader{ + Size: binary.LittleEndian.Uint32(d.buf[oldEnd:]), + Elements: binary.LittleEndian.Uint32(d.buf[oldEnd+4:]), + } + return header, nil +} + +// Finish indicates the decoder that you have finished reading elements of +// a one-level value. +func (d *Decoder) Finish() error { + if d.state() == nil { + return fmt.Errorf("state stack is empty") + } + if d.state().elementBitSize != 0 && d.state().elementsProcessed != d.state().elements { + return fmt.Errorf("unexpected number of elements read: defined in header %d, but read %d", d.state().elements, d.state().elementsProcessed) + } + d.popState() + return nil +} + +// ReadBool reads a bool value. +func (d *Decoder) ReadBool() (bool, error) { + if err := ensureElementBitSizeAndCapacity(d.state(), 1); err != nil { + return false, err + } + value := ((d.buf[d.state().offset] >> d.state().bitOffset) & 1) == 1 + d.state().skipBits(1) + d.state().elementsProcessed++ + return value, nil +} + +// ReadInt8 reads an int8 value. +func (d *Decoder) ReadInt8() (int8, error) { + value, err := d.ReadUint8() + return int8(value), err +} + +// ReadUint8 reads an uint8 value. +func (d *Decoder) ReadUint8() (uint8, error) { + if err := ensureElementBitSizeAndCapacity(d.state(), 8); err != nil { + return 0, err + } + value := d.buf[d.state().offset] + d.state().skipBytes(1) + d.state().elementsProcessed++ + return value, nil +} + +// ReadInt16 reads an int16 value. +func (d *Decoder) ReadInt16() (int16, error) { + value, err := d.ReadUint16() + return int16(value), err +} + +// ReadUint16 reads an uint16 value. +func (d *Decoder) ReadUint16() (uint16, error) { + if err := ensureElementBitSizeAndCapacity(d.state(), 16); err != nil { + return 0, err + } + d.state().offset = align(d.state().offset, 2) + value := binary.LittleEndian.Uint16(d.buf[d.state().offset:]) + d.state().skipBytes(2) + d.state().elementsProcessed++ + return value, nil +} + +// ReadInt32 reads an int32 value. +func (d *Decoder) ReadInt32() (int32, error) { + value, err := d.ReadUint32() + return int32(value), err +} + +// ReadUint32 reads an uint32 value. +func (d *Decoder) ReadUint32() (uint32, error) { + if err := ensureElementBitSizeAndCapacity(d.state(), 32); err != nil { + return 0, err + } + d.state().offset = align(d.state().offset, 4) + value := binary.LittleEndian.Uint32(d.buf[d.state().offset:]) + d.state().skipBytes(4) + d.state().elementsProcessed++ + return value, nil +} + +// ReadInt64 reads an int64 value. +func (d *Decoder) ReadInt64() (int64, error) { + value, err := d.ReadUint64() + return int64(value), err +} + +// ReadUint64 reads an uint64 value. +func (d *Decoder) ReadUint64() (uint64, error) { + if err := ensureElementBitSizeAndCapacity(d.state(), 64); err != nil { + return 0, err + } + d.state().offset = align(d.state().offset, 8) + value := binary.LittleEndian.Uint64(d.buf[d.state().offset:]) + d.state().skipBytes(8) + d.state().elementsProcessed++ + return value, nil +} + +// ReadFloat32 reads a float32 value. +func (d *Decoder) ReadFloat32() (float32, error) { + bits, err := d.ReadUint32() + return math.Float32frombits(bits), err +} + +// ReadFloat64 reads a float64 value. +func (d *Decoder) ReadFloat64() (float64, error) { + bits, err := d.ReadUint64() + return math.Float64frombits(bits), err +} + +// ReadString reads a string value. It doesn't read a pointer to the encoded +// string. +func (d *Decoder) ReadString() (string, error) { + length, err := d.StartArray(8) + if err != nil { + return "", err + } + var bytes []byte + for i := uint32(0); i < length; i++ { + b, err := d.ReadUint8() + if err != nil { + return "", err + } + bytes = append(bytes, b) + } + if err := d.Finish(); err != nil { + return "", err + } + return string(bytes), nil +} + +// ReadPointer reads a pointer and reassigns first unclaimed byte index if the +// pointer is not null. +func (d *Decoder) ReadPointer() (uint64, error) { + oldEnd := d.state().offset + pointer, err := d.ReadUint64() + if err != nil { + return pointer, err + } + if pointer == 0 { + return pointer, nil + } + + newEnd := uint64(oldEnd) + pointer + if newEnd >= uint64(len(d.buf)) { + return 0, fmt.Errorf("trying to access out of range memory") + } + if newEnd < uint64(d.end) { + return 0, fmt.Errorf("trying to access memory out of order") + } + if newEnd%8 != 0 { + return 0, fmt.Errorf("incorrect pointer data alignment: %d", newEnd) + } + d.claimData(d.end - int(newEnd)) + return pointer, nil +} + +// ReadMessagePipeHandle reads a message pipe handle. +func (d *Decoder) ReadMessagePipeHandle() (system.MessagePipeHandle, error) { + handleIndex, err := d.ReadUint32() + if err != nil { + return nil, err + } + untypedHandle, err := d.claimHandle(int(handleIndex)) + return untypedHandle.ToMessagePipeHandle(), err +} diff --git a/third_party/mojo/src/mojo/public/go/bindings/encoder.go b/third_party/mojo/src/mojo/public/go/bindings/encoder.go new file mode 100644 index 0000000..210d9b0 --- /dev/null +++ b/third_party/mojo/src/mojo/public/go/bindings/encoder.go @@ -0,0 +1,319 @@ +// 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 bindings + +import ( + "encoding/binary" + "fmt" + "math" + + "mojo/public/go/system" +) + +// encodingState has information required to encode/decode a one-level value. +type encodingState struct { + // Index of the first unprocessed byte. + offset int + + // Index of the first unprocessed bit of buffer[offset] byte. + bitOffset uint32 + + // Index of the first byte after the claimed buffer block for the current + // one-level value. + limit int + + // Element size in bits of the current one-level array, 0 for other types. + elementBitSize uint32 + + // Number of elements declared in the data header for the current one-level + // value. + elements uint32 + + // Number of elements already encoded/decoded of the current one-level + // value. + elementsProcessed uint32 +} + +func (s *encodingState) skipBits(count uint32) { + s.bitOffset += count + s.offset += int(s.bitOffset >> 3) // equal to s.bitOffset / 8 + s.bitOffset &= 7 // equal to s.bitOffset % 8 +} + +func (s *encodingState) skipBytes(count int) { + s.bitOffset = 0 + s.offset += count +} + +// Encoder is a helper to encode mojo complex elements into mojo archive format. +type Encoder struct { + // Buffer containing encoded data. + buf []byte + + // Index of the first unclaimed byte in buf. + end int + + // Array containing encoded handles. + handles []system.UntypedHandle + + // A stack of encoder states matching current one-level value stack + // of the encoding data structure. + stateStack []encodingState +} + +func align(size, alignment int) int { + return ((size - 1) | (alignment - 1)) + 1 +} + +// bytesForBits returns minimum number of bytes required to store provided +// number of bits. +func bytesForBits(bits uint64) int { + return int((bits + 7) / 8) +} + +func ensureElementBitSizeAndCapacity(state *encodingState, bitSize uint32) error { + if state == nil { + return fmt.Errorf("empty state stack") + } + if state.elementBitSize > 0 && state.elementBitSize != bitSize { + return fmt.Errorf("unexpected element bit size: expected %d, but got %d", state.elementBitSize, bitSize) + } + if state.elementsProcessed >= state.elements { + return fmt.Errorf("can't process more than elements defined in header(%d)", state.elements) + } + byteSize := bytesForBits(uint64(state.bitOffset + bitSize)) + if align(state.offset+byteSize, byteSize) > state.limit { + return fmt.Errorf("buffer size limit exceeded") + } + return nil +} + +// claimData claims a block of |size| bytes for a one-level value, resizing +// buffer if needed. +func (e *Encoder) claimData(size int) { + e.end += size + if e.end < len(e.buf) { + return + } + newLen := e.end + if l := 2 * len(e.buf); newLen < l { + newLen = l + } + tmp := make([]byte, newLen) + copy(tmp, e.buf) + e.buf = tmp +} + +func (e *Encoder) popState() { + if len(e.stateStack) != 0 { + e.stateStack = e.stateStack[:len(e.stateStack)-1] + } +} + +func (e *Encoder) pushState(header DataHeader, elementBitSize uint32) { + oldEnd := e.end + e.claimData(align(int(header.Size), defaultAlignment)) + e.stateStack = append(e.stateStack, encodingState{ + offset: oldEnd, + limit: e.end, + elementBitSize: elementBitSize, + elements: header.Elements, + }) + e.writeDataHeader(header) +} + +// state returns encoder state of the top-level value. +func (e *Encoder) state() *encodingState { + if len(e.stateStack) == 0 { + return nil + } + return &e.stateStack[len(e.stateStack)-1] +} + +// NewEncoder returns a new instance of encoder. +func NewEncoder() *Encoder { + return &Encoder{} +} + +// StartArray starts encoding an array and writes its data header. +// Note: it doesn't write a pointer to the encoded array. +// Call |Finish()| after writing all array elements. +func (e *Encoder) StartArray(length, elementBitSize uint32) { + dataSize := dataHeaderSize + bytesForBits(uint64(length)*uint64(elementBitSize)) + header := DataHeader{uint32(dataSize), length} + e.pushState(header, elementBitSize) +} + +// StartMap starts encoding a map and writes its data header. +// Note: it doesn't write a pointer to the encoded map. +// Call |Finish()| after writing keys array and values array. +func (e *Encoder) StartMap() { + e.pushState(mapHeader, pointerBitSize) +} + +// StartStruct starts encoding a struct and writes its data header. +// Note: it doesn't write a pointer to the encoded struct. +// Call |Finish()| after writing all fields. +func (e *Encoder) StartStruct(size, numFields uint32) { + dataSize := dataHeaderSize + int(size) + header := DataHeader{uint32(dataSize), numFields} + e.pushState(header, 0) +} + +func (e *Encoder) writeDataHeader(header DataHeader) { + binary.LittleEndian.PutUint32(e.buf[e.state().offset:], header.Size) + binary.LittleEndian.PutUint32(e.buf[e.state().offset+4:], header.Elements) + e.state().offset += 8 +} + +// Finish indicates the encoder that you have finished writing elements of +// a one-level value. +func (e *Encoder) Finish() error { + if e.state() == nil { + return fmt.Errorf("state stack is empty") + } + if e.state().elementsProcessed != e.state().elements { + return fmt.Errorf("unexpected number of elements written: defined in header %d, but written %d", e.state().elements, e.state().elementsProcessed) + } + e.popState() + return nil +} + +// Data returns an encoded message with attached handles. +// Call this method after finishing encoding of a value. +func (e *Encoder) Data() ([]byte, []system.UntypedHandle, error) { + if len(e.stateStack) != 0 { + return nil, nil, fmt.Errorf("can't return data when encoder has non-empty state stack") + } + return e.buf[:e.end], e.handles, nil +} + +// WriteBool writes a bool value. +func (e *Encoder) WriteBool(value bool) error { + if err := ensureElementBitSizeAndCapacity(e.state(), 1); err != nil { + return err + } + if value { + e.buf[e.state().offset] |= 1 << e.state().bitOffset + } + e.state().skipBits(1) + e.state().elementsProcessed++ + return nil +} + +// WriteBool writes an int8 value. +func (e *Encoder) WriteInt8(value int8) error { + return e.WriteUint8(uint8(value)) +} + +// WriteUint8 writes an uint8 value. +func (e *Encoder) WriteUint8(value uint8) error { + if err := ensureElementBitSizeAndCapacity(e.state(), 8); err != nil { + return err + } + e.buf[e.state().offset] = value + e.state().skipBytes(1) + e.state().elementsProcessed++ + return nil +} + +// WriteInt16 writes an int16 value. +func (e *Encoder) WriteInt16(value int16) error { + return e.WriteUint16(uint16(value)) +} + +// WriteUint16 writes an uint16 value. +func (e *Encoder) WriteUint16(value uint16) error { + if err := ensureElementBitSizeAndCapacity(e.state(), 16); err != nil { + return err + } + e.state().offset = align(e.state().offset, 2) + binary.LittleEndian.PutUint16(e.buf[e.state().offset:], value) + e.state().skipBytes(2) + e.state().elementsProcessed++ + return nil +} + +// WriteInt32 writes an int32 value. +func (e *Encoder) WriteInt32(value int32) error { + return e.WriteUint32(uint32(value)) +} + +// WriteUint32 writes an uint32 value. +func (e *Encoder) WriteUint32(value uint32) error { + if err := ensureElementBitSizeAndCapacity(e.state(), 32); err != nil { + return err + } + e.state().offset = align(e.state().offset, 4) + binary.LittleEndian.PutUint32(e.buf[e.state().offset:], value) + e.state().skipBytes(4) + e.state().elementsProcessed++ + return nil +} + +// WriteInt64 writes an int64 value. +func (e *Encoder) WriteInt64(value int64) error { + return e.WriteUint64(uint64(value)) +} + +// WriteUint64 writes an uint64 value. +func (e *Encoder) WriteUint64(value uint64) error { + if err := ensureElementBitSizeAndCapacity(e.state(), 64); err != nil { + return err + } + e.state().offset = align(e.state().offset, 8) + binary.LittleEndian.PutUint64(e.buf[e.state().offset:], value) + e.state().skipBytes(8) + e.state().elementsProcessed++ + return nil +} + +// WriteFloat32 writes a float32 value. +func (e *Encoder) WriteFloat32(value float32) error { + return e.WriteUint32(math.Float32bits(value)) +} + +// WriteFloat64 writes a float64 value. +func (e *Encoder) WriteFloat64(value float64) error { + return e.WriteUint64(math.Float64bits(value)) +} + +// WriteNullPointer writes a null pointer. +func (e *Encoder) WriteNullPointer() error { + return e.WriteUint64(0) +} + +// WriteString writes a string value. It doesn't write a pointer to the encoded +// string. +func (e *Encoder) WriteString(value string) error { + bytes := []byte(value) + e.StartArray(uint32(len(bytes)), 8) + for _, b := range bytes { + if err := e.WriteUint8(b); err != nil { + return err + } + } + return e.Finish() +} + +// WritePointer writes a pointer to first unclaimed byte index. +func (e *Encoder) WritePointer() error { + return e.WriteUint64(uint64(e.end - e.state().offset)) +} + +// WriteInvalidHandle an invalid handle. +func (e *Encoder) WriteInvalidHandle() error { + return e.WriteInt32(-1) +} + +// WriteHandle writes a handle and invalidates the passed handle object. +func (e *Encoder) WriteHandle(handle system.Handle) error { + if !handle.IsValid() { + return fmt.Errorf("can't write an invalid handle") + } + UntypedHandle := handle.ToUntypedHandle() + e.handles = append(e.handles, UntypedHandle) + return e.WriteUint32(uint32(len(e.handles) - 1)) +} diff --git a/third_party/mojo/src/mojo/public/go/bindings/message.go b/third_party/mojo/src/mojo/public/go/bindings/message.go new file mode 100644 index 0000000..786ec47 --- /dev/null +++ b/third_party/mojo/src/mojo/public/go/bindings/message.go @@ -0,0 +1,164 @@ +// 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 bindings + +import ( + "fmt" + + "mojo/public/go/system" +) + +const ( + // Flag for a header of a simple message. + MessageNoFlag = 0 + + // Flag for a header of a message that expected a response. + MessageExpectsResponseFlag = 1 << 0 + + // Flag for a header of a message that is a response. + MessageIsResponseFlag = 1 << 1 + + dataHeaderSize = 8 + defaultAlignment = 8 + pointerBitSize = 64 +) + +var mapHeader DataHeader + +func init() { + mapHeader = DataHeader{24, 2} +} + +// Payload is an interface implemented by a mojo struct that can encode/decode +// itself into mojo archive format. +type Payload interface { + Encode(encoder *Encoder) error + Decode(decoder *Decoder) error +} + +// DataHeader is a header for a mojo complex element. +type DataHeader struct { + Size uint32 + Elements uint32 +} + +// MessageHeader is a header information for a message. +type MessageHeader struct { + Type uint32 + Flags uint32 + RequestId uint64 +} + +func (h *MessageHeader) Encode(encoder *Encoder) error { + encoder.StartStruct(h.dataSize(), h.numFields()) + if err := encoder.WriteUint32(h.Type); err != nil { + return err + } + if err := encoder.WriteUint32(h.Flags); err != nil { + return err + } + if h.RequestId != 0 { + if err := encoder.WriteUint64(h.RequestId); err != nil { + return err + } + } + return encoder.Finish() +} + +func (h *MessageHeader) Decode(decoder *Decoder) error { + numFields, err := decoder.StartStruct() + if err != nil { + return err + } + if numFields < 2 || numFields > 3 { + return fmt.Errorf("Invalid message header: it should have 2 or 3 fileds, but has %d", numFields) + } + if h.Type, err = decoder.ReadUint32(); err != nil { + return err + } + if h.Flags, err = decoder.ReadUint32(); err != nil { + return err + } + if numFields == 3 { + if h.Flags != MessageExpectsResponseFlag && h.Flags != MessageIsResponseFlag { + return fmt.Errorf("Message header flags(%v) should be MessageExpectsResponseFlag or MessageIsResponseFlag", h.Flags) + } + if h.RequestId, err = decoder.ReadUint64(); err != nil { + return err + } + } else { + if h.Flags != MessageNoFlag { + return fmt.Errorf("Message header flags(%v) should be MessageNoFlag", h.Flags) + } + } + return decoder.Finish() +} + +func (h *MessageHeader) dataSize() uint32 { + var size uint32 + size = 2 * 4 + if h.RequestId != 0 { + size += 8 + } + return size +} + +func (h *MessageHeader) numFields() uint32 { + if h.RequestId != 0 { + return 3 + } else { + return 2 + } +} + +// Message is a a raw message to be sent/received from a message pipe handle +// which contains a message header. +type Message struct { + Header MessageHeader + Bytes []byte + Handles []system.UntypedHandle + Payload []byte +} + +func newMessage(header MessageHeader, bytes []byte, handles []system.UntypedHandle) *Message { + return &Message{header, bytes, handles, bytes[header.dataSize()+dataHeaderSize:]} +} + +// DecodePayload decodes the provided payload from the message. +func (m *Message) DecodePayload(payload Payload) error { + decoder := NewDecoder(m.Payload, m.Handles) + if err := payload.Decode(decoder); err != nil { + return err + } + return nil +} + +// EncodeMessage returns a message with provided header that has provided +// payload encoded in mojo archive format. +func EncodeMessage(header MessageHeader, payload Payload) (*Message, error) { + encoder := NewEncoder() + if err := header.Encode(encoder); err != nil { + return nil, err + } + if err := payload.Encode(encoder); err != nil { + return nil, err + } + if bytes, handles, err := encoder.Data(); err != nil { + return nil, err + } else { + return newMessage(header, bytes, handles), nil + } +} + +// ParseMessage parses message header from byte buffer with attached handles +// and returnes parsed message. +func ParseMessage(bytes []byte, handles []system.UntypedHandle) (*Message, error) { + decoder := NewDecoder(bytes, []system.UntypedHandle{}) + var header MessageHeader + if err := header.Decode(decoder); err != nil { + return nil, err + } + return newMessage(header, bytes, handles), nil +} diff --git a/third_party/mojo/src/mojo/public/interfaces/application/application.mojom b/third_party/mojo/src/mojo/public/interfaces/application/application.mojom index b1508a8e..cffcb1f 100644 --- a/third_party/mojo/src/mojo/public/interfaces/application/application.mojom +++ b/third_party/mojo/src/mojo/public/interfaces/application/application.mojom @@ -5,6 +5,7 @@ module mojo; import "mojo/public/interfaces/application/service_provider.mojom"; +import "mojo/public/interfaces/application/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 @@ -13,7 +14,7 @@ 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. - Initialize(array<string>? args); + Initialize(Shell shell, array<string>? args); // Called when another application (identified by |requestor_url|) attempts to // open a connection to this application. diff --git a/third_party/mojo/src/mojo/public/interfaces/application/shell.mojom b/third_party/mojo/src/mojo/public/interfaces/application/shell.mojom index 7804b9f..6fc0cca 100644 --- a/third_party/mojo/src/mojo/public/interfaces/application/shell.mojom +++ b/third_party/mojo/src/mojo/public/interfaces/application/shell.mojom @@ -4,12 +4,10 @@ module mojo; -import "mojo/public/interfaces/application/application.mojom"; import "mojo/public/interfaces/application/service_provider.mojom"; // An interface through which a Mojo application may communicate with the Mojo // system and request connections to other applications. -[Client=Application] interface Shell { // Establishes a connection with another application (identified by // |application_url|) through which the calling application and the other diff --git a/third_party/mojo/src/mojo/public/interfaces/bindings/tests/math_calculator.mojom b/third_party/mojo/src/mojo/public/interfaces/bindings/tests/math_calculator.mojom index f99bb07..7d1b171 100644 --- a/third_party/mojo/src/mojo/public/interfaces/bindings/tests/math_calculator.mojom +++ b/third_party/mojo/src/mojo/public/interfaces/bindings/tests/math_calculator.mojom @@ -5,13 +5,8 @@ [JavaPackage="org.chromium.mojo.bindings.test.mojom.math"] module math; -[Client=CalculatorUI] interface Calculator { - Clear@0(); - Add@1(double value@0); - Multiply@2(double value@0); -}; - -interface CalculatorUI { - Output@0(double value@0); + Clear@0() => (double value@0); + Add@1(double value@0) => (double value@0); + Multiply@2(double value@0) => (double value@0); }; diff --git a/third_party/mojo/src/mojo/public/java/bindings/src/org/chromium/mojo/bindings/ConnectionErrorHandler.java b/third_party/mojo/src/mojo/public/java/bindings/src/org/chromium/mojo/bindings/ConnectionErrorHandler.java index abb0bdf..f601fb8 100644 --- a/third_party/mojo/src/mojo/public/java/bindings/src/org/chromium/mojo/bindings/ConnectionErrorHandler.java +++ b/third_party/mojo/src/mojo/public/java/bindings/src/org/chromium/mojo/bindings/ConnectionErrorHandler.java @@ -10,6 +10,6 @@ import org.chromium.mojo.system.MojoException; * A {@link ConnectionErrorHandler} is notified of an error happening while using the bindings over * message pipes. */ -interface ConnectionErrorHandler { +public interface ConnectionErrorHandler { public void onConnectionError(MojoException e); } diff --git a/third_party/mojo/src/mojo/public/js/bindings.js b/third_party/mojo/src/mojo/public/js/bindings.js index 5f85860..44aa9f4 100644 --- a/third_party/mojo/src/mojo/public/js/bindings.js +++ b/third_party/mojo/src/mojo/public/js/bindings.js @@ -4,7 +4,8 @@ define("mojo/public/js/bindings", [ "mojo/public/js/router", -], function(router) { + "mojo/public/js/core", +], function(router, core) { var Router = router.Router; @@ -17,10 +18,12 @@ define("mojo/public/js/bindings", [ this.receiver = receiver; } + // TODO(hansmuller): remove then after 'Client=' has been removed from Mojom. ProxyProperties.prototype.getLocalDelegate = function() { return this.local && StubBindings(this.local).delegate; } + // TODO(hansmuller): remove then after 'Client=' has been removed from Mojom. ProxyProperties.prototype.setLocalDelegate = function(impl) { if (this.local) StubBindings(this.local).delegate = impl; @@ -28,12 +31,31 @@ define("mojo/public/js/bindings", [ throw new Error("no stub object"); } + function connectionHandle(connection) { + return connection && + connection.router && + connection.router.connector_ && + connection.router.connector_.handle_; + } + + ProxyProperties.prototype.close = function() { + var handle = connectionHandle(this.connection); + if (handle) + core.close(handle); + } + // Public stub class properties that are managed at runtime by the JS // bindings. See StubBindings below. function StubProperties(delegate) { this.delegate = delegate; } + StubProperties.prototype.close = function() { + var handle = connectionHandle(this.connection); + if (handle) + core.close(handle); + } + // The base class for generated proxy classes. function ProxyBase(receiver) { this[kProxyProperties] = new ProxyProperties(receiver); @@ -48,6 +70,9 @@ define("mojo/public/js/bindings", [ this[kStubProperties] = new StubProperties(delegate); } + // TODO(hansmuller): remove everything except the connection property doc + // after 'Client=' has been removed from Mojom. + // Provides access to properties added to a proxy object without risking // Mojo interface name collisions. Unless otherwise specified, the initial // value of all properties is undefined. @@ -71,6 +96,9 @@ define("mojo/public/js/bindings", [ return (proxy instanceof ProxyBase) ? proxy[kProxyProperties] : proxy; } + // TODO(hansmuller): remove the remote doc after 'Client=' has been + // removed from Mojom. + // Provides access to properties added to a stub object without risking // Mojo interface name collisions. Unless otherwise specified, the initial // value of all properties is undefined. diff --git a/third_party/mojo/src/mojo/public/js/connection.js b/third_party/mojo/src/mojo/public/js/connection.js index 3976efd..223e711 100644 --- a/third_party/mojo/src/mojo/public/js/connection.js +++ b/third_party/mojo/src/mojo/public/js/connection.js @@ -73,6 +73,7 @@ define("mojo/public/js/connection", [ TestConnection.prototype = Object.create(Connection.prototype); + // TODO(hansmuller): remove when Shell.mojom loses its client. function createOpenConnection( messagePipeHandle, client, localInterface, remoteInterface) { var stubClass = (localInterface && localInterface.stubClass) || EmptyStub; @@ -95,6 +96,7 @@ define("mojo/public/js/connection", [ return connection; } + // TODO(hansmuller): remove when Shell.mojom loses its client. // Return a message pipe handle. function bindProxyClient(clientImpl, localInterface, remoteInterface) { var messagePipe = core.createMessagePipe(); @@ -106,6 +108,7 @@ define("mojo/public/js/connection", [ return messagePipe.handle1; } + // TODO(hansmuller): remove when Shell.mojom loses its client. // Return a proxy. function bindProxyHandle(proxyHandle, localInterface, remoteInterface) { if (!core.isHandle(proxyHandle)) @@ -116,10 +119,84 @@ define("mojo/public/js/connection", [ return connection.remote; } + // Return a handle for a message pipe that's connected to a proxy + // for remoteInterface. Used by generated code for outgoing interface& + // (request) parameters: the caller is given the generated proxy via + // |proxyCallback(proxy)| and the generated code sends the handle + // returned by this function. + function bindProxy(proxyCallback, remoteInterface) { + var messagePipe = core.createMessagePipe(); + if (messagePipe.result != core.RESULT_OK) + throw new Error("createMessagePipe failed " + messagePipe.result); + + var proxy = new remoteInterface.proxyClass; + var router = new Router(messagePipe.handle0); + var connection = new BaseConnection(undefined, proxy, router); + ProxyBindings(proxy).connection = connection; + if (proxyCallback) + proxyCallback(proxy); + + return messagePipe.handle1; + } + + // Return a handle for a message pipe that's connected to a stub for + // localInterface. The stub delegates to impl. Used by generated code for + // outgoing interface parameters: the caller specifies impl, and the generated + // code sends the handle returned by this function. + function bindImpl(impl, localInterface) { + var messagePipe = core.createMessagePipe(); + if (messagePipe.result != core.RESULT_OK) + throw new Error("createMessagePipe failed " + messagePipe.result); + + var stub = new localInterface.stubClass; + var router = new Router(messagePipe.handle0); + var connection = new BaseConnection(stub, undefined, router); + StubBindings(stub).connection = connection; + if (impl) + StubBindings(stub).delegate = impl; + + return messagePipe.handle1; + } + + // Return a remoteInterface proxy for handle. Used by generated code + // for converting incoming interface parameters to proxies. + function bindHandleToProxy(handle, remoteInterface) { + if (!core.isHandle(handle)) + throw new Error("Not a handle " + handle); + + var proxy = new remoteInterface.proxyClass; + var router = new Router(handle); + var connection = new BaseConnection(undefined, proxy, router); + ProxyBindings(proxy).connection = connection; + return proxy; + } + + // Return a localInterface stub for handle. Used by generated code + // for converting incoming interface& request parameters to localInterface + // stubs. The caller can specify the stub's implementation of localInterface + // like this: StubBindings(stub).delegate = myStubImpl. + function bindHandleToStub(handle, localInterface) { + if (!core.isHandle(handle)) + throw new Error("Not a handle " + handle); + + var stub = new localInterface.stubClass; + var router = new Router(handle); + var connection = new BaseConnection(stub, undefined, router); + StubBindings(stub).connection = connection; + return stub; + } + var exports = {}; exports.Connection = Connection; exports.TestConnection = TestConnection; + + // TODO(hansmuller): remove these when Shell.mojom loses its client. exports.bindProxyHandle = bindProxyHandle; exports.bindProxyClient = bindProxyClient; + + exports.bindProxy = bindProxy; + exports.bindImpl = bindImpl; + exports.bindHandleToProxy = bindHandleToProxy; + exports.bindHandleToStub = bindHandleToStub; return exports; }); diff --git a/third_party/mojo/src/mojo/public/mojo_sdk.gni b/third_party/mojo/src/mojo/public/mojo_sdk.gni index 6ac3e94..8ab7c7d 100644 --- a/third_party/mojo/src/mojo/public/mojo_sdk.gni +++ b/third_party/mojo/src/mojo/public/mojo_sdk.gni @@ -70,6 +70,10 @@ template("mojo_sdk_source_set") { defines = invoker.defines } + if (defined(invoker.libs)) { + libs = invoker.libs + } + public_configs = [ rebase_path("mojo/public/build/config:mojo_sdk", ".", mojo_root) ] if (defined(invoker.public_configs)) { diff --git a/third_party/mojo/src/mojo/public/platform/nacl/mojo_initial_handle.h b/third_party/mojo/src/mojo/public/platform/nacl/mojo_initial_handle.h new file mode 100644 index 0000000..2d7bc4f --- /dev/null +++ b/third_party/mojo/src/mojo/public/platform/nacl/mojo_initial_handle.h @@ -0,0 +1,14 @@ +// 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_PUBLIC_PLATFORM_NACL_MOJO_INTIIAL_HANDLE_H_ +#define MOJO_PUBLIC_PLATFORM_NACL_MOJO_INTIIAL_HANDLE_H_ + +#include "mojo/public/c/system/types.h" + +// Provides a MojoHandle that allows untrusted code to communicate with Mojo +// interfaces outside the sandbox or in other processes. +MojoResult _MojoGetInitialHandle(MojoHandle* out_handle); + +#endif // MOJO_PUBLIC_PLATFORM_NACL_MOJO_INTIIAL_HANDLE_H_ diff --git a/third_party/mojo/src/mojo/public/sky/convert_amd_modules_to_sky.py b/third_party/mojo/src/mojo/public/sky/convert_amd_modules_to_sky.py index ddff565..906bc05 100755 --- a/third_party/mojo/src/mojo/public/sky/convert_amd_modules_to_sky.py +++ b/third_party/mojo/src/mojo/public/sky/convert_amd_modules_to_sky.py @@ -7,7 +7,7 @@ import argparse import sys import re -IMPORT_TEMPLATE = '<import src="/%s.sky" as="%s" />' +IMPORT_TEMPLATE = '<import src="/gen/%s.sky" as="%s" />' PREAMBLE_TEMPLATE = '<script>' POSTAMBLE_TEMPLATE = ' module.exports = exports;\n</script>' diff --git a/third_party/mojo/src/mojo/public/tools/bindings/generators/cpp_templates/interface_declaration.tmpl b/third_party/mojo/src/mojo/public/tools/bindings/generators/cpp_templates/interface_declaration.tmpl index 30d20d8..25c01bb 100644 --- a/third_party/mojo/src/mojo/public/tools/bindings/generators/cpp_templates/interface_declaration.tmpl +++ b/third_party/mojo/src/mojo/public/tools/bindings/generators/cpp_templates/interface_declaration.tmpl @@ -14,19 +14,19 @@ class {{interface.name}} { public: static const char* Name_; - typedef {{interface.name}}Proxy Proxy_; - typedef {{interface.name}}Stub Stub_; + using Proxy_ = {{interface.name}}Proxy; + using Stub_ = {{interface.name}}Stub; - typedef {{interface.name}}RequestValidator RequestValidator_; + using RequestValidator_ = {{interface.name}}RequestValidator; {%- if interface|has_callbacks %} - typedef {{interface.name}}ResponseValidator ResponseValidator_; + using ResponseValidator_ = {{interface.name}}ResponseValidator; {%- else %} - typedef mojo::PassThroughFilter ResponseValidator_; + using ResponseValidator_ = mojo::PassThroughFilter; {%- endif %} {% if interface.client %} - typedef {{interface.client}} Client; + using Client = {{interface.client}}; {% else %} - typedef mojo::NoInterface Client; + using Client = mojo::NoInterface; {% endif %} {#--- Constants #} @@ -44,6 +44,9 @@ class {{interface.name}} { virtual ~{{interface.name}}() {} {%- for method in interface.methods %} +{% if method.response_parameters != None %} + using {{method.name}}Callback = {{interface_macros.declare_callback(method)}}; +{%- endif %} virtual void {{method.name}}({{interface_macros.declare_request_params("", method)}}) = 0; {%- endfor %} }; diff --git a/third_party/mojo/src/mojo/public/tools/bindings/generators/cpp_templates/interface_definition.tmpl b/third_party/mojo/src/mojo/public/tools/bindings/generators/cpp_templates/interface_definition.tmpl index fdbe8ca..2b45808 100644 --- a/third_party/mojo/src/mojo/public/tools/bindings/generators/cpp_templates/interface_definition.tmpl +++ b/third_party/mojo/src/mojo/public/tools/bindings/generators/cpp_templates/interface_definition.tmpl @@ -104,12 +104,12 @@ class {{class_name}}_{{method.name}}_ForwardToCallback : public mojo::MessageReceiver { public: {{class_name}}_{{method.name}}_ForwardToCallback( - const {{interface_macros.declare_callback(method)}}& callback) + const {{class_name}}::{{method.name}}Callback& callback) : callback_(callback) { } virtual bool Accept(mojo::Message* message) override; private: - {{interface_macros.declare_callback(method)}} callback_; + {{class_name}}::{{method.name}}Callback callback_; MOJO_DISALLOW_COPY_AND_ASSIGN({{class_name}}_{{method.name}}_ForwardToCallback); }; bool {{class_name}}_{{method.name}}_ForwardToCallback::Accept( @@ -175,7 +175,7 @@ void {{proxy_name}}::{{method.name}}( {%- set params_description = "%s.%s response"|format(interface.name, method.name) %} class {{class_name}}_{{method.name}}_ProxyToResponder - : public {{interface_macros.declare_callback(method)}}::Runnable { + : public {{class_name}}::{{method.name}}Callback::Runnable { public: virtual ~{{class_name}}_{{method.name}}_ProxyToResponder() { delete responder_; @@ -257,10 +257,10 @@ bool {{class_name}}Stub::AcceptWithResponder( message->mutable_payload()); params->DecodePointersAndHandles(message->mutable_handles()); - {{interface_macros.declare_callback(method)}}::Runnable* runnable = + {{class_name}}::{{method.name}}Callback::Runnable* runnable = new {{class_name}}_{{method.name}}_ProxyToResponder( message->request_id(), responder); - {{interface_macros.declare_callback(method)}} callback(runnable); + {{class_name}}::{{method.name}}Callback callback(runnable); {{alloc_params(method.parameters)|indent(6)}} // A null |sink_| typically means there is a missing call to // InterfacePtr::set_client(). diff --git a/third_party/mojo/src/mojo/public/tools/bindings/generators/cpp_templates/interface_macros.tmpl b/third_party/mojo/src/mojo/public/tools/bindings/generators/cpp_templates/interface_macros.tmpl index fbefce2..7644b2e 100644 --- a/third_party/mojo/src/mojo/public/tools/bindings/generators/cpp_templates/interface_macros.tmpl +++ b/third_party/mojo/src/mojo/public/tools/bindings/generators/cpp_templates/interface_macros.tmpl @@ -17,7 +17,7 @@ mojo::Callback<void( {%- macro declare_request_params(prefix, method) -%} {{declare_params(prefix, method.parameters)}} {%- if method.response_parameters != None -%} -{%- if method.parameters %}, {% endif %} -const {{declare_callback(method)}}& callback +{%- if method.parameters %}, {% endif -%} +const {{method.name}}Callback& callback {%- endif -%} {%- endmacro -%} diff --git a/third_party/mojo/src/mojo/public/tools/bindings/generators/cpp_templates/module.h.tmpl b/third_party/mojo/src/mojo/public/tools/bindings/generators/cpp_templates/module.h.tmpl index 3da6c32..25449b7 100644 --- a/third_party/mojo/src/mojo/public/tools/bindings/generators/cpp_templates/module.h.tmpl +++ b/third_party/mojo/src/mojo/public/tools/bindings/generators/cpp_templates/module.h.tmpl @@ -40,16 +40,16 @@ extern const {{constant.kind|cpp_pod_type}} {{constant.name}}; {#--- Interface Forward Declarations -#} {% for interface in interfaces %} class {{interface.name}}; -typedef mojo::InterfacePtr<{{interface.name}}> {{interface.name}}Ptr; +using {{interface.name}}Ptr = mojo::InterfacePtr<{{interface.name}}>; {% endfor %} {#--- Struct Forward Declarations -#} {% for struct in structs %} class {{struct.name}}; {% if struct|should_inline %} -typedef mojo::InlinedStructPtr<{{struct.name}}> {{struct.name}}Ptr; +using {{struct.name}}Ptr = mojo::InlinedStructPtr<{{struct.name}}>; {% else %} -typedef mojo::StructPtr<{{struct.name}}> {{struct.name}}Ptr; +using {{struct.name}}Ptr = mojo::StructPtr<{{struct.name}}>; {% endif %} {% endfor %} diff --git a/third_party/mojo/src/mojo/public/tools/bindings/generators/cpp_templates/wrapper_class_declaration.tmpl b/third_party/mojo/src/mojo/public/tools/bindings/generators/cpp_templates/wrapper_class_declaration.tmpl index 25b39b3..ebcba7b 100644 --- a/third_party/mojo/src/mojo/public/tools/bindings/generators/cpp_templates/wrapper_class_declaration.tmpl +++ b/third_party/mojo/src/mojo/public/tools/bindings/generators/cpp_templates/wrapper_class_declaration.tmpl @@ -1,7 +1,7 @@ class {{struct.name}} { public: - typedef internal::{{struct.name}}_Data Data_; + using Data_ = internal::{{struct.name}}_Data; {#--- Constants #} {%- for constant in struct.constants %} diff --git a/third_party/mojo/src/mojo/public/tools/bindings/generators/cpp_templates/wrapper_union_class_declaration.tmpl b/third_party/mojo/src/mojo/public/tools/bindings/generators/cpp_templates/wrapper_union_class_declaration.tmpl index 91ea7cf..0420c8d 100644 --- a/third_party/mojo/src/mojo/public/tools/bindings/generators/cpp_templates/wrapper_union_class_declaration.tmpl +++ b/third_party/mojo/src/mojo/public/tools/bindings/generators/cpp_templates/wrapper_union_class_declaration.tmpl @@ -1,7 +1,7 @@ class {{union.name}} { public: - typedef internal::{{union.name}}_Data Data_; - typedef Data_::{{union.name}}_Tag Tag; + using Data_ = internal::{{union.name}}_Data; + using Tag = Data_::{{union.name}}_Tag; static {{union.name}}Ptr New(); diff --git a/third_party/mojo/src/mojo/public/tools/bindings/generators/dart_templates/interface_definition.tmpl b/third_party/mojo/src/mojo/public/tools/bindings/generators/dart_templates/interface_definition.tmpl index 0289345..952c0dc 100644 --- a/third_party/mojo/src/mojo/public/tools/bindings/generators/dart_templates/interface_definition.tmpl +++ b/third_party/mojo/src/mojo/public/tools/bindings/generators/dart_templates/interface_definition.tmpl @@ -2,58 +2,61 @@ const int k{{interface|name}}_{{method|name}}_name = {{method.ordinal}}; {%- endfor %} -abstract class {{interface|name}}Calls { - void sendMessage(bindings.Struct message, int name); - Future sendMessageWithRequestId(bindings.Struct message, int name, int id); +abstract class {{interface|name}} implements core.Listener { + static const String name = '{{namespace|replace(".","::")}}::{{interface|name}}'; + {{interface|name}}Stub stub; + + {{interface|name}}(core.MojoMessagePipeEndpoint endpoint) : + stub = new {{interface|name}}Stub(endpoint); + + {{interface|name}}.fromHandle(core.MojoHandle handle) : + stub = new {{interface|name}}Stub.fromHandle(handle); + + {{interface|name}}.fromStub(this.stub); + + {{interface|name}}.unbound() : + stub = new {{interface|name}}Stub.unbound(); + + void close() => stub.close(); + + StreamSubscription<int> listen() => stub.listen(); + + {{interface|name}} get delegate => stub.delegate; + set delegate({{interface|name}} d) { + stub.delegate = d; + } {%- for method in interface.methods %} {%- if method.response_parameters == None %} - void call{{method|name|upper_camel_case}}( -{%- for parameter in method.parameters -%} - {{parameter.kind|dart_type}} {{parameter|name}}{% if not loop.last %}, {% endif %} -{%- endfor -%} -{%- set request_struct = method|struct_from_method %} - ) { - var params = new {{request_struct|name}}(); -{%- for parameter in method.parameters %} - params.{{parameter|name}} = {{parameter|name}}; -{%- endfor %} - sendMessage(params, k{{interface|name}}_{{method|name}}_name); - } -{% else %} + void {{method|name}}( + {%- for parameter in method.parameters -%} + {{parameter.kind|dart_type}} {{parameter|name}}{% if not loop.last %}, {% endif %} + {%- endfor -%} + ); +{%- else %} {%- set response_struct = method|response_struct_from_method %} - Future<{{response_struct|name}}> call{{method|name|upper_camel_case}}( -{%- for parameter in method.parameters -%} + Future<{{response_struct|name}}> {{method|name}}( + {%- for parameter in method.parameters -%} {{parameter.kind|dart_type}} {{parameter|name}}, -{%- endfor -%} -{%- set request_struct = method|struct_from_method %} - [int requestId = -1] - ) { - var params = new {{request_struct|name}}(); -{%- for parameter in method.parameters %} - params.{{parameter|name}} = {{parameter|name}}; -{%- endfor %} - return sendMessageWithRequestId( - params, - k{{interface|name}}_{{method|name}}_name, - requestId, - bindings.MessageHeader.kMessageExpectsResponse); - } + {%- endfor -%} + [Function responseFactory = null]); {%- endif %} {%- endfor %} } -class {{interface|name}}Client extends bindings.Client with {{interface|name}}Calls { - {{interface|name}}Client(core.MojoMessagePipeEndpoint endpoint) : super(endpoint); +class {{interface|name}}Proxy extends bindings.Proxy implements {{interface|name}} { + {{interface|name}}Proxy(core.MojoMessagePipeEndpoint endpoint) : super(endpoint); - {{interface|name}}Client.fromHandle(core.MojoHandle handle) : + {{interface|name}}Proxy.fromHandle(core.MojoHandle handle) : super.fromHandle(handle); - {{interface|name}}Client.unbound() : super.unbound(); + {{interface|name}}Proxy.unbound() : super.unbound(); + + String get name => {{interface|name}}.name; - static {{interface|name}}Client newFromEndpoint( + static {{interface|name}}Proxy newFromEndpoint( core.MojoMessagePipeEndpoint endpoint) => - new {{interface|name}}Client(endpoint); + new {{interface|name}}Proxy(endpoint); void handleResponse(bindings.ServiceMessage message) { switch (message.header.type) { @@ -77,45 +80,61 @@ class {{interface|name}}Client extends bindings.Client with {{interface|name}}Ca break; } } + +{%- for method in interface.methods %} +{%- if method.response_parameters == None %} + void {{method|name}}( +{%- for parameter in method.parameters -%} + {{parameter.kind|dart_type}} {{parameter|name}}{% if not loop.last %}, {% endif %} +{%- endfor -%} +{%- set request_struct = method|struct_from_method -%} + ) { + var params = new {{request_struct|name}}(); +{%- for parameter in method.parameters %} + params.{{parameter|name}} = {{parameter|name}}; +{%- endfor %} + sendMessage(params, k{{interface|name}}_{{method|name}}_name); + } +{% else %} +{%- set response_struct = method|response_struct_from_method %} +{%- set request_struct = method|struct_from_method %} + Future<{{response_struct|name}}> {{method|name}}( +{%- for parameter in method.parameters -%} + {{parameter.kind|dart_type}} {{parameter|name}}, +{%- endfor -%} + [Function responseFactory = null]) { + var params = new {{request_struct|name}}(); +{%- for parameter in method.parameters %} + params.{{parameter|name}} = {{parameter|name}}; +{%- endfor %} + return sendMessageWithRequestId( + params, + k{{interface|name}}_{{method|name}}_name, + -1, + bindings.MessageHeader.kMessageExpectsResponse); + } +{%- endif %} +{%- endfor %} } -{#--- TODO(zra): Remove Interface suffix from the name of this class. - This is tricky because some mojom files have interfaces named both - X and XClient. This leads to an XClient for the Client of X, and an - XClient for the Interface of XClient, which conflict with eachother. #} -class {{interface|name}}Interface extends bindings.Interface -{% if interface.client != None -%} -with {{imported_from[interface.client]}}{{interface.client|upper_camel_case}}Calls -{% endif -%} { - {{interface|name}}Interface _delegate = null; +class {{interface|name}}Stub extends bindings.Stub { + {{interface|name}} _delegate = null; - {{interface|name}}Interface(core.MojoMessagePipeEndpoint endpoint) : super(endpoint); + {{interface|name}}Stub(core.MojoMessagePipeEndpoint endpoint) : super(endpoint); - {{interface|name}}Interface.fromHandle(core.MojoHandle handle) : + {{interface|name}}Stub.fromHandle(core.MojoHandle handle) : super.fromHandle(handle); - {{interface|name}}Interface.unbound() : super.unbound(); + {{interface|name}}Stub.unbound() : super.unbound(); - static {{interface|name}}Interface newFromEndpoint( + static {{interface|name}}Stub newFromEndpoint( core.MojoMessagePipeEndpoint endpoint) => - new {{interface|name}}Interface(endpoint); + new {{interface|name}}Stub(endpoint); - static const String name = '{{namespace|replace(".","::")}}::{{interface|name}}'; + static const String name = {{interface|name}}.name; {% for method in interface.methods %} -{%- if method.response_parameters == None %} - void {{method|name}}( - {%- for parameter in method.parameters -%} - {{parameter.kind|dart_type}} {{parameter|name}}{% if not loop.last %}, {% endif %} - {%- endfor -%} - ) { - assert(_delegate != null); - _delegate.{{method|name}}( - {%- for parameter in method.parameters -%} - {{parameter|name}}{% if not loop.last %}, {% endif %} - {%- endfor %}); - } -{%- else %} +{%- if method.response_parameters != None %} {%- set response_struct = method|response_struct_from_method %} {{response_struct|name}} _{{response_struct|name}}Factory( {%- for param in method.response_parameters -%} @@ -128,23 +147,11 @@ with {{imported_from[interface.client]}}{{interface.client|upper_camel_case}}Cal {%- endfor %} return result; } - - Future<{{response_struct|name}}> {{method|name}}( - {%- for parameter in method.parameters -%} - {{parameter.kind|dart_type}} {{parameter|name}}, - {%- endfor -%} - Function responseFactory) { - assert(_delegate != null); - return _delegate.{{method|name}}( - {%- for parameter in method.parameters -%} - {{parameter|name}}, - {%- endfor %} - responseFactory); - } {%- endif %} {%- endfor %} Future<bindings.Message> handleMessage(bindings.ServiceMessage message) { + assert(_delegate != null); switch (message.header.type) { {%- for method in interface.methods %} {%- set request_struct = method|struct_from_method %} @@ -152,14 +159,14 @@ with {{imported_from[interface.client]}}{{interface.client|upper_camel_case}}Cal var params = {{request_struct|name}}.deserialize( message.payload); {%- if method.response_parameters == None %} - {{method|name}}( + _delegate.{{method|name}}( {%- for parameter in method.parameters -%} params.{{parameter|name}}{% if not loop.last %}, {% endif %} {%- endfor -%} ); {%- else %} {%- set response_struct = method|response_struct_from_method %} - return {{method|name}}( + return _delegate.{{method|name}}( {%- for parameter in method.parameters -%} params.{{parameter|name}}, {%- endfor -%} @@ -182,8 +189,8 @@ with {{imported_from[interface.client]}}{{interface.client|upper_camel_case}}Cal return null; } - {{interface|name}}Interface get delegate => _delegate; - set delegate({{interface|name}}Interface d) { + {{interface|name}} get delegate => _delegate; + set delegate({{interface|name}} d) { assert(_delegate == null); _delegate = d; } diff --git a/third_party/mojo/src/mojo/public/tools/bindings/generators/js_templates/module.sky.tmpl b/third_party/mojo/src/mojo/public/tools/bindings/generators/js_templates/module.sky.tmpl index 78cdf3f..878cd20 100644 --- a/third_party/mojo/src/mojo/public/tools/bindings/generators/js_templates/module.sky.tmpl +++ b/third_party/mojo/src/mojo/public/tools/bindings/generators/js_templates/module.sky.tmpl @@ -2,13 +2,13 @@ Use of this source code is governed by a BSD-style license that can be found in the LICENSE file. --> -<import src="/mojo/public/sky/bindings.sky" as="bindings" /> -<import src="/mojo/public/sky/codec.sky" as="codec" /> -<import src="/mojo/public/sky/connection.sky" as="connection" /> -<import src="/mojo/public/sky/core.sky" as="core" /> -<import src="/mojo/public/sky/validator.sky" as="validator" /> +<import src="/gen/mojo/public/sky/bindings.sky" as="bindings" /> +<import src="/gen/mojo/public/sky/codec.sky" as="codec" /> +<import src="/gen/mojo/public/sky/connection.sky" as="connection" /> +<import src="/gen/mojo/public/sky/core.sky" as="core" /> +<import src="/gen/mojo/public/sky/validator.sky" as="validator" /> {%- for import in imports %} -<import src="/{{import.module.path}}.sky" as="{{import.unique_name}}" /> +<import src="/gen/{{import.module.path}}.sky" as="{{import.unique_name}}" /> {%- endfor %} <script> {%- include "module_definition.tmpl" %} diff --git a/third_party/mojo/src/mojo/public/tools/bindings/generators/mojom_dart_generator.py b/third_party/mojo/src/mojo/public/tools/bindings/generators/mojom_dart_generator.py index 9862943..3251095 100644 --- a/third_party/mojo/src/mojo/public/tools/bindings/generators/mojom_dart_generator.py +++ b/third_party/mojo/src/mojo/public/tools/bindings/generators/mojom_dart_generator.py @@ -253,13 +253,13 @@ def AppendDecodeParams(initial_params, kind, bit): else: params.append(GetDartTrueFalse(mojom.IsNullableKind(kind))) if mojom.IsInterfaceKind(kind): - params.append('%sClient.newFromEndpoint' % GetDartType(kind)) + params.append('%sProxy.newFromEndpoint' % GetDartType(kind)) if mojom.IsArrayKind(kind) and mojom.IsInterfaceKind(kind.kind): - params.append('%sClient.newFromEndpoint' % GetDartType(kind.kind)) + params.append('%sProxy.newFromEndpoint' % GetDartType(kind.kind)) if mojom.IsInterfaceRequestKind(kind): - params.append('%sInterface.newFromEndpoint' % GetDartType(kind.kind)) + params.append('%sStub.newFromEndpoint' % GetDartType(kind.kind)) if mojom.IsArrayKind(kind) and mojom.IsInterfaceRequestKind(kind.kind): - params.append('%sInterface.newFromEndpoint' % GetDartType(kind.kind.kind)) + params.append('%sStub.newFromEndpoint' % GetDartType(kind.kind.kind)) if mojom.IsArrayKind(kind): params.append(GetArrayExpectedLength(kind)) return params @@ -381,7 +381,6 @@ class Generator(generator.Generator): 'interface_response_name': GetInterfaceResponseName, 'response_struct_from_method': GetResponseStructFromMethod, 'struct_from_method': GetStructFromMethod, - 'upper_camel_case': UpperCamelCase, 'struct_size': lambda ps: ps.GetTotalSize() + _HEADER_SIZE, } @@ -420,7 +419,7 @@ class Generator(generator.Generator): unique_name = simple_name + str(counter) used_names.add(unique_name) - each_import["unique_name"] = unique_name + each_import["unique_name"] = unique_name + '_mojom' counter += 1 return self.module.imports diff --git a/third_party/mojo/src/mojo/public/tools/bindings/generators/mojom_js_generator.py b/third_party/mojo/src/mojo/public/tools/bindings/generators/mojom_js_generator.py index 7aaeaa2..fd1407e 100644 --- a/third_party/mojo/src/mojo/public/tools/bindings/generators/mojom_js_generator.py +++ b/third_party/mojo/src/mojo/public/tools/bindings/generators/mojom_js_generator.py @@ -223,30 +223,30 @@ def JavaScriptValidateHandleParams(packed_field): return "%s, %s" % (field_offset, nullable) - - def JavaScriptProxyMethodParameterValue(parameter): name = parameter.name; if (IsInterfaceParameter(parameter)): type = JavaScriptType(parameter.kind) - return "core.isHandle(%s) ? %s : connection.bindProxyClient" \ - "(%s, %s, %s.client)" % (name, name, name, type, type) + return "core.isHandle(%s) ? %s : connection.bindImpl" \ + "(%s, %s)" % (name, name, name, type) if (IsInterfaceRequestParameter(parameter)): type = JavaScriptType(parameter.kind.kind) - return "core.isHandle(%s) ? %s : connection.bindProxyClient" \ - "(%s, %s.client, %s)" % (name, name, name, type, type) + return "core.isHandle(%s) ? %s : connection.bindProxy" \ + "(%s, %s)" % (name, name, name, type) return name; + def JavaScriptStubMethodParameterValue(parameter): name = parameter.name; if (IsInterfaceParameter(parameter)): type = JavaScriptType(parameter.kind) - return "connection.bindProxyHandle(%s, %s.client, %s)" % (name, type, type) + return "connection.bindHandleToProxy(%s, %s)" % (name, type) if (IsInterfaceRequestParameter(parameter)): type = JavaScriptType(parameter.kind.kind) - return "connection.bindProxyHandle(%s, %s, %s.client)" % (name, type, type) + return "connection.bindHandleToStub(%s, %s)" % (name, type) return name; + def TranslateConstants(token): if isinstance(token, (mojom.EnumValue, mojom.NamedValue)): # Both variable and enum constants are constructed like: diff --git a/third_party/mojo/src/mojo/public/tools/bindings/pylib/mojom/parse/ast.py b/third_party/mojo/src/mojo/public/tools/bindings/pylib/mojom/parse/ast.py index e1fa4f8..ffb44e0 100644 --- a/third_party/mojo/src/mojo/public/tools/bindings/pylib/mojom/parse/ast.py +++ b/third_party/mojo/src/mojo/public/tools/bindings/pylib/mojom/parse/ast.py @@ -136,28 +136,34 @@ class Const(Definition): class Enum(Definition): """Represents an enum definition.""" - def __init__(self, name, enum_value_list, **kwargs): + def __init__(self, name, attribute_list, enum_value_list, **kwargs): + assert attribute_list is None or isinstance(attribute_list, AttributeList) assert isinstance(enum_value_list, EnumValueList) super(Enum, self).__init__(name, **kwargs) + self.attribute_list = attribute_list self.enum_value_list = enum_value_list def __eq__(self, other): return super(Enum, self).__eq__(other) and \ + self.attribute_list == other.attribute_list and \ self.enum_value_list == other.enum_value_list class EnumValue(Definition): """Represents a definition of an enum value.""" - def __init__(self, name, value, **kwargs): + def __init__(self, name, attribute_list, value, **kwargs): # The optional value is either an int (which is current a string) or a # "wrapped identifier". + assert attribute_list is None or isinstance(attribute_list, AttributeList) assert value is None or isinstance(value, (str, tuple)) super(EnumValue, self).__init__(name, **kwargs) + self.attribute_list = attribute_list self.value = value def __eq__(self, other): return super(EnumValue, self).__eq__(other) and \ + self.attribute_list == other.attribute_list and \ self.value == other.value @@ -206,19 +212,22 @@ class Interface(Definition): class Method(Definition): """Represents a method definition.""" - def __init__(self, name, ordinal, parameter_list, response_parameter_list, - **kwargs): + def __init__(self, name, attribute_list, ordinal, parameter_list, + response_parameter_list, **kwargs): + assert attribute_list is None or isinstance(attribute_list, AttributeList) assert ordinal is None or isinstance(ordinal, Ordinal) assert isinstance(parameter_list, ParameterList) assert response_parameter_list is None or \ isinstance(response_parameter_list, ParameterList) super(Method, self).__init__(name, **kwargs) + self.attribute_list = attribute_list self.ordinal = ordinal self.parameter_list = parameter_list self.response_parameter_list = response_parameter_list def __eq__(self, other): return super(Method, self).__eq__(other) and \ + self.attribute_list == other.attribute_list and \ self.ordinal == other.ordinal and \ self.parameter_list == other.parameter_list and \ self.response_parameter_list == other.response_parameter_list @@ -287,18 +296,21 @@ class Ordinal(NodeBase): class Parameter(NodeBase): """Represents a method request or response parameter.""" - def __init__(self, name, ordinal, typename, **kwargs): + def __init__(self, name, attribute_list, ordinal, typename, **kwargs): assert isinstance(name, str) + assert attribute_list is None or isinstance(attribute_list, AttributeList) assert ordinal is None or isinstance(ordinal, Ordinal) assert isinstance(typename, str) super(Parameter, self).__init__(**kwargs) self.name = name + self.attribute_list = attribute_list self.ordinal = ordinal self.typename = typename def __eq__(self, other): return super(Parameter, self).__eq__(other) and \ self.name == other.name and \ + self.attribute_list == other.attribute_list and \ self.ordinal == other.ordinal and \ self.typename == other.typename @@ -328,20 +340,24 @@ class Struct(Definition): class StructField(Definition): """Represents a struct field definition.""" - def __init__(self, name, ordinal, typename, default_value, **kwargs): + def __init__(self, name, attribute_list, ordinal, typename, default_value, + **kwargs): assert isinstance(name, str) + assert attribute_list is None or isinstance(attribute_list, AttributeList) assert ordinal is None or isinstance(ordinal, Ordinal) assert isinstance(typename, str) # The optional default value is currently either a value as a string or a # "wrapped identifier". assert default_value is None or isinstance(default_value, (str, tuple)) super(StructField, self).__init__(name, **kwargs) + self.attribute_list = attribute_list self.ordinal = ordinal self.typename = typename self.default_value = default_value def __eq__(self, other): return super(StructField, self).__eq__(other) and \ + self.attribute_list == other.attribute_list and \ self.ordinal == other.ordinal and \ self.typename == other.typename and \ self.default_value == other.default_value @@ -357,28 +373,34 @@ class StructBody(NodeListBase): class Union(Definition): """Represents a union definition.""" - def __init__(self, name, body, **kwargs): + def __init__(self, name, attribute_list, body, **kwargs): + assert attribute_list is None or isinstance(attribute_list, AttributeList) assert isinstance(body, UnionBody) super(Union, self).__init__(name, **kwargs) + self.attribute_list = attribute_list self.body = body def __eq__(self, other): return super(Union, self).__eq__(other) and \ + self.attribute_list == other.attribute_list and \ self.body == other.body class UnionField(Definition): - def __init__(self, name, ordinal, typename, **kwargs): + def __init__(self, name, attribute_list, ordinal, typename, **kwargs): assert isinstance(name, str) + assert attribute_list is None or isinstance(attribute_list, AttributeList) assert ordinal is None or isinstance(ordinal, Ordinal) assert isinstance(typename, str) super(UnionField, self).__init__(name, **kwargs) + self.attribute_list = attribute_list self.ordinal = ordinal self.typename = typename def __eq__(self, other): return super(UnionField, self).__eq__(other) and \ + self.attribute_list == other.attribute_list and \ self.ordinal == other.ordinal and \ self.typename == other.typename diff --git a/third_party/mojo/src/mojo/public/tools/bindings/pylib/mojom/parse/parser.py b/third_party/mojo/src/mojo/public/tools/bindings/pylib/mojom/parse/parser.py index 4225f0a..dc9d097 100644 --- a/third_party/mojo/src/mojo/public/tools/bindings/pylib/mojom/parse/parser.py +++ b/third_party/mojo/src/mojo/public/tools/bindings/pylib/mojom/parse/parser.py @@ -171,12 +171,12 @@ class Parser(object): p[0].Append(p[2]) def p_struct_field(self, p): - """struct_field : typename NAME ordinal default SEMI""" - p[0] = ast.StructField(p[2], p[3], p[1], p[4]) + """struct_field : attribute_section typename NAME ordinal default SEMI""" + p[0] = ast.StructField(p[3], p[1], p[4], p[2], p[5]) def p_union(self, p): - """union : UNION NAME LBRACE union_body RBRACE SEMI""" - p[0] = ast.Union(p[2], p[4]) + """union : attribute_section UNION NAME LBRACE union_body RBRACE SEMI""" + p[0] = ast.Union(p[3], p[1], p[5]) def p_union_body_1(self, p): """union_body : """ @@ -188,8 +188,8 @@ class Parser(object): p[1].Append(p[2]) def p_union_field(self, p): - """union_field : typename NAME ordinal SEMI""" - p[0] = ast.UnionField(p[2], p[3], p[1]) + """union_field : attribute_section typename NAME ordinal SEMI""" + p[0] = ast.UnionField(p[3], p[1], p[4], p[2]) def p_default_1(self, p): """default : """ @@ -224,8 +224,9 @@ class Parser(object): p[0] = p[3] def p_method(self, p): - """method : NAME ordinal LPAREN parameter_list RPAREN response SEMI""" - p[0] = ast.Method(p[1], p[2], p[4], p[6]) + """method : attribute_section NAME ordinal LPAREN parameter_list RPAREN \ + response SEMI""" + p[0] = ast.Method(p[2], p[1], p[3], p[5], p[7]) def p_parameter_list_1(self, p): """parameter_list : """ @@ -245,9 +246,9 @@ class Parser(object): p[0].Append(p[3]) def p_parameter(self, p): - """parameter : typename NAME ordinal""" - p[0] = ast.Parameter(p[2], p[3], p[1], - filename=self.filename, lineno=p.lineno(2)) + """parameter : attribute_section typename NAME ordinal""" + p[0] = ast.Parameter(p[3], p[1], p[4], p[2], + filename=self.filename, lineno=p.lineno(3)) def p_typename(self, p): """typename : nonnullable_typename QSTN @@ -322,9 +323,12 @@ class Parser(object): p[0] = ast.Ordinal(value, filename=self.filename, lineno=p.lineno(1)) def p_enum(self, p): - """enum : ENUM NAME LBRACE nonempty_enum_value_list RBRACE SEMI - | ENUM NAME LBRACE nonempty_enum_value_list COMMA RBRACE SEMI""" - p[0] = ast.Enum(p[2], p[4], filename=self.filename, lineno=p.lineno(1)) + """enum : attribute_section ENUM NAME LBRACE nonempty_enum_value_list \ + RBRACE SEMI + | attribute_section ENUM NAME LBRACE nonempty_enum_value_list \ + COMMA RBRACE SEMI""" + p[0] = ast.Enum(p[3], p[1], p[5], filename=self.filename, + lineno=p.lineno(2)) def p_nonempty_enum_value_list_1(self, p): """nonempty_enum_value_list : enum_value""" @@ -336,11 +340,11 @@ class Parser(object): p[0].Append(p[3]) def p_enum_value(self, p): - """enum_value : NAME - | NAME EQUALS int - | NAME EQUALS identifier_wrapped""" - p[0] = ast.EnumValue(p[1], p[3] if len(p) == 4 else None, - filename=self.filename, lineno=p.lineno(1)) + """enum_value : attribute_section NAME + | attribute_section NAME EQUALS int + | attribute_section NAME EQUALS identifier_wrapped""" + p[0] = ast.EnumValue(p[2], p[1], p[4] if len(p) == 5 else None, + filename=self.filename, lineno=p.lineno(2)) def p_const(self, p): """const : CONST typename NAME EQUALS constant SEMI""" diff --git a/third_party/mojo/src/mojo/public/tools/bindings/pylib/mojom_tests/parse/parser_unittest.py b/third_party/mojo/src/mojo/public/tools/bindings/pylib/mojom_tests/parse/parser_unittest.py index 65c2fca..d3f8907 100644 --- a/third_party/mojo/src/mojo/public/tools/bindings/pylib/mojom_tests/parse/parser_unittest.py +++ b/third_party/mojo/src/mojo/public/tools/bindings/pylib/mojom_tests/parse/parser_unittest.py @@ -146,8 +146,8 @@ class ParserTest(unittest.TestCase): 'MyStruct', None, ast.StructBody( - [ast.StructField('a', None, 'int32', None), - ast.StructField('b', None, 'double', None)]))]) + [ast.StructField('a', None, None, 'int32', None), + ast.StructField('b', None, None, 'double', None)]))]) self.assertEquals(parser.Parse(source, "my_file.mojom"), expected) def testSimpleStructWithoutModule(self): @@ -166,8 +166,8 @@ class ParserTest(unittest.TestCase): 'MyStruct', None, ast.StructBody( - [ast.StructField('a', None, 'int32', None), - ast.StructField('b', None, 'double', None)]))]) + [ast.StructField('a', None, None, 'int32', None), + ast.StructField('b', None, None, 'double', None)]))]) self.assertEquals(parser.Parse(source, "my_file.mojom"), expected) def testValidStructDefinitions(self): @@ -189,10 +189,12 @@ class ParserTest(unittest.TestCase): None, ast.StructBody( [ast.Enum('MyEnum', - ast.EnumValueList(ast.EnumValue('VALUE', None))), + None, + ast.EnumValueList( + ast.EnumValue('VALUE', None, None))), ast.Const('kMyConst', 'double', '1.23'), - ast.StructField('a', None, 'int32', None), - ast.StructField('b', None, 'SomeOtherStruct', None)]))]) + ast.StructField('a', None, None, 'int32', None), + ast.StructField('b', None, None, 'SomeOtherStruct', None)]))]) self.assertEquals(parser.Parse(source, "my_file.mojom"), expected) def testInvalidStructDefinitions(self): @@ -344,18 +346,20 @@ class ParserTest(unittest.TestCase): ast.ImportList(), [ast.Enum( 'MyEnum1', - ast.EnumValueList([ast.EnumValue('VALUE1', None), - ast.EnumValue('VALUE2', None)])), + None, + ast.EnumValueList([ast.EnumValue('VALUE1', None, None), + ast.EnumValue('VALUE2', None, None)])), ast.Enum( 'MyEnum2', - ast.EnumValueList([ast.EnumValue('VALUE1', '-1'), - ast.EnumValue('VALUE2', '0'), - ast.EnumValue('VALUE3', '+987'), - ast.EnumValue('VALUE4', '0xAF12'), - ast.EnumValue('VALUE5', '-0x09bcd'), - ast.EnumValue('VALUE6', ('IDENTIFIER', + None, + ast.EnumValueList([ast.EnumValue('VALUE1', None, '-1'), + ast.EnumValue('VALUE2', None, '0'), + ast.EnumValue('VALUE3', None, '+987'), + ast.EnumValue('VALUE4', None, '0xAF12'), + ast.EnumValue('VALUE5', None, '-0x09bcd'), + ast.EnumValue('VALUE6', None, ('IDENTIFIER', 'VALUE5')), - ast.EnumValue('VALUE7', None)]))]) + ast.EnumValue('VALUE7', None, None)]))]) self.assertEquals(parser.Parse(source, "my_file.mojom"), expected) def testInvalidEnumInitializers(self): @@ -406,7 +410,7 @@ class ParserTest(unittest.TestCase): 'MyStruct', None, ast.StructBody( [ast.Const('kNumber', 'int8', '-1'), - ast.StructField('number', ast.Ordinal(0), 'int8', + ast.StructField('number', None, ast.Ordinal(0), 'int8', ('IDENTIFIER', 'kNumber'))]))]) self.assertEquals(parser.Parse(source, "my_file.mojom"), expected) @@ -452,14 +456,14 @@ class ParserTest(unittest.TestCase): 'MyStruct', None, ast.StructBody( - [ast.StructField('a0', ast.Ordinal(0), 'int32', None), - ast.StructField('a1', ast.Ordinal(1), 'int32', None), - ast.StructField('a2', ast.Ordinal(2), 'int32', None), - ast.StructField('a9', ast.Ordinal(9), 'int32', None), - ast.StructField('a10', ast.Ordinal(10), 'int32', None), - ast.StructField('a11', ast.Ordinal(11), 'int32', None), - ast.StructField('a29', ast.Ordinal(29), 'int32', None), - ast.StructField('a1234567890', ast.Ordinal(1234567890), + [ast.StructField('a0', None, ast.Ordinal(0), 'int32', None), + ast.StructField('a1', None, ast.Ordinal(1), 'int32', None), + ast.StructField('a2', None, ast.Ordinal(2), 'int32', None), + ast.StructField('a9', None, ast.Ordinal(9), 'int32', None), + ast.StructField('a10', None, ast.Ordinal(10), 'int32', None), + ast.StructField('a11', None, ast.Ordinal(11), 'int32', None), + ast.StructField('a29', None, ast.Ordinal(29), 'int32', None), + ast.StructField('a1234567890', None, ast.Ordinal(1234567890), 'int32', None)]))]) self.assertEquals(parser.Parse(source, "my_file.mojom"), expected) @@ -542,7 +546,7 @@ class ParserTest(unittest.TestCase): [ast.Struct( 'MyStruct', None, - ast.StructBody(ast.StructField('a', None, 'int32', None)))]) + ast.StructBody(ast.StructField('a', None, None, 'int32', None)))]) self.assertEquals(parser.Parse(source, "my_file.mojom"), expected) def testValidHandleTypes(self): @@ -566,11 +570,14 @@ class ParserTest(unittest.TestCase): 'MyStruct', None, ast.StructBody( - [ast.StructField('a', None, 'handle', None), - ast.StructField('b', None, 'handle<data_pipe_consumer>', None), - ast.StructField('c', None, 'handle<data_pipe_producer>', None), - ast.StructField('d', None, 'handle<message_pipe>', None), - ast.StructField('e', None, 'handle<shared_buffer>', None)]))]) + [ast.StructField('a', None, None, 'handle', None), + ast.StructField('b', None, None, 'handle<data_pipe_consumer>', + None), + ast.StructField('c', None, None, 'handle<data_pipe_producer>', + None), + ast.StructField('d', None, None, 'handle<message_pipe>', None), + ast.StructField('e', None, None, 'handle<shared_buffer>', + None)]))]) self.assertEquals(parser.Parse(source, "my_file.mojom"), expected) def testInvalidHandleType(self): @@ -625,29 +632,29 @@ class ParserTest(unittest.TestCase): 'MyStruct', None, ast.StructBody( - [ast.StructField('a0', None, 'int16', '0'), - ast.StructField('a1', None, 'uint16', '0x0'), - ast.StructField('a2', None, 'uint16', '0x00'), - ast.StructField('a3', None, 'uint16', '0x01'), - ast.StructField('a4', None, 'uint16', '0xcd'), - ast.StructField('a5' , None, 'int32', '12345'), - ast.StructField('a6', None, 'int64', '-12345'), - ast.StructField('a7', None, 'int64', '+12345'), - ast.StructField('a8', None, 'uint32', '0x12cd3'), - ast.StructField('a9', None, 'uint32', '-0x12cD3'), - ast.StructField('a10', None, 'uint32', '+0x12CD3'), - ast.StructField('a11', None, 'bool', 'true'), - ast.StructField('a12', None, 'bool', 'false'), - ast.StructField('a13', None, 'float', '1.2345'), - ast.StructField('a14', None, 'float', '-1.2345'), - ast.StructField('a15', None, 'float', '+1.2345'), - ast.StructField('a16', None, 'float', '123.'), - ast.StructField('a17', None, 'float', '.123'), - ast.StructField('a18', None, 'double', '1.23E10'), - ast.StructField('a19', None, 'double', '1.E-10'), - ast.StructField('a20', None, 'double', '.5E+10'), - ast.StructField('a21', None, 'double', '-1.23E10'), - ast.StructField('a22', None, 'double', '+.123E10')]))]) + [ast.StructField('a0', None, None, 'int16', '0'), + ast.StructField('a1', None, None, 'uint16', '0x0'), + ast.StructField('a2', None, None, 'uint16', '0x00'), + ast.StructField('a3', None, None, 'uint16', '0x01'), + ast.StructField('a4', None, None, 'uint16', '0xcd'), + ast.StructField('a5' , None, None, 'int32', '12345'), + ast.StructField('a6', None, None, 'int64', '-12345'), + ast.StructField('a7', None, None, 'int64', '+12345'), + ast.StructField('a8', None, None, 'uint32', '0x12cd3'), + ast.StructField('a9', None, None, 'uint32', '-0x12cD3'), + ast.StructField('a10', None, None, 'uint32', '+0x12CD3'), + ast.StructField('a11', None, None, 'bool', 'true'), + ast.StructField('a12', None, None, 'bool', 'false'), + ast.StructField('a13', None, None, 'float', '1.2345'), + ast.StructField('a14', None, None, 'float', '-1.2345'), + ast.StructField('a15', None, None, 'float', '+1.2345'), + ast.StructField('a16', None, None, 'float', '123.'), + ast.StructField('a17', None, None, 'float', '.123'), + ast.StructField('a18', None, None, 'double', '1.23E10'), + ast.StructField('a19', None, None, 'double', '1.E-10'), + ast.StructField('a20', None, None, 'double', '.5E+10'), + ast.StructField('a21', None, None, 'double', '-1.23E10'), + ast.StructField('a22', None, None, 'double', '+.123E10')]))]) self.assertEquals(parser.Parse(source, "my_file.mojom"), expected) def testValidFixedSizeArray(self): @@ -668,12 +675,12 @@ class ParserTest(unittest.TestCase): 'MyStruct', None, ast.StructBody( - [ast.StructField('normal_array', None, 'int32[]', None), - ast.StructField('fixed_size_array_one_entry', None, 'int32[1]', - None), - ast.StructField('fixed_size_array_ten_entries', None, + [ast.StructField('normal_array', None, None, 'int32[]', None), + ast.StructField('fixed_size_array_one_entry', None, None, + 'int32[1]', None), + ast.StructField('fixed_size_array_ten_entries', None, None, 'int32[10]', None), - ast.StructField('nested_arrays', None, + ast.StructField('nested_arrays', None, None, 'int32[1][][2]', None)]))]) self.assertEquals(parser.Parse(source, "my_file.mojom"), expected) @@ -688,7 +695,8 @@ class ParserTest(unittest.TestCase): 'MyStruct', None, ast.StructBody( - ast.StructField('nested_array', None, 'int32[][]', None)))]) + ast.StructField('nested_array', None, None, 'int32[][]', + None)))]) self.assertEquals(parser.Parse(source, "my_file.mojom"), expected) def testInvalidFixedArraySize(self): @@ -738,7 +746,7 @@ class ParserTest(unittest.TestCase): 'MyStruct', None, ast.StructBody( - [ast.StructField('data', None, 'uint8{string}', None)]))]) + [ast.StructField('data', None, None, 'uint8{string}', None)]))]) self.assertEquals(parser.Parse(source1, "my_file.mojom"), expected1) source2 = "interface MyInterface { MyMethod(map<string, uint8> a); };" @@ -752,8 +760,9 @@ class ParserTest(unittest.TestCase): ast.Method( 'MyMethod', None, + None, ast.ParameterList( - ast.Parameter('a', None, 'uint8{string}')), + ast.Parameter('a', None, None, 'uint8{string}')), None)))]) self.assertEquals(parser.Parse(source2, "my_file.mojom"), expected2) @@ -765,7 +774,8 @@ class ParserTest(unittest.TestCase): 'MyStruct', None, ast.StructBody( - [ast.StructField('data', None, 'uint8[]{string}', None)]))]) + [ast.StructField('data', None, None, 'uint8[]{string}', + None)]))]) self.assertEquals(parser.Parse(source3, "my_file.mojom"), expected3) def testValidMethod(self): @@ -782,7 +792,8 @@ class ParserTest(unittest.TestCase): ast.Method( 'MyMethod', None, - ast.ParameterList(ast.Parameter('a', None, 'int32')), + None, + ast.ParameterList(ast.Parameter('a', None, None, 'int32')), None)))]) self.assertEquals(parser.Parse(source1, "my_file.mojom"), expected1) @@ -801,14 +812,16 @@ class ParserTest(unittest.TestCase): ast.InterfaceBody( [ast.Method( 'MyMethod1', + None, ast.Ordinal(0), - ast.ParameterList([ast.Parameter('a', ast.Ordinal(0), + ast.ParameterList([ast.Parameter('a', None, ast.Ordinal(0), 'int32'), - ast.Parameter('b', ast.Ordinal(1), + ast.Parameter('b', None, ast.Ordinal(1), 'int64')]), None), ast.Method( 'MyMethod2', + None, ast.Ordinal(1), ast.ParameterList(), ast.ParameterList())]))]) @@ -829,9 +842,11 @@ class ParserTest(unittest.TestCase): ast.Method( 'MyMethod', None, - ast.ParameterList(ast.Parameter('a', None, 'string')), - ast.ParameterList([ast.Parameter('a', None, 'int32'), - ast.Parameter('b', None, 'bool')]))))]) + None, + ast.ParameterList(ast.Parameter('a', None, None, 'string')), + ast.ParameterList([ast.Parameter('a', None, None, 'int32'), + ast.Parameter('b', None, None, + 'bool')]))))]) self.assertEquals(parser.Parse(source3, "my_file.mojom"), expected3) def testInvalidMethods(self): @@ -879,13 +894,17 @@ class ParserTest(unittest.TestCase): None, ast.InterfaceBody( [ast.Enum('MyEnum', - ast.EnumValueList(ast.EnumValue('VALUE', None))), + None, + ast.EnumValueList( + ast.EnumValue('VALUE', None, None))), ast.Const('kMyConst', 'int32', '123'), ast.Method( 'MyMethod', None, - ast.ParameterList(ast.Parameter('x', None, 'int32')), - ast.ParameterList(ast.Parameter('y', None, 'MyEnum')))]))]) + None, + ast.ParameterList(ast.Parameter('x', None, None, 'int32')), + ast.ParameterList(ast.Parameter('y', None, None, + 'MyEnum')))]))]) self.assertEquals(parser.Parse(source, "my_file.mojom"), expected) def testInvalidInterfaceDefinitions(self): @@ -967,6 +986,67 @@ class ParserTest(unittest.TestCase): ast.StructBody())]) self.assertEquals(parser.Parse(source3, "my_file.mojom"), expected3) + # Various places that attribute list is allowed. + source4 = """\ + [Attr0=0] module my_module; + + [Attr1=1] struct MyStruct { + [Attr2=2] int32 a; + }; + [Attr3=3] union MyUnion { + [Attr4=4] int32 a; + }; + [Attr5=5] enum MyEnum { + [Attr6=6] a + }; + [Attr7=7] interface MyInterface { + [Attr8=8] MyMethod([Attr9=9] int32 a) => ([Attr10=10] bool b); + }; + """ + expected4 = ast.Mojom( + ast.Module(('IDENTIFIER', 'my_module'), + ast.AttributeList([ast.Attribute("Attr0", 0)])), + ast.ImportList(), + [ast.Struct( + 'MyStruct', + ast.AttributeList(ast.Attribute("Attr1", 1)), + ast.StructBody( + ast.StructField( + 'a', ast.AttributeList([ast.Attribute("Attr2", 2)]), + None, 'int32', None))), + ast.Union( + 'MyUnion', + ast.AttributeList(ast.Attribute("Attr3", 3)), + ast.UnionBody( + ast.UnionField( + 'a', ast.AttributeList([ast.Attribute("Attr4", 4)]), None, + 'int32'))), + ast.Enum( + 'MyEnum', + ast.AttributeList(ast.Attribute("Attr5", 5)), + ast.EnumValueList( + ast.EnumValue( + 'VALUE', ast.AttributeList([ast.Attribute("Attr6", 6)]), + None))), + ast.Interface( + 'MyInterface', + ast.AttributeList(ast.Attribute("Attr7", 7)), + ast.InterfaceBody( + ast.Method( + 'MyMethod', + ast.AttributeList(ast.Attribute("Attr8", 8)), + None, + ast.ParameterList( + ast.Parameter( + 'a', ast.AttributeList([ast.Attribute("Attr9", 9)]), + None, 'int32')), + ast.ParameterList( + ast.Parameter( + 'b', + ast.AttributeList([ast.Attribute("Attr10", 10)]), + None, 'bool')))))]) + self.assertEquals(parser.Parse(source4, "my_file.mojom"), expected4) + # TODO(vtl): Boolean attributes don't work yet. (In fact, we just |eval()| # literal (non-name) values, which is extremely dubious.) @@ -1100,22 +1180,23 @@ class ParserTest(unittest.TestCase): 'MyStruct', None, ast.StructBody( - [ast.StructField('a', None, 'int32?', None), - ast.StructField('b', None, 'string?', None), - ast.StructField('c', None, 'int32[]?', None), - ast.StructField('d', None, 'string?[]?', None), - ast.StructField('e', None, 'int32[]?[]?', None), - ast.StructField('f', None, 'int32[1]?', None), - ast.StructField('g', None, 'string?[1]?', None), - ast.StructField('h', None, 'some_struct?', None), - ast.StructField('i', None, 'handle?', None), - ast.StructField('j', None, 'handle<data_pipe_consumer>?', + [ast.StructField('a', None, None,'int32?', None), + ast.StructField('b', None, None,'string?', None), + ast.StructField('c', None, None,'int32[]?', None), + ast.StructField('d', None, None,'string?[]?', None), + ast.StructField('e', None, None,'int32[]?[]?', None), + ast.StructField('f', None, None,'int32[1]?', None), + ast.StructField('g', None, None,'string?[1]?', None), + ast.StructField('h', None, None,'some_struct?', None), + ast.StructField('i', None, None,'handle?', None), + ast.StructField('j', None, None,'handle<data_pipe_consumer>?', + None), + ast.StructField('k', None, None,'handle<data_pipe_producer>?', None), - ast.StructField('k', None, 'handle<data_pipe_producer>?', + ast.StructField('l', None, None,'handle<message_pipe>?', None), + ast.StructField('m', None, None,'handle<shared_buffer>?', None), - ast.StructField('l', None, 'handle<message_pipe>?', None), - ast.StructField('m', None, 'handle<shared_buffer>?', None), - ast.StructField('n', None, 'some_interface&?', None)]))]) + ast.StructField('n', None, None,'some_interface&?', None)]))]) self.assertEquals(parser.Parse(source, "my_file.mojom"), expected) def testInvalidNullableTypes(self): @@ -1168,9 +1249,10 @@ class ParserTest(unittest.TestCase): ast.ImportList(), [ast.Union( 'MyUnion', + None, ast.UnionBody([ - ast.UnionField('a', None, 'int32'), - ast.UnionField('b', None, 'double') + ast.UnionField('a', None, None, 'int32'), + ast.UnionField('b', None, None, 'double') ]))]) actual = parser.Parse(source, "my_file.mojom") self.assertEquals(actual, expected) @@ -1190,9 +1272,10 @@ class ParserTest(unittest.TestCase): ast.ImportList(), [ast.Union( 'MyUnion', + None, ast.UnionBody([ - ast.UnionField('a', ast.Ordinal(10), 'int32'), - ast.UnionField('b', ast.Ordinal(30), 'double') + ast.UnionField('a', None, ast.Ordinal(10), 'int32'), + ast.UnionField('b', None, ast.Ordinal(30), 'double') ]))]) actual = parser.Parse(source, "my_file.mojom") self.assertEquals(actual, expected) @@ -1211,8 +1294,9 @@ class ParserTest(unittest.TestCase): ast.ImportList(), [ast.Union( 'MyUnion', + None, ast.UnionBody([ - ast.UnionField('s', None, 'SomeStruct') + ast.UnionField('s', None, None, 'SomeStruct') ]))]) actual = parser.Parse(source, "my_file.mojom") self.assertEquals(actual, expected) @@ -1231,8 +1315,9 @@ class ParserTest(unittest.TestCase): ast.ImportList(), [ast.Union( 'MyUnion', + None, ast.UnionBody([ - ast.UnionField('a', None, 'int32[]') + ast.UnionField('a', None, None, 'int32[]') ]))]) actual = parser.Parse(source, "my_file.mojom") self.assertEquals(actual, expected) @@ -1251,8 +1336,9 @@ class ParserTest(unittest.TestCase): ast.ImportList(), [ast.Union( 'MyUnion', + None, ast.UnionBody([ - ast.UnionField('m', None, 'string{int32}') + ast.UnionField('m', None, None, 'string{int32}') ]))]) actual = parser.Parse(source, "my_file.mojom") self.assertEquals(actual, expected) diff --git a/third_party/mojo/src/mojo/public/tools/bindings/pylib/mojom_tests/parse/translate_unittest.py b/third_party/mojo/src/mojo/public/tools/bindings/pylib/mojom_tests/parse/translate_unittest.py index 9f2d985..20704dc 100644 --- a/third_party/mojo/src/mojo/public/tools/bindings/pylib/mojom_tests/parse/translate_unittest.py +++ b/third_party/mojo/src/mojo/public/tools/bindings/pylib/mojom_tests/parse/translate_unittest.py @@ -49,9 +49,9 @@ class TranslateTest(unittest.TestCase): tree = ast.Mojom( None, ast.ImportList(), - [ast.Union("SomeUnion", ast.UnionBody( - [ast.UnionField("a", None, "int32"), - ast.UnionField("b", None, "string")]))]) + [ast.Union("SomeUnion", None, ast.UnionBody( + [ast.UnionField("a", None, None, "int32"), + ast.UnionField("b", None, None, "string")]))]) expected = [{ "name": "SomeUnion", "fields": [ |