| // Copyright (c) 2014 The Chromium Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| 'use strict'; |
| |
| /** |
| * @fileoverview This extension manages communications between Chrome, |
| * Google.com pages and the Chrome Hotword extension. |
| * |
| * This helper extension is required due to the depoyment plan for Chrome M34: |
| * |
| * - The hotword extension will be distributed as an externally loaded |
| * component extension. |
| * - Settings for enabling and disabling the hotword extension has moved to |
| * Chrome settings. |
| * - Newtab page is served via chrome://newtab/ |
| * |
| */ |
| |
| |
| |
| /** @constructor */ |
| var OptInManager = function() {}; |
| |
| |
| /** |
| * @const {string} |
| * @private |
| */ |
| OptInManager.HOTWORD_EXTENSION_ID_ = 'bepbmhgboaologfdajaanbcjmnhjmhfn'; |
| |
| |
| /** |
| * Test extension ID. |
| * @const {string} |
| * @private |
| */ |
| OptInManager.TEST_EXTENSION_ID_ = 'cpfhkdbjfdgdebcjlifoldbijinjfifp'; |
| |
| |
| /** |
| * Commands sent from the page to this content script. |
| * @enum {string} |
| */ |
| OptInManager.CommandFromPage = { |
| // User has explicitly clicked 'no'. |
| CLICKED_NO_OPTIN: 'hcno', |
| // User has opted in. |
| CLICKED_OPTIN: 'hco', |
| // Audio logging is opted in. |
| AUDIO_LOGGING_ON: 'alon', |
| // Audio logging is opted out. |
| AUDIO_LOGGING_OFF: 'aloff', |
| // User visited an eligible page. |
| PAGE_WAKEUP: 'wu' |
| }; |
| |
| |
| /** |
| * @param {Tab} tab Tab to inject. |
| * @param {function(HotwordStatus)} sendResponse Callback function to respond |
| * to sender. |
| * @param {HotwordStatus} hotwordStatus Status of the hotword extension. |
| * @private |
| */ |
| OptInManager.prototype.injectTab_ = function( |
| tab, sendResponse, hotwordStatus) { |
| var response = {'doNotShowOptinMessage': true}; |
| |
| if (!tab.incognito && hotwordStatus.available) { |
| if (!hotwordStatus.enabledSet) |
| response = hotwordStatus; |
| else if (hotwordStatus.enabled) |
| chrome.tabs.executeScript(tab.id, {'file': 'audio_client.js'}); |
| } |
| |
| try { |
| sendResponse(response); |
| } catch (err) { |
| // Suppress the exception thrown by sendResponse() when the page doesn't |
| // specify a response callback in the call to chrome.runtime.sendMessage(). |
| // Unfortunately, there doesn't appear to be a way to detect one-way |
| // messages without explicitly saying in the message itself. This message |
| // is defined as a constant in extensions/renderer/messaging_bindings.cc |
| if (err.message == 'Attempting to use a disconnected port object') |
| return; |
| throw err; |
| } |
| }; |
| |
| |
| /** |
| * Handles messages from the helper content script. |
| * @param {*} request Message from the sender. |
| * @param {MessageSender} sender Information about the sender. |
| * @param {function(HotwordStatus)} sendResponse Callback function to respond |
| * to sender. |
| * @return {boolean} Whether to maintain the port open to call sendResponse. |
| * @private |
| */ |
| OptInManager.prototype.handleMessage_ = function( |
| request, sender, sendResponse) { |
| switch (request.type) { |
| case OptInManager.CommandFromPage.PAGE_WAKEUP: |
| if (((sender.tab && this.isEligibleUrl(sender.tab.url)) || |
| sender.id == OptInManager.HOTWORD_EXTENSION_ID_ || |
| sender.id == OptInManager.TEST_EXTENSION_ID_) && |
| chrome.hotwordPrivate && chrome.hotwordPrivate.getStatus) { |
| chrome.hotwordPrivate.getStatus( |
| this.injectTab_.bind( |
| this, |
| request.tab || sender.tab || {incognito: true}, |
| sendResponse)); |
| return true; |
| } |
| break; |
| case OptInManager.CommandFromPage.CLICKED_OPTIN: |
| if (chrome.hotwordPrivate && chrome.hotwordPrivate.setEnabled && |
| chrome.hotwordPrivate.getStatus) { |
| chrome.hotwordPrivate.setEnabled(true); |
| chrome.hotwordPrivate.getStatus( |
| this.injectTab_.bind(this, sender.tab, sendResponse)); |
| return true; |
| } |
| break; |
| // User has explicitly clicked 'no thanks'. |
| case OptInManager.CommandFromPage.CLICKED_NO_OPTIN: |
| if (chrome.hotwordPrivate && chrome.hotwordPrivate.setEnabled) { |
| chrome.hotwordPrivate.setEnabled(false); |
| } |
| break; |
| // Information regarding the audio logging preference was sent. |
| case OptInManager.CommandFromPage.AUDIO_LOGGING_ON: |
| if (chrome.hotwordPrivate && |
| chrome.hotwordPrivate.setAudioLoggingEnabled) { |
| chrome.hotwordPrivate.setAudioLoggingEnabled(true); |
| } |
| break; |
| case OptInManager.CommandFromPage.AUDIO_LOGGING_OFF: |
| if (chrome.hotwordPrivate && |
| chrome.hotwordPrivate.setAudioLoggingEnabled) { |
| chrome.hotwordPrivate.setAudioLoggingEnabled(false); |
| } |
| break; |
| default: |
| break; |
| } |
| return false; |
| }; |
| |
| /** |
| * Helper function to test URLs as being valid for running the |
| * hotwording extension. It's used by isEligibleUrl to make that |
| * function clearer. |
| * @param {string} url URL to check. |
| * @param {string} base Base URL to compare against.. |
| * @return {boolean} True if url is an eligible hotword URL. |
| */ |
| OptInManager.prototype.checkEligibleUrl = function(url, base) { |
| if (!url) |
| return false; |
| |
| if (url === base || |
| url === base + '/' || |
| url.indexOf(base + '/_/chrome/newtab?') === 0 || // Appcache NTP. |
| url.indexOf(base + '/?') === 0 || |
| url.indexOf(base + '/#') === 0 || |
| url.indexOf(base + '/webhp') === 0 || |
| url.indexOf(base + '/search') === 0) { |
| return true; |
| } |
| return false; |
| |
| }; |
| |
| /** |
| * Determines if a URL is eligible for hotwording. For now, the |
| * valid pages are the Google HP and SERP (this will include the NTP). |
| * @param {string} url URL to check. |
| * @return {boolean} True if url is an eligible hotword URL. |
| */ |
| OptInManager.prototype.isEligibleUrl = function(url) { |
| if (!url) |
| return false; |
| |
| // More URLs will be added in the future so leaving this as an array. |
| var baseUrls = [ |
| 'chrome://newtab' |
| ]; |
| var baseGoogleUrls = [ |
| 'https://www.google.', |
| 'https://encrypted.google.' |
| ]; |
| var tlds = [ |
| 'com', |
| 'co.uk', |
| 'de', |
| 'fr', |
| 'ru' |
| ]; |
| |
| // Check URLs which do not have locale-based TLDs first. |
| if (this.checkEligibleUrl(url, baseUrls[0])) |
| return true; |
| |
| // Check URLs with each type of local-based TLD. |
| for (var i = 0; i < baseGoogleUrls.length; i++) { |
| for (var j = 0; j < tlds.length; j++) { |
| var base = baseGoogleUrls[i] + tlds[j]; |
| if (this.checkEligibleUrl(url, base)) |
| return true; |
| } |
| } |
| return false; |
| }; |
| |
| |
| /** |
| * Initializes the extension. |
| */ |
| OptInManager.prototype.initialize = function() { |
| // TODO(rlp): Possibly remove the next line. It's proably not used, but |
| // leaving for now to be safe. We should remove it once all messsage |
| // relaying is removed form the content scripts. |
| chrome.runtime.onMessage.addListener(this.handleMessage_.bind(this)); |
| chrome.runtime.onMessageExternal.addListener( |
| this.handleMessage_.bind(this)); |
| }; |
| |
| |
| new OptInManager().initialize(); |