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