iframe.js 3.7 KB

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