142 lines
		
	
	
		
			3.7 KiB
		
	
	
	
		
			JavaScript
		
	
	
	
	
	
			
		
		
	
	
			142 lines
		
	
	
		
			3.7 KiB
		
	
	
	
		
			JavaScript
		
	
	
	
	
	
| '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;
 |