OLD | NEW |
1 // Copyright (c) 2014 The Chromium Authors. All rights reserved. | 1 // Copyright 2014 The Chromium Authors. All rights reserved. |
2 // Use of this source code is governed by a BSD-style license that can be | 2 // Use of this source code is governed by a BSD-style license that can be |
3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
4 | 4 |
5 'use strict'; | 5 'use strict'; |
6 | 6 |
7 /** | 7 /** |
8 * @fileoverview This is the audio client content script injected into eligible | 8 * @fileoverview This is the audio client content script injected into eligible |
9 * Google.com and New tab pages for interaction between the Webpage and the | 9 * Google.com and New tab pages for interaction between the Webpage and the |
10 * Hotword extension. | 10 * Hotword extension. |
11 */ | 11 */ |
12 | 12 |
13 | |
14 | |
15 (function() { | 13 (function() { |
16 /** | 14 /** |
17 * @constructor | 15 * @constructor |
18 */ | 16 */ |
19 var AudioClient = function() { | 17 var AudioClient = function() { |
20 /** @private {Element} */ | 18 /** @private {Element} */ |
21 this.speechOverlay_ = null; | 19 this.speechOverlay_ = null; |
22 | 20 |
23 /** @private {number} */ | 21 /** @private {number} */ |
24 this.checkSpeechUiRetries_ = 0; | 22 this.checkSpeechUiRetries_ = 0; |
(...skipping 11 matching lines...) Expand all Loading... |
36 */ | 34 */ |
37 this.uiStatus_ = null; | 35 this.uiStatus_ = null; |
38 | 36 |
39 /** | 37 /** |
40 * Bound function used to handle commands sent from the page to this script. | 38 * Bound function used to handle commands sent from the page to this script. |
41 * @private {Function} | 39 * @private {Function} |
42 */ | 40 */ |
43 this.handleCommandFromPageFunc_ = null; | 41 this.handleCommandFromPageFunc_ = null; |
44 }; | 42 }; |
45 | 43 |
46 | |
47 /** | 44 /** |
48 * Messages sent to the page to control the voice search UI. | 45 * Messages sent to the page to control the voice search UI. |
49 * @enum {string} | 46 * @enum {string} |
50 */ | 47 */ |
51 AudioClient.CommandToPage = { | 48 AudioClient.CommandToPage = { |
52 HOTWORD_VOICE_TRIGGER: 'vt', | 49 HOTWORD_VOICE_TRIGGER: 'vt', |
53 HOTWORD_STARTED: 'hs', | 50 HOTWORD_STARTED: 'hs', |
54 HOTWORD_ENDED: 'hd', | 51 HOTWORD_ENDED: 'hd', |
55 HOTWORD_TIMEOUT: 'ht', | 52 HOTWORD_TIMEOUT: 'ht', |
56 HOTWORD_ERROR: 'he' | 53 HOTWORD_ERROR: 'he' |
57 }; | 54 }; |
58 | 55 |
59 | |
60 /** | 56 /** |
61 * Messages received from the page used to indicate voice search state. | 57 * Messages received from the page used to indicate voice search state. |
62 * @enum {string} | 58 * @enum {string} |
63 */ | 59 */ |
64 AudioClient.CommandFromPage = { | 60 AudioClient.CommandFromPage = { |
65 SPEECH_START: 'ss', | 61 SPEECH_START: 'ss', |
66 SPEECH_END: 'se', | 62 SPEECH_END: 'se', |
67 SPEECH_RESET: 'sr', | 63 SPEECH_RESET: 'sr', |
68 SHOWING_HOTWORD_START: 'shs', | 64 SHOWING_HOTWORD_START: 'shs', |
69 SHOWING_ERROR_MESSAGE: 'sem', | 65 SHOWING_ERROR_MESSAGE: 'sem', |
70 SHOWING_TIMEOUT_MESSAGE: 'stm', | 66 SHOWING_TIMEOUT_MESSAGE: 'stm', |
71 CLICKED_RESUME: 'hcc', | 67 CLICKED_RESUME: 'hcc', |
72 CLICKED_RESTART: 'hcr', | 68 CLICKED_RESTART: 'hcr', |
73 CLICKED_DEBUG: 'hcd' | 69 CLICKED_DEBUG: 'hcd' |
74 }; | 70 }; |
75 | 71 |
76 | |
77 /** | 72 /** |
78 * Errors that are sent to the hotword extension. | 73 * Errors that are sent to the hotword extension. |
79 * @enum {string} | 74 * @enum {string} |
80 */ | 75 */ |
81 AudioClient.Error = { | 76 AudioClient.Error = { |
82 NO_SPEECH_UI: 'ac1', | 77 NO_SPEECH_UI: 'ac1', |
83 NO_HOTWORD_STARTED_UI: 'ac2', | 78 NO_HOTWORD_STARTED_UI: 'ac2', |
84 NO_HOTWORD_TIMEOUT_UI: 'ac3', | 79 NO_HOTWORD_TIMEOUT_UI: 'ac3', |
85 NO_HOTWORD_ERROR_UI: 'ac4' | 80 NO_HOTWORD_ERROR_UI: 'ac4' |
86 }; | 81 }; |
87 | 82 |
88 | |
89 /** | 83 /** |
90 * @const {string} | 84 * @const {string} |
91 * @private | 85 * @private |
92 */ | 86 */ |
93 AudioClient.HOTWORD_EXTENSION_ID_ = 'bepbmhgboaologfdajaanbcjmnhjmhfn'; | 87 AudioClient.HOTWORD_EXTENSION_ID_ = 'nbpagnldghgfoolbancepceaanlmhfmd'; |
94 | |
95 | 88 |
96 /** | 89 /** |
97 * Number of times to retry checking a transient error. | 90 * Number of times to retry checking a transient error. |
98 * @const {number} | 91 * @const {number} |
99 * @private | 92 * @private |
100 */ | 93 */ |
101 AudioClient.MAX_RETRIES = 3; | 94 AudioClient.MAX_RETRIES = 3; |
102 | 95 |
103 | |
104 /** | 96 /** |
105 * Delay to wait in milliseconds before rechecking for any transient errors. | 97 * Delay to wait in milliseconds before rechecking for any transient errors. |
106 * @const {number} | 98 * @const {number} |
107 * @private | 99 * @private |
108 */ | 100 */ |
109 AudioClient.RETRY_TIME_MS_ = 2000; | 101 AudioClient.RETRY_TIME_MS_ = 2000; |
110 | 102 |
111 | |
112 /** | 103 /** |
113 * DOM ID for the speech UI overlay. | 104 * DOM ID for the speech UI overlay. |
114 * @const {string} | 105 * @const {string} |
115 * @private | 106 * @private |
116 */ | 107 */ |
117 AudioClient.SPEECH_UI_OVERLAY_ID_ = 'spch'; | 108 AudioClient.SPEECH_UI_OVERLAY_ID_ = 'spch'; |
118 | 109 |
119 | |
120 /** | 110 /** |
121 * @const {string} | 111 * @const {string} |
122 * @private | 112 * @private |
123 */ | 113 */ |
124 AudioClient.HELP_CENTER_URL_ = | 114 AudioClient.HELP_CENTER_URL_ = |
125 'https://support.google.com/chrome/?p=ui_hotword_search'; | 115 'https://support.google.com/chrome/?p=ui_hotword_search'; |
126 | 116 |
127 | |
128 /** | 117 /** |
129 * @const {string} | 118 * @const {string} |
130 * @private | 119 * @private |
131 */ | 120 */ |
132 AudioClient.CLIENT_PORT_NAME_ = 'chwcpn'; | 121 AudioClient.CLIENT_PORT_NAME_ = 'chwcpn'; |
133 | 122 |
134 /** | 123 /** |
135 * Existence of the Audio Client. | 124 * Existence of the Audio Client. |
136 * @const {string} | 125 * @const {string} |
137 * @private | 126 * @private |
138 */ | 127 */ |
139 AudioClient.EXISTS_ = 'chwace'; | 128 AudioClient.EXISTS_ = 'chwace'; |
140 | 129 |
141 | |
142 /** | 130 /** |
143 * Checks for the presence of speech overlay UI DOM elements. | 131 * Checks for the presence of speech overlay UI DOM elements. |
144 * @private | 132 * @private |
145 */ | 133 */ |
146 AudioClient.prototype.checkSpeechOverlayUi_ = function() { | 134 AudioClient.prototype.checkSpeechOverlayUi_ = function() { |
147 if (!this.speechOverlay_) { | 135 if (!this.speechOverlay_) { |
148 window.setTimeout(this.delayedCheckSpeechOverlayUi_.bind(this), | 136 window.setTimeout(this.delayedCheckSpeechOverlayUi_.bind(this), |
149 AudioClient.RETRY_TIME_MS_); | 137 AudioClient.RETRY_TIME_MS_); |
150 } else { | 138 } else { |
151 this.checkSpeechUiRetries_ = 0; | 139 this.checkSpeechUiRetries_ = 0; |
152 } | 140 } |
153 }; | 141 }; |
154 | 142 |
155 | |
156 /** | 143 /** |
157 * Function called to check for the speech UI overlay after some time has | 144 * Function called to check for the speech UI overlay after some time has |
158 * passed since an initial check. Will either retry triggering the speech | 145 * passed since an initial check. Will either retry triggering the speech |
159 * or sends an error message depending on the number of retries. | 146 * or sends an error message depending on the number of retries. |
160 * @private | 147 * @private |
161 */ | 148 */ |
162 AudioClient.prototype.delayedCheckSpeechOverlayUi_ = function() { | 149 AudioClient.prototype.delayedCheckSpeechOverlayUi_ = function() { |
163 this.speechOverlay_ = document.getElementById( | 150 this.speechOverlay_ = document.getElementById( |
164 AudioClient.SPEECH_UI_OVERLAY_ID_); | 151 AudioClient.SPEECH_UI_OVERLAY_ID_); |
165 if (!this.speechOverlay_) { | 152 if (!this.speechOverlay_) { |
166 if (this.checkSpeechUiRetries_++ < AudioClient.MAX_RETRIES) { | 153 if (this.checkSpeechUiRetries_++ < AudioClient.MAX_RETRIES) { |
167 this.sendCommandToPage_(AudioClient.CommandToPage.VOICE_TRIGGER); | 154 this.sendCommandToPage_(AudioClient.CommandToPage.VOICE_TRIGGER); |
168 this.checkSpeechOverlayUi_(); | 155 this.checkSpeechOverlayUi_(); |
169 } else { | 156 } else { |
170 this.sendCommandToExtension_(AudioClient.Error.NO_SPEECH_UI); | 157 this.sendCommandToExtension_(AudioClient.Error.NO_SPEECH_UI); |
171 } | 158 } |
172 } else { | 159 } else { |
173 this.checkSpeechUiRetries_ = 0; | 160 this.checkSpeechUiRetries_ = 0; |
174 } | 161 } |
175 }; | 162 }; |
176 | 163 |
177 | |
178 /** | 164 /** |
179 * Checks that the triggered UI is actually displayed. | 165 * Checks that the triggered UI is actually displayed. |
180 * @param {AudioClient.CommandToPage} command Command that was send. | 166 * @param {AudioClient.CommandToPage} command Command that was send. |
181 * @private | 167 * @private |
182 */ | 168 */ |
183 AudioClient.prototype.checkUi_ = function(command) { | 169 AudioClient.prototype.checkUi_ = function(command) { |
184 this.uiStatus_[command].timeoutId = | 170 this.uiStatus_[command].timeoutId = |
185 window.setTimeout(this.failedCheckUi_.bind(this, command), | 171 window.setTimeout(this.failedCheckUi_.bind(this, command), |
186 AudioClient.RETRY_TIME_MS_); | 172 AudioClient.RETRY_TIME_MS_); |
187 }; | 173 }; |
188 | 174 |
189 | |
190 /** | 175 /** |
191 * Function called when the UI verification is not called in time. Will either | 176 * Function called when the UI verification is not called in time. Will either |
192 * retry the command or sends an error message, depending on the number of | 177 * retry the command or sends an error message, depending on the number of |
193 * retries for the command. | 178 * retries for the command. |
194 * @param {AudioClient.CommandToPage} command Command that was sent. | 179 * @param {AudioClient.CommandToPage} command Command that was sent. |
195 * @private | 180 * @private |
196 */ | 181 */ |
197 AudioClient.prototype.failedCheckUi_ = function(command) { | 182 AudioClient.prototype.failedCheckUi_ = function(command) { |
198 if (this.uiStatus_[command].tries++ < AudioClient.MAX_RETRIES) { | 183 if (this.uiStatus_[command].tries++ < AudioClient.MAX_RETRIES) { |
199 this.sendCommandToPage_(command); | 184 this.sendCommandToPage_(command); |
200 this.checkUi_(command); | 185 this.checkUi_(command); |
201 } else { | 186 } else { |
202 this.sendCommandToExtension_(this.uiStatus_[command].error); | 187 this.sendCommandToExtension_(this.uiStatus_[command].error); |
203 } | 188 } |
204 }; | 189 }; |
205 | 190 |
206 | |
207 /** | 191 /** |
208 * Confirm that an UI element has been shown. | 192 * Confirm that an UI element has been shown. |
209 * @param {AudioClient.CommandToPage} command UI to confirm. | 193 * @param {AudioClient.CommandToPage} command UI to confirm. |
210 * @private | 194 * @private |
211 */ | 195 */ |
212 AudioClient.prototype.verifyUi_ = function(command) { | 196 AudioClient.prototype.verifyUi_ = function(command) { |
213 if (this.uiStatus_[command].timeoutId) { | 197 if (this.uiStatus_[command].timeoutId) { |
214 window.clearTimeout(this.uiStatus_[command].timeoutId); | 198 window.clearTimeout(this.uiStatus_[command].timeoutId); |
215 this.uiStatus_[command].timeoutId = null; | 199 this.uiStatus_[command].timeoutId = null; |
216 this.uiStatus_[command].tries = 0; | 200 this.uiStatus_[command].tries = 0; |
217 } | 201 } |
218 }; | 202 }; |
219 | 203 |
220 | |
221 /** | 204 /** |
222 * Sends a command to the audio manager. | 205 * Sends a command to the audio manager. |
223 * @param {string} commandStr command to send to plugin. | 206 * @param {string} commandStr command to send to plugin. |
224 * @private | 207 * @private |
225 */ | 208 */ |
226 AudioClient.prototype.sendCommandToExtension_ = function(commandStr) { | 209 AudioClient.prototype.sendCommandToExtension_ = function(commandStr) { |
227 if (this.port_) | 210 if (this.port_) |
228 this.port_.postMessage({'cmd': commandStr}); | 211 this.port_.postMessage({'cmd': commandStr}); |
229 }; | 212 }; |
230 | 213 |
231 | |
232 /** | 214 /** |
233 * Handles a message from the audio manager. | 215 * Handles a message from the audio manager. |
234 * @param {{cmd: string}} commandObj Command from the audio manager. | 216 * @param {{cmd: string}} commandObj Command from the audio manager. |
235 * @private | 217 * @private |
236 */ | 218 */ |
237 AudioClient.prototype.handleCommandFromExtension_ = function(commandObj) { | 219 AudioClient.prototype.handleCommandFromExtension_ = function(commandObj) { |
238 var command = commandObj['cmd']; | 220 var command = commandObj['cmd']; |
239 if (command) { | 221 if (command) { |
240 switch (command) { | 222 switch (command) { |
241 case AudioClient.CommandToPage.HOTWORD_VOICE_TRIGGER: | 223 case AudioClient.CommandToPage.HOTWORD_VOICE_TRIGGER: |
(...skipping 12 matching lines...) Expand all Loading... |
254 this.checkUi_(command); | 236 this.checkUi_(command); |
255 break; | 237 break; |
256 case AudioClient.CommandToPage.HOTWORD_ERROR: | 238 case AudioClient.CommandToPage.HOTWORD_ERROR: |
257 this.sendCommandToPage_(command); | 239 this.sendCommandToPage_(command); |
258 this.checkUi_(command); | 240 this.checkUi_(command); |
259 break; | 241 break; |
260 } | 242 } |
261 } | 243 } |
262 }; | 244 }; |
263 | 245 |
264 | |
265 /** | 246 /** |
266 * @param {AudioClient.CommandToPage} commandStr Command to send. | 247 * @param {AudioClient.CommandToPage} commandStr Command to send. |
267 * @private | 248 * @private |
268 */ | 249 */ |
269 AudioClient.prototype.sendCommandToPage_ = function(commandStr) { | 250 AudioClient.prototype.sendCommandToPage_ = function(commandStr) { |
270 window.postMessage({'type': commandStr}, '*'); | 251 window.postMessage({'type': commandStr}, '*'); |
271 }; | 252 }; |
272 | 253 |
273 | |
274 /** | 254 /** |
275 * Handles a message from the html window. | 255 * Handles a message from the html window. |
276 * @param {!MessageEvent} messageEvent Message event from the window. | 256 * @param {!MessageEvent} messageEvent Message event from the window. |
277 * @private | 257 * @private |
278 */ | 258 */ |
279 AudioClient.prototype.handleCommandFromPage_ = function(messageEvent) { | 259 AudioClient.prototype.handleCommandFromPage_ = function(messageEvent) { |
280 if (messageEvent.source == window && messageEvent.data.type) { | 260 if (messageEvent.source == window && messageEvent.data.type) { |
281 var command = messageEvent.data.type; | 261 var command = messageEvent.data.type; |
282 switch (command) { | 262 switch (command) { |
283 case AudioClient.CommandFromPage.SPEECH_START: | 263 case AudioClient.CommandFromPage.SPEECH_START: |
(...skipping 27 matching lines...) Expand all Loading... |
311 case AudioClient.CommandFromPage.SHOWING_ERROR_MESSAGE: | 291 case AudioClient.CommandFromPage.SHOWING_ERROR_MESSAGE: |
312 this.verifyUi_(AudioClient.CommandToPage.HOTWORD_ERROR); | 292 this.verifyUi_(AudioClient.CommandToPage.HOTWORD_ERROR); |
313 break; | 293 break; |
314 case AudioClient.CommandFromPage.SHOWING_TIMEOUT_MESSAGE: | 294 case AudioClient.CommandFromPage.SHOWING_TIMEOUT_MESSAGE: |
315 this.verifyUi_(AudioClient.CommandToPage.HOTWORD_TIMEOUT); | 295 this.verifyUi_(AudioClient.CommandToPage.HOTWORD_TIMEOUT); |
316 break; | 296 break; |
317 } | 297 } |
318 } | 298 } |
319 }; | 299 }; |
320 | 300 |
321 | |
322 /** | 301 /** |
323 * Initialize the content script. | 302 * Initialize the content script. |
324 */ | 303 */ |
325 AudioClient.prototype.initialize = function() { | 304 AudioClient.prototype.initialize = function() { |
326 if (AudioClient.EXISTS_ in window) | 305 if (AudioClient.EXISTS_ in window) |
327 return; | 306 return; |
328 window[AudioClient.EXISTS_] = true; | 307 window[AudioClient.EXISTS_] = true; |
329 | 308 |
330 // UI verification object. | 309 // UI verification object. |
331 this.uiStatus_ = {}; | 310 this.uiStatus_ = {}; |
(...skipping 11 matching lines...) Expand all Loading... |
343 timeoutId: null, | 322 timeoutId: null, |
344 tries: 0, | 323 tries: 0, |
345 error: AudioClient.Error.NO_HOTWORD_ERROR_UI | 324 error: AudioClient.Error.NO_HOTWORD_ERROR_UI |
346 }; | 325 }; |
347 | 326 |
348 this.handleCommandFromPageFunc_ = this.handleCommandFromPage_.bind(this); | 327 this.handleCommandFromPageFunc_ = this.handleCommandFromPage_.bind(this); |
349 window.addEventListener('message', this.handleCommandFromPageFunc_, false); | 328 window.addEventListener('message', this.handleCommandFromPageFunc_, false); |
350 this.initPort_(); | 329 this.initPort_(); |
351 }; | 330 }; |
352 | 331 |
353 | |
354 /** | 332 /** |
355 * Initialize the communications port with the audio manager. This | 333 * Initialize the communications port with the audio manager. This |
356 * function will be also be called again if the audio-manager | 334 * function will be also be called again if the audio-manager |
357 * disconnects for some reason (such as the extension | 335 * disconnects for some reason (such as the extension |
358 * background.html page being reloaded). | 336 * background.html page being reloaded). |
359 * @private | 337 * @private |
360 */ | 338 */ |
361 AudioClient.prototype.initPort_ = function() { | 339 AudioClient.prototype.initPort_ = function() { |
362 this.port_ = chrome.runtime.connect( | 340 this.port_ = chrome.runtime.connect( |
363 AudioClient.HOTWORD_EXTENSION_ID_, | 341 AudioClient.HOTWORD_EXTENSION_ID_, |
(...skipping 10 matching lines...) Expand all Loading... |
374 }).bind(this)); | 352 }).bind(this)); |
375 | 353 |
376 // See note above. | 354 // See note above. |
377 this.port_.onMessage.addListener( | 355 this.port_.onMessage.addListener( |
378 this.handleCommandFromExtension_.bind(this)); | 356 this.handleCommandFromExtension_.bind(this)); |
379 | 357 |
380 if (this.speechActive_) | 358 if (this.speechActive_) |
381 this.sendCommandToExtension_(AudioClient.CommandFromPage.SPEECH_START); | 359 this.sendCommandToExtension_(AudioClient.CommandFromPage.SPEECH_START); |
382 }; | 360 }; |
383 | 361 |
384 | |
385 // Initializes as soon as the code is ready, do not wait for the page. | 362 // Initializes as soon as the code is ready, do not wait for the page. |
386 new AudioClient().initialize(); | 363 new AudioClient().initialize(); |
387 })(); | 364 })(); |
OLD | NEW |