1 // Copyright (c) 2014 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
8 * @fileoverview This extension manages communications between Chrome,
9 * Google.com pages and the Chrome Hotword extension.
11 * This helper extension is required due to the depoyment plan for Chrome M34:
13 * - The hotword extension will be distributed as an externally loaded
14 * component extension.
15 * - Settings for enabling and disabling the hotword extension has moved to
17 * - NewTab page is served via chrome://newtab/
24 var OptInManager = function() {};
31 OptInManager.HOTWORD_EXTENSION_ID_ = 'bepbmhgboaologfdajaanbcjmnhjmhfn';
39 OptInManager.TEST_EXTENSION_ID_ = 'cpfhkdbjfdgdebcjlifoldbijinjfifp';
43 * Commands sent from the page to this content script.
46 OptInManager.CommandFromPage = {
47 // User has explicitly clicked 'no'.
48 CLICKED_NO_OPTIN: 'hcno',
51 // Audio logging is opted in.
52 AUDIO_LOGGING_ON: 'alon',
53 // Audio logging is opted out.
54 AUDIO_LOGGING_OFF: 'aloff',
55 // User visited an eligible page.
61 * @param {Tab} tab Tab to inject.
62 * @param {function(HotwordStatus)} sendResponse Callback function to respond
64 * @param {HotwordStatus} hotwordStatus Status of the hotword extension.
67 OptInManager.prototype.injectTab_ = function(
68 tab, sendResponse, hotwordStatus) {
69 var response = {'doNotShowOptinMessage': true};
71 if (!tab.incognito && hotwordStatus.available) {
72 if (!hotwordStatus.enabledSet)
73 response = hotwordStatus;
74 else if (hotwordStatus.enabled)
75 chrome.tabs.executeScript(tab.id, {'file': 'audio_client.js'});
79 sendResponse(response);
81 // Suppress the exception thrown by sendResponse() when the page doesn't
82 // specify a response callback in the call to chrome.runtime.sendMessage().
83 // Unfortunately, there doesn't appear to be a way to detect one-way
84 // messages without explicitly saying in the message itself. This message
85 // is defined as a constant in extensions/renderer/messaging_bindings.cc
86 if (err.message == 'Attempting to use a disconnected port object')
94 * Handles messages from the helper content script.
95 * @param {*} request Message from the sender.
96 * @param {MessageSender} sender Information about the sender.
97 * @param {function(HotwordStatus)} sendResponse Callback function to respond
99 * @return {boolean} Whether to maintain the port open to call sendResponse.
102 OptInManager.prototype.handleMessage_ = function(
103 request, sender, sendResponse) {
104 switch (request.type) {
105 case OptInManager.CommandFromPage.PAGE_WAKEUP:
106 if (((sender.tab && this.isEligibleUrl(sender.tab.url)) ||
107 sender.id == OptInManager.HOTWORD_EXTENSION_ID_ ||
108 sender.id == OptInManager.TEST_EXTENSION_ID_) &&
109 chrome.hotwordPrivate && chrome.hotwordPrivate.getStatus) {
110 chrome.hotwordPrivate.getStatus(
111 this.injectTab_.bind(
113 request.tab || sender.tab || {incognito: true},
118 case OptInManager.CommandFromPage.CLICKED_OPTIN:
119 if (chrome.hotwordPrivate && chrome.hotwordPrivate.setEnabled &&
120 chrome.hotwordPrivate.getStatus) {
121 chrome.hotwordPrivate.setEnabled(true);
122 chrome.hotwordPrivate.getStatus(
123 this.injectTab_.bind(this, sender.tab, sendResponse));
127 // User has explicitly clicked 'no thanks'.
128 case OptInManager.CommandFromPage.CLICKED_NO_OPTIN:
129 if (chrome.hotwordPrivate && chrome.hotwordPrivate.setEnabled) {
130 chrome.hotwordPrivate.setEnabled(false);
133 // Information regarding the audio logging preference was sent.
134 case OptInManager.CommandFromPage.AUDIO_LOGGING_ON:
135 if (chrome.hotwordPrivate &&
136 chrome.hotwordPrivate.setAudioLoggingEnabled) {
137 chrome.hotwordPrivate.setAudioLoggingEnabled(true);
140 case OptInManager.CommandFromPage.AUDIO_LOGGING_OFF:
141 if (chrome.hotwordPrivate &&
142 chrome.hotwordPrivate.setAudioLoggingEnabled) {
143 chrome.hotwordPrivate.setAudioLoggingEnabled(false);
153 * Helper function to test URLs as being valid for running the
154 * hotwording extension. It's used by isEligibleUrl to make that
156 * @param {string} url URL to check.
157 * @param {string} base Base URL to compare against..
158 * @return {boolean} True if url is an eligible hotword URL.
160 OptInManager.prototype.checkEligibleUrl = function(url, base) {
165 url === base + '/' ||
166 url.indexOf(base + '/_/chrome/newtab?') === 0 || // Appcache NTP.
167 url.indexOf(base + '/?') === 0 ||
168 url.indexOf(base + '/#') === 0 ||
169 url.indexOf(base + '/webhp') === 0 ||
170 url.indexOf(base + '/search') === 0) {
178 * Determines if a URL is eligible for hotwording. For now, the
179 * valid pages are the Google HP and SERP (this will include the NTP).
180 * @param {string} url URL to check.
181 * @return {boolean} True if url is an eligible hotword URL.
183 OptInManager.prototype.isEligibleUrl = function(url) {
187 // More URLs will be added in the future so leaving this as an array.
191 var baseGoogleUrls = [
192 'https://www.google.',
193 'https://encrypted.google.'
203 // Check URLs which do not have locale-based TLDs first.
204 if (this.checkEligibleUrl(url, baseUrls[0]))
207 // Check URLs with each type of local-based TLD.
208 for (var i = 0; i < baseGoogleUrls.length; i++) {
209 for (var j = 0; j < tlds.length; j++) {
210 var base = baseGoogleUrls[i] + tlds[j];
211 if (this.checkEligibleUrl(url, base))
220 * Initializes the extension.
222 OptInManager.prototype.initialize = function() {
223 // TODO(rlp): Possibly remove the next line. It's proably not used, but
224 // leaving for now to be safe. We should remove it once all messsage
225 // relaying is removed form the content scripts.
226 chrome.runtime.onMessage.addListener(this.handleMessage_.bind(this));
227 chrome.runtime.onMessageExternal.addListener(
228 this.handleMessage_.bind(this));
232 new OptInManager().initialize();