Home | History | Annotate | Download | only in lib
      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   function InterfaceEndpointClient(interfaceEndpointHandle, receiver,
      9       interfaceVersion) {
     10     this.controller_ = null;
     11     this.encounteredError_ = false;
     12     this.handle_ = interfaceEndpointHandle;
     13     this.incomingReceiver_ = receiver;
     14 
     15     if (interfaceVersion !== undefined) {
     16       this.controlMessageHandler_ = new internal.ControlMessageHandler(
     17           interfaceVersion);
     18     } else {
     19       this.controlMessageProxy_ = new internal.ControlMessageProxy(this);
     20     }
     21 
     22     this.nextRequestID_ = 0;
     23     this.completers_ = new Map();
     24     this.payloadValidators_ = [];
     25     this.connectionErrorHandler_ = null;
     26 
     27     if (interfaceEndpointHandle.pendingAssociation()) {
     28       interfaceEndpointHandle.setAssociationEventHandler(
     29           this.onAssociationEvent.bind(this));
     30     } else {
     31       this.initControllerIfNecessary_();
     32     }
     33   }
     34 
     35   InterfaceEndpointClient.prototype.initControllerIfNecessary_ = function() {
     36     if (this.controller_ || this.handle_.pendingAssociation()) {
     37       return;
     38     }
     39 
     40     this.controller_ = this.handle_.groupController().attachEndpointClient(
     41         this.handle_, this);
     42   };
     43 
     44   InterfaceEndpointClient.prototype.onAssociationEvent = function(
     45       associationEvent) {
     46     if (associationEvent === internal.AssociationEvent.ASSOCIATED) {
     47       this.initControllerIfNecessary_();
     48     } else if (associationEvent ===
     49           internal.AssociationEvent.PEER_CLOSED_BEFORE_ASSOCIATION) {
     50       setTimeout(this.notifyError.bind(this, this.handle_.disconnectReason()),
     51                  0);
     52     }
     53   };
     54 
     55   InterfaceEndpointClient.prototype.passHandle = function() {
     56     if (!this.handle_.isValid()) {
     57       return new internal.InterfaceEndpointHandle();
     58     }
     59 
     60     // Used to clear the previously set callback.
     61     this.handle_.setAssociationEventHandler(undefined);
     62 
     63     if (this.controller_) {
     64       this.controller_ = null;
     65       this.handle_.groupController().detachEndpointClient(this.handle_);
     66     }
     67     var handle = this.handle_;
     68     this.handle_ = null;
     69     return handle;
     70   };
     71 
     72   InterfaceEndpointClient.prototype.close = function(reason) {
     73     var handle = this.passHandle();
     74     handle.reset(reason);
     75   };
     76 
     77   InterfaceEndpointClient.prototype.accept = function(message) {
     78     if (message.associatedEndpointHandles.length > 0) {
     79       message.serializeAssociatedEndpointHandles(
     80           this.handle_.groupController());
     81     }
     82 
     83     if (this.encounteredError_) {
     84       return false;
     85     }
     86 
     87     this.initControllerIfNecessary_();
     88     return this.controller_.sendMessage(message);
     89   };
     90 
     91   InterfaceEndpointClient.prototype.acceptAndExpectResponse = function(
     92       message) {
     93     if (message.associatedEndpointHandles.length > 0) {
     94       message.serializeAssociatedEndpointHandles(
     95           this.handle_.groupController());
     96     }
     97 
     98     if (this.encounteredError_) {
     99       return Promise.reject();
    100     }
    101 
    102     this.initControllerIfNecessary_();
    103 
    104     // Reserve 0 in case we want it to convey special meaning in the future.
    105     var requestID = this.nextRequestID_++;
    106     if (requestID === 0)
    107       requestID = this.nextRequestID_++;
    108 
    109     message.setRequestID(requestID);
    110     var result = this.controller_.sendMessage(message);
    111     if (!result)
    112       return Promise.reject(Error("Connection error"));
    113 
    114     var completer = {};
    115     this.completers_.set(requestID, completer);
    116     return new Promise(function(resolve, reject) {
    117       completer.resolve = resolve;
    118       completer.reject = reject;
    119     });
    120   };
    121 
    122   InterfaceEndpointClient.prototype.setPayloadValidators = function(
    123       payloadValidators) {
    124     this.payloadValidators_ = payloadValidators;
    125   };
    126 
    127   InterfaceEndpointClient.prototype.setIncomingReceiver = function(receiver) {
    128     this.incomingReceiver_ = receiver;
    129   };
    130 
    131   InterfaceEndpointClient.prototype.setConnectionErrorHandler = function(
    132       handler) {
    133     this.connectionErrorHandler_ = handler;
    134   };
    135 
    136   InterfaceEndpointClient.prototype.handleIncomingMessage = function(message,
    137       messageValidator) {
    138     var noError = internal.validationError.NONE;
    139     var err = noError;
    140     for (var i = 0; err === noError && i < this.payloadValidators_.length; ++i)
    141       err = this.payloadValidators_[i](messageValidator);
    142 
    143     if (err == noError) {
    144       return this.handleValidIncomingMessage_(message);
    145     } else {
    146       internal.reportValidationError(err);
    147       return false;
    148     }
    149   };
    150 
    151   InterfaceEndpointClient.prototype.handleValidIncomingMessage_ = function(
    152       message) {
    153     if (internal.isTestingMode()) {
    154       return true;
    155     }
    156 
    157     if (this.encounteredError_) {
    158       return false;
    159     }
    160 
    161     var ok = false;
    162 
    163     if (message.expectsResponse()) {
    164       if (internal.isInterfaceControlMessage(message) &&
    165           this.controlMessageHandler_) {
    166         ok = this.controlMessageHandler_.acceptWithResponder(message, this);
    167       } else if (this.incomingReceiver_) {
    168         ok = this.incomingReceiver_.acceptWithResponder(message, this);
    169       }
    170     } else if (message.isResponse()) {
    171       var reader = new internal.MessageReader(message);
    172       var requestID = reader.requestID;
    173       var completer = this.completers_.get(requestID);
    174       if (completer) {
    175         this.completers_.delete(requestID);
    176         completer.resolve(message);
    177         ok = true;
    178       } else {
    179         console.log("Unexpected response with request ID: " + requestID);
    180       }
    181     } else {
    182       if (internal.isInterfaceControlMessage(message) &&
    183           this.controlMessageHandler_) {
    184         ok = this.controlMessageHandler_.accept(message);
    185       } else if (this.incomingReceiver_) {
    186         ok = this.incomingReceiver_.accept(message);
    187       }
    188     }
    189     return ok;
    190   };
    191 
    192   InterfaceEndpointClient.prototype.notifyError = function(reason) {
    193     if (this.encounteredError_) {
    194       return;
    195     }
    196     this.encounteredError_ = true;
    197 
    198     this.completers_.forEach(function(value) {
    199       value.reject();
    200     });
    201     this.completers_.clear();  // Drop any responders.
    202 
    203     if (this.connectionErrorHandler_) {
    204       this.connectionErrorHandler_(reason);
    205     }
    206   };
    207 
    208   InterfaceEndpointClient.prototype.queryVersion = function() {
    209     return this.controlMessageProxy_.queryVersion();
    210   };
    211 
    212   InterfaceEndpointClient.prototype.requireVersion = function(version) {
    213     this.controlMessageProxy_.requireVersion(version);
    214   };
    215 
    216   InterfaceEndpointClient.prototype.getEncounteredError = function() {
    217     return this.encounteredError_;
    218   };
    219 
    220   internal.InterfaceEndpointClient = InterfaceEndpointClient;
    221 })();
    222