| 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
|
| -}
|
| -```
|
| -
|
|
|