blob: 6e757af06e95a57f3bfc8c8f8324956830d5bf74 [file] [view]
JavaScript Mojo Example Applications
=====================
hello.js, world.js - A minimal application that connects to another.
wget.js - Uses the network service to load a URL.
--- 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 either 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 class. 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.
The overall structure of a JS Mojo application is this:
#!mojo:js_content_handler
define("main", [<list of modules this application depends on>],
function(<one parameter per dependent module>) {
function Application(appShell, url) {
}
Application.prototype.initialize = function(args) {
}
Application.prototype.acceptConnection = function(url, spHandle) {
}
return Application;
});
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 the application's class. 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 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.
--- 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 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:
define("main", [
"mojo/services/public/interfaces/network/network_service.mojom",
"mojo/services/public/js/shell",
]
function(netModule, shellModule) {
function Application(appShell, url) {
this.shell = new shellModule.Shell(appShell);
}
Application.prototype.initialize = function(args) {
var netService = this.shell.connectToService(
"mojo:network_service", netModule.NetworkService);
}
...
return Application;
});
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):
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 its service called
'NetworkService.name' with an instance of 'NetworkService.proxyClass'. The proxy
instance is returned.
--- Interface Parameters ---
Mojo functions with interface valued parameters allow one to request a service
from a service or to provide a service to a service. The
indirect_service example demonstrates this.
The NetworkService CreateURLLoader() method has an interface request parameter:
interface NetworkService {
CreateURLLoader(URLLoader& loader); // Return a URLLoader to the caller.
...
}
Interface request parameters can be specified as an instance of the interface's
proxy class. Ordinary interface parameters can be specified as an instance of
the interface's stub class. The stub class constructor has an optional delegate
parameter that defines the stub's implementation.
Here's an example of an interface request parameter taken from wget.js:
var urlLoader = new loader.URLLoader.proxyClass;
netService.createURLLoader(urlLoader); // interface& parameter
var urlRequest = new loader.URLRequest({
url: "http://www.cnn.com",
method: "GET",
auto_follow_redirects: true
});
urlLoader.start(urlRequest).then(function(result) {
// ..Do something with result.response
});
--- 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.
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:
MyEchoStringImpl.prototype.EchoString = function(s) {
return Promise.resolve({value: s});
};