• 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  var AssociationEvent = {
9    // The interface has been associated with a message pipe.
10    ASSOCIATED: 'associated',
11    // The peer of this object has been closed before association.
12    PEER_CLOSED_BEFORE_ASSOCIATION: 'peer_closed_before_association'
13  };
14
15  function State(interfaceId, associatedGroupController) {
16    if (interfaceId === undefined) {
17      interfaceId = internal.kInvalidInterfaceId;
18    }
19
20    this.interfaceId = interfaceId;
21    this.associatedGroupController = associatedGroupController;
22    this.pendingAssociation = false;
23    this.disconnectReason = null;
24    this.peerState_ = null;
25    this.associationEventHandler_ = null;
26  }
27
28  State.prototype.initPendingState = function(peer) {
29    this.pendingAssociation = true;
30    this.peerState_ = peer;
31  };
32
33  State.prototype.isValid = function() {
34    return this.pendingAssociation ||
35        internal.isValidInterfaceId(this.interfaceId);
36  };
37
38  State.prototype.close = function(disconnectReason) {
39    var cachedGroupController;
40    var cachedPeerState;
41    var cachedId = internal.kInvalidInterfaceId;
42
43    if (!this.pendingAssociation) {
44      if (internal.isValidInterfaceId(this.interfaceId)) {
45        cachedGroupController = this.associatedGroupController;
46        this.associatedGroupController = null;
47        cachedId = this.interfaceId;
48        this.interfaceId = internal.kInvalidInterfaceId;
49      }
50    } else {
51      this.pendingAssociation = false;
52      cachedPeerState = this.peerState_;
53      this.peerState_ = null;
54    }
55
56    if (cachedGroupController) {
57      cachedGroupController.closeEndpointHandle(cachedId,
58          disconnectReason);
59    } else if (cachedPeerState) {
60      cachedPeerState.onPeerClosedBeforeAssociation(disconnectReason);
61    }
62  };
63
64  State.prototype.runAssociationEventHandler = function(associationEvent) {
65    if (this.associationEventHandler_) {
66      var handler = this.associationEventHandler_;
67      this.associationEventHandler_ = null;
68      handler(associationEvent);
69    }
70  };
71
72  State.prototype.setAssociationEventHandler = function(handler) {
73    if (!this.pendingAssociation &&
74        !internal.isValidInterfaceId(this.interfaceId)) {
75      return;
76    }
77
78    if (!handler) {
79      this.associationEventHandler_ = null;
80      return;
81    }
82
83    this.associationEventHandler_ = handler;
84    if (!this.pendingAssociation) {
85      setTimeout(this.runAssociationEventHandler.bind(this,
86          AssociationEvent.ASSOCIATED), 0);
87    } else if (!this.peerState_) {
88      setTimeout(this.runAssociationEventHandler.bind(this,
89          AssociationEvent.PEER_CLOSED_BEFORE_ASSOCIATION), 0);
90    }
91  };
92
93  State.prototype.notifyAssociation = function(interfaceId,
94                                               peerGroupController) {
95    var cachedPeerState = this.peerState_;
96    this.peerState_ = null;
97
98    this.pendingAssociation = false;
99
100    if (cachedPeerState) {
101      cachedPeerState.onAssociated(interfaceId, peerGroupController);
102      return true;
103    }
104    return false;
105  };
106
107  State.prototype.onAssociated = function(interfaceId,
108      associatedGroupController) {
109    if (!this.pendingAssociation) {
110      return;
111    }
112
113    this.pendingAssociation = false;
114    this.peerState_ = null;
115    this.interfaceId = interfaceId;
116    this.associatedGroupController = associatedGroupController;
117    this.runAssociationEventHandler(AssociationEvent.ASSOCIATED);
118  };
119
120  State.prototype.onPeerClosedBeforeAssociation = function(disconnectReason) {
121    if (!this.pendingAssociation) {
122      return;
123    }
124
125    this.peerState_ = null;
126    this.disconnectReason = disconnectReason;
127
128    this.runAssociationEventHandler(
129        AssociationEvent.PEER_CLOSED_BEFORE_ASSOCIATION);
130  };
131
132  function createPairPendingAssociation() {
133    var handle0 = new InterfaceEndpointHandle();
134    var handle1 = new InterfaceEndpointHandle();
135    handle0.state_.initPendingState(handle1.state_);
136    handle1.state_.initPendingState(handle0.state_);
137    return {handle0: handle0, handle1: handle1};
138  }
139
140  function InterfaceEndpointHandle(interfaceId, associatedGroupController) {
141    this.state_ = new State(interfaceId, associatedGroupController);
142  }
143
144  InterfaceEndpointHandle.prototype.isValid = function() {
145    return this.state_.isValid();
146  };
147
148  InterfaceEndpointHandle.prototype.pendingAssociation = function() {
149    return this.state_.pendingAssociation;
150  };
151
152  InterfaceEndpointHandle.prototype.id = function() {
153    return this.state_.interfaceId;
154  };
155
156  InterfaceEndpointHandle.prototype.groupController = function() {
157    return this.state_.associatedGroupController;
158  };
159
160  InterfaceEndpointHandle.prototype.disconnectReason = function() {
161    return this.state_.disconnectReason;
162  };
163
164  InterfaceEndpointHandle.prototype.setAssociationEventHandler = function(
165      handler) {
166    this.state_.setAssociationEventHandler(handler);
167  };
168
169  InterfaceEndpointHandle.prototype.notifyAssociation = function(interfaceId,
170      peerGroupController) {
171    return this.state_.notifyAssociation(interfaceId, peerGroupController);
172  };
173
174  InterfaceEndpointHandle.prototype.reset = function(reason) {
175    this.state_.close(reason);
176    this.state_ = new State();
177  };
178
179  internal.AssociationEvent = AssociationEvent;
180  internal.InterfaceEndpointHandle = InterfaceEndpointHandle;
181  internal.createPairPendingAssociation = createPairPendingAssociation;
182})();
183