Index: examples/js/README.md |
diff --git a/examples/js/README.md b/examples/js/README.md |
index 7006327a6f4193f6cac616d1f013e2ea3bf9ba50..f65f863f7c1c2400788b86bbba7b5e656ceb4f27 100644 |
--- a/examples/js/README.md |
+++ b/examples/js/README.md |
@@ -1,381 +1,10 @@ |
JavaScript Mojo Example Applications |
===================== |
+users-guide.md - How to build and run JS Mojo applications |
+ |
hello.js, world.js - A minimal application that connects to another. |
wget.js - Uses the network service to load a URL. |
cube.js - A JS version of examples/sample_app. |
- |
---- Running Mojo Applications --- |
- |
-A Mojo application written in JavaScript is launched with mojo_shell like this: |
- |
- mojo_shell <js-application-url> |
- |
-Where js-application-url is a URL understood by the shell. For example |
-a file or an http URL that names a JS source file. The JS file itself |
-must begin with a Mojo "shebang" that specifies the Mojo URL of the JS |
-content handler. In other words, the first line of the JS source file |
-must be: |
- |
- #!mojo:js_content_handler |
- |
-Following the shebang should be a single AMD module called "main" whose value |
-is an Application subclass. The JS content handler will create an instance of |
-the Application and make it the client of the Mojo shell. The JS content handler |
-is itself a Mojo application and it's responsible for creating an instance of V8 |
-and loading the "main" JS module and all of the modules the main module |
-depends on. |
- |
-This is the overall structure of a JS Mojo application: |
- |
-```javascript |
-#!mojo:js_content_handler |
- |
-define("main", ["mojo/services/public/js/application", |
- <list of other modules that this application depends on> |
-], |
- function(appModule, <one parameter per dependent module>) { |
- class MyApplication extends appModule.Application { |
- constructor(appShell, url) { |
- super(appShell, url); // Initializes this.shell, this.url. |
- // MyApplication initializations here. |
- } |
- |
- initialize(args) { |
- } |
- |
- acceptConnection(url, serviceProvider) { |
- } |
- } |
- |
- return MyApplication; |
- }); |
-``` |
- |
-The hello.js example is little more than this basic skeleton. |
- |
-The JS content handler loads the "main" module and makes an instance of its |
-value, which must be an Application subclass. The application's constructor is |
-passed two arguments: |
- |
-appShell - a pointer to the Mojo shell. Typically this will be wrapped by a |
- Shell object, see below. |
- |
-url - the URL this application was loaded from as a String. |
- |
-The (inherited) Application class constructor initializes the shell and url |
-properties. It's unlikely that you'll want to use the appShell argument |
-directly. |
- |
-The initialize() and acceptConnection() methods are defined by application.mojom |
-and they're needed because the JS content handler makes the JS application the |
-Mojo shell's client. |
- |
- |
---- JavaScript Classes --- |
- |
-The JS content handler depends on the ECMAScript6 ("Harmony") classes feature. |
- |
- |
- |
---- Mojo Application Structure --- |
- |
-Mojo applications can connect to services provided by other applications and |
-they can provide services of their own. A service is an implementation of a Mojo |
-interface that was defined as part of a Mojo module in a ".mojom" file. |
- |
-To implement a service you'll need the JS "bindings" for the Mojo interface. The |
-bindings are generated by the build system and end up in files whose name is the |
-same as the '.mojom' file with a '.js' suffix. It's often helpful to look at the |
-generated 'mojom.js' files. |
- |
-The JS Shell class simplifies connecting to applications and services. It's a |
-wrapper for the Application's appShell argument. The Application constructor |
-creates a Shell and assigns it to |this.shell|. |
- |
-The Shell's connectToService() method returns a "proxy" to a service provided by |
-another application. |
- |
-The JS bindings for a Mojo interface's API are delivered as a JS module whose |
-name is based on the '.mojom' file's path. For example, to use the Mojo network |
-service you need the JS module based on network_service.mojom: |
- |
-```javascript |
-define("main", [ |
- "mojo/services/network/public/interfaces/network_service.mojom", |
- "mojo/services/public/js/application", |
-] |
- function(netModule, appModule) { |
- class MyApplication extends appModule.Application { |
- initialize(args) { |
- var netService = this.shell.connectToService( |
- "mojo:network_service", netModule.NetworkService); |
- // Use netService's NetworkService methods. |
- } |
- ... |
- } |
- |
- return MyApplication; |
- }); |
-``` |
- |
-The first connectToService() parameter is the Mojo URL for the network service |
-application and the second is the JS "interface" object for NetworkService. The |
-JS interface object's properties identify the (generated) JS bindings classes |
-used to provide or connect to a service. For example (from |
-network_service.mojom.js): |
- |
-```javascript |
-var NetworkService = { |
- name: 'mojo::NetworkService', // Fully qualified Mojo interface name. |
- proxyClass: NetworkServiceProxy, |
- stubClass: NetworkServiceStub, |
- // ... |
-}; |
-``` |
- |
-The 'proxyClass' is used to access another application's NetworkService and the |
-'stubClass' is used to create an implementation of NetworkService. |
- |
-In the netService case above the Shell connects to the Mojo application at |
-"mojo:network_service", then connects to its service called |
-'NetworkService.name' with an instance of 'NetworkService.proxyClass'. The proxy |
-instance is returned. The netService proxy can be used immediately. |
- |
- |
---- Mojo Responses are Promises --- |
- |
-Mojo functions can return zero or more values called a "response". For example |
-the EchoString function below returns a string or null. |
- |
-```javascript |
-interface EchoService { |
- EchoString(string? value) => (string? value); |
-}; |
-``` |
- |
-The response is delivered to the function caller asynchronously. In C++ the |
-caller provides a Callback object whose Run() method has one argument for |
-each response parameter. In JS, Mojo functions that specify a response return |
-a Promise object. The Promise resolves to an object with one property per |
-response parameter. In the EchoString case that would be something like |
-{value: "foo"}. |
- |
-Similarly, the implementation of a Mojo interface functions that specify a |
-response, must return a Promise. The implementation of EchoString() could |
-be written like this: |
- |
-```javascript |
-MyEchoStringImpl.prototype.EchoString = function(s) { |
- return Promise.resolve({value: s}); |
-}; |
-``` |
- |
-- Applications can request and provide services |
- |
-When an application starts, its initialize() method runs and then its |
-acceptConnection() method runs. The acceptConnection() method |
-indicates that another application has connected to this one and it |
-always runs at least once. |
- |
-```javascript |
-acceptConnection(initiatorURL, serviceProvider) { |
- // provide services to the initiator here |
- // request services from the initiator here |
-} |
-``` |
- |
-The acceptConnection serviceProvider argument can be used to provide |
-services to the initiator, and to request services from the |
-initiator. An application can decide exactly what to do based on the |
-initiator's URL. The serviceProvider argument is-a JS ServiceProvider, |
-an object that wraps a Mojo ServiceProvider proxy. |
- |
-The ServiceProvider requestService() method gets a proxy for a service |
-from the initator and optionally provides a client implementation. |
- |
-The ServiceProvider provideService() method registers an interface |
-implementation factory for a Mojo interface. The factory function is |
-provided with an proxy for the interface's client, if it has one. |
- |
-An application can also connect to other applications and their |
-services using its shell's connectToApplication() and |
-connectToService() methods. The shell's connectToApplication() returns |
-a ServiceProvider. The shell's connectToService() method is just a |
-convenience, it's defined like this: |
- |
-```javascript |
-connectToService(url, service, client) { |
- return this.connectToApplication(url).requestService(service, clientImpl); |
-}; |
-``` |
- |
-The value of service is an interface object that identifies a Mojo |
-interface that the application at url implements. |
- |
-The usage examples that follow are based on the following trivial Mojo |
-interface: |
- |
-```javascript |
-interface EchoService { |
- EchoString(string? value) => (string? value); |
-}; |
-``` |
- |
--- Requesting a service using the Application's Shell |
- |
-Given the URL of a Mojo application that implements the EchoService we |
-can use the application's shell to get an EchoService proxy. Here's a |
-complete application: |
- |
-```javascript |
-#!mojo:js_content_handler |
- |
-define("main", [ |
- "console", |
- "mojo/services/public/js/application", |
- "services/js/test/echo_service.mojom" |
-], function(console, appModule, echoModule) { |
- |
- class EchoShellRequest extends appModule.Application { |
- initialize(args) { |
- var url = "file:/foo/bar/echo.js"; |
- var echoService = this.shell.connectToService(url, echoModule.EchoService); |
- echoService.echoString("foo").then(function(result) { |
- console.log("echoString(foo) => " + result.value); |
- }); |
- } |
- } |
- return EchoShellRequest; |
-}); |
-``` |
- |
- |
--- Providing a service |
- |
-A complete application that unconditionally provides the EchoService |
-looks like this: |
- |
-```javascript |
-#!mojo:js_content_handler |
- |
-define("main", [ |
- "mojo/services/public/js/application", |
- "services/js/test/echo_service.mojom" |
-], function(appModule, echoModule) { |
- |
- class EchoService extends appModule.Application { |
- acceptConnection(initiatorURL, serviceProvider) { |
- function EchoServiceImpl(client) { |
- this.echoString = function(s) { |
- return Promise.resolve({value: s}); |
- }; |
- } |
- serviceProvider.provideService(echoModule.EchoService, EchoServiceImpl); |
- } |
- } |
- return EchoService; |
-}); |
-``` |
- |
-As you can see, EchoServiceImpl is just a function that returns an |
-object that implements the methods in the Mojo EchoService |
-interface. If the EchoService defined a client interface, the factory |
-function's client parameter would be a proxy for the initiator's |
-client service. EchoService doesn't have a client so we could have |
-omitted this parameter. |
- |
-Each time another application connects to this one, the EchoServiceImpl |
-function will be called. The caller will be able to run the |
-echoString() method and will get its response via a Promise. |
- |
- |
--- Final note |
- |
-An initiator's serviceProvider object can be retained and used to |
-request or provide services at any time, not just from within |
-application's acceptConnection() method. |
- |
- |
---- Interface Parameters --- |
- |
-The caller and callee use cases that follow are in terms of the following mojom: |
- |
-``` |
-[Client=Bar] |
-interface Foo { |
-} |
- |
-[Client=Foo] // Redundant but always implicitly true. |
-interface Bar { |
-} |
- |
-interface I { |
- provideFoo(Foo foo); |
- requestFoo(Foo& foo); // effectively: provideFoo(Bar bar) |
-} |
-``` |
- |
--- In General |
- |
-From a user's point of view, the bindings are in terms of the (remote) |
-proxy class and the (local) stub class's implementation delegate |
-(internally, that's the stub class's delegate$ property). The |
-bindings will add/use a local$ property on proxy objects which points |
-to the stub class's implementation delegate. They also manage remote$ |
-property on the implementation delegate whose value is the proxy. |
- |
-All that implies: |
- |
-fooImpl.remote$.local$ == fooImpl (the stub class's delegate) |
-fooProxy.local$.remote$ == fooProxy |
- |
- |
--- Callers |
- |
-Assuming that we have a proxy for interface I, iProxy. |
- |
-An iProxy.provideFoo() call implies that we have an implementation of |
-Foo, and want a proxy for Bar (Foo's client). |
- |
-```javascript |
-var myFooImpl; |
-provideFoo(myFooImpl); |
-myFooImpl.remote$; // A Bar proxy initialized by provideFoo(), undefined if Foo has no client. |
-``` |
- |
-An iProxy.requestFoo() call implies that we have an implementation of |
-Bar and want a proxy for Foo (Bar's client). |
- |
-```javascript |
-var myBarImpl; // If Foo has no client then this is just {}. |
-requestFoo(myBarImpl); |
-myBarImpl.remote$; // A Foo proxy initialized by requestFoo. |
-``` |
- |
-The wget.js example includes a request for the URLLoader service. |
- |
--- Callees |
- |
-An implementation of provideFoo(Foo foo) implies that we have an |
-implementation of Bar (Foo's client) and want a proxy to the Foo |
-that has been passed to us. |
- |
-```javascript |
-void provideFoo(fooProxy) { |
- fooProxy.local$ = myBarImpl; // sets myFooImpl.remote$ = fooProxy |
-} |
-``` |
- |
-An implementation of requestFoo(Foo& foo) implies that we have an |
-implementation of Foo and want a proxy for the Bar (Foo's client) |
-that's been passed to us. |
- |
-```javascript |
-void requestFoo(barProxy) { |
- barProxy.local$ = myFooImpl; // sets myFooImpl.remote$ = barProxy |
-} |
-``` |
- |