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 */ 24var OptInManager = function() {}; 25 26 27/** 28 * @const {string} 29 * @private 30 */ 31OptInManager.HOTWORD_EXTENSION_ID_ = 'bepbmhgboaologfdajaanbcjmnhjmhfn'; 32 33 34/** 35 * Commands sent from the page to this content script. 36 * @enum {string} 37 */ 38OptInManager.CommandFromPage = { 39 // User has explicitly clicked 'no'. 40 CLICKED_NO_OPTIN: 'hcno', 41 // User has opted in. 42 CLICKED_OPTIN: 'hco', 43 // Audio logging is opted in. 44 AUDIO_LOGGING_ON: 'alon', 45 // Audio logging is opted out. 46 AUDIO_LOGGING_OFF: 'aloff', 47 // User visited an eligible page. 48 PAGE_WAKEUP: 'wu' 49}; 50 51 52/** 53 * @param {Tab} tab Tab to inject. 54 * @param {function(HotwordStatus)} sendResponse Callback function to respond 55 * to sender. 56 * @param {HotwordStatus} hotwordStatus Status of the hotword extension. 57 * @private 58 */ 59OptInManager.prototype.injectTab_ = function( 60 tab, sendResponse, hotwordStatus) { 61 if (tab.incognito || !hotwordStatus.available) { 62 sendResponse({'doNotShowOptinMessage': true}); 63 return; 64 } 65 66 if (!hotwordStatus.enabledSet) { 67 sendResponse(hotwordStatus); 68 return; 69 } 70 71 if (hotwordStatus.enabled) 72 chrome.tabs.executeScript(tab.id, {'file': 'audio_client.js'}); 73 sendResponse({'doNotShowOptinMessage': true}); 74}; 75 76 77/** 78 * Handles messages from the helper content script. 79 * @param {*} request Message from the sender. 80 * @param {MessageSender} sender Information about the sender. 81 * @param {function(HotwordStatus)} sendResponse Callback function to respond 82 * to sender. 83 * @return {boolean} Whether to maintain the port open to call sendResponse. 84 * @private 85 */ 86OptInManager.prototype.handleMessage_ = function( 87 request, sender, sendResponse) { 88 switch (request.type) { 89 case OptInManager.CommandFromPage.PAGE_WAKEUP: 90 if (((sender.tab && this.isEligibleUrl(sender.tab.url)) || 91 sender.id == OptInManager.HOTWORD_EXTENSION_ID_) && 92 chrome.hotwordPrivate && chrome.hotwordPrivate.getStatus) { 93 chrome.hotwordPrivate.getStatus( 94 this.injectTab_.bind(this, request.tab || sender.tab, 95 sendResponse)); 96 return true; 97 } 98 break; 99 case OptInManager.CommandFromPage.CLICKED_OPTIN: 100 if (chrome.hotwordPrivate && chrome.hotwordPrivate.setEnabled && 101 chrome.hotwordPrivate.getStatus) { 102 chrome.hotwordPrivate.setEnabled(true); 103 chrome.hotwordPrivate.getStatus( 104 this.injectTab_.bind(this, sender.tab, sendResponse)); 105 return true; 106 } 107 break; 108 // User has explicitly clicked 'no thanks'. 109 case OptInManager.CommandFromPage.CLICKED_NO_OPTIN: 110 if (chrome.hotwordPrivate && chrome.hotwordPrivate.setEnabled) { 111 chrome.hotwordPrivate.setEnabled(false); 112 } 113 break; 114 // Information regarding the audio logging preference was sent. 115 case OptInManager.CommandFromPage.AUDIO_LOGGING_ON: 116 if (chrome.hotwordPrivate && 117 chrome.hotwordPrivate.setAudioLoggingEnabled) { 118 chrome.hotwordPrivate.setAudioLoggingEnabled(true); 119 } 120 break; 121 case OptInManager.CommandFromPage.AUDIO_LOGGING_OFF: 122 if (chrome.hotwordPrivate && 123 chrome.hotwordPrivate.setAudioLoggingEnabled) { 124 chrome.hotwordPrivate.setAudioLoggingEnabled(false); 125 } 126 break; 127 default: 128 break; 129 } 130 return false; 131}; 132 133/** 134 * Helper function to test URLs as being valid for running the 135 * hotwording extension. It's used by isEligibleUrl to make that 136 * function clearer. 137 * @param {string} url URL to check. 138 * @param {string} base Base URL to compare against.. 139 * @return {boolean} True if url is an eligible hotword URL. 140 */ 141OptInManager.prototype.checkEligibleUrl = function(url, base) { 142 if (!url) 143 return false; 144 145 if (url === base || 146 url === base + '/' || 147 url.indexOf(base + '/_/chrome/newtab?') === 0 || // Appcache NTP. 148 url.indexOf(base + '/?') === 0 || 149 url.indexOf(base + '/#') === 0 || 150 url.indexOf(base + '/webhp') === 0 || 151 url.indexOf(base + '/search') === 0) { 152 return true; 153 } 154 return false; 155 156}; 157 158/** 159 * Determines if a URL is eligible for hotwording. For now, the 160 * valid pages are the Google HP and SERP (this will include the NTP). 161 * @param {string} url URL to check. 162 * @return {boolean} True if url is an eligible hotword URL. 163 */ 164OptInManager.prototype.isEligibleUrl = function(url) { 165 if (!url) 166 return false; 167 168 // More URLs will be added in the future so leaving this as an array. 169 var baseUrls = [ 170 'chrome://newtab' 171 ]; 172 var baseGoogleUrls = [ 173 'https://www.google.', 174 'https://encrypted.google.' 175 ]; 176 var tlds = [ 177 'com', 178 'co.uk', 179 'de', 180 'fr', 181 'ru' 182 ]; 183 184 // Check URLs which do not have locale-based TLDs first. 185 if (this.checkEligibleUrl(url, baseUrls[0])) 186 return true; 187 188 // Check URLs with each type of local-based TLD. 189 for (var i = 0; i < baseGoogleUrls.length; i++) { 190 for (var j = 0; j < tlds.length; j++) { 191 var base = baseGoogleUrls[i] + tlds[j]; 192 if (this.checkEligibleUrl(url, base)) 193 return true; 194 } 195 } 196 return false; 197}; 198 199 200/** 201 * Initializes the extension. 202 */ 203OptInManager.prototype.initialize = function() { 204 // TODO(rlp): Possibly remove the next line. It's proably not used, but 205 // leaving for now to be safe. We should remove it once all messsage 206 // relaying is removed form the content scripts. 207 chrome.runtime.onMessage.addListener(this.handleMessage_.bind(this)); 208 chrome.runtime.onMessageExternal.addListener( 209 this.handleMessage_.bind(this)); 210}; 211 212 213new OptInManager().initialize(); 214