extension_bridge.js revision cedac228d2dd51db4b79ea1e72c7f249408ee061
15821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// Copyright 2014 The Chromium Authors. All rights reserved.
25821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// Use of this source code is governed by a BSD-style license that can be
35821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// found in the LICENSE file.
45821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
55821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)/**
65821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * @fileoverview Bridge to aid in communication between a Chrome
75821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * background page and content script.
85821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) *
95821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * It automatically figures out where it's being run and initializes itself
105821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * appropriately. Then just call send() to send a message from the background
115821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * to the page or vice versa, and addMessageListener() to provide a message
125821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * listener.  Messages can be any object that can be serialized using JSON.
135821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) *
145821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) */
155821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
165821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)goog.provide('cvox.ExtensionBridge');
175821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
185821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)goog.require('cvox.ChromeVoxJSON');
195821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
205821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)/**
215821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * @constructor
225821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) */
235821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)cvox.ExtensionBridge = function() {};
245821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
255821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)/**
265821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * Initialize the extension bridge. Dynamically figure out whether we're in
275821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * the background page, content script, or in a page, and call the
285821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * corresponding function for more specific initialization.
295821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) */
305821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)cvox.ExtensionBridge.init = function() {
315821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  var self = cvox.ExtensionBridge;
325821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  self.messageListeners = [];
335821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  self.disconnectListeners = [];
345821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
355821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (/^chrome-extension:\/\/.*background\.html$/.test(window.location.href)) {
365821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    // This depends on the fact that the background page has a specific url. We
375821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    // should never be loaded into another extension's background page, so this
385821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    // is a safe check.
395821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    self.context = self.BACKGROUND;
405821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    self.initBackground();
415821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return;
425821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
435821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
445821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (chrome && chrome.extension) {
455821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    self.context = self.CONTENT_SCRIPT;
465821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    self.initContentScript();
475821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
485821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)};
495821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
505821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)/**
515821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * Constant indicating we're in a background page.
525821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * @type {number}
535821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * @const
545821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) */
555821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)cvox.ExtensionBridge.BACKGROUND = 0;
565821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
575821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)/**
585821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * Constant indicating we're in a content script.
595821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * @type {number}
605821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * @const
615821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) */
625821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)cvox.ExtensionBridge.CONTENT_SCRIPT = 1;
635821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
645821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)/**
655821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * The name of the port between the content script and background page.
665821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * @type {string}
675821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * @const
685821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) */
695821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)cvox.ExtensionBridge.PORT_NAME = 'cvox.ExtensionBridge.Port';
705821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
715821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)/**
725821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * The name of the message between the content script and background to
735821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * see if they're connected.
745821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * @type {string}
755821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * @const
765821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) */
775821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)cvox.ExtensionBridge.PING_MSG = 'cvox.ExtensionBridge.Ping';
785821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
795821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)/**
805821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * The name of the message between the background and content script to
815821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * confirm that they're connected.
825821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * @type {string}
835821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * @const
845821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) */
855821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)cvox.ExtensionBridge.PONG_MSG = 'cvox.ExtensionBridge.Pong';
865821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
875821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)/**
885821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * Send a message. If the context is a page, sends a message to the
895821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * extension background page. If the context is a background page, sends
905821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * a message to the current active tab (not all tabs).
915821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) *
925821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * @param {Object} message The message to be sent.
935821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) */
945821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)cvox.ExtensionBridge.send = function(message) {
955821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  var self = cvox.ExtensionBridge;
965821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  switch (self.context) {
975821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  case self.BACKGROUND:
985821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    self.sendBackgroundToContentScript(message);
995821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    break;
1005821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  case self.CONTENT_SCRIPT:
1015821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    self.sendContentScriptToBackground(message);
1025821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    break;
1035821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
1045821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)};
1055821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1065821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)/**
1075821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * Provide a function to listen to messages. In page context, this
1085821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * listens to messages from the background. In background context,
1095821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * this listens to messages from all pages.
1105821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) *
1115821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * The function gets called with two parameters: the message, and a
1125821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * port that can be used to send replies.
1135821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) *
1145821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * @param {function(Object, Port)} listener The message listener.
1155821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) */
1165821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)cvox.ExtensionBridge.addMessageListener = function(listener) {
1175821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  cvox.ExtensionBridge.messageListeners.push(listener);
1185821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)};
1195821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1205821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)/**
1215821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * Provide a function to be called when the connection is
1225821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * disconnected.
1235821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) *
1245821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * @param {function()} listener The listener.
1255821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) */
1265821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)cvox.ExtensionBridge.addDisconnectListener = function(listener) {
1275821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  cvox.ExtensionBridge.disconnectListeners.push(listener);
1285821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)};
1295821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1305821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)/**
1315821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * Removes all message listeners from the extension bridge.
1325821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) */
1335821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)cvox.ExtensionBridge.removeMessageListeners = function() {
1345821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  cvox.ExtensionBridge.messageListeners.length = 0;
1355821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)};
1365821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1375821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)/**
1385821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * Returns a unique id for this instance of the script.
1395821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) *
1405821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * @return {number}
1415821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) */
1425821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)cvox.ExtensionBridge.uniqueId = function() {
1435821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  return cvox.ExtensionBridge.id_;
1445821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)};
1455821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1465821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)/**
1475821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * Initialize the extension bridge in a background page context by registering
1485821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * a listener for connections from the content script.
1495821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) */
1505821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)cvox.ExtensionBridge.initBackground = function() {
1515821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  var self = cvox.ExtensionBridge;
1525821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1535821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  /** @type {!Array.<Port>} @private */
1545821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  self.portCache_ = [];
1555821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  /** @type {number} */
1565821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  self.nextPongId_ = 1;
1575821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  /** @type {number} */
1585f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)  self.id_ = 0;
1595f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)
1605821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  var onConnectHandler = function(port) {
1615821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    if (port.name != self.PORT_NAME) {
1625821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      return;
1635821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    }
1645821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1655821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    self.portCache_.push(port);
1665821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1675821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    port.onMessage.addListener(
1685821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        function(message) {
1695821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          if (message[cvox.ExtensionBridge.PING_MSG]) {
1705821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)            var pongMessage = {};
1715821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)            pongMessage[cvox.ExtensionBridge.PONG_MSG] = self.nextPongId_++;
1725821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)            port.postMessage(pongMessage);
1735821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)	    return;
1745821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)	  }
1755821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          for (var i = 0; i < self.messageListeners.length; i++) {
1765821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)            self.messageListeners[i](message, port);
1775821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          }
1785821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        });
1795821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1805821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    port.onDisconnect.addListener(function(message) {
1815821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      for (var i = 0; i < self.portCache_.length; i++) {
1825821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        if (self.portCache_[i] == port) {
1835821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          self.portCache_.splice(i, 1);
1845821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          break;
1855821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        }
1865821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      }
1875821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    });
1885821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  };
1895821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1905821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  chrome.extension.onConnect.addListener(onConnectHandler);
1915821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)};
1925821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1935821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)/**
1945821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * Initialize the extension bridge in a content script context, listening
1955821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * for messages from the background page.
1965821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) */
1975821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)cvox.ExtensionBridge.initContentScript = function() {
1985821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  var self = cvox.ExtensionBridge;
1995821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  self.connected = false;
2005821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  self.pingAttempts = 0;
2015821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  self.queuedMessages = [];
2025821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  /** @type {number} */
2035821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  self.id_ = -1;
2045821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2055821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  var onMessageHandler = function(request, sender, sendResponse) {
2065821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    if (request && request['srcFile']) {
2075821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      // TODO (clchen, deboer): Investigate this further and come up with a
2085821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      // cleaner solution. The root issue is that this should never be run on
2095821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      // the background page, but it is in the Chrome OS case.
2105821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      return;
2115821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    }
2125f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)    if (request[cvox.ExtensionBridge.PONG_MSG]) {
2135f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)      self.gotPongFromBackgroundPage(request[cvox.ExtensionBridge.PONG_MSG]);
2145821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    } else {
2155821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      for (var i = 0; i < self.messageListeners.length; i++) {
2165f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)        self.messageListeners[i](request, cvox.ExtensionBridge.backgroundPort);
2175f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)      }
2185f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)    }
2195f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)    sendResponse({});
2205f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)  };
2215f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)
2225f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)  // Listen to requests from the background that don't come from
2235f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)  // our connection port.
2245f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)  chrome.extension.onMessage.addListener(onMessageHandler);
2255f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)
2265f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)  self.setupBackgroundPort();
2275f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)
2285f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)  self.tryToPingBackgroundPage();
2295f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)};
2305f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)
2315f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)/**
2325f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) * Set up the connection to the background page.
2335f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) */
2345f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)cvox.ExtensionBridge.setupBackgroundPort = function() {
2355f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)  // Set up the connection to the background page.
2365f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)  var self = cvox.ExtensionBridge;
2375f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)  self.backgroundPort = chrome.extension.connect({name: self.PORT_NAME});
2385f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)  self.backgroundPort.onMessage.addListener(
2395f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)      function(message) {
2405f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)        if (message[cvox.ExtensionBridge.PONG_MSG]) {
2415f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)          self.gotPongFromBackgroundPage(
2425f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)              message[cvox.ExtensionBridge.PONG_MSG]);
2435f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)        } else {
2445f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)          for (var i = 0; i < self.messageListeners.length; i++) {
2455f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)            self.messageListeners[i](message, self.backgroundPort);
2465f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)          }
2475f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)        }
2485f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)      });
2495f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)  self.backgroundPort.onDisconnect.addListener(
2505f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)      function(event) {
2515f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)        // If we're not connected yet, don't give up - try again.
2525f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)        if (!self.connected) {
2535f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)          self.backgroundPort = null;
2545f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)	  return;
2555f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)	}
2565f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)
2575f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)        for (var i = 0; i < self.disconnectListeners.length; i++) {
2585f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)          self.disconnectListeners[i]();
2595f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)        }
2605f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)      });
2615f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)};
2625f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)
2635f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)/**
2645f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) * Try to ping the background page.
2655f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) */
2665f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)cvox.ExtensionBridge.tryToPingBackgroundPage = function() {
2675f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)  var self = cvox.ExtensionBridge;
2685f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)
2695f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)  // If we already got a pong, great - we're done.
2705f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)  if (self.connected) {
2715f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)    return;
2725f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)  }
2735f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)
2745f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)  self.pingAttempts++;
2755f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)  if (self.pingAttempts > 5) {
2765f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)    // Could not connect after 5 ping attempts. Call the disconnect
2775f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)    // handlers, which will disable ChromeVox.
2785f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)    for (var i = 0; i < self.disconnectListeners.length; i++) {
2795f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)      self.disconnectListeners[i]();
2805f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)    }
2815f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)    return;
2825f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)  }
2835f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)
2845f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)  // Send the ping.
2855f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)  var msg = {};
2865f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)  msg[cvox.ExtensionBridge.PING_MSG] = 1;
2875f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)  if (!self.backgroundPort) {
2885f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)    self.setupBackgroundPort();
2895f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)  }
2905f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)  self.backgroundPort.postMessage(msg);
2915f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)
2925f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)  // Check again in 500 ms in case we get no response.
2935f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)  window.setTimeout(cvox.ExtensionBridge.tryToPingBackgroundPage, 500);
2945f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)};
2955f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)
2965f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)/**
2975f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) * Got pong from the background page, now we know the connection was
2985f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) * successful.
2995f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) * @param {number} pongId unique id assigned to us by the background page
3005f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) */
3015f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)cvox.ExtensionBridge.gotPongFromBackgroundPage = function(pongId) {
3025f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)  var self = cvox.ExtensionBridge;
3035f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)  self.connected = true;
3045f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)  self.id_ = pongId;
3055f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)
3065f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)  while (self.queuedMessages.length > 0) {
3075f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)    self.sendContentScriptToBackground(self.queuedMessages.shift());
3085f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)  }
3095f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)};
3105f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)
3115f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)/**
3125f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) * Send a message from the content script to the background page.
3135f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) *
3145f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) * @param {Object} message The message to send.
3155f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) */
3165f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)cvox.ExtensionBridge.sendContentScriptToBackground = function(message) {
3175f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)  var self = cvox.ExtensionBridge;
3185f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)  if (!self.connected) {
3195f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)    // We're not connected to the background page, so queue this message
3205f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)    // until we're connected.
3215f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)    self.queuedMessages.push(message);
3225f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)    return;
3235f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)  }
3245f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)
3255f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)  if (cvox.ExtensionBridge.backgroundPort) {
3265f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)    cvox.ExtensionBridge.backgroundPort.postMessage(message);
3275f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)  } else {
3285f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)    chrome.extension.sendMessage(message);
3295f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)  }
3305f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)};
3315f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)
3325f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)/**
3335f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) * Send a message from the background page to the content script of the
3345f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) * current selected tab.
3355f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) *
3365f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) * @param {Object} message The message to send.
3375f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) */
3385f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)cvox.ExtensionBridge.sendBackgroundToContentScript = function(message) {
3395f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)  chrome.tabs.query(
3405f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)      {'active': true, 'lastFocusedWindow': true},
3415f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)      function(tabs) {
3425f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)        if (tabs && tabs.length > 0) {
3435821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          chrome.tabs.sendMessage(tabs[0].id, message);
3445821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        } else {
3455821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          cvox.ExtensionBridge.portCache_.forEach(function(port) {
3465821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)            port.postMessage(message);
3475821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          });
3485821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        }
3495821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      });
3505f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)};
3515821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
3525821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)cvox.ExtensionBridge.init();
3535821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)