| OLD | NEW |
| (Empty) |
| 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. | |
| 4 | |
| 5 'use strict'; | |
| 6 | |
| 7 /** | |
| 8 * @fileoverview This extension manages communications between Chrome, | |
| 9 * Google.com pages and the Chrome Hotword extension. | |
| 10 * | |
| 11 * This helper extension is required due to the depoyment plan for Chrome M34: | |
| 12 * | |
| 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 | |
| 16 * Chrome settings. | |
| 17 * - NewTab page is served via chrome://newtab/ | |
| 18 * | |
| 19 */ | |
| 20 | |
| 21 | |
| 22 | |
| 23 /** @constructor */ | |
| 24 var OptInManager = function() {}; | |
| 25 | |
| 26 | |
| 27 /** | |
| 28 * @const {string} | |
| 29 * @private | |
| 30 */ | |
| 31 OptInManager.HOTWORD_EXTENSION_ID_ = 'bepbmhgboaologfdajaanbcjmnhjmhfn'; | |
| 32 | |
| 33 | |
| 34 /** | |
| 35 * Test extension ID. | |
| 36 * @const {string} | |
| 37 * @private | |
| 38 */ | |
| 39 OptInManager.TEST_EXTENSION_ID_ = 'cpfhkdbjfdgdebcjlifoldbijinjfifp'; | |
| 40 | |
| 41 | |
| 42 /** | |
| 43 * Commands sent from the page to this content script. | |
| 44 * @enum {string} | |
| 45 */ | |
| 46 OptInManager.CommandFromPage = { | |
| 47 // User has explicitly clicked 'no'. | |
| 48 CLICKED_NO_OPTIN: 'hcno', | |
| 49 // User has opted in. | |
| 50 CLICKED_OPTIN: 'hco', | |
| 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. | |
| 56 PAGE_WAKEUP: 'wu' | |
| 57 }; | |
| 58 | |
| 59 | |
| 60 /** | |
| 61 * @param {Tab} tab Tab to inject. | |
| 62 * @param {function(HotwordStatus)} sendResponse Callback function to respond | |
| 63 * to sender. | |
| 64 * @param {HotwordStatus} hotwordStatus Status of the hotword extension. | |
| 65 * @private | |
| 66 */ | |
| 67 OptInManager.prototype.injectTab_ = function( | |
| 68 tab, sendResponse, hotwordStatus) { | |
| 69 var response = {'doNotShowOptinMessage': true}; | |
| 70 | |
| 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'}); | |
| 76 } | |
| 77 | |
| 78 try { | |
| 79 sendResponse(response); | |
| 80 } catch (err) { | |
| 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') | |
| 87 return; | |
| 88 throw err; | |
| 89 } | |
| 90 }; | |
| 91 | |
| 92 | |
| 93 /** | |
| 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 | |
| 98 * to sender. | |
| 99 * @return {boolean} Whether to maintain the port open to call sendResponse. | |
| 100 * @private | |
| 101 */ | |
| 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( | |
| 112 this, | |
| 113 request.tab || sender.tab || {incognito: true}, | |
| 114 sendResponse)); | |
| 115 return true; | |
| 116 } | |
| 117 break; | |
| 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)); | |
| 124 return true; | |
| 125 } | |
| 126 break; | |
| 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); | |
| 131 } | |
| 132 break; | |
| 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); | |
| 138 } | |
| 139 break; | |
| 140 case OptInManager.CommandFromPage.AUDIO_LOGGING_OFF: | |
| 141 if (chrome.hotwordPrivate && | |
| 142 chrome.hotwordPrivate.setAudioLoggingEnabled) { | |
| 143 chrome.hotwordPrivate.setAudioLoggingEnabled(false); | |
| 144 } | |
| 145 break; | |
| 146 default: | |
| 147 break; | |
| 148 } | |
| 149 return false; | |
| 150 }; | |
| 151 | |
| 152 /** | |
| 153 * Helper function to test URLs as being valid for running the | |
| 154 * hotwording extension. It's used by isEligibleUrl to make that | |
| 155 * function clearer. | |
| 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. | |
| 159 */ | |
| 160 OptInManager.prototype.checkEligibleUrl = function(url, base) { | |
| 161 if (!url) | |
| 162 return false; | |
| 163 | |
| 164 if (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) { | |
| 171 return true; | |
| 172 } | |
| 173 return false; | |
| 174 | |
| 175 }; | |
| 176 | |
| 177 /** | |
| 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. | |
| 182 */ | |
| 183 OptInManager.prototype.isEligibleUrl = function(url) { | |
| 184 if (!url) | |
| 185 return false; | |
| 186 | |
| 187 // More URLs will be added in the future so leaving this as an array. | |
| 188 var baseUrls = [ | |
| 189 'chrome://newtab' | |
| 190 ]; | |
| 191 var baseGoogleUrls = [ | |
| 192 'https://www.google.', | |
| 193 'https://encrypted.google.' | |
| 194 ]; | |
| 195 var tlds = [ | |
| 196 'com', | |
| 197 'co.uk', | |
| 198 'de', | |
| 199 'fr', | |
| 200 'ru' | |
| 201 ]; | |
| 202 | |
| 203 // Check URLs which do not have locale-based TLDs first. | |
| 204 if (this.checkEligibleUrl(url, baseUrls[0])) | |
| 205 return true; | |
| 206 | |
| 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)) | |
| 212 return true; | |
| 213 } | |
| 214 } | |
| 215 return false; | |
| 216 }; | |
| 217 | |
| 218 | |
| 219 /** | |
| 220 * Initializes the extension. | |
| 221 */ | |
| 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)); | |
| 229 }; | |
| 230 | |
| 231 | |
| 232 new OptInManager().initialize(); | |
| OLD | NEW |