frameelementmethodtransport.js 7.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259
  1. // Copyright 2007 The Closure Library Authors. All Rights Reserved.
  2. //
  3. // Licensed under the Apache License, Version 2.0 (the "License");
  4. // you may not use this file except in compliance with the License.
  5. // You may obtain a copy of the License at
  6. //
  7. // http://www.apache.org/licenses/LICENSE-2.0
  8. //
  9. // Unless required by applicable law or agreed to in writing, software
  10. // distributed under the License is distributed on an "AS-IS" BASIS,
  11. // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  12. // See the License for the specific language governing permissions and
  13. // limitations under the License.
  14. /**
  15. * @fileoverview Contains the frame element method transport for cross-domain
  16. * communication. It exploits the fact that FF lets a page in an
  17. * iframe call a method on the iframe-element it is contained in, even if the
  18. * containing page is from a different domain.
  19. *
  20. */
  21. goog.provide('goog.net.xpc.FrameElementMethodTransport');
  22. goog.require('goog.log');
  23. goog.require('goog.net.xpc');
  24. goog.require('goog.net.xpc.CrossPageChannelRole');
  25. goog.require('goog.net.xpc.Transport');
  26. goog.require('goog.net.xpc.TransportTypes');
  27. /**
  28. * Frame-element method transport.
  29. *
  30. * Firefox allows a document within an iframe to call methods on the
  31. * iframe-element added by the containing document.
  32. * NOTE(user): Tested in all FF versions starting from 1.0
  33. *
  34. * @param {goog.net.xpc.CrossPageChannel} channel The channel this transport
  35. * belongs to.
  36. * @param {goog.dom.DomHelper=} opt_domHelper The dom helper to use for finding
  37. * the correct window.
  38. * @constructor
  39. * @extends {goog.net.xpc.Transport}
  40. * @final
  41. */
  42. goog.net.xpc.FrameElementMethodTransport = function(channel, opt_domHelper) {
  43. goog.net.xpc.FrameElementMethodTransport.base(
  44. this, 'constructor', opt_domHelper);
  45. /**
  46. * The channel this transport belongs to.
  47. * @type {goog.net.xpc.CrossPageChannel}
  48. * @private
  49. */
  50. this.channel_ = channel;
  51. // To transfer messages, this transport basically uses normal function calls,
  52. // which are synchronous. To avoid endless recursion, the delivery has to
  53. // be artificially made asynchronous.
  54. /**
  55. * Array for queued messages.
  56. * @type {Array<{serviceName: string, payload: string}>}
  57. * @private
  58. */
  59. this.queue_ = [];
  60. /**
  61. * Callback function which wraps deliverQueued_.
  62. * @type {Function}
  63. * @private
  64. */
  65. this.deliverQueuedCb_ = goog.bind(this.deliverQueued_, this);
  66. };
  67. goog.inherits(goog.net.xpc.FrameElementMethodTransport, goog.net.xpc.Transport);
  68. /**
  69. * The transport type.
  70. * @type {number}
  71. * @protected
  72. * @override
  73. */
  74. goog.net.xpc.FrameElementMethodTransport.prototype.transportType =
  75. goog.net.xpc.TransportTypes.FRAME_ELEMENT_METHOD;
  76. /** @private {!Function|undefined} */
  77. goog.net.xpc.FrameElementMethodTransport.prototype.attemptSetupCb_;
  78. /** @private */
  79. goog.net.xpc.FrameElementMethodTransport.prototype.outgoing_;
  80. /** @private */
  81. goog.net.xpc.FrameElementMethodTransport.prototype.iframeElm_;
  82. /**
  83. * Flag used to enforce asynchronous messaging semantics.
  84. * @type {boolean}
  85. * @private
  86. */
  87. goog.net.xpc.FrameElementMethodTransport.prototype.recursive_ = false;
  88. /**
  89. * Holds the function to send messages to the peer
  90. * (once it becomes available).
  91. * @type {Function}
  92. * @private
  93. */
  94. goog.net.xpc.FrameElementMethodTransport.outgoing_ = null;
  95. /**
  96. * Connect this transport.
  97. * @override
  98. */
  99. goog.net.xpc.FrameElementMethodTransport.prototype.connect = function() {
  100. if (this.channel_.getRole() == goog.net.xpc.CrossPageChannelRole.OUTER) {
  101. // get shortcut to iframe-element
  102. this.iframeElm_ = this.channel_.getIframeElement();
  103. // add the gateway function to the iframe-element
  104. // (to be called by the peer)
  105. this.iframeElm_['XPC_toOuter'] = goog.bind(this.incoming_, this);
  106. // at this point we just have to wait for a notification from the peer...
  107. } else {
  108. this.attemptSetup_();
  109. }
  110. };
  111. /**
  112. * Only used from within an iframe. Attempts to attach the method
  113. * to be used for sending messages by the containing document. Has to
  114. * wait until the containing document has finished. Therefore calls
  115. * itself in a timeout if not successful.
  116. * @private
  117. */
  118. goog.net.xpc.FrameElementMethodTransport.prototype.attemptSetup_ = function() {
  119. var retry = true;
  120. try {
  121. if (!this.iframeElm_) {
  122. // throws security exception when called too early
  123. this.iframeElm_ = this.getWindow().frameElement;
  124. }
  125. // check if iframe-element and the gateway-function to the
  126. // outer-frame are present
  127. // TODO(user) Make sure the following code doesn't throw any exceptions
  128. if (this.iframeElm_ && this.iframeElm_['XPC_toOuter']) {
  129. // get a reference to the gateway function
  130. this.outgoing_ = this.iframeElm_['XPC_toOuter'];
  131. // attach the gateway function the other document will use
  132. this.iframeElm_['XPC_toOuter']['XPC_toInner'] =
  133. goog.bind(this.incoming_, this);
  134. // stop retrying
  135. retry = false;
  136. // notify outer frame
  137. this.send(goog.net.xpc.TRANSPORT_SERVICE_, goog.net.xpc.SETUP_ACK_);
  138. // notify channel that the transport is ready
  139. this.channel_.notifyConnected();
  140. }
  141. } catch (e) {
  142. goog.log.error(
  143. goog.net.xpc.logger, 'exception caught while attempting setup: ' + e);
  144. }
  145. // retry necessary?
  146. if (retry) {
  147. if (!this.attemptSetupCb_) {
  148. this.attemptSetupCb_ = goog.bind(this.attemptSetup_, this);
  149. }
  150. this.getWindow().setTimeout(this.attemptSetupCb_, 100);
  151. }
  152. };
  153. /**
  154. * Handles transport service messages.
  155. * @param {string} payload The message content.
  156. * @override
  157. */
  158. goog.net.xpc.FrameElementMethodTransport.prototype.transportServiceHandler =
  159. function(payload) {
  160. if (this.channel_.getRole() == goog.net.xpc.CrossPageChannelRole.OUTER &&
  161. !this.channel_.isConnected() && payload == goog.net.xpc.SETUP_ACK_) {
  162. // get a reference to the gateway function
  163. this.outgoing_ = this.iframeElm_['XPC_toOuter']['XPC_toInner'];
  164. // notify the channel we're ready
  165. this.channel_.notifyConnected();
  166. } else {
  167. throw Error('Got unexpected transport message.');
  168. }
  169. };
  170. /**
  171. * Process incoming message.
  172. * @param {string} serviceName The name of the service the message is to be
  173. * delivered to.
  174. * @param {string} payload The message to process.
  175. * @private
  176. */
  177. goog.net.xpc.FrameElementMethodTransport.prototype.incoming_ = function(
  178. serviceName, payload) {
  179. if (!this.recursive_ && this.queue_.length == 0) {
  180. this.channel_.xpcDeliver(serviceName, payload);
  181. } else {
  182. this.queue_.push({serviceName: serviceName, payload: payload});
  183. if (this.queue_.length == 1) {
  184. this.getWindow().setTimeout(this.deliverQueuedCb_, 1);
  185. }
  186. }
  187. };
  188. /**
  189. * Delivers queued messages.
  190. * @private
  191. */
  192. goog.net.xpc.FrameElementMethodTransport.prototype.deliverQueued_ = function() {
  193. while (this.queue_.length) {
  194. var msg = this.queue_.shift();
  195. this.channel_.xpcDeliver(msg.serviceName, msg.payload);
  196. }
  197. };
  198. /**
  199. * Send a message
  200. * @param {string} service The name off the service the message is to be
  201. * delivered to.
  202. * @param {string} payload The message content.
  203. * @override
  204. */
  205. goog.net.xpc.FrameElementMethodTransport.prototype.send = function(
  206. service, payload) {
  207. this.recursive_ = true;
  208. this.outgoing_(service, payload);
  209. this.recursive_ = false;
  210. };
  211. /** @override */
  212. goog.net.xpc.FrameElementMethodTransport.prototype.disposeInternal =
  213. function() {
  214. goog.net.xpc.FrameElementMethodTransport.superClass_.disposeInternal.call(
  215. this);
  216. this.outgoing_ = null;
  217. this.iframeElm_ = null;
  218. };