| OLD | NEW |
| 1 JavaScript Mojo Example Applications | 1 JavaScript Mojo Example Applications |
| 2 ===================== | 2 ===================== |
| 3 | 3 |
| 4 users-guide.md - How to build and run JS Mojo applications |
| 5 |
| 4 hello.js, world.js - A minimal application that connects to another. | 6 hello.js, world.js - A minimal application that connects to another. |
| 5 | 7 |
| 6 wget.js - Uses the network service to load a URL. | 8 wget.js - Uses the network service to load a URL. |
| 7 | 9 |
| 8 cube.js - A JS version of examples/sample_app. | 10 cube.js - A JS version of examples/sample_app. |
| 9 | |
| 10 --- Running Mojo Applications --- | |
| 11 | |
| 12 A Mojo application written in JavaScript is launched with mojo_shell like this: | |
| 13 | |
| 14 mojo_shell <js-application-url> | |
| 15 | |
| 16 Where js-application-url is a URL understood by the shell. For example | |
| 17 a file or an http URL that names a JS source file. The JS file itself | |
| 18 must begin with a Mojo "shebang" that specifies the Mojo URL of the JS | |
| 19 content handler. In other words, the first line of the JS source file | |
| 20 must be: | |
| 21 | |
| 22 #!mojo:js_content_handler | |
| 23 | |
| 24 Following the shebang should be a single AMD module called "main" whose value | |
| 25 is an Application subclass. The JS content handler will create an instance of | |
| 26 the Application and make it the client of the Mojo shell. The JS content handler | |
| 27 is itself a Mojo application and it's responsible for creating an instance of V8 | |
| 28 and loading the "main" JS module and all of the modules the main module | |
| 29 depends on. | |
| 30 | |
| 31 This is the overall structure of a JS Mojo application: | |
| 32 | |
| 33 ```javascript | |
| 34 #!mojo:js_content_handler | |
| 35 | |
| 36 define("main", ["mojo/services/public/js/application", | |
| 37 <list of other modules that this application depends on> | |
| 38 ], | |
| 39 function(appModule, <one parameter per dependent module>) { | |
| 40 class MyApplication extends appModule.Application { | |
| 41 constructor(appShell, url) { | |
| 42 super(appShell, url); // Initializes this.shell, this.url. | |
| 43 // MyApplication initializations here. | |
| 44 } | |
| 45 | |
| 46 initialize(args) { | |
| 47 } | |
| 48 | |
| 49 acceptConnection(url, serviceProvider) { | |
| 50 } | |
| 51 } | |
| 52 | |
| 53 return MyApplication; | |
| 54 }); | |
| 55 ``` | |
| 56 | |
| 57 The hello.js example is little more than this basic skeleton. | |
| 58 | |
| 59 The JS content handler loads the "main" module and makes an instance of its | |
| 60 value, which must be an Application subclass. The application's constructor is | |
| 61 passed two arguments: | |
| 62 | |
| 63 appShell - a pointer to the Mojo shell. Typically this will be wrapped by a | |
| 64 Shell object, see below. | |
| 65 | |
| 66 url - the URL this application was loaded from as a String. | |
| 67 | |
| 68 The (inherited) Application class constructor initializes the shell and url | |
| 69 properties. It's unlikely that you'll want to use the appShell argument | |
| 70 directly. | |
| 71 | |
| 72 The initialize() and acceptConnection() methods are defined by application.mojom | |
| 73 and they're needed because the JS content handler makes the JS application the | |
| 74 Mojo shell's client. | |
| 75 | |
| 76 | |
| 77 --- JavaScript Classes --- | |
| 78 | |
| 79 The JS content handler depends on the ECMAScript6 ("Harmony") classes feature. | |
| 80 | |
| 81 | |
| 82 | |
| 83 --- Mojo Application Structure --- | |
| 84 | |
| 85 Mojo applications can connect to services provided by other applications and | |
| 86 they can provide services of their own. A service is an implementation of a Mojo | |
| 87 interface that was defined as part of a Mojo module in a ".mojom" file. | |
| 88 | |
| 89 To implement a service you'll need the JS "bindings" for the Mojo interface. The | |
| 90 bindings are generated by the build system and end up in files whose name is the | |
| 91 same as the '.mojom' file with a '.js' suffix. It's often helpful to look at the | |
| 92 generated 'mojom.js' files. | |
| 93 | |
| 94 The JS Shell class simplifies connecting to applications and services. It's a | |
| 95 wrapper for the Application's appShell argument. The Application constructor | |
| 96 creates a Shell and assigns it to |this.shell|. | |
| 97 | |
| 98 The Shell's connectToService() method returns a "proxy" to a service provided by | |
| 99 another application. | |
| 100 | |
| 101 The JS bindings for a Mojo interface's API are delivered as a JS module whose | |
| 102 name is based on the '.mojom' file's path. For example, to use the Mojo network | |
| 103 service you need the JS module based on network_service.mojom: | |
| 104 | |
| 105 ```javascript | |
| 106 define("main", [ | |
| 107 "mojo/services/network/public/interfaces/network_service.mojom", | |
| 108 "mojo/services/public/js/application", | |
| 109 ] | |
| 110 function(netModule, appModule) { | |
| 111 class MyApplication extends appModule.Application { | |
| 112 initialize(args) { | |
| 113 var netService = this.shell.connectToService( | |
| 114 "mojo:network_service", netModule.NetworkService); | |
| 115 // Use netService's NetworkService methods. | |
| 116 } | |
| 117 ... | |
| 118 } | |
| 119 | |
| 120 return MyApplication; | |
| 121 }); | |
| 122 ``` | |
| 123 | |
| 124 The first connectToService() parameter is the Mojo URL for the network service | |
| 125 application and the second is the JS "interface" object for NetworkService. The | |
| 126 JS interface object's properties identify the (generated) JS bindings classes | |
| 127 used to provide or connect to a service. For example (from | |
| 128 network_service.mojom.js): | |
| 129 | |
| 130 ```javascript | |
| 131 var NetworkService = { | |
| 132 name: 'mojo::NetworkService', // Fully qualified Mojo interface name. | |
| 133 proxyClass: NetworkServiceProxy, | |
| 134 stubClass: NetworkServiceStub, | |
| 135 // ... | |
| 136 }; | |
| 137 ``` | |
| 138 | |
| 139 The 'proxyClass' is used to access another application's NetworkService and the | |
| 140 'stubClass' is used to create an implementation of NetworkService. | |
| 141 | |
| 142 In the netService case above the Shell connects to the Mojo application at | |
| 143 "mojo:network_service", then connects to its service called | |
| 144 'NetworkService.name' with an instance of 'NetworkService.proxyClass'. The proxy | |
| 145 instance is returned. The netService proxy can be used immediately. | |
| 146 | |
| 147 | |
| 148 --- Mojo Responses are Promises --- | |
| 149 | |
| 150 Mojo functions can return zero or more values called a "response". For example | |
| 151 the EchoString function below returns a string or null. | |
| 152 | |
| 153 ```javascript | |
| 154 interface EchoService { | |
| 155 EchoString(string? value) => (string? value); | |
| 156 }; | |
| 157 ``` | |
| 158 | |
| 159 The response is delivered to the function caller asynchronously. In C++ the | |
| 160 caller provides a Callback object whose Run() method has one argument for | |
| 161 each response parameter. In JS, Mojo functions that specify a response return | |
| 162 a Promise object. The Promise resolves to an object with one property per | |
| 163 response parameter. In the EchoString case that would be something like | |
| 164 {value: "foo"}. | |
| 165 | |
| 166 Similarly, the implementation of a Mojo interface functions that specify a | |
| 167 response, must return a Promise. The implementation of EchoString() could | |
| 168 be written like this: | |
| 169 | |
| 170 ```javascript | |
| 171 MyEchoStringImpl.prototype.EchoString = function(s) { | |
| 172 return Promise.resolve({value: s}); | |
| 173 }; | |
| 174 ``` | |
| 175 | |
| 176 - Applications can request and provide services | |
| 177 | |
| 178 When an application starts, its initialize() method runs and then its | |
| 179 acceptConnection() method runs. The acceptConnection() method | |
| 180 indicates that another application has connected to this one and it | |
| 181 always runs at least once. | |
| 182 | |
| 183 ```javascript | |
| 184 acceptConnection(initiatorURL, serviceProvider) { | |
| 185 // provide services to the initiator here | |
| 186 // request services from the initiator here | |
| 187 } | |
| 188 ``` | |
| 189 | |
| 190 The acceptConnection serviceProvider argument can be used to provide | |
| 191 services to the initiator, and to request services from the | |
| 192 initiator. An application can decide exactly what to do based on the | |
| 193 initiator's URL. The serviceProvider argument is-a JS ServiceProvider, | |
| 194 an object that wraps a Mojo ServiceProvider proxy. | |
| 195 | |
| 196 The ServiceProvider requestService() method gets a proxy for a service | |
| 197 from the initator and optionally provides a client implementation. | |
| 198 | |
| 199 The ServiceProvider provideService() method registers an interface | |
| 200 implementation factory for a Mojo interface. The factory function is | |
| 201 provided with an proxy for the interface's client, if it has one. | |
| 202 | |
| 203 An application can also connect to other applications and their | |
| 204 services using its shell's connectToApplication() and | |
| 205 connectToService() methods. The shell's connectToApplication() returns | |
| 206 a ServiceProvider. The shell's connectToService() method is just a | |
| 207 convenience, it's defined like this: | |
| 208 | |
| 209 ```javascript | |
| 210 connectToService(url, service, client) { | |
| 211 return this.connectToApplication(url).requestService(service, clientImpl); | |
| 212 }; | |
| 213 ``` | |
| 214 | |
| 215 The value of service is an interface object that identifies a Mojo | |
| 216 interface that the application at url implements. | |
| 217 | |
| 218 The usage examples that follow are based on the following trivial Mojo | |
| 219 interface: | |
| 220 | |
| 221 ```javascript | |
| 222 interface EchoService { | |
| 223 EchoString(string? value) => (string? value); | |
| 224 }; | |
| 225 ``` | |
| 226 | |
| 227 -- Requesting a service using the Application's Shell | |
| 228 | |
| 229 Given the URL of a Mojo application that implements the EchoService we | |
| 230 can use the application's shell to get an EchoService proxy. Here's a | |
| 231 complete application: | |
| 232 | |
| 233 ```javascript | |
| 234 #!mojo:js_content_handler | |
| 235 | |
| 236 define("main", [ | |
| 237 "console", | |
| 238 "mojo/services/public/js/application", | |
| 239 "services/js/test/echo_service.mojom" | |
| 240 ], function(console, appModule, echoModule) { | |
| 241 | |
| 242 class EchoShellRequest extends appModule.Application { | |
| 243 initialize(args) { | |
| 244 var url = "file:/foo/bar/echo.js"; | |
| 245 var echoService = this.shell.connectToService(url, echoModule.EchoService)
; | |
| 246 echoService.echoString("foo").then(function(result) { | |
| 247 console.log("echoString(foo) => " + result.value); | |
| 248 }); | |
| 249 } | |
| 250 } | |
| 251 return EchoShellRequest; | |
| 252 }); | |
| 253 ``` | |
| 254 | |
| 255 | |
| 256 -- Providing a service | |
| 257 | |
| 258 A complete application that unconditionally provides the EchoService | |
| 259 looks like this: | |
| 260 | |
| 261 ```javascript | |
| 262 #!mojo:js_content_handler | |
| 263 | |
| 264 define("main", [ | |
| 265 "mojo/services/public/js/application", | |
| 266 "services/js/test/echo_service.mojom" | |
| 267 ], function(appModule, echoModule) { | |
| 268 | |
| 269 class EchoService extends appModule.Application { | |
| 270 acceptConnection(initiatorURL, serviceProvider) { | |
| 271 function EchoServiceImpl(client) { | |
| 272 this.echoString = function(s) { | |
| 273 return Promise.resolve({value: s}); | |
| 274 }; | |
| 275 } | |
| 276 serviceProvider.provideService(echoModule.EchoService, EchoServiceImpl); | |
| 277 } | |
| 278 } | |
| 279 return EchoService; | |
| 280 }); | |
| 281 ``` | |
| 282 | |
| 283 As you can see, EchoServiceImpl is just a function that returns an | |
| 284 object that implements the methods in the Mojo EchoService | |
| 285 interface. If the EchoService defined a client interface, the factory | |
| 286 function's client parameter would be a proxy for the initiator's | |
| 287 client service. EchoService doesn't have a client so we could have | |
| 288 omitted this parameter. | |
| 289 | |
| 290 Each time another application connects to this one, the EchoServiceImpl | |
| 291 function will be called. The caller will be able to run the | |
| 292 echoString() method and will get its response via a Promise. | |
| 293 | |
| 294 | |
| 295 -- Final note | |
| 296 | |
| 297 An initiator's serviceProvider object can be retained and used to | |
| 298 request or provide services at any time, not just from within | |
| 299 application's acceptConnection() method. | |
| 300 | |
| 301 | |
| 302 --- Interface Parameters --- | |
| 303 | |
| 304 The caller and callee use cases that follow are in terms of the following mojom
: | |
| 305 | |
| 306 ``` | |
| 307 [Client=Bar] | |
| 308 interface Foo { | |
| 309 } | |
| 310 | |
| 311 [Client=Foo] // Redundant but always implicitly true. | |
| 312 interface Bar { | |
| 313 } | |
| 314 | |
| 315 interface I { | |
| 316 provideFoo(Foo foo); | |
| 317 requestFoo(Foo& foo); // effectively: provideFoo(Bar bar) | |
| 318 } | |
| 319 ``` | |
| 320 | |
| 321 -- In General | |
| 322 | |
| 323 From a user's point of view, the bindings are in terms of the (remote) | |
| 324 proxy class and the (local) stub class's implementation delegate | |
| 325 (internally, that's the stub class's delegate$ property). The | |
| 326 bindings will add/use a local$ property on proxy objects which points | |
| 327 to the stub class's implementation delegate. They also manage remote$ | |
| 328 property on the implementation delegate whose value is the proxy. | |
| 329 | |
| 330 All that implies: | |
| 331 | |
| 332 fooImpl.remote$.local$ == fooImpl (the stub class's delegate) | |
| 333 fooProxy.local$.remote$ == fooProxy | |
| 334 | |
| 335 | |
| 336 -- Callers | |
| 337 | |
| 338 Assuming that we have a proxy for interface I, iProxy. | |
| 339 | |
| 340 An iProxy.provideFoo() call implies that we have an implementation of | |
| 341 Foo, and want a proxy for Bar (Foo's client). | |
| 342 | |
| 343 ```javascript | |
| 344 var myFooImpl; | |
| 345 provideFoo(myFooImpl); | |
| 346 myFooImpl.remote$; // A Bar proxy initialized by provideFoo(), undefined if Foo
has no client. | |
| 347 ``` | |
| 348 | |
| 349 An iProxy.requestFoo() call implies that we have an implementation of | |
| 350 Bar and want a proxy for Foo (Bar's client). | |
| 351 | |
| 352 ```javascript | |
| 353 var myBarImpl; // If Foo has no client then this is just {}. | |
| 354 requestFoo(myBarImpl); | |
| 355 myBarImpl.remote$; // A Foo proxy initialized by requestFoo. | |
| 356 ``` | |
| 357 | |
| 358 The wget.js example includes a request for the URLLoader service. | |
| 359 | |
| 360 -- Callees | |
| 361 | |
| 362 An implementation of provideFoo(Foo foo) implies that we have an | |
| 363 implementation of Bar (Foo's client) and want a proxy to the Foo | |
| 364 that has been passed to us. | |
| 365 | |
| 366 ```javascript | |
| 367 void provideFoo(fooProxy) { | |
| 368 fooProxy.local$ = myBarImpl; // sets myFooImpl.remote$ = fooProxy | |
| 369 } | |
| 370 ``` | |
| 371 | |
| 372 An implementation of requestFoo(Foo& foo) implies that we have an | |
| 373 implementation of Foo and want a proxy for the Bar (Foo's client) | |
| 374 that's been passed to us. | |
| 375 | |
| 376 ```javascript | |
| 377 void requestFoo(barProxy) { | |
| 378 barProxy.local$ = myFooImpl; // sets myFooImpl.remote$ = barProxy | |
| 379 } | |
| 380 ``` | |
| 381 | |
| OLD | NEW |