diff options
Diffstat (limited to 'contexts/data/lib/closure-library/closure/goog/net/xpc/nativemessagingtransport.js')
-rw-r--r-- | contexts/data/lib/closure-library/closure/goog/net/xpc/nativemessagingtransport.js | 647 |
1 files changed, 0 insertions, 647 deletions
diff --git a/contexts/data/lib/closure-library/closure/goog/net/xpc/nativemessagingtransport.js b/contexts/data/lib/closure-library/closure/goog/net/xpc/nativemessagingtransport.js deleted file mode 100644 index e0674fa..0000000 --- a/contexts/data/lib/closure-library/closure/goog/net/xpc/nativemessagingtransport.js +++ /dev/null @@ -1,647 +0,0 @@ -// 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.net.xpc.logger.info('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.net.xpc.logger.fine('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.net.xpc.logger.fine('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.net.xpc.logger.info('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.net.xpc.logger.info('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.net.xpc.logger.fine('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.net.xpc.logger.warning('Peer window had no postMessage ' + - 'function.'); - return; - } - - obj.postMessage(channelName + '|' + service + ':' + payload, - transport.peerHostname_); - goog.net.xpc.logger.fine('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.net.xpc.logger.warning('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; -}; |