650 lines
21 KiB
JavaScript
650 lines
21 KiB
JavaScript
// Copyright 2007 The Closure Library Authors. All Rights Reserved.
|
|
//
|
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
|
// you may not use this file except in compliance with the License.
|
|
// You may obtain a copy of the License at
|
|
//
|
|
// http://www.apache.org/licenses/LICENSE-2.0
|
|
//
|
|
// Unless required by applicable law or agreed to in writing, software
|
|
// distributed under the License is distributed on an "AS-IS" BASIS,
|
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
// See the License for the specific language governing permissions and
|
|
// limitations under the License.
|
|
|
|
/**
|
|
* @fileoverview Contains the class which uses native messaging
|
|
* facilities for cross domain communication.
|
|
*
|
|
*/
|
|
|
|
|
|
goog.provide('goog.net.xpc.NativeMessagingTransport');
|
|
|
|
goog.require('goog.Timer');
|
|
goog.require('goog.asserts');
|
|
goog.require('goog.async.Deferred');
|
|
goog.require('goog.events');
|
|
goog.require('goog.events.EventHandler');
|
|
goog.require('goog.net.xpc');
|
|
goog.require('goog.net.xpc.CrossPageChannelRole');
|
|
goog.require('goog.net.xpc.Transport');
|
|
|
|
|
|
|
|
/**
|
|
* The native messaging transport
|
|
*
|
|
* Uses document.postMessage() to send messages to other documents.
|
|
* Receiving is done by listening on 'message'-events on the document.
|
|
*
|
|
* @param {goog.net.xpc.CrossPageChannel} channel The channel this
|
|
* transport belongs to.
|
|
* @param {string} peerHostname The hostname (protocol, domain, and port) of the
|
|
* peer.
|
|
* @param {goog.dom.DomHelper=} opt_domHelper The dom helper to use for
|
|
* finding the correct window/document.
|
|
* @param {boolean=} opt_oneSidedHandshake If this is true, only the outer
|
|
* transport sends a SETUP message and expects a SETUP_ACK. The inner
|
|
* transport goes connected when it receives the SETUP.
|
|
* @param {number=} opt_protocolVersion Which version of its setup protocol the
|
|
* transport should use. The default is '2'.
|
|
* @constructor
|
|
* @extends {goog.net.xpc.Transport}
|
|
*/
|
|
goog.net.xpc.NativeMessagingTransport = function(channel, peerHostname,
|
|
opt_domHelper, opt_oneSidedHandshake, opt_protocolVersion) {
|
|
goog.base(this, opt_domHelper);
|
|
|
|
/**
|
|
* The channel this transport belongs to.
|
|
* @type {goog.net.xpc.CrossPageChannel}
|
|
* @private
|
|
*/
|
|
this.channel_ = channel;
|
|
|
|
/**
|
|
* Which version of the transport's protocol should be used.
|
|
* @type {number}
|
|
* @private
|
|
*/
|
|
this.protocolVersion_ = opt_protocolVersion || 2;
|
|
goog.asserts.assert(this.protocolVersion_ >= 1);
|
|
goog.asserts.assert(this.protocolVersion_ <= 2);
|
|
|
|
/**
|
|
* The hostname of the peer. This parameterizes all calls to postMessage, and
|
|
* should contain the precise protocol, domain, and port of the peer window.
|
|
* @type {string}
|
|
* @private
|
|
*/
|
|
this.peerHostname_ = peerHostname || '*';
|
|
|
|
/**
|
|
* The event handler.
|
|
* @type {!goog.events.EventHandler}
|
|
* @private
|
|
*/
|
|
this.eventHandler_ = new goog.events.EventHandler(this);
|
|
|
|
/**
|
|
* Timer for connection reattempts.
|
|
* @type {!goog.Timer}
|
|
* @private
|
|
*/
|
|
this.maybeAttemptToConnectTimer_ = new goog.Timer(100, this.getWindow());
|
|
|
|
/**
|
|
* Whether one-sided handshakes are enabled.
|
|
* @type {boolean}
|
|
* @private
|
|
*/
|
|
this.oneSidedHandshake_ = !!opt_oneSidedHandshake;
|
|
|
|
/**
|
|
* Fires once we've received our SETUP_ACK message.
|
|
* @type {!goog.async.Deferred}
|
|
* @private
|
|
*/
|
|
this.setupAckReceived_ = new goog.async.Deferred();
|
|
|
|
/**
|
|
* Fires once we've sent our SETUP_ACK message.
|
|
* @type {!goog.async.Deferred}
|
|
* @private
|
|
*/
|
|
this.setupAckSent_ = new goog.async.Deferred();
|
|
|
|
/**
|
|
* Fires once we're marked connected.
|
|
* @type {!goog.async.Deferred}
|
|
* @private
|
|
*/
|
|
this.connected_ = new goog.async.Deferred();
|
|
|
|
/**
|
|
* The unique ID of this side of the connection. Used to determine when a peer
|
|
* is reloaded.
|
|
* @type {string}
|
|
* @private
|
|
*/
|
|
this.endpointId_ = goog.net.xpc.getRandomString(10);
|
|
|
|
/**
|
|
* The unique ID of the peer. If we get a message from a peer with an ID we
|
|
* don't expect, we reset the connection.
|
|
* @type {?string}
|
|
* @private
|
|
*/
|
|
this.peerEndpointId_ = null;
|
|
|
|
// We don't want to mark ourselves connected until we have sent whatever
|
|
// message will cause our counterpart in the other frame to also declare
|
|
// itself connected, if there is such a message. Otherwise we risk a user
|
|
// message being sent in advance of that message, and it being discarded.
|
|
if (this.oneSidedHandshake_) {
|
|
if (this.channel_.getRole() == goog.net.xpc.CrossPageChannelRole.INNER) {
|
|
// One sided handshake, inner frame:
|
|
// SETUP_ACK must be received.
|
|
this.connected_.awaitDeferred(this.setupAckReceived_);
|
|
} else {
|
|
// One sided handshake, outer frame:
|
|
// SETUP_ACK must be sent.
|
|
this.connected_.awaitDeferred(this.setupAckSent_);
|
|
}
|
|
} else {
|
|
// Two sided handshake:
|
|
// SETUP_ACK has to have been received, and sent.
|
|
this.connected_.awaitDeferred(this.setupAckReceived_);
|
|
if (this.protocolVersion_ == 2) {
|
|
this.connected_.awaitDeferred(this.setupAckSent_);
|
|
}
|
|
}
|
|
this.connected_.addCallback(this.notifyConnected_, this);
|
|
this.connected_.callback(true);
|
|
|
|
this.eventHandler_.
|
|
listen(this.maybeAttemptToConnectTimer_, goog.Timer.TICK,
|
|
this.maybeAttemptToConnect_);
|
|
|
|
goog.log.info(goog.net.xpc.logger, 'NativeMessagingTransport created. ' +
|
|
'protocolVersion=' + this.protocolVersion_ + ', oneSidedHandshake=' +
|
|
this.oneSidedHandshake_ + ', role=' + this.channel_.getRole());
|
|
};
|
|
goog.inherits(goog.net.xpc.NativeMessagingTransport, goog.net.xpc.Transport);
|
|
|
|
|
|
/**
|
|
* Length of the delay in milliseconds between the channel being connected and
|
|
* the connection callback being called, in cases where coverage of timing flaws
|
|
* is required.
|
|
* @type {number}
|
|
* @private
|
|
*/
|
|
goog.net.xpc.NativeMessagingTransport.CONNECTION_DELAY_MS_ = 200;
|
|
|
|
|
|
/**
|
|
* Current determination of peer's protocol version, or null for unknown.
|
|
* @type {?number}
|
|
* @private
|
|
*/
|
|
goog.net.xpc.NativeMessagingTransport.prototype.peerProtocolVersion_ = null;
|
|
|
|
|
|
/**
|
|
* Flag indicating if this instance of the transport has been initialized.
|
|
* @type {boolean}
|
|
* @private
|
|
*/
|
|
goog.net.xpc.NativeMessagingTransport.prototype.initialized_ = false;
|
|
|
|
|
|
/**
|
|
* The transport type.
|
|
* @type {number}
|
|
* @override
|
|
*/
|
|
goog.net.xpc.NativeMessagingTransport.prototype.transportType =
|
|
goog.net.xpc.TransportTypes.NATIVE_MESSAGING;
|
|
|
|
|
|
/**
|
|
* The delimiter used for transport service messages.
|
|
* @type {string}
|
|
* @private
|
|
*/
|
|
goog.net.xpc.NativeMessagingTransport.MESSAGE_DELIMITER_ = ',';
|
|
|
|
|
|
/**
|
|
* Tracks the number of NativeMessagingTransport channels that have been
|
|
* initialized but not disposed yet in a map keyed by the UID of the window
|
|
* object. This allows for multiple windows to be initiallized and listening
|
|
* for messages.
|
|
* @type {Object.<number>}
|
|
* @private
|
|
*/
|
|
goog.net.xpc.NativeMessagingTransport.activeCount_ = {};
|
|
|
|
|
|
/**
|
|
* Id of a timer user during postMessage sends.
|
|
* @type {number}
|
|
* @private
|
|
*/
|
|
goog.net.xpc.NativeMessagingTransport.sendTimerId_ = 0;
|
|
|
|
|
|
/**
|
|
* Checks whether the peer transport protocol version could be as indicated.
|
|
* @param {number} version The version to check for.
|
|
* @return {boolean} Whether the peer transport protocol version is as
|
|
* indicated, or null.
|
|
* @private
|
|
*/
|
|
goog.net.xpc.NativeMessagingTransport.prototype.couldPeerVersionBe_ =
|
|
function(version) {
|
|
return this.peerProtocolVersion_ == null ||
|
|
this.peerProtocolVersion_ == version;
|
|
};
|
|
|
|
|
|
/**
|
|
* Initializes this transport. Registers a listener for 'message'-events
|
|
* on the document.
|
|
* @param {Window} listenWindow The window to listen to events on.
|
|
* @private
|
|
*/
|
|
goog.net.xpc.NativeMessagingTransport.initialize_ = function(listenWindow) {
|
|
var uid = goog.getUid(listenWindow);
|
|
var value = goog.net.xpc.NativeMessagingTransport.activeCount_[uid];
|
|
if (!goog.isNumber(value)) {
|
|
value = 0;
|
|
}
|
|
if (value == 0) {
|
|
// Listen for message-events. These are fired on window in FF3 and on
|
|
// document in Opera.
|
|
goog.events.listen(
|
|
listenWindow.postMessage ? listenWindow : listenWindow.document,
|
|
'message',
|
|
goog.net.xpc.NativeMessagingTransport.messageReceived_,
|
|
false,
|
|
goog.net.xpc.NativeMessagingTransport);
|
|
}
|
|
goog.net.xpc.NativeMessagingTransport.activeCount_[uid] = value + 1;
|
|
};
|
|
|
|
|
|
/**
|
|
* Processes an incoming message-event.
|
|
* @param {goog.events.BrowserEvent} msgEvt The message event.
|
|
* @return {boolean} True if message was successfully delivered to a channel.
|
|
* @private
|
|
*/
|
|
goog.net.xpc.NativeMessagingTransport.messageReceived_ = function(msgEvt) {
|
|
var data = msgEvt.getBrowserEvent().data;
|
|
|
|
if (!goog.isString(data)) {
|
|
return false;
|
|
}
|
|
|
|
var headDelim = data.indexOf('|');
|
|
var serviceDelim = data.indexOf(':');
|
|
|
|
// make sure we got something reasonable
|
|
if (headDelim == -1 || serviceDelim == -1) {
|
|
return false;
|
|
}
|
|
|
|
var channelName = data.substring(0, headDelim);
|
|
var service = data.substring(headDelim + 1, serviceDelim);
|
|
var payload = data.substring(serviceDelim + 1);
|
|
|
|
goog.log.fine(goog.net.xpc.logger,
|
|
'messageReceived: channel=' + channelName +
|
|
', service=' + service + ', payload=' + payload);
|
|
|
|
// Attempt to deliver message to the channel. Keep in mind that it may not
|
|
// exist for several reasons, including but not limited to:
|
|
// - a malformed message
|
|
// - the channel simply has not been created
|
|
// - channel was created in a different namespace
|
|
// - message was sent to the wrong window
|
|
// - channel has become stale (e.g. caching iframes and back clicks)
|
|
var channel = goog.net.xpc.channels[channelName];
|
|
if (channel) {
|
|
channel.xpcDeliver(service, payload, msgEvt.getBrowserEvent().origin);
|
|
return true;
|
|
}
|
|
|
|
var transportMessageType =
|
|
goog.net.xpc.NativeMessagingTransport.parseTransportPayload_(payload)[0];
|
|
|
|
// Check if there are any stale channel names that can be updated.
|
|
for (var staleChannelName in goog.net.xpc.channels) {
|
|
var staleChannel = goog.net.xpc.channels[staleChannelName];
|
|
if (staleChannel.getRole() == goog.net.xpc.CrossPageChannelRole.INNER &&
|
|
!staleChannel.isConnected() &&
|
|
service == goog.net.xpc.TRANSPORT_SERVICE_ &&
|
|
(transportMessageType == goog.net.xpc.SETUP ||
|
|
transportMessageType == goog.net.xpc.SETUP_NTPV2)) {
|
|
// Inner peer received SETUP message but channel names did not match.
|
|
// Start using the channel name sent from outer peer. The channel name
|
|
// of the inner peer can easily become out of date, as iframe's and their
|
|
// JS state get cached in many browsers upon page reload or history
|
|
// navigation (particularly Firefox 1.5+). We can trust the outer peer,
|
|
// since we only accept postMessage messages from the same hostname that
|
|
// originally setup the channel.
|
|
goog.log.fine(goog.net.xpc.logger,
|
|
'changing channel name to ' + channelName);
|
|
staleChannel.name = channelName;
|
|
// Remove old stale pointer to channel.
|
|
delete goog.net.xpc.channels[staleChannelName];
|
|
// Create fresh pointer to channel.
|
|
goog.net.xpc.channels[channelName] = staleChannel;
|
|
staleChannel.xpcDeliver(service, payload);
|
|
return true;
|
|
}
|
|
}
|
|
|
|
// Failed to find a channel to deliver this message to, so simply ignore it.
|
|
goog.log.info(goog.net.xpc.logger, 'channel name mismatch; message ignored"');
|
|
return false;
|
|
};
|
|
|
|
|
|
/**
|
|
* Handles transport service messages.
|
|
* @param {string} payload The message content.
|
|
* @override
|
|
*/
|
|
goog.net.xpc.NativeMessagingTransport.prototype.transportServiceHandler =
|
|
function(payload) {
|
|
var transportParts =
|
|
goog.net.xpc.NativeMessagingTransport.parseTransportPayload_(payload);
|
|
var transportMessageType = transportParts[0];
|
|
var peerEndpointId = transportParts[1];
|
|
switch (transportMessageType) {
|
|
case goog.net.xpc.SETUP_ACK_:
|
|
this.setPeerProtocolVersion_(1);
|
|
if (!this.setupAckReceived_.hasFired()) {
|
|
this.setupAckReceived_.callback(true);
|
|
}
|
|
break;
|
|
case goog.net.xpc.SETUP_ACK_NTPV2:
|
|
if (this.protocolVersion_ == 2) {
|
|
this.setPeerProtocolVersion_(2);
|
|
if (!this.setupAckReceived_.hasFired()) {
|
|
this.setupAckReceived_.callback(true);
|
|
}
|
|
}
|
|
break;
|
|
case goog.net.xpc.SETUP:
|
|
this.setPeerProtocolVersion_(1);
|
|
this.sendSetupAckMessage_(1);
|
|
break;
|
|
case goog.net.xpc.SETUP_NTPV2:
|
|
if (this.protocolVersion_ == 2) {
|
|
var prevPeerProtocolVersion = this.peerProtocolVersion_;
|
|
this.setPeerProtocolVersion_(2);
|
|
this.sendSetupAckMessage_(2);
|
|
if ((prevPeerProtocolVersion == 1 || this.peerEndpointId_ != null) &&
|
|
this.peerEndpointId_ != peerEndpointId) {
|
|
// Send a new SETUP message since the peer has been replaced.
|
|
goog.log.info(goog.net.xpc.logger,
|
|
'Sending SETUP and changing peer ID to: ' + peerEndpointId);
|
|
this.sendSetupMessage_();
|
|
}
|
|
this.peerEndpointId_ = peerEndpointId;
|
|
}
|
|
break;
|
|
}
|
|
};
|
|
|
|
|
|
/**
|
|
* Sends a SETUP transport service message of the correct protocol number for
|
|
* our current situation.
|
|
* @private
|
|
*/
|
|
goog.net.xpc.NativeMessagingTransport.prototype.sendSetupMessage_ =
|
|
function() {
|
|
// 'real' (legacy) v1 transports don't know about there being v2 ones out
|
|
// there, and we shouldn't either.
|
|
goog.asserts.assert(!(this.protocolVersion_ == 1 &&
|
|
this.peerProtocolVersion_ == 2));
|
|
|
|
if (this.protocolVersion_ == 2 && this.couldPeerVersionBe_(2)) {
|
|
var payload = goog.net.xpc.SETUP_NTPV2;
|
|
payload += goog.net.xpc.NativeMessagingTransport.MESSAGE_DELIMITER_;
|
|
payload += this.endpointId_;
|
|
this.send(goog.net.xpc.TRANSPORT_SERVICE_, payload);
|
|
}
|
|
|
|
// For backward compatibility reasons, the V1 SETUP message can be sent by
|
|
// both V1 and V2 transports. Once a V2 transport has 'heard' another V2
|
|
// transport it starts ignoring V1 messages, so the V2 message must be sent
|
|
// first.
|
|
if (this.couldPeerVersionBe_(1)) {
|
|
this.send(goog.net.xpc.TRANSPORT_SERVICE_, goog.net.xpc.SETUP);
|
|
}
|
|
};
|
|
|
|
|
|
/**
|
|
* Sends a SETUP_ACK transport service message of the correct protocol number
|
|
* for our current situation.
|
|
* @param {number} protocolVersion The protocol version of the SETUP message
|
|
* which gave rise to this ack message.
|
|
* @private
|
|
*/
|
|
goog.net.xpc.NativeMessagingTransport.prototype.sendSetupAckMessage_ =
|
|
function(protocolVersion) {
|
|
goog.asserts.assert(this.protocolVersion_ != 1 || protocolVersion != 2,
|
|
'Shouldn\'t try to send a v2 setup ack in v1 mode.');
|
|
if (this.protocolVersion_ == 2 && this.couldPeerVersionBe_(2) &&
|
|
protocolVersion == 2) {
|
|
this.send(goog.net.xpc.TRANSPORT_SERVICE_, goog.net.xpc.SETUP_ACK_NTPV2);
|
|
} else if (this.couldPeerVersionBe_(1) && protocolVersion == 1) {
|
|
this.send(goog.net.xpc.TRANSPORT_SERVICE_, goog.net.xpc.SETUP_ACK_);
|
|
} else {
|
|
return;
|
|
}
|
|
|
|
if (!this.setupAckSent_.hasFired()) {
|
|
this.setupAckSent_.callback(true);
|
|
}
|
|
};
|
|
|
|
|
|
/**
|
|
* Attempts to set the peer protocol number. Downgrades from 2 to 1 are not
|
|
* permitted.
|
|
* @param {number} version The new protocol number.
|
|
* @private
|
|
*/
|
|
goog.net.xpc.NativeMessagingTransport.prototype.setPeerProtocolVersion_ =
|
|
function(version) {
|
|
if (version > this.peerProtocolVersion_) {
|
|
this.peerProtocolVersion_ = version;
|
|
}
|
|
if (this.peerProtocolVersion_ == 1) {
|
|
if (!this.setupAckSent_.hasFired() && !this.oneSidedHandshake_) {
|
|
this.setupAckSent_.callback(true);
|
|
}
|
|
this.peerEndpointId_ = null;
|
|
}
|
|
};
|
|
|
|
|
|
/**
|
|
* Connects this transport.
|
|
* @override
|
|
*/
|
|
goog.net.xpc.NativeMessagingTransport.prototype.connect = function() {
|
|
goog.net.xpc.NativeMessagingTransport.initialize_(this.getWindow());
|
|
this.initialized_ = true;
|
|
this.maybeAttemptToConnect_();
|
|
};
|
|
|
|
|
|
/**
|
|
* Connects to other peer. In the case of the outer peer, the setup messages are
|
|
* likely sent before the inner peer is ready to receive them. Therefore, this
|
|
* function will continue trying to send the SETUP message until the inner peer
|
|
* responds. In the case of the inner peer, it will occasionally have its
|
|
* channel name fall out of sync with the outer peer, particularly during
|
|
* soft-reloads and history navigations.
|
|
* @private
|
|
*/
|
|
goog.net.xpc.NativeMessagingTransport.prototype.maybeAttemptToConnect_ =
|
|
function() {
|
|
// In a one-sided handshake, the outer frame does not send a SETUP message,
|
|
// but the inner frame does.
|
|
var outerFrame = this.channel_.getRole() ==
|
|
goog.net.xpc.CrossPageChannelRole.OUTER;
|
|
if ((this.oneSidedHandshake_ && outerFrame) ||
|
|
this.channel_.isConnected() ||
|
|
this.isDisposed()) {
|
|
this.maybeAttemptToConnectTimer_.stop();
|
|
return;
|
|
}
|
|
this.maybeAttemptToConnectTimer_.start();
|
|
this.sendSetupMessage_();
|
|
};
|
|
|
|
|
|
/**
|
|
* Sends a message.
|
|
* @param {string} service The name off the service the message is to be
|
|
* delivered to.
|
|
* @param {string} payload The message content.
|
|
* @override
|
|
*/
|
|
goog.net.xpc.NativeMessagingTransport.prototype.send = function(service,
|
|
payload) {
|
|
var win = this.channel_.getPeerWindowObject();
|
|
if (!win) {
|
|
goog.log.fine(goog.net.xpc.logger, 'send(): window not ready');
|
|
return;
|
|
}
|
|
|
|
this.send = function(service, payload) {
|
|
// In IE8 (and perhaps elsewhere), it seems like postMessage is sometimes
|
|
// implemented as a synchronous call. That is, calling it synchronously
|
|
// calls whatever listeners it has, and control is not returned to the
|
|
// calling thread until those listeners are run. This produces different
|
|
// ordering to all other browsers, and breaks this protocol. This timer
|
|
// callback is introduced to produce standard behavior across all browsers.
|
|
var transport = this;
|
|
var channelName = this.channel_.name;
|
|
var sendFunctor = function() {
|
|
transport.sendTimerId_ = 0;
|
|
|
|
try {
|
|
// postMessage is a method of the window object, except in some
|
|
// versions of Opera, where it is a method of the document object. It
|
|
// also seems that the appearance of postMessage on the peer window
|
|
// object can sometimes be delayed.
|
|
var obj = win.postMessage ? win : win.document;
|
|
if (!obj.postMessage) {
|
|
goog.log.warning(goog.net.xpc.logger,
|
|
'Peer window had no postMessage function.');
|
|
return;
|
|
}
|
|
|
|
obj.postMessage(channelName + '|' + service + ':' + payload,
|
|
transport.peerHostname_);
|
|
goog.log.fine(goog.net.xpc.logger, 'send(): service=' + service +
|
|
' payload=' + payload + ' to hostname=' + transport.peerHostname_);
|
|
} catch (error) {
|
|
// There is some evidence (not totally convincing) that postMessage can
|
|
// be missing or throw errors during a narrow timing window during
|
|
// startup. This protects against that.
|
|
goog.log.warning(goog.net.xpc.logger,
|
|
'Error performing postMessage, ignoring.', error);
|
|
}
|
|
};
|
|
this.sendTimerId_ = goog.Timer.callOnce(sendFunctor, 0);
|
|
};
|
|
this.send(service, payload);
|
|
};
|
|
|
|
|
|
/**
|
|
* Notify the channel that this transport is connected. If either transport is
|
|
* protocol v1, a short delay is required to paper over timing vulnerabilities
|
|
* in that protocol version.
|
|
* @private
|
|
*/
|
|
goog.net.xpc.NativeMessagingTransport.prototype.notifyConnected_ =
|
|
function() {
|
|
var delay = (this.protocolVersion_ == 1 || this.peerProtocolVersion_ == 1) ?
|
|
goog.net.xpc.NativeMessagingTransport.CONNECTION_DELAY_MS_ : undefined;
|
|
this.channel_.notifyConnected(delay);
|
|
};
|
|
|
|
|
|
/** @override */
|
|
goog.net.xpc.NativeMessagingTransport.prototype.disposeInternal = function() {
|
|
if (this.initialized_) {
|
|
var listenWindow = this.getWindow();
|
|
var uid = goog.getUid(listenWindow);
|
|
var value = goog.net.xpc.NativeMessagingTransport.activeCount_[uid];
|
|
goog.net.xpc.NativeMessagingTransport.activeCount_[uid] = value - 1;
|
|
if (value == 1) {
|
|
goog.events.unlisten(
|
|
listenWindow.postMessage ? listenWindow : listenWindow.document,
|
|
'message',
|
|
goog.net.xpc.NativeMessagingTransport.messageReceived_,
|
|
false,
|
|
goog.net.xpc.NativeMessagingTransport);
|
|
}
|
|
}
|
|
|
|
if (this.sendTimerId_) {
|
|
goog.Timer.clear(this.sendTimerId_);
|
|
this.sendTimerId_ = 0;
|
|
}
|
|
|
|
goog.dispose(this.eventHandler_);
|
|
delete this.eventHandler_;
|
|
|
|
goog.dispose(this.maybeAttemptToConnectTimer_);
|
|
delete this.maybeAttemptToConnectTimer_;
|
|
|
|
this.setupAckReceived_.cancel();
|
|
delete this.setupAckReceived_;
|
|
this.setupAckSent_.cancel();
|
|
delete this.setupAckSent_;
|
|
this.connected_.cancel();
|
|
delete this.connected_;
|
|
|
|
// Cleaning up this.send as it is an instance method, created in
|
|
// goog.net.xpc.NativeMessagingTransport.prototype.send and has a closure over
|
|
// this.channel_.peerWindowObject_.
|
|
delete this.send;
|
|
|
|
goog.base(this, 'disposeInternal');
|
|
};
|
|
|
|
|
|
/**
|
|
* Parse a transport service payload message. For v1, it is simply expected to
|
|
* be 'SETUP' or 'SETUP_ACK'. For v2, an example setup message is
|
|
* 'SETUP_NTPV2,abc123', where the second part is the endpoint id. The v2 setup
|
|
* ack message is simply 'SETUP_ACK_NTPV2'.
|
|
* @param {string} payload The payload.
|
|
* @return {!Array.<?string>} An array with the message type as the first member
|
|
* and the endpoint id as the second, if one was sent, or null otherwise.
|
|
* @private
|
|
*/
|
|
goog.net.xpc.NativeMessagingTransport.parseTransportPayload_ =
|
|
function(payload) {
|
|
var transportParts = /** @type {!Array.<?string>} */ (payload.split(
|
|
goog.net.xpc.NativeMessagingTransport.MESSAGE_DELIMITER_));
|
|
transportParts[1] = transportParts[1] || null;
|
|
return transportParts;
|
|
};
|