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 |