| OLD | NEW |
| 1 # Running Mojo Applications | 1 # Running Mojo Applications |
| 2 | 2 |
| 3 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: |
| 4 | 4 |
| 5 ``` | 5 ``` |
| 6 mojo_shell <js-application-url> | 6 mojo_shell <js-application-url> |
| 7 ``` | 7 ``` |
| 8 | 8 |
| 9 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 |
| 10 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 |
| (...skipping 66 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 77 The inherited Application class constructor initializes the shell and url | 77 The inherited Application class constructor initializes the shell and url |
| 78 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 |
| 79 directly. | 79 directly. |
| 80 | 80 |
| 81 The initialize() and acceptConnection() methods are defined by application.mojom | 81 The initialize() and acceptConnection() methods are defined by application.mojom |
| 82 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 |
| 83 Mojo shell's client. | 83 Mojo shell's client. |
| 84 | 84 |
| 85 The intiailize() method is called once, after the constructor has run | 85 The intiailize() method is called once, after the constructor has run |
| 86 and before any calls to acceptConnection(). The value of its parameter | 86 and before any calls to acceptConnection(). The value of its parameter |
| 87 is argument list specified for this application with | 87 is the argument list specified for this application with |
| 88 mojo_shell. Arguments can be specified using the mojo_shell | 88 mojo_shell. Arguments can be specified using the mojo_shell |
| 89 `--args-for` command line argument or by just adding them after the | 89 `--args-for` command line argument or by just adding them after the |
| 90 application's URL and enclosing the entire expression in quotes. | 90 application's URL and enclosing the entire expression in quotes: |
| 91 | 91 |
| 92 ``` | 92 ``` |
| 93 mojo_shell '<js-application-url> arg1 arg2' | 93 mojo_shell '<js-application-url> arg1 arg2' |
| 94 ``` | 94 ``` |
| 95 See the wget.js for an example of command-line argument use. | 95 See the wget.js for an example of command-line argument use. |
| 96 | 96 |
| 97 The acceptConnection() method is called each time another application connects | 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. | 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 | 99 The serviceProvider parameter is a JS ServiceProvider, see the "Requesting |
| 100 and Providing Services" section below. | 100 and Providing Services" section below. |
| 101 | 101 |
| 102 ## JS Bindings | 102 # JS Bindings |
| 103 | 103 |
| 104 To use or implement a service you'll need the JS "bindings" for the | 104 The JS bindings map from incoming Mojo messages to JS values, and |
| 105 Mojo interface. The bindings are generated by the build system and end | 105 similarly from outgoing JS values to Mojo messages. |
| 106 |
| 107 To use or implement a service you'll need the JS bindings for the |
| 108 service's 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' | 109 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. | 110 suffix. It's often helpful to look at the generated '.mojom.js' files. |
| 108 | 111 |
| 109 | |
| 110 The JS bindings for a Mojo interface's API are delivered as a JS module whose | 112 The JS bindings for a Mojo interface's API are delivered as a JS module whose |
| 111 name is based on the '.mojom' file's path. For example, to use the Mojo network | 113 name is based on the '.mojom' file's path. For example, to use the Mojo network |
| 112 service you need the JS module based on network_service.mojom: | 114 service you need the JS module based on network_service.mojom: |
| 113 | 115 |
| 114 ```javascript | 116 ```javascript |
| 115 define("main", [ | 117 define("main", [ |
| 116 "mojo/services/network/public/interfaces/network_service.mojom", | 118 "mojo/services/network/public/interfaces/network_service.mojom", |
| 117 "mojo/services/public/js/application", | 119 "mojo/services/public/js/application", |
| 118 ] | 120 ] |
| 119 function(net, application) { | 121 function(net, application) { |
| (...skipping 22 matching lines...) Expand all Loading... |
| 142 name: 'mojo::NetworkService', // Fully qualified Mojo interface name. | 144 name: 'mojo::NetworkService', // Fully qualified Mojo interface name. |
| 143 proxyClass: NetworkServiceProxy, | 145 proxyClass: NetworkServiceProxy, |
| 144 stubClass: NetworkServiceStub, | 146 stubClass: NetworkServiceStub, |
| 145 // ... | 147 // ... |
| 146 }; | 148 }; |
| 147 ``` | 149 ``` |
| 148 | 150 |
| 149 The 'proxyClass' is used to access another application's NetworkService and the | 151 The 'proxyClass' is used to access another application's NetworkService and the |
| 150 'stubClass' is used to create an implementation of NetworkService. | 152 'stubClass' is used to create an implementation of NetworkService. |
| 151 | 153 |
| 152 ## The JS Shell Class | 154 ## JS Bindings for Basic Types |
| 153 | 155 |
| 154 The JS Shell class simplifies connecting to applications and services. It's a | 156 In most cases the mapping from Mojo types to JS types is simple. |
| 155 wrapper for the Application's appShell argument. The Application constructor | |
| 156 creates a Shell and assigns it to `this.shell`. | |
| 157 | 157 |
| 158 The Shell's connectToService() method returns a "proxy" to a service provided by | 158 Mojo Type | JS Type |
| 159 another application. | 159 ------------- | ------------- |
| 160 bool | true or false |
| 161 int8, uint8 | Number |
| 162 int16, uint16 | Number |
| 163 int32, uint32 | Number |
| 164 int64, uint64 | Number* |
| 165 float, double | Number |
| 166 string | String |
| 167 array | Array |
| 168 map | Map |
| 160 | 169 |
| 161 In the netService case above the Shell connects to the Mojo application at | 170 The support for 64 bit integers is currently limited to 53 bits per |
| 162 "mojo:network_service", then connects to its service called | 171 the current JS standard. Only integer values in the range from |
| 163 NetworkService.name with an instance of NetworkService.proxyClass. The proxy | 172 ``Number.MIN_SAFE_INTEGER`` to ``Number.MAX_SAFE_INTEGER`` can be |
| 164 instance is returned. The netService proxy can be used immediately. | 173 represented exactly. Larger and smaller values are approximated by |
| 174 double precision Numbers. |
| 165 | 175 |
| 176 Unspecified bool parameter or struct field values default to false |
| 177 unless an explicit Mojo default was specified. |
| 178 |
| 179 Unspecified integer values similarly default to 0. |
| 180 |
| 181 Unspecified nullable string, array, and map values similarly default to |
| 182 null and can be specified as null. In Mojom a nullable type has a |
| 183 ``?`` suffix. |
| 184 |
| 185 ## JS Bindings for Structs |
| 186 |
| 187 Mojo structs are mapped to JS objects. An eponymous class is generated for each |
| 188 struct type. The struct class constructor has an object-valued parameter to make |
| 189 it a little easier to specify a struct value. For example: |
| 190 |
| 191 ```javascript |
| 192 // Mojom definitions |
| 193 struct Foo { |
| 194 string? name; |
| 195 array<int32>? values; |
| 196 }; |
| 197 |
| 198 interface I { |
| 199 PassFoo(Foo foo); |
| 200 } |
| 201 |
| 202 // JavaScript Usage, assuming we have a proxy for I, iProxy |
| 203 |
| 204 // Rough and ready struct construction: |
| 205 var foo = new Foo; |
| 206 foo.name = "foo"; |
| 207 foo.values = [1,2,3]; |
| 208 iProxy.PassFoo(foo); |
| 209 |
| 210 // Using the Foo constructor parameter: |
| 211 iProxy.PassFoo(new Foo({name: "foo", values: [1,2,3]})); |
| 212 iProxy.PassFoo(new Foo); // name, values are null |
| 213 iProxy.PassFoo(new Foo({name: null}); // Same as previous line. |
| 214 ``` |
| 215 |
| 216 An unspecified nullable struct parameter or struct field value defaults to |
| 217 null. |
| 218 |
| 219 |
| 220 ## JS Bindings for Interface Parameters |
| 221 |
| 222 ## Stubs and Proxies |
| 223 |
| 224 TODO: briefly introduce message pipes. |
| 225 TODO: explain what stubs and proxies are, explain what's meant by "local" and "r
emote". |
| 226 TODO: explain the StubBindings and ProxyBindings functions. |
| 227 TODO: support creating a proxy from a handle new MyProxy(someHandle); |
| 228 TODO: explain the Connection object and how it relates to this stuff. |
| 229 |
| 230 From a user's point of view, the bindings are in terms of the (remote) |
| 231 proxy class and the (local) stub class. Properties are added to instances |
| 232 of these classes using functions called StubBindings and ProxyBindings. |
| 233 |
| 234 The caller and callee use cases that follow are in terms of the following mojom
: |
| 235 |
| 236 ``` |
| 237 [Client=Bar] |
| 238 interface Foo { |
| 239 } |
| 240 |
| 241 [Client=Foo] // Redundant but always implicitly true. |
| 242 interface Bar { |
| 243 } |
| 244 |
| 245 interface I { |
| 246 provideFoo(Foo foo); |
| 247 requestFoo(Foo& foo); // effectively: provideFoo(Bar bar) |
| 248 } |
| 249 ``` |
| 250 |
| 251 ## Callers |
| 252 |
| 253 Assuming that we have a proxy for interface I, iProxy. |
| 254 |
| 255 An iProxy.provideFoo() call implies that we have an implementation of |
| 256 Foo, and want a proxy for Bar (Foo's client). |
| 257 |
| 258 ```javascript |
| 259 var barProxy; |
| 260 iProxy.provideFoo(function(remote) { |
| 261 barProxy = remote; |
| 262 return myFooImpl; |
| 263 }); |
| 264 ``` |
| 265 |
| 266 An iProxy.requestFoo() call implies that we have an implementation of |
| 267 Bar and want a proxy for Foo (Bar's client). |
| 268 |
| 269 ```javascript |
| 270 var fooProxy; |
| 271 iProxy.requestFoo(function(remote) { |
| 272 fooProxy = remote; |
| 273 return myBarImpl; |
| 274 }); |
| 275 ``` |
| 276 |
| 277 In the requestFoo() case, if no client were defined for Bar the function |
| 278 parameter need not return anything. |
| 279 |
| 280 The wget.js example includes a request for the URLLoader service. |
| 281 |
| 282 ## Callees |
| 283 |
| 284 An implementation of provideFoo(Foo foo) implies that we have an |
| 285 implementation of Bar (Foo's client) and want a proxy to the Foo |
| 286 that has been passed to us. |
| 287 |
| 288 ```javascript |
| 289 void provideFoo(fooProxy) { |
| 290 ProxyBindings(fooProxy).setLocalDelegate(myMyBarImpl); |
| 291 } |
| 292 ``` |
| 293 |
| 294 An implementation of requestFoo(Foo& foo) implies that we have an |
| 295 implementation of Foo and want a proxy for the Bar (Foo's client) |
| 296 that's been passed to us. |
| 297 |
| 298 ```javascript |
| 299 void requestFoo(barProxy) { |
| 300 ProxyBindings(barProxy).setLocallocalDelegate(myFooImpl); |
| 301 } |
| 302 ``` |
| 166 | 303 |
| 167 ## Mojo Responses are Promises | 304 ## Mojo Responses are Promises |
| 168 | 305 |
| 169 Mojo functions can return zero or more values called a "response". For example | 306 Mojo functions can return zero or more values called a "response". For example |
| 170 the EchoString function below returns a string or null. | 307 the EchoString function below returns a string or null. |
| 171 | 308 |
| 172 ```javascript | 309 ```javascript |
| 173 interface EchoService { | 310 interface EchoService { |
| 174 EchoString(string? value) => (string? value); | 311 EchoString(string? value) => (string? value); |
| 175 }; | 312 }; |
| 176 ``` | 313 ``` |
| 177 | 314 |
| 178 The response is delivered to the function caller asynchronously. In C++ the | 315 The response is delivered to the function caller asynchronously. In C++ the |
| 179 caller provides a Callback object whose Run() method has one argument for | 316 caller provides a Callback object whose Run() method has one argument for |
| 180 each response parameter. In JS, Mojo functions that specify a response return | 317 each response parameter. In JS, Mojo functions that specify a response return |
| 181 a Promise object. The Promise resolves to an object with one property per | 318 a Promise object. The Promise resolves to an object with one property per |
| 182 response parameter. In the EchoString case that would be something like | 319 response parameter. In the EchoString case that would be something like |
| 183 `{value: "foo"}`. | 320 `{value: "foo"}`. |
| 184 | 321 |
| 185 Similarly, the implementation of a Mojo interface functions that specify a | 322 Similarly, the implementation of a Mojo interface functions that specify a |
| 186 response, must return a Promise. The implementation of EchoString() could | 323 response, must return a Promise. The implementation of EchoString() could |
| 187 be written like this: | 324 be written like this: |
| 188 | 325 |
| 189 ```javascript | 326 ```javascript |
| 190 MyEchoStringImpl.prototype.EchoString = function(s) { | 327 MyEchoStringImpl.prototype.EchoString = function(s) { |
| 191 return Promise.resolve({value: s}); | 328 return Promise.resolve({value: s}); |
| 192 }; | 329 }; |
| 193 ``` | 330 ``` |
| 194 | 331 |
| 332 # The JS Shell Class |
| 333 |
| 334 The JS Shell class simplifies connecting to applications and services. It's a |
| 335 wrapper for the Application's appShell argument. The Application constructor |
| 336 creates a Shell and assigns it to `this.shell`. |
| 337 |
| 338 The Shell's connectToService() method returns a "proxy" to a service provided by |
| 339 another application. |
| 340 |
| 341 ```javascript |
| 342 define("main", [ |
| 343 "mojo/services/network/public/interfaces/network_service.mojom", |
| 344 "mojo/services/public/js/application", |
| 345 ] |
| 346 function(net, application) { |
| 347 class MyApplication extends application.Application { |
| 348 initialize(args) { |
| 349 var netService = this.shell.connectToService( |
| 350 "mojo:network_service", net.NetworkService); |
| 351 // Use netService's NetworkService methods. |
| 352 } |
| 353 ... |
| 354 } |
| 355 ... |
| 356 } |
| 357 return MyApplication; |
| 358 }); |
| 359 ``` |
| 360 |
| 361 In the netService case above the Shell connects to the Mojo application at |
| 362 "mojo:network_service", then connects to its service called |
| 363 NetworkService.name with an instance of NetworkService.proxyClass. The proxy |
| 364 instance is returned. The netService proxy can be used immediately. |
| 365 |
| 366 The wget.js example demonstrates using the network service. |
| 367 |
| 195 ## Requesting and Providing Services | 368 ## Requesting and Providing Services |
| 196 | 369 |
| 197 Mojo applications can connect to services provided by other applications and | 370 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 | 371 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. | 372 interface that was defined as part of a Mojo module in a ".mojom" file. |
| 200 | 373 |
| 201 When an application starts, its initialize() method runs and then its | 374 When an application starts, its initialize() method runs and then its |
| 202 acceptConnection() method runs. The acceptConnection() method | 375 acceptConnection() method runs. The acceptConnection() method |
| 203 indicates that another application has connected to this one and it | 376 indicates that another application has connected to this one and it |
| 204 always runs at least once. | 377 always runs at least once. |
| (...skipping 37 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 242 interface: | 415 interface: |
| 243 | 416 |
| 244 ```javascript | 417 ```javascript |
| 245 interface EchoService { | 418 interface EchoService { |
| 246 EchoString(string? value) => (string? value); | 419 EchoString(string? value) => (string? value); |
| 247 }; | 420 }; |
| 248 ``` | 421 ``` |
| 249 | 422 |
| 250 ### Requesting a Service Using the Application's Shell | 423 ### Requesting a Service Using the Application's Shell |
| 251 | 424 |
| 425 The Shell's `connectToService()` method returns a proxy to a Mojo |
| 426 service provided by another application. The proxy can be used immediately. |
| 427 |
| 252 Given the URL of a Mojo application that implements the EchoService we | 428 Given the URL of a Mojo application that implements the EchoService we |
| 253 can use the application's shell to get an EchoService proxy. Here's a | 429 can use the application's shell to get an EchoService proxy. Here's a |
| 254 complete application: | 430 complete application: |
| 255 | 431 |
| 256 ```javascript | 432 ```javascript |
| 257 #!mojo:js_content_handler | 433 #!mojo:js_content_handler |
| 258 | 434 |
| 259 define("main", [ | 435 define("main", [ |
| 260 "console", | 436 "console", |
| 261 "mojo/services/public/js/application", | 437 "mojo/services/public/js/application", |
| 262 "services/js/test/echo_service.mojom" | 438 "services/js/test/echo_service.mojom" |
| 263 ], function(console, appModule, echoModule) { | 439 ], function(console, appModule, echoModule) { |
| 264 | 440 |
| 265 class EchoShellRequest extends appModule.Application { | 441 class EchoShellRequest extends appModule.Application { |
| 266 initialize(args) { | 442 initialize(args) { |
| 267 var url = "file:/foo/bar/echo.js"; | 443 var url = "file:/foo/bar/echo.js"; |
| 268 var echoService = this.shell.connectToService(url, echoModule.EchoService)
; | 444 var echoService = this.shell.connectToService(url, echoModule.EchoService)
; |
| 269 echoService.echoString("foo").then(function(result) { | 445 echoService.echoString("foo").then(function(result) { |
| 270 console.log("echoString(foo) => " + result.value); | 446 console.log("echoString(foo) => " + result.value); |
| 271 }); | 447 }); |
| 272 } | 448 } |
| 273 } | 449 } |
| 274 return EchoShellRequest; | 450 return EchoShellRequest; |
| 275 }); | 451 }); |
| 276 ``` | 452 ``` |
| 277 | 453 |
| 454 ### Requesting a Service from an Application's ServiceProvider |
| 278 | 455 |
| 279 ### Providing a Service | 456 The Shell's `connectToApplication()` method returns a JS ServiceProvider |
| 457 object that serves as a proxy to a ServiceProvider implemented by the |
| 458 target application. The ServiceProvider can be used to request services |
| 459 from the target application and to provide services to the target application. |
| 460 |
| 461 The echo_share.js and echo_share_target.js applications demonstrate this. |
| 462 |
| 463 |
| 464 ### Providing a Service with an Application's ServiceProvider |
| 280 | 465 |
| 281 A complete application that unconditionally provides the EchoService | 466 A complete application that unconditionally provides the EchoService |
| 282 looks like this: | 467 looks like this: |
| 283 | 468 |
| 284 ```javascript | 469 ```javascript |
| 285 #!mojo:js_content_handler | 470 #!mojo:js_content_handler |
| 286 | 471 |
| 287 define("main", [ | 472 define("main", [ |
| 288 "mojo/services/public/js/application", | 473 "mojo/services/public/js/application", |
| 289 "services/js/test/echo_service.mojom" | 474 "services/js/test/echo_service.mojom" |
| (...skipping 17 matching lines...) Expand all Loading... |
| 307 object that implements the methods in the Mojo EchoService | 492 object that implements the methods in the Mojo EchoService |
| 308 interface. If the EchoService defined a client interface, the factory | 493 interface. If the EchoService defined a client interface, the factory |
| 309 function's client parameter would be a proxy for the initiator's | 494 function's client parameter would be a proxy for the initiator's |
| 310 client service. EchoService doesn't have a client so we could have | 495 client service. EchoService doesn't have a client so we could have |
| 311 omitted this parameter. | 496 omitted this parameter. |
| 312 | 497 |
| 313 Each time another application connects to this one, the EchoServiceImpl | 498 Each time another application connects to this one, the EchoServiceImpl |
| 314 function will be called. The caller will be able to run the | 499 function will be called. The caller will be able to run the |
| 315 echoString() method and will get its response via a Promise. | 500 echoString() method and will get its response via a Promise. |
| 316 | 501 |
| 502 The echo_share.js and echo_share_target.js applications demonstrate this. |
| 317 | 503 |
| 318 ## Final note | 504 ## Final note |
| 319 | 505 |
| 320 An initiator's serviceProvider object can be retained and used to | 506 An initiator's serviceProvider object can be retained and used to |
| 321 request or provide services at any time, not just from within | 507 request or provide services at any time, not just from within |
| 322 application's acceptConnection() method. | 508 application's acceptConnection() method. |
| 323 | 509 |
| 324 | 510 |
| 325 # Interface Parameters | |
| 326 | |
| 327 The caller and callee use cases that follow are in terms of the following mojom
: | |
| 328 | |
| 329 ``` | |
| 330 [Client=Bar] | |
| 331 interface Foo { | |
| 332 } | |
| 333 | |
| 334 [Client=Foo] // Redundant but always implicitly true. | |
| 335 interface Bar { | |
| 336 } | |
| 337 | |
| 338 interface I { | |
| 339 provideFoo(Foo foo); | |
| 340 requestFoo(Foo& foo); // effectively: provideFoo(Bar bar) | |
| 341 } | |
| 342 ``` | |
| 343 | |
| 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. | |
| 351 | |
| 352 From a user's point of view, the bindings are in terms of the (remote) | |
| 353 proxy class and the (local) stub class. Properties are added to instances | |
| 354 of these classes using functions called StubBindings and ProxyBindings. | |
| 355 | |
| 356 | |
| 357 | |
| 358 ## Callers | |
| 359 | |
| 360 Assuming that we have a proxy for interface I, iProxy. | |
| 361 | |
| 362 An iProxy.provideFoo() call implies that we have an implementation of | |
| 363 Foo, and want a proxy for Bar (Foo's client). | |
| 364 | |
| 365 ```javascript | |
| 366 var barProxy; | |
| 367 iProxy.provideFoo(function(remote) { | |
| 368 barProxy = remote; | |
| 369 return myFooImpl; | |
| 370 }); | |
| 371 ``` | |
| 372 | |
| 373 An iProxy.requestFoo() call implies that we have an implementation of | |
| 374 Bar and want a proxy for Foo (Bar's client). | |
| 375 | |
| 376 ```javascript | |
| 377 var fooProxy; | |
| 378 iProxy.requestFoo(function(remote) { | |
| 379 fooProxy = remote; | |
| 380 return myBarImpl; | |
| 381 }); | |
| 382 ``` | |
| 383 | |
| 384 In the requestFoo() case, if no client were defined for Bar the function | |
| 385 parameter need not return anything. | |
| 386 | |
| 387 The wget.js example includes a request for the URLLoader service. | |
| 388 | |
| 389 ## Callees | |
| 390 | |
| 391 An implementation of provideFoo(Foo foo) implies that we have an | |
| 392 implementation of Bar (Foo's client) and want a proxy to the Foo | |
| 393 that has been passed to us. | |
| 394 | |
| 395 ```javascript | |
| 396 void provideFoo(fooProxy) { | |
| 397 ProxyBindings(fooProxy).setLocalDelegate(myMyBarImpl); | |
| 398 } | |
| 399 ``` | |
| 400 | |
| 401 An implementation of requestFoo(Foo& foo) implies that we have an | |
| 402 implementation of Foo and want a proxy for the Bar (Foo's client) | |
| 403 that's been passed to us. | |
| 404 | |
| 405 ```javascript | |
| 406 void requestFoo(barProxy) { | |
| 407 ProxyBindings(barProxy).setLocallocalDelegate(myFooImpl); | |
| 408 } | |
| 409 ``` | |
| OLD | NEW |