|
|
'use strict';
// Few cool transports do work only for same-origin. In order to make
// them work cross-domain we shall use iframe, served from the
// remote domain. New browsers have capabilities to communicate with
// cross domain iframe using postMessage(). In IE it was implemented
// from IE 8+, but of course, IE got some details wrong:
// http://msdn.microsoft.com/en-us/library/cc197015(v=VS.85).aspx
// http://stevesouders.com/misc/test-postmessage.php
var inherits = require('inherits') , JSON3 = require('json3') , EventEmitter = require('events').EventEmitter , version = require('../version') , urlUtils = require('../utils/url') , iframeUtils = require('../utils/iframe') , eventUtils = require('../utils/event') , random = require('../utils/random') ;
var debug = function() {}; if (process.env.NODE_ENV !== 'production') { debug = require('debug')('sockjs-client:transport:iframe'); }
function IframeTransport(transport, transUrl, baseUrl) { if (!IframeTransport.enabled()) { throw new Error('Transport created when disabled'); } EventEmitter.call(this);
var self = this; this.origin = urlUtils.getOrigin(baseUrl); this.baseUrl = baseUrl; this.transUrl = transUrl; this.transport = transport; this.windowId = random.string(8);
var iframeUrl = urlUtils.addPath(baseUrl, '/iframe.html') + '#' + this.windowId; debug(transport, transUrl, iframeUrl);
this.iframeObj = iframeUtils.createIframe(iframeUrl, function(r) { debug('err callback'); self.emit('close', 1006, 'Unable to load an iframe (' + r + ')'); self.close(); });
this.onmessageCallback = this._message.bind(this); eventUtils.attachEvent('message', this.onmessageCallback); }
inherits(IframeTransport, EventEmitter);
IframeTransport.prototype.close = function() { debug('close'); this.removeAllListeners(); if (this.iframeObj) { eventUtils.detachEvent('message', this.onmessageCallback); try { // When the iframe is not loaded, IE raises an exception
// on 'contentWindow'.
this.postMessage('c'); } catch (x) { // intentionally empty
} this.iframeObj.cleanup(); this.iframeObj = null; this.onmessageCallback = this.iframeObj = null; } };
IframeTransport.prototype._message = function(e) { debug('message', e.data); if (!urlUtils.isOriginEqual(e.origin, this.origin)) { debug('not same origin', e.origin, this.origin); return; }
var iframeMessage; try { iframeMessage = JSON3.parse(e.data); } catch (ignored) { debug('bad json', e.data); return; }
if (iframeMessage.windowId !== this.windowId) { debug('mismatched window id', iframeMessage.windowId, this.windowId); return; }
switch (iframeMessage.type) { case 's': this.iframeObj.loaded(); // window global dependency
this.postMessage('s', JSON3.stringify([ version , this.transport , this.transUrl , this.baseUrl ])); break; case 't': this.emit('message', iframeMessage.data); break; case 'c': var cdata; try { cdata = JSON3.parse(iframeMessage.data); } catch (ignored) { debug('bad json', iframeMessage.data); return; } this.emit('close', cdata[0], cdata[1]); this.close(); break; } };
IframeTransport.prototype.postMessage = function(type, data) { debug('postMessage', type, data); this.iframeObj.post(JSON3.stringify({ windowId: this.windowId , type: type , data: data || '' }), this.origin); };
IframeTransport.prototype.send = function(message) { debug('send', message); this.postMessage('m', message); };
IframeTransport.enabled = function() { return iframeUtils.iframeEnabled; };
IframeTransport.transportName = 'iframe'; IframeTransport.roundTrips = 2;
module.exports = IframeTransport;
|