Home | History | Annotate | Download | only in js
      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