[6a3a178] | 1 | 'use strict';
|
---|
| 2 |
|
---|
| 3 | // Few cool transports do work only for same-origin. In order to make
|
---|
| 4 | // them work cross-domain we shall use iframe, served from the
|
---|
| 5 | // remote domain. New browsers have capabilities to communicate with
|
---|
| 6 | // cross domain iframe using postMessage(). In IE it was implemented
|
---|
| 7 | // from IE 8+, but of course, IE got some details wrong:
|
---|
| 8 | // http://msdn.microsoft.com/en-us/library/cc197015(v=VS.85).aspx
|
---|
| 9 | // http://stevesouders.com/misc/test-postmessage.php
|
---|
| 10 |
|
---|
| 11 | var inherits = require('inherits')
|
---|
| 12 | , JSON3 = require('json3')
|
---|
| 13 | , EventEmitter = require('events').EventEmitter
|
---|
| 14 | , version = require('../version')
|
---|
| 15 | , urlUtils = require('../utils/url')
|
---|
| 16 | , iframeUtils = require('../utils/iframe')
|
---|
| 17 | , eventUtils = require('../utils/event')
|
---|
| 18 | , random = require('../utils/random')
|
---|
| 19 | ;
|
---|
| 20 |
|
---|
| 21 | var debug = function() {};
|
---|
| 22 | if (process.env.NODE_ENV !== 'production') {
|
---|
| 23 | debug = require('debug')('sockjs-client:transport:iframe');
|
---|
| 24 | }
|
---|
| 25 |
|
---|
| 26 | function IframeTransport(transport, transUrl, baseUrl) {
|
---|
| 27 | if (!IframeTransport.enabled()) {
|
---|
| 28 | throw new Error('Transport created when disabled');
|
---|
| 29 | }
|
---|
| 30 | EventEmitter.call(this);
|
---|
| 31 |
|
---|
| 32 | var self = this;
|
---|
| 33 | this.origin = urlUtils.getOrigin(baseUrl);
|
---|
| 34 | this.baseUrl = baseUrl;
|
---|
| 35 | this.transUrl = transUrl;
|
---|
| 36 | this.transport = transport;
|
---|
| 37 | this.windowId = random.string(8);
|
---|
| 38 |
|
---|
| 39 | var iframeUrl = urlUtils.addPath(baseUrl, '/iframe.html') + '#' + this.windowId;
|
---|
| 40 | debug(transport, transUrl, iframeUrl);
|
---|
| 41 |
|
---|
| 42 | this.iframeObj = iframeUtils.createIframe(iframeUrl, function(r) {
|
---|
| 43 | debug('err callback');
|
---|
| 44 | self.emit('close', 1006, 'Unable to load an iframe (' + r + ')');
|
---|
| 45 | self.close();
|
---|
| 46 | });
|
---|
| 47 |
|
---|
| 48 | this.onmessageCallback = this._message.bind(this);
|
---|
| 49 | eventUtils.attachEvent('message', this.onmessageCallback);
|
---|
| 50 | }
|
---|
| 51 |
|
---|
| 52 | inherits(IframeTransport, EventEmitter);
|
---|
| 53 |
|
---|
| 54 | IframeTransport.prototype.close = function() {
|
---|
| 55 | debug('close');
|
---|
| 56 | this.removeAllListeners();
|
---|
| 57 | if (this.iframeObj) {
|
---|
| 58 | eventUtils.detachEvent('message', this.onmessageCallback);
|
---|
| 59 | try {
|
---|
| 60 | // When the iframe is not loaded, IE raises an exception
|
---|
| 61 | // on 'contentWindow'.
|
---|
| 62 | this.postMessage('c');
|
---|
| 63 | } catch (x) {
|
---|
| 64 | // intentionally empty
|
---|
| 65 | }
|
---|
| 66 | this.iframeObj.cleanup();
|
---|
| 67 | this.iframeObj = null;
|
---|
| 68 | this.onmessageCallback = this.iframeObj = null;
|
---|
| 69 | }
|
---|
| 70 | };
|
---|
| 71 |
|
---|
| 72 | IframeTransport.prototype._message = function(e) {
|
---|
| 73 | debug('message', e.data);
|
---|
| 74 | if (!urlUtils.isOriginEqual(e.origin, this.origin)) {
|
---|
| 75 | debug('not same origin', e.origin, this.origin);
|
---|
| 76 | return;
|
---|
| 77 | }
|
---|
| 78 |
|
---|
| 79 | var iframeMessage;
|
---|
| 80 | try {
|
---|
| 81 | iframeMessage = JSON3.parse(e.data);
|
---|
| 82 | } catch (ignored) {
|
---|
| 83 | debug('bad json', e.data);
|
---|
| 84 | return;
|
---|
| 85 | }
|
---|
| 86 |
|
---|
| 87 | if (iframeMessage.windowId !== this.windowId) {
|
---|
| 88 | debug('mismatched window id', iframeMessage.windowId, this.windowId);
|
---|
| 89 | return;
|
---|
| 90 | }
|
---|
| 91 |
|
---|
| 92 | switch (iframeMessage.type) {
|
---|
| 93 | case 's':
|
---|
| 94 | this.iframeObj.loaded();
|
---|
| 95 | // window global dependency
|
---|
| 96 | this.postMessage('s', JSON3.stringify([
|
---|
| 97 | version
|
---|
| 98 | , this.transport
|
---|
| 99 | , this.transUrl
|
---|
| 100 | , this.baseUrl
|
---|
| 101 | ]));
|
---|
| 102 | break;
|
---|
| 103 | case 't':
|
---|
| 104 | this.emit('message', iframeMessage.data);
|
---|
| 105 | break;
|
---|
| 106 | case 'c':
|
---|
| 107 | var cdata;
|
---|
| 108 | try {
|
---|
| 109 | cdata = JSON3.parse(iframeMessage.data);
|
---|
| 110 | } catch (ignored) {
|
---|
| 111 | debug('bad json', iframeMessage.data);
|
---|
| 112 | return;
|
---|
| 113 | }
|
---|
| 114 | this.emit('close', cdata[0], cdata[1]);
|
---|
| 115 | this.close();
|
---|
| 116 | break;
|
---|
| 117 | }
|
---|
| 118 | };
|
---|
| 119 |
|
---|
| 120 | IframeTransport.prototype.postMessage = function(type, data) {
|
---|
| 121 | debug('postMessage', type, data);
|
---|
| 122 | this.iframeObj.post(JSON3.stringify({
|
---|
| 123 | windowId: this.windowId
|
---|
| 124 | , type: type
|
---|
| 125 | , data: data || ''
|
---|
| 126 | }), this.origin);
|
---|
| 127 | };
|
---|
| 128 |
|
---|
| 129 | IframeTransport.prototype.send = function(message) {
|
---|
| 130 | debug('send', message);
|
---|
| 131 | this.postMessage('m', message);
|
---|
| 132 | };
|
---|
| 133 |
|
---|
| 134 | IframeTransport.enabled = function() {
|
---|
| 135 | return iframeUtils.iframeEnabled;
|
---|
| 136 | };
|
---|
| 137 |
|
---|
| 138 | IframeTransport.transportName = 'iframe';
|
---|
| 139 | IframeTransport.roundTrips = 2;
|
---|
| 140 |
|
---|
| 141 | module.exports = IframeTransport;
|
---|