// Copyright 2017 The Chromium Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. (function() { var internal = mojo.internal; function InterfaceEndpointClient(interfaceEndpointHandle, receiver, interfaceVersion) { this.controller_ = null; this.encounteredError_ = false; this.handle_ = interfaceEndpointHandle; this.incomingReceiver_ = receiver; if (interfaceVersion !== undefined) { this.controlMessageHandler_ = new internal.ControlMessageHandler( interfaceVersion); } else { this.controlMessageProxy_ = new internal.ControlMessageProxy(this); } this.nextRequestID_ = 0; this.completers_ = new Map(); this.payloadValidators_ = []; this.connectionErrorHandler_ = null; if (interfaceEndpointHandle.pendingAssociation()) { interfaceEndpointHandle.setAssociationEventHandler( this.onAssociationEvent.bind(this)); } else { this.initControllerIfNecessary_(); } } InterfaceEndpointClient.prototype.initControllerIfNecessary_ = function() { if (this.controller_ || this.handle_.pendingAssociation()) { return; } this.controller_ = this.handle_.groupController().attachEndpointClient( this.handle_, this); }; InterfaceEndpointClient.prototype.onAssociationEvent = function( associationEvent) { if (associationEvent === internal.AssociationEvent.ASSOCIATED) { this.initControllerIfNecessary_(); } else if (associationEvent === internal.AssociationEvent.PEER_CLOSED_BEFORE_ASSOCIATION) { setTimeout(this.notifyError.bind(this, this.handle_.disconnectReason()), 0); } }; InterfaceEndpointClient.prototype.passHandle = function() { if (!this.handle_.isValid()) { return new internal.InterfaceEndpointHandle(); } // Used to clear the previously set callback. this.handle_.setAssociationEventHandler(undefined); if (this.controller_) { this.controller_ = null; this.handle_.groupController().detachEndpointClient(this.handle_); } var handle = this.handle_; this.handle_ = null; return handle; }; InterfaceEndpointClient.prototype.close = function(reason) { var handle = this.passHandle(); handle.reset(reason); }; InterfaceEndpointClient.prototype.accept = function(message) { if (message.associatedEndpointHandles.length > 0) { message.serializeAssociatedEndpointHandles( this.handle_.groupController()); } if (this.encounteredError_) { return false; } this.initControllerIfNecessary_(); return this.controller_.sendMessage(message); }; InterfaceEndpointClient.prototype.acceptAndExpectResponse = function( message) { if (message.associatedEndpointHandles.length > 0) { message.serializeAssociatedEndpointHandles( this.handle_.groupController()); } if (this.encounteredError_) { return Promise.reject(); } this.initControllerIfNecessary_(); // Reserve 0 in case we want it to convey special meaning in the future. var requestID = this.nextRequestID_++; if (requestID === 0) requestID = this.nextRequestID_++; message.setRequestID(requestID); var result = this.controller_.sendMessage(message); if (!result) return Promise.reject(Error("Connection error")); var completer = {}; this.completers_.set(requestID, completer); return new Promise(function(resolve, reject) { completer.resolve = resolve; completer.reject = reject; }); }; InterfaceEndpointClient.prototype.setPayloadValidators = function( payloadValidators) { this.payloadValidators_ = payloadValidators; }; InterfaceEndpointClient.prototype.setIncomingReceiver = function(receiver) { this.incomingReceiver_ = receiver; }; InterfaceEndpointClient.prototype.setConnectionErrorHandler = function( handler) { this.connectionErrorHandler_ = handler; }; InterfaceEndpointClient.prototype.handleIncomingMessage = function(message, messageValidator) { var noError = internal.validationError.NONE; var err = noError; for (var i = 0; err === noError && i < this.payloadValidators_.length; ++i) err = this.payloadValidators_[i](messageValidator); if (err == noError) { return this.handleValidIncomingMessage_(message); } else { internal.reportValidationError(err); return false; } }; InterfaceEndpointClient.prototype.handleValidIncomingMessage_ = function( message) { if (internal.isTestingMode()) { return true; } if (this.encounteredError_) { return false; } var ok = false; if (message.expectsResponse()) { if (internal.isInterfaceControlMessage(message) && this.controlMessageHandler_) { ok = this.controlMessageHandler_.acceptWithResponder(message, this); } else if (this.incomingReceiver_) { ok = this.incomingReceiver_.acceptWithResponder(message, this); } } else if (message.isResponse()) { var reader = new internal.MessageReader(message); var requestID = reader.requestID; var completer = this.completers_.get(requestID); if (completer) { this.completers_.delete(requestID); completer.resolve(message); ok = true; } else { console.log("Unexpected response with request ID: " + requestID); } } else { if (internal.isInterfaceControlMessage(message) && this.controlMessageHandler_) { ok = this.controlMessageHandler_.accept(message); } else if (this.incomingReceiver_) { ok = this.incomingReceiver_.accept(message); } } return ok; }; InterfaceEndpointClient.prototype.notifyError = function(reason) { if (this.encounteredError_) { return; } this.encounteredError_ = true; this.completers_.forEach(function(value) { value.reject(); }); this.completers_.clear(); // Drop any responders. if (this.connectionErrorHandler_) { this.connectionErrorHandler_(reason); } }; InterfaceEndpointClient.prototype.queryVersion = function() { return this.controlMessageProxy_.queryVersion(); }; InterfaceEndpointClient.prototype.requireVersion = function(version) { this.controlMessageProxy_.requireVersion(version); }; InterfaceEndpointClient.prototype.getEncounteredError = function() { return this.encounteredError_; }; internal.InterfaceEndpointClient = InterfaceEndpointClient; })();