nativemessagingtransport.js 21 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652
  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 class which uses native messaging
  16. * facilities for cross domain communication.
  17. *
  18. */
  19. goog.provide('goog.net.xpc.NativeMessagingTransport');
  20. goog.require('goog.Timer');
  21. goog.require('goog.asserts');
  22. goog.require('goog.async.Deferred');
  23. goog.require('goog.events');
  24. goog.require('goog.events.EventHandler');
  25. goog.require('goog.log');
  26. goog.require('goog.net.xpc');
  27. goog.require('goog.net.xpc.CrossPageChannelRole');
  28. goog.require('goog.net.xpc.Transport');
  29. goog.require('goog.net.xpc.TransportTypes');
  30. /**
  31. * The native messaging transport
  32. *
  33. * Uses document.postMessage() to send messages to other documents.
  34. * Receiving is done by listening on 'message'-events on the document.
  35. *
  36. * @param {goog.net.xpc.CrossPageChannel} channel The channel this
  37. * transport belongs to.
  38. * @param {string} peerHostname The hostname (protocol, domain, and port) of the
  39. * peer.
  40. * @param {goog.dom.DomHelper=} opt_domHelper The dom helper to use for
  41. * finding the correct window/document.
  42. * @param {boolean=} opt_oneSidedHandshake If this is true, only the outer
  43. * transport sends a SETUP message and expects a SETUP_ACK. The inner
  44. * transport goes connected when it receives the SETUP.
  45. * @param {number=} opt_protocolVersion Which version of its setup protocol the
  46. * transport should use. The default is '2'.
  47. * @constructor
  48. * @extends {goog.net.xpc.Transport}
  49. * @final
  50. */
  51. goog.net.xpc.NativeMessagingTransport = function(
  52. channel, peerHostname, opt_domHelper, opt_oneSidedHandshake,
  53. opt_protocolVersion) {
  54. goog.net.xpc.NativeMessagingTransport.base(
  55. this, 'constructor', opt_domHelper);
  56. /**
  57. * The channel this transport belongs to.
  58. * @type {goog.net.xpc.CrossPageChannel}
  59. * @private
  60. */
  61. this.channel_ = channel;
  62. /**
  63. * Which version of the transport's protocol should be used.
  64. * @type {number}
  65. * @private
  66. */
  67. this.protocolVersion_ = opt_protocolVersion || 2;
  68. goog.asserts.assert(this.protocolVersion_ >= 1);
  69. goog.asserts.assert(this.protocolVersion_ <= 2);
  70. /**
  71. * The hostname of the peer. This parameterizes all calls to postMessage, and
  72. * should contain the precise protocol, domain, and port of the peer window.
  73. * @type {string}
  74. * @private
  75. */
  76. this.peerHostname_ = peerHostname || '*';
  77. /**
  78. * The event handler.
  79. * @type {!goog.events.EventHandler<!goog.net.xpc.NativeMessagingTransport>}
  80. * @private
  81. */
  82. this.eventHandler_ = new goog.events.EventHandler(this);
  83. /**
  84. * Timer for connection reattempts.
  85. * @type {!goog.Timer}
  86. * @private
  87. */
  88. this.maybeAttemptToConnectTimer_ = new goog.Timer(100, this.getWindow());
  89. /**
  90. * Whether one-sided handshakes are enabled.
  91. * @type {boolean}
  92. * @private
  93. */
  94. this.oneSidedHandshake_ = !!opt_oneSidedHandshake;
  95. /**
  96. * Fires once we've received our SETUP_ACK message.
  97. * @type {!goog.async.Deferred}
  98. * @private
  99. */
  100. this.setupAckReceived_ = new goog.async.Deferred();
  101. /**
  102. * Fires once we've sent our SETUP_ACK message.
  103. * @type {!goog.async.Deferred}
  104. * @private
  105. */
  106. this.setupAckSent_ = new goog.async.Deferred();
  107. /**
  108. * Fires once we're marked connected.
  109. * @type {!goog.async.Deferred}
  110. * @private
  111. */
  112. this.connected_ = new goog.async.Deferred();
  113. /**
  114. * The unique ID of this side of the connection. Used to determine when a peer
  115. * is reloaded.
  116. * @type {string}
  117. * @private
  118. */
  119. this.endpointId_ = goog.net.xpc.getRandomString(10);
  120. /**
  121. * The unique ID of the peer. If we get a message from a peer with an ID we
  122. * don't expect, we reset the connection.
  123. * @type {?string}
  124. * @private
  125. */
  126. this.peerEndpointId_ = null;
  127. // We don't want to mark ourselves connected until we have sent whatever
  128. // message will cause our counterpart in the other frame to also declare
  129. // itself connected, if there is such a message. Otherwise we risk a user
  130. // message being sent in advance of that message, and it being discarded.
  131. if (this.oneSidedHandshake_) {
  132. if (this.channel_.getRole() == goog.net.xpc.CrossPageChannelRole.INNER) {
  133. // One sided handshake, inner frame:
  134. // SETUP_ACK must be received.
  135. this.connected_.awaitDeferred(this.setupAckReceived_);
  136. } else {
  137. // One sided handshake, outer frame:
  138. // SETUP_ACK must be sent.
  139. this.connected_.awaitDeferred(this.setupAckSent_);
  140. }
  141. } else {
  142. // Two sided handshake:
  143. // SETUP_ACK has to have been received, and sent.
  144. this.connected_.awaitDeferred(this.setupAckReceived_);
  145. if (this.protocolVersion_ == 2) {
  146. this.connected_.awaitDeferred(this.setupAckSent_);
  147. }
  148. }
  149. this.connected_.addCallback(this.notifyConnected_, this);
  150. this.connected_.callback(true);
  151. this.eventHandler_.listen(
  152. this.maybeAttemptToConnectTimer_, goog.Timer.TICK,
  153. this.maybeAttemptToConnect_);
  154. goog.log.info(
  155. goog.net.xpc.logger, 'NativeMessagingTransport created. ' +
  156. 'protocolVersion=' + this.protocolVersion_ + ', oneSidedHandshake=' +
  157. this.oneSidedHandshake_ + ', role=' + this.channel_.getRole());
  158. };
  159. goog.inherits(goog.net.xpc.NativeMessagingTransport, goog.net.xpc.Transport);
  160. /**
  161. * Length of the delay in milliseconds between the channel being connected and
  162. * the connection callback being called, in cases where coverage of timing flaws
  163. * is required.
  164. * @type {number}
  165. * @private
  166. */
  167. goog.net.xpc.NativeMessagingTransport.CONNECTION_DELAY_MS_ = 200;
  168. /**
  169. * Current determination of peer's protocol version, or null for unknown.
  170. * @type {?number}
  171. * @private
  172. */
  173. goog.net.xpc.NativeMessagingTransport.prototype.peerProtocolVersion_ = null;
  174. /**
  175. * Flag indicating if this instance of the transport has been initialized.
  176. * @type {boolean}
  177. * @private
  178. */
  179. goog.net.xpc.NativeMessagingTransport.prototype.initialized_ = false;
  180. /**
  181. * The transport type.
  182. * @type {number}
  183. * @override
  184. */
  185. goog.net.xpc.NativeMessagingTransport.prototype.transportType =
  186. goog.net.xpc.TransportTypes.NATIVE_MESSAGING;
  187. /**
  188. * The delimiter used for transport service messages.
  189. * @type {string}
  190. * @private
  191. */
  192. goog.net.xpc.NativeMessagingTransport.MESSAGE_DELIMITER_ = ',';
  193. /**
  194. * Tracks the number of NativeMessagingTransport channels that have been
  195. * initialized but not disposed yet in a map keyed by the UID of the window
  196. * object. This allows for multiple windows to be initiallized and listening
  197. * for messages.
  198. * @type {Object<number>}
  199. * @private
  200. */
  201. goog.net.xpc.NativeMessagingTransport.activeCount_ = {};
  202. /**
  203. * Id of a timer user during postMessage sends.
  204. * @type {number}
  205. * @private
  206. */
  207. goog.net.xpc.NativeMessagingTransport.prototype.sendTimerId_ = 0;
  208. /**
  209. * Checks whether the peer transport protocol version could be as indicated.
  210. * @param {number} version The version to check for.
  211. * @return {boolean} Whether the peer transport protocol version is as
  212. * indicated, or null.
  213. * @private
  214. */
  215. goog.net.xpc.NativeMessagingTransport.prototype.couldPeerVersionBe_ = function(
  216. version) {
  217. return this.peerProtocolVersion_ == null ||
  218. this.peerProtocolVersion_ == version;
  219. };
  220. /**
  221. * Initializes this transport. Registers a listener for 'message'-events
  222. * on the document.
  223. * @param {Window} listenWindow The window to listen to events on.
  224. * @private
  225. */
  226. goog.net.xpc.NativeMessagingTransport.initialize_ = function(listenWindow) {
  227. var uid = goog.getUid(listenWindow);
  228. var value = goog.net.xpc.NativeMessagingTransport.activeCount_[uid];
  229. if (!goog.isNumber(value)) {
  230. value = 0;
  231. }
  232. if (value == 0) {
  233. // Listen for message-events. These are fired on window in FF3 and on
  234. // document in Opera.
  235. goog.events.listen(
  236. listenWindow.postMessage ? listenWindow : listenWindow.document,
  237. 'message', goog.net.xpc.NativeMessagingTransport.messageReceived_,
  238. false, goog.net.xpc.NativeMessagingTransport);
  239. }
  240. goog.net.xpc.NativeMessagingTransport.activeCount_[uid] = value + 1;
  241. };
  242. /**
  243. * Processes an incoming message-event.
  244. * @param {goog.events.BrowserEvent} msgEvt The message event.
  245. * @return {boolean} True if message was successfully delivered to a channel.
  246. * @private
  247. */
  248. goog.net.xpc.NativeMessagingTransport.messageReceived_ = function(msgEvt) {
  249. var data = msgEvt.getBrowserEvent().data;
  250. if (!goog.isString(data)) {
  251. return false;
  252. }
  253. var headDelim = data.indexOf('|');
  254. var serviceDelim = data.indexOf(':');
  255. // make sure we got something reasonable
  256. if (headDelim == -1 || serviceDelim == -1) {
  257. return false;
  258. }
  259. var channelName = data.substring(0, headDelim);
  260. var service = data.substring(headDelim + 1, serviceDelim);
  261. var payload = data.substring(serviceDelim + 1);
  262. goog.log.fine(
  263. goog.net.xpc.logger, 'messageReceived: channel=' + channelName +
  264. ', service=' + service + ', payload=' + payload);
  265. // Attempt to deliver message to the channel. Keep in mind that it may not
  266. // exist for several reasons, including but not limited to:
  267. // - a malformed message
  268. // - the channel simply has not been created
  269. // - channel was created in a different namespace
  270. // - message was sent to the wrong window
  271. // - channel has become stale (e.g. caching iframes and back clicks)
  272. var channel = goog.net.xpc.channels[channelName];
  273. if (channel) {
  274. channel.xpcDeliver(
  275. service, payload,
  276. /** @type {!MessageEvent} */ (msgEvt.getBrowserEvent()).origin);
  277. return true;
  278. }
  279. var transportMessageType =
  280. goog.net.xpc.NativeMessagingTransport.parseTransportPayload_(payload)[0];
  281. // Check if there are any stale channel names that can be updated.
  282. for (var staleChannelName in goog.net.xpc.channels) {
  283. var staleChannel = goog.net.xpc.channels[staleChannelName];
  284. if (staleChannel.getRole() == goog.net.xpc.CrossPageChannelRole.INNER &&
  285. !staleChannel.isConnected() &&
  286. service == goog.net.xpc.TRANSPORT_SERVICE_ &&
  287. (transportMessageType == goog.net.xpc.SETUP ||
  288. transportMessageType == goog.net.xpc.SETUP_NTPV2) &&
  289. staleChannel.isMessageOriginAcceptable(
  290. msgEvt.getBrowserEvent().origin)) {
  291. // Inner peer received SETUP message but channel names did not match.
  292. // Start using the channel name sent from outer peer. The channel name
  293. // of the inner peer can easily become out of date, as iframe's and their
  294. // JS state get cached in many browsers upon page reload or history
  295. // navigation (particularly Firefox 1.5+). We can trust the outer peer,
  296. // since we only accept postMessage messages from the same hostname that
  297. // originally setup the channel.
  298. staleChannel.updateChannelNameAndCatalog(channelName);
  299. staleChannel.xpcDeliver(service, payload);
  300. return true;
  301. }
  302. }
  303. // Failed to find a channel to deliver this message to, so simply ignore it.
  304. goog.log.info(goog.net.xpc.logger, 'channel name mismatch; message ignored"');
  305. return false;
  306. };
  307. /**
  308. * Handles transport service messages.
  309. * @param {string} payload The message content.
  310. * @override
  311. */
  312. goog.net.xpc.NativeMessagingTransport.prototype.transportServiceHandler =
  313. function(payload) {
  314. var transportParts =
  315. goog.net.xpc.NativeMessagingTransport.parseTransportPayload_(payload);
  316. var transportMessageType = transportParts[0];
  317. var peerEndpointId = transportParts[1];
  318. switch (transportMessageType) {
  319. case goog.net.xpc.SETUP_ACK_:
  320. this.setPeerProtocolVersion_(1);
  321. if (!this.setupAckReceived_.hasFired()) {
  322. this.setupAckReceived_.callback(true);
  323. }
  324. break;
  325. case goog.net.xpc.SETUP_ACK_NTPV2:
  326. if (this.protocolVersion_ == 2) {
  327. this.setPeerProtocolVersion_(2);
  328. if (!this.setupAckReceived_.hasFired()) {
  329. this.setupAckReceived_.callback(true);
  330. }
  331. }
  332. break;
  333. case goog.net.xpc.SETUP:
  334. this.setPeerProtocolVersion_(1);
  335. this.sendSetupAckMessage_(1);
  336. break;
  337. case goog.net.xpc.SETUP_NTPV2:
  338. if (this.protocolVersion_ == 2) {
  339. var prevPeerProtocolVersion = this.peerProtocolVersion_;
  340. this.setPeerProtocolVersion_(2);
  341. this.sendSetupAckMessage_(2);
  342. if ((prevPeerProtocolVersion == 1 || this.peerEndpointId_ != null) &&
  343. this.peerEndpointId_ != peerEndpointId) {
  344. // Send a new SETUP message since the peer has been replaced.
  345. goog.log.info(
  346. goog.net.xpc.logger,
  347. 'Sending SETUP and changing peer ID to: ' + peerEndpointId);
  348. this.sendSetupMessage_();
  349. }
  350. this.peerEndpointId_ = peerEndpointId;
  351. }
  352. break;
  353. }
  354. };
  355. /**
  356. * Sends a SETUP transport service message of the correct protocol number for
  357. * our current situation.
  358. * @private
  359. */
  360. goog.net.xpc.NativeMessagingTransport.prototype.sendSetupMessage_ = function() {
  361. // 'real' (legacy) v1 transports don't know about there being v2 ones out
  362. // there, and we shouldn't either.
  363. goog.asserts.assert(
  364. !(this.protocolVersion_ == 1 && this.peerProtocolVersion_ == 2));
  365. if (this.protocolVersion_ == 2 && this.couldPeerVersionBe_(2)) {
  366. var payload = goog.net.xpc.SETUP_NTPV2;
  367. payload += goog.net.xpc.NativeMessagingTransport.MESSAGE_DELIMITER_;
  368. payload += this.endpointId_;
  369. this.send(goog.net.xpc.TRANSPORT_SERVICE_, payload);
  370. }
  371. // For backward compatibility reasons, the V1 SETUP message can be sent by
  372. // both V1 and V2 transports. Once a V2 transport has 'heard' another V2
  373. // transport it starts ignoring V1 messages, so the V2 message must be sent
  374. // first.
  375. if (this.couldPeerVersionBe_(1)) {
  376. this.send(goog.net.xpc.TRANSPORT_SERVICE_, goog.net.xpc.SETUP);
  377. }
  378. };
  379. /**
  380. * Sends a SETUP_ACK transport service message of the correct protocol number
  381. * for our current situation.
  382. * @param {number} protocolVersion The protocol version of the SETUP message
  383. * which gave rise to this ack message.
  384. * @private
  385. */
  386. goog.net.xpc.NativeMessagingTransport.prototype.sendSetupAckMessage_ = function(
  387. protocolVersion) {
  388. goog.asserts.assert(
  389. this.protocolVersion_ != 1 || protocolVersion != 2,
  390. 'Shouldn\'t try to send a v2 setup ack in v1 mode.');
  391. if (this.protocolVersion_ == 2 && this.couldPeerVersionBe_(2) &&
  392. protocolVersion == 2) {
  393. this.send(goog.net.xpc.TRANSPORT_SERVICE_, goog.net.xpc.SETUP_ACK_NTPV2);
  394. } else if (this.couldPeerVersionBe_(1) && protocolVersion == 1) {
  395. this.send(goog.net.xpc.TRANSPORT_SERVICE_, goog.net.xpc.SETUP_ACK_);
  396. } else {
  397. return;
  398. }
  399. if (!this.setupAckSent_.hasFired()) {
  400. this.setupAckSent_.callback(true);
  401. }
  402. };
  403. /**
  404. * Attempts to set the peer protocol number. Downgrades from 2 to 1 are not
  405. * permitted.
  406. * @param {number} version The new protocol number.
  407. * @private
  408. */
  409. goog.net.xpc.NativeMessagingTransport.prototype.setPeerProtocolVersion_ =
  410. function(version) {
  411. if (version > this.peerProtocolVersion_) {
  412. this.peerProtocolVersion_ = version;
  413. }
  414. if (this.peerProtocolVersion_ == 1) {
  415. if (!this.setupAckSent_.hasFired() && !this.oneSidedHandshake_) {
  416. this.setupAckSent_.callback(true);
  417. }
  418. this.peerEndpointId_ = null;
  419. }
  420. };
  421. /**
  422. * Connects this transport.
  423. * @override
  424. */
  425. goog.net.xpc.NativeMessagingTransport.prototype.connect = function() {
  426. goog.net.xpc.NativeMessagingTransport.initialize_(this.getWindow());
  427. this.initialized_ = true;
  428. this.maybeAttemptToConnect_();
  429. };
  430. /**
  431. * Connects to other peer. In the case of the outer peer, the setup messages are
  432. * likely sent before the inner peer is ready to receive them. Therefore, this
  433. * function will continue trying to send the SETUP message until the inner peer
  434. * responds. In the case of the inner peer, it will occasionally have its
  435. * channel name fall out of sync with the outer peer, particularly during
  436. * soft-reloads and history navigations.
  437. * @private
  438. */
  439. goog.net.xpc.NativeMessagingTransport.prototype.maybeAttemptToConnect_ =
  440. function() {
  441. // In a one-sided handshake, the outer frame does not send a SETUP message,
  442. // but the inner frame does.
  443. var outerFrame =
  444. this.channel_.getRole() == goog.net.xpc.CrossPageChannelRole.OUTER;
  445. if ((this.oneSidedHandshake_ && outerFrame) || this.channel_.isConnected() ||
  446. this.isDisposed()) {
  447. this.maybeAttemptToConnectTimer_.stop();
  448. return;
  449. }
  450. this.maybeAttemptToConnectTimer_.start();
  451. this.sendSetupMessage_();
  452. };
  453. /**
  454. * Sends a message.
  455. * @param {string} service The name off the service the message is to be
  456. * delivered to.
  457. * @param {string} payload The message content.
  458. * @override
  459. */
  460. goog.net.xpc.NativeMessagingTransport.prototype.send = function(
  461. service, payload) {
  462. var win = this.channel_.getPeerWindowObject();
  463. if (!win) {
  464. goog.log.fine(goog.net.xpc.logger, 'send(): window not ready');
  465. return;
  466. }
  467. this.send = function(service, payload) {
  468. // In IE8 (and perhaps elsewhere), it seems like postMessage is sometimes
  469. // implemented as a synchronous call. That is, calling it synchronously
  470. // calls whatever listeners it has, and control is not returned to the
  471. // calling thread until those listeners are run. This produces different
  472. // ordering to all other browsers, and breaks this protocol. This timer
  473. // callback is introduced to produce standard behavior across all browsers.
  474. var transport = this;
  475. var channelName = this.channel_.name;
  476. var sendFunctor = function() {
  477. transport.sendTimerId_ = 0;
  478. try {
  479. // postMessage is a method of the window object, except in some
  480. // versions of Opera, where it is a method of the document object. It
  481. // also seems that the appearance of postMessage on the peer window
  482. // object can sometimes be delayed.
  483. var obj = win.postMessage ? win : win.document;
  484. if (!obj.postMessage) {
  485. goog.log.warning(
  486. goog.net.xpc.logger, 'Peer window had no postMessage function.');
  487. return;
  488. }
  489. obj.postMessage(
  490. channelName + '|' + service + ':' + payload,
  491. transport.peerHostname_);
  492. goog.log.fine(
  493. goog.net.xpc.logger, 'send(): service=' + service + ' payload=' +
  494. payload + ' to hostname=' + transport.peerHostname_);
  495. } catch (error) {
  496. // There is some evidence (not totally convincing) that postMessage can
  497. // be missing or throw errors during a narrow timing window during
  498. // startup. This protects against that.
  499. goog.log.warning(
  500. goog.net.xpc.logger, 'Error performing postMessage, ignoring.',
  501. error);
  502. }
  503. };
  504. this.sendTimerId_ = goog.Timer.callOnce(sendFunctor, 0);
  505. };
  506. this.send(service, payload);
  507. };
  508. /**
  509. * Notify the channel that this transport is connected. If either transport is
  510. * protocol v1, a short delay is required to paper over timing vulnerabilities
  511. * in that protocol version.
  512. * @private
  513. */
  514. goog.net.xpc.NativeMessagingTransport.prototype.notifyConnected_ = function() {
  515. var delay = (this.protocolVersion_ == 1 || this.peerProtocolVersion_ == 1) ?
  516. goog.net.xpc.NativeMessagingTransport.CONNECTION_DELAY_MS_ :
  517. undefined;
  518. this.channel_.notifyConnected(delay);
  519. };
  520. /** @override */
  521. goog.net.xpc.NativeMessagingTransport.prototype.disposeInternal = function() {
  522. if (this.initialized_) {
  523. var listenWindow = this.getWindow();
  524. var uid = goog.getUid(listenWindow);
  525. var value = goog.net.xpc.NativeMessagingTransport.activeCount_[uid];
  526. goog.net.xpc.NativeMessagingTransport.activeCount_[uid] = value - 1;
  527. if (value == 1) {
  528. goog.events.unlisten(
  529. listenWindow.postMessage ? listenWindow : listenWindow.document,
  530. 'message', goog.net.xpc.NativeMessagingTransport.messageReceived_,
  531. false, goog.net.xpc.NativeMessagingTransport);
  532. }
  533. }
  534. if (this.sendTimerId_) {
  535. goog.Timer.clear(this.sendTimerId_);
  536. this.sendTimerId_ = 0;
  537. }
  538. goog.dispose(this.eventHandler_);
  539. delete this.eventHandler_;
  540. goog.dispose(this.maybeAttemptToConnectTimer_);
  541. delete this.maybeAttemptToConnectTimer_;
  542. this.setupAckReceived_.cancel();
  543. delete this.setupAckReceived_;
  544. this.setupAckSent_.cancel();
  545. delete this.setupAckSent_;
  546. this.connected_.cancel();
  547. delete this.connected_;
  548. // Cleaning up this.send as it is an instance method, created in
  549. // goog.net.xpc.NativeMessagingTransport.prototype.send and has a closure over
  550. // this.channel_.peerWindowObject_.
  551. delete this.send;
  552. goog.net.xpc.NativeMessagingTransport.base(this, 'disposeInternal');
  553. };
  554. /**
  555. * Parse a transport service payload message. For v1, it is simply expected to
  556. * be 'SETUP' or 'SETUP_ACK'. For v2, an example setup message is
  557. * 'SETUP_NTPV2,abc123', where the second part is the endpoint id. The v2 setup
  558. * ack message is simply 'SETUP_ACK_NTPV2'.
  559. * @param {string} payload The payload.
  560. * @return {!Array<?string>} An array with the message type as the first member
  561. * and the endpoint id as the second, if one was sent, or null otherwise.
  562. * @private
  563. */
  564. goog.net.xpc.NativeMessagingTransport.parseTransportPayload_ = function(
  565. payload) {
  566. var transportParts = /** @type {!Array<?string>} */ (
  567. payload.split(goog.net.xpc.NativeMessagingTransport.MESSAGE_DELIMITER_));
  568. transportParts[1] = transportParts[1] || null;
  569. return transportParts;
  570. };