1 // Copyright 2017 The Chromium Authors. All rights reserved. 2 // Use of this source code is governed by a BSD-style license that can be 3 // found in the LICENSE file. 4 5 (function() { 6 var internal = mojo.internal; 7 8 // --------------------------------------------------------------------------- 9 10 // |output| could be an interface pointer, InterfacePtrInfo or 11 // AssociatedInterfacePtrInfo. 12 function makeRequest(output) { 13 if (output instanceof mojo.AssociatedInterfacePtrInfo) { 14 var {handle0, handle1} = internal.createPairPendingAssociation(); 15 output.interfaceEndpointHandle = handle0; 16 output.version = 0; 17 18 return new mojo.AssociatedInterfaceRequest(handle1); 19 } 20 21 if (output instanceof mojo.InterfacePtrInfo) { 22 var pipe = Mojo.createMessagePipe(); 23 output.handle = pipe.handle0; 24 output.version = 0; 25 26 return new mojo.InterfaceRequest(pipe.handle1); 27 } 28 29 var pipe = Mojo.createMessagePipe(); 30 output.ptr.bind(new mojo.InterfacePtrInfo(pipe.handle0, 0)); 31 return new mojo.InterfaceRequest(pipe.handle1); 32 } 33 34 // --------------------------------------------------------------------------- 35 36 // Operations used to setup/configure an interface pointer. Exposed as the 37 // |ptr| field of generated interface pointer classes. 38 // |ptrInfoOrHandle| could be omitted and passed into bind() later. 39 function InterfacePtrController(interfaceType, ptrInfoOrHandle) { 40 this.version = 0; 41 42 this.interfaceType_ = interfaceType; 43 this.router_ = null; 44 this.interfaceEndpointClient_ = null; 45 this.proxy_ = null; 46 47 // |router_| and |interfaceEndpointClient_| are lazily initialized. 48 // |handle_| is valid between bind() and 49 // the initialization of |router_| and |interfaceEndpointClient_|. 50 this.handle_ = null; 51 52 if (ptrInfoOrHandle) 53 this.bind(ptrInfoOrHandle); 54 } 55 56 InterfacePtrController.prototype.bind = function(ptrInfoOrHandle) { 57 this.reset(); 58 59 if (ptrInfoOrHandle instanceof mojo.InterfacePtrInfo) { 60 this.version = ptrInfoOrHandle.version; 61 this.handle_ = ptrInfoOrHandle.handle; 62 } else { 63 this.handle_ = ptrInfoOrHandle; 64 } 65 }; 66 67 InterfacePtrController.prototype.isBound = function() { 68 return this.interfaceEndpointClient_ !== null || this.handle_ !== null; 69 }; 70 71 // Although users could just discard the object, reset() closes the pipe 72 // immediately. 73 InterfacePtrController.prototype.reset = function() { 74 this.version = 0; 75 if (this.interfaceEndpointClient_) { 76 this.interfaceEndpointClient_.close(); 77 this.interfaceEndpointClient_ = null; 78 } 79 if (this.router_) { 80 this.router_.close(); 81 this.router_ = null; 82 83 this.proxy_ = null; 84 } 85 if (this.handle_) { 86 this.handle_.close(); 87 this.handle_ = null; 88 } 89 }; 90 91 InterfacePtrController.prototype.resetWithReason = function(reason) { 92 if (this.isBound()) { 93 this.configureProxyIfNecessary_(); 94 this.interfaceEndpointClient_.close(reason); 95 this.interfaceEndpointClient_ = null; 96 } 97 this.reset(); 98 }; 99 100 InterfacePtrController.prototype.setConnectionErrorHandler = function( 101 callback) { 102 if (!this.isBound()) 103 throw new Error("Cannot set connection error handler if not bound."); 104 105 this.configureProxyIfNecessary_(); 106 this.interfaceEndpointClient_.setConnectionErrorHandler(callback); 107 }; 108 109 InterfacePtrController.prototype.passInterface = function() { 110 var result; 111 if (this.router_) { 112 // TODO(yzshen): Fix Router interface to support extracting handle. 113 result = new mojo.InterfacePtrInfo( 114 this.router_.connector_.handle_, this.version); 115 this.router_.connector_.handle_ = null; 116 } else { 117 // This also handles the case when this object is not bound. 118 result = new mojo.InterfacePtrInfo(this.handle_, this.version); 119 this.handle_ = null; 120 } 121 122 this.reset(); 123 return result; 124 }; 125 126 InterfacePtrController.prototype.getProxy = function() { 127 this.configureProxyIfNecessary_(); 128 return this.proxy_; 129 }; 130 131 InterfacePtrController.prototype.configureProxyIfNecessary_ = function() { 132 if (!this.handle_) 133 return; 134 135 this.router_ = new internal.Router(this.handle_, true); 136 this.handle_ = null; 137 138 this.interfaceEndpointClient_ = new internal.InterfaceEndpointClient( 139 this.router_.createLocalEndpointHandle(internal.kMasterInterfaceId)); 140 141 this.interfaceEndpointClient_ .setPayloadValidators([ 142 this.interfaceType_.validateResponse]); 143 this.proxy_ = new this.interfaceType_.proxyClass( 144 this.interfaceEndpointClient_); 145 }; 146 147 InterfacePtrController.prototype.queryVersion = function() { 148 function onQueryVersion(version) { 149 this.version = version; 150 return version; 151 } 152 153 this.configureProxyIfNecessary_(); 154 return this.interfaceEndpointClient_.queryVersion().then( 155 onQueryVersion.bind(this)); 156 }; 157 158 InterfacePtrController.prototype.requireVersion = function(version) { 159 this.configureProxyIfNecessary_(); 160 161 if (this.version >= version) { 162 return; 163 } 164 this.version = version; 165 this.interfaceEndpointClient_.requireVersion(version); 166 }; 167 168 // --------------------------------------------------------------------------- 169 170 // |request| could be omitted and passed into bind() later. 171 // 172 // Example: 173 // 174 // // FooImpl implements mojom.Foo. 175 // function FooImpl() { ... } 176 // FooImpl.prototype.fooMethod1 = function() { ... } 177 // FooImpl.prototype.fooMethod2 = function() { ... } 178 // 179 // var fooPtr = new mojom.FooPtr(); 180 // var request = makeRequest(fooPtr); 181 // var binding = new Binding(mojom.Foo, new FooImpl(), request); 182 // fooPtr.fooMethod1(); 183 function Binding(interfaceType, impl, requestOrHandle) { 184 this.interfaceType_ = interfaceType; 185 this.impl_ = impl; 186 this.router_ = null; 187 this.interfaceEndpointClient_ = null; 188 this.stub_ = null; 189 190 if (requestOrHandle) 191 this.bind(requestOrHandle); 192 } 193 194 Binding.prototype.isBound = function() { 195 return this.router_ !== null; 196 }; 197 198 Binding.prototype.createInterfacePtrAndBind = function() { 199 var ptr = new this.interfaceType_.ptrClass(); 200 // TODO(yzshen): Set the version of the interface pointer. 201 this.bind(makeRequest(ptr)); 202 return ptr; 203 }; 204 205 Binding.prototype.bind = function(requestOrHandle) { 206 this.close(); 207 208 var handle = requestOrHandle instanceof mojo.InterfaceRequest ? 209 requestOrHandle.handle : requestOrHandle; 210 if (!(handle instanceof MojoHandle)) 211 return; 212 213 this.router_ = new internal.Router(handle); 214 215 this.stub_ = new this.interfaceType_.stubClass(this.impl_); 216 this.interfaceEndpointClient_ = new internal.InterfaceEndpointClient( 217 this.router_.createLocalEndpointHandle(internal.kMasterInterfaceId), 218 this.stub_, this.interfaceType_.kVersion); 219 220 this.interfaceEndpointClient_ .setPayloadValidators([ 221 this.interfaceType_.validateRequest]); 222 }; 223 224 Binding.prototype.close = function() { 225 if (!this.isBound()) 226 return; 227 228 if (this.interfaceEndpointClient_) { 229 this.interfaceEndpointClient_.close(); 230 this.interfaceEndpointClient_ = null; 231 } 232 233 this.router_.close(); 234 this.router_ = null; 235 this.stub_ = null; 236 }; 237 238 Binding.prototype.closeWithReason = function(reason) { 239 if (this.interfaceEndpointClient_) { 240 this.interfaceEndpointClient_.close(reason); 241 this.interfaceEndpointClient_ = null; 242 } 243 this.close(); 244 }; 245 246 Binding.prototype.setConnectionErrorHandler = function(callback) { 247 if (!this.isBound()) { 248 throw new Error("Cannot set connection error handler if not bound."); 249 } 250 this.interfaceEndpointClient_.setConnectionErrorHandler(callback); 251 }; 252 253 Binding.prototype.unbind = function() { 254 if (!this.isBound()) 255 return new mojo.InterfaceRequest(null); 256 257 var result = new mojo.InterfaceRequest(this.router_.connector_.handle_); 258 this.router_.connector_.handle_ = null; 259 this.close(); 260 return result; 261 }; 262 263 // --------------------------------------------------------------------------- 264 265 function BindingSetEntry(bindingSet, interfaceType, bindingType, impl, 266 requestOrHandle, bindingId) { 267 this.bindingSet_ = bindingSet; 268 this.bindingId_ = bindingId; 269 this.binding_ = new bindingType(interfaceType, impl, 270 requestOrHandle); 271 272 this.binding_.setConnectionErrorHandler(function(reason) { 273 this.bindingSet_.onConnectionError(bindingId, reason); 274 }.bind(this)); 275 } 276 277 BindingSetEntry.prototype.close = function() { 278 this.binding_.close(); 279 }; 280 281 function BindingSet(interfaceType) { 282 this.interfaceType_ = interfaceType; 283 this.nextBindingId_ = 0; 284 this.bindings_ = new Map(); 285 this.errorHandler_ = null; 286 this.bindingType_ = Binding; 287 } 288 289 BindingSet.prototype.isEmpty = function() { 290 return this.bindings_.size == 0; 291 }; 292 293 BindingSet.prototype.addBinding = function(impl, requestOrHandle) { 294 this.bindings_.set( 295 this.nextBindingId_, 296 new BindingSetEntry(this, this.interfaceType_, this.bindingType_, impl, 297 requestOrHandle, this.nextBindingId_)); 298 ++this.nextBindingId_; 299 }; 300 301 BindingSet.prototype.closeAllBindings = function() { 302 for (var entry of this.bindings_.values()) 303 entry.close(); 304 this.bindings_.clear(); 305 }; 306 307 BindingSet.prototype.setConnectionErrorHandler = function(callback) { 308 this.errorHandler_ = callback; 309 }; 310 311 BindingSet.prototype.onConnectionError = function(bindingId, reason) { 312 this.bindings_.delete(bindingId); 313 314 if (this.errorHandler_) 315 this.errorHandler_(reason); 316 }; 317 318 // --------------------------------------------------------------------------- 319 320 // Operations used to setup/configure an associated interface pointer. 321 // Exposed as |ptr| field of generated associated interface pointer classes. 322 // |associatedPtrInfo| could be omitted and passed into bind() later. 323 // 324 // Example: 325 // // IntegerSenderImpl implements mojom.IntegerSender 326 // function IntegerSenderImpl() { ... } 327 // IntegerSenderImpl.prototype.echo = function() { ... } 328 // 329 // // IntegerSenderConnectionImpl implements mojom.IntegerSenderConnection 330 // function IntegerSenderConnectionImpl() { 331 // this.senderBinding_ = null; 332 // } 333 // IntegerSenderConnectionImpl.prototype.getSender = function( 334 // associatedRequest) { 335 // this.senderBinding_ = new AssociatedBinding(mojom.IntegerSender, 336 // new IntegerSenderImpl(), 337 // associatedRequest); 338 // } 339 // 340 // var integerSenderConnection = new mojom.IntegerSenderConnectionPtr(); 341 // var integerSenderConnectionBinding = new Binding( 342 // mojom.IntegerSenderConnection, 343 // new IntegerSenderConnectionImpl(), 344 // mojo.makeRequest(integerSenderConnection)); 345 // 346 // // A locally-created associated interface pointer can only be used to 347 // // make calls when the corresponding associated request is sent over 348 // // another interface (either the master interface or another 349 // // associated interface). 350 // var associatedInterfacePtrInfo = new AssociatedInterfacePtrInfo(); 351 // var associatedRequest = makeRequest(interfacePtrInfo); 352 // 353 // integerSenderConnection.getSender(associatedRequest); 354 // 355 // // Create an associated interface and bind the associated handle. 356 // var integerSender = new mojom.AssociatedIntegerSenderPtr(); 357 // integerSender.ptr.bind(associatedInterfacePtrInfo); 358 // integerSender.echo(); 359 360 function AssociatedInterfacePtrController(interfaceType, associatedPtrInfo) { 361 this.version = 0; 362 363 this.interfaceType_ = interfaceType; 364 this.interfaceEndpointClient_ = null; 365 this.proxy_ = null; 366 367 if (associatedPtrInfo) { 368 this.bind(associatedPtrInfo); 369 } 370 } 371 372 AssociatedInterfacePtrController.prototype.bind = function( 373 associatedPtrInfo) { 374 this.reset(); 375 this.version = associatedPtrInfo.version; 376 377 this.interfaceEndpointClient_ = new internal.InterfaceEndpointClient( 378 associatedPtrInfo.interfaceEndpointHandle); 379 380 this.interfaceEndpointClient_ .setPayloadValidators([ 381 this.interfaceType_.validateResponse]); 382 this.proxy_ = new this.interfaceType_.proxyClass( 383 this.interfaceEndpointClient_); 384 }; 385 386 AssociatedInterfacePtrController.prototype.isBound = function() { 387 return this.interfaceEndpointClient_ !== null; 388 }; 389 390 AssociatedInterfacePtrController.prototype.reset = function() { 391 this.version = 0; 392 if (this.interfaceEndpointClient_) { 393 this.interfaceEndpointClient_.close(); 394 this.interfaceEndpointClient_ = null; 395 } 396 if (this.proxy_) { 397 this.proxy_ = null; 398 } 399 }; 400 401 AssociatedInterfacePtrController.prototype.resetWithReason = function( 402 reason) { 403 if (this.isBound()) { 404 this.interfaceEndpointClient_.close(reason); 405 this.interfaceEndpointClient_ = null; 406 } 407 this.reset(); 408 }; 409 410 // Indicates whether an error has been encountered. If true, method calls 411 // on this interface will be dropped (and may already have been dropped). 412 AssociatedInterfacePtrController.prototype.getEncounteredError = function() { 413 return this.interfaceEndpointClient_ ? 414 this.interfaceEndpointClient_.getEncounteredError() : false; 415 }; 416 417 AssociatedInterfacePtrController.prototype.setConnectionErrorHandler = 418 function(callback) { 419 if (!this.isBound()) { 420 throw new Error("Cannot set connection error handler if not bound."); 421 } 422 423 this.interfaceEndpointClient_.setConnectionErrorHandler(callback); 424 }; 425 426 AssociatedInterfacePtrController.prototype.passInterface = function() { 427 if (!this.isBound()) { 428 return new mojo.AssociatedInterfacePtrInfo(null); 429 } 430 431 var result = new mojo.AssociatedInterfacePtrInfo( 432 this.interfaceEndpointClient_.passHandle(), this.version); 433 this.reset(); 434 return result; 435 }; 436 437 AssociatedInterfacePtrController.prototype.getProxy = function() { 438 return this.proxy_; 439 }; 440 441 AssociatedInterfacePtrController.prototype.queryVersion = function() { 442 function onQueryVersion(version) { 443 this.version = version; 444 return version; 445 } 446 447 return this.interfaceEndpointClient_.queryVersion().then( 448 onQueryVersion.bind(this)); 449 }; 450 451 AssociatedInterfacePtrController.prototype.requireVersion = function( 452 version) { 453 if (this.version >= version) { 454 return; 455 } 456 this.version = version; 457 this.interfaceEndpointClient_.requireVersion(version); 458 }; 459 460 // --------------------------------------------------------------------------- 461 462 // |associatedInterfaceRequest| could be omitted and passed into bind() 463 // later. 464 function AssociatedBinding(interfaceType, impl, associatedInterfaceRequest) { 465 this.interfaceType_ = interfaceType; 466 this.impl_ = impl; 467 this.interfaceEndpointClient_ = null; 468 this.stub_ = null; 469 470 if (associatedInterfaceRequest) { 471 this.bind(associatedInterfaceRequest); 472 } 473 } 474 475 AssociatedBinding.prototype.isBound = function() { 476 return this.interfaceEndpointClient_ !== null; 477 }; 478 479 AssociatedBinding.prototype.bind = function(associatedInterfaceRequest) { 480 this.close(); 481 482 this.stub_ = new this.interfaceType_.stubClass(this.impl_); 483 this.interfaceEndpointClient_ = new internal.InterfaceEndpointClient( 484 associatedInterfaceRequest.interfaceEndpointHandle, this.stub_, 485 this.interfaceType_.kVersion); 486 487 this.interfaceEndpointClient_ .setPayloadValidators([ 488 this.interfaceType_.validateRequest]); 489 }; 490 491 492 AssociatedBinding.prototype.close = function() { 493 if (!this.isBound()) { 494 return; 495 } 496 497 if (this.interfaceEndpointClient_) { 498 this.interfaceEndpointClient_.close(); 499 this.interfaceEndpointClient_ = null; 500 } 501 502 this.stub_ = null; 503 }; 504 505 AssociatedBinding.prototype.closeWithReason = function(reason) { 506 if (this.interfaceEndpointClient_) { 507 this.interfaceEndpointClient_.close(reason); 508 this.interfaceEndpointClient_ = null; 509 } 510 this.close(); 511 }; 512 513 AssociatedBinding.prototype.setConnectionErrorHandler = function(callback) { 514 if (!this.isBound()) { 515 throw new Error("Cannot set connection error handler if not bound."); 516 } 517 this.interfaceEndpointClient_.setConnectionErrorHandler(callback); 518 }; 519 520 AssociatedBinding.prototype.unbind = function() { 521 if (!this.isBound()) { 522 return new mojo.AssociatedInterfaceRequest(null); 523 } 524 525 var result = new mojo.AssociatedInterfaceRequest( 526 this.interfaceEndpointClient_.passHandle()); 527 this.close(); 528 return result; 529 }; 530 531 // --------------------------------------------------------------------------- 532 533 function AssociatedBindingSet(interfaceType) { 534 mojo.BindingSet.call(this, interfaceType); 535 this.bindingType_ = AssociatedBinding; 536 } 537 538 AssociatedBindingSet.prototype = Object.create(BindingSet.prototype); 539 AssociatedBindingSet.prototype.constructor = AssociatedBindingSet; 540 541 mojo.makeRequest = makeRequest; 542 mojo.AssociatedInterfacePtrController = AssociatedInterfacePtrController; 543 mojo.AssociatedBinding = AssociatedBinding; 544 mojo.AssociatedBindingSet = AssociatedBindingSet; 545 mojo.Binding = Binding; 546 mojo.BindingSet = BindingSet; 547 mojo.InterfacePtrController = InterfacePtrController; 548 })(); 549