'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;
|