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