• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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