OLD | NEW |
1 // Copyright 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 cr.define('hotword', function() { | 5 cr.define('hotword', function() { |
6 'use strict'; | 6 'use strict'; |
7 | 7 |
8 /** | 8 /** |
9 * Class used to manage the interaction between hotwording and the | 9 * Class used to manage the interaction between hotwording and the |
10 * NTP/google.com. Injects a content script to interact with NTP/google.com | 10 * NTP/google.com. Injects a content script to interact with NTP/google.com |
(...skipping 44 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
55 | 55 |
56 PageAudioManager.prototype = { | 56 PageAudioManager.prototype = { |
57 /** | 57 /** |
58 * Helper function to test if a URL path is eligible for hotwording. | 58 * Helper function to test if a URL path is eligible for hotwording. |
59 * @param {!string} url URL to check. | 59 * @param {!string} url URL to check. |
60 * @param {!string} base Base URL to compare against.. | 60 * @param {!string} base Base URL to compare against.. |
61 * @return {boolean} True if url is an eligible hotword URL. | 61 * @return {boolean} True if url is an eligible hotword URL. |
62 * @private | 62 * @private |
63 */ | 63 */ |
64 checkUrlPathIsEligible_: function(url, base) { | 64 checkUrlPathIsEligible_: function(url, base) { |
65 if (url == base || | 65 if (url == base || url == base + '/' || |
66 url == base + '/' || | |
67 url.startsWith(base + '/_/chrome/newtab?') || // Appcache NTP. | 66 url.startsWith(base + '/_/chrome/newtab?') || // Appcache NTP. |
68 url.startsWith(base + '/?') || | 67 url.startsWith(base + '/?') || url.startsWith(base + '/#') || |
69 url.startsWith(base + '/#') || | 68 url.startsWith(base + '/webhp') || url.startsWith(base + '/search') || |
70 url.startsWith(base + '/webhp') || | |
71 url.startsWith(base + '/search') || | |
72 url.startsWith(base + '/imghp')) { | 69 url.startsWith(base + '/imghp')) { |
73 return true; | 70 return true; |
74 } | 71 } |
75 return false; | 72 return false; |
76 }, | 73 }, |
77 | 74 |
78 /** | 75 /** |
79 * Determines if a URL is eligible for hotwording. For now, the valid pages | 76 * Determines if a URL is eligible for hotwording. For now, the valid pages |
80 * are the Google HP and SERP (this will include the NTP). | 77 * are the Google HP and SERP (this will include the NTP). |
81 * @param {!string} url URL to check. | 78 * @param {!string} url URL to check. |
82 * @return {boolean} True if url is an eligible hotword URL. | 79 * @return {boolean} True if url is an eligible hotword URL. |
83 * @private | 80 * @private |
84 */ | 81 */ |
85 isEligibleUrl_: function(url) { | 82 isEligibleUrl_: function(url) { |
86 if (!url) | 83 if (!url) |
87 return false; | 84 return false; |
88 | 85 |
89 var baseGoogleUrls = [ | 86 var baseGoogleUrls = [ |
90 'https://encrypted.google.', | 87 'https://encrypted.google.', 'https://images.google.', |
91 'https://images.google.', | |
92 'https://www.google.' | 88 'https://www.google.' |
93 ]; | 89 ]; |
94 // TODO(amistry): Get this list from a file in the shared module instead. | 90 // TODO(amistry): Get this list from a file in the shared module instead. |
95 var tlds = [ | 91 var tlds = [ |
96 'at', | 92 'at', 'ca', 'com', 'com.au', 'com.mx', 'com.br', 'co.jp', 'co.kr', |
97 'ca', | 93 'co.nz', 'co.uk', 'co.za', 'de', 'es', 'fr', 'it', 'ru' |
98 'com', | |
99 'com.au', | |
100 'com.mx', | |
101 'com.br', | |
102 'co.jp', | |
103 'co.kr', | |
104 'co.nz', | |
105 'co.uk', | |
106 'co.za', | |
107 'de', | |
108 'es', | |
109 'fr', | |
110 'it', | |
111 'ru' | |
112 ]; | 94 ]; |
113 | 95 |
114 // Check for the new tab page first. | 96 // Check for the new tab page first. |
115 if (this.checkUrlPathIsEligible_(url, 'chrome://newtab')) | 97 if (this.checkUrlPathIsEligible_(url, 'chrome://newtab')) |
116 return true; | 98 return true; |
117 | 99 |
118 // Check URLs with each type of local-based TLD. | 100 // Check URLs with each type of local-based TLD. |
119 for (var i = 0; i < baseGoogleUrls.length; i++) { | 101 for (var i = 0; i < baseGoogleUrls.length; i++) { |
120 for (var j = 0; j < tlds.length; j++) { | 102 for (var j = 0; j < tlds.length; j++) { |
121 var base = baseGoogleUrls[i] + tlds[j]; | 103 var base = baseGoogleUrls[i] + tlds[j]; |
122 if (this.checkUrlPathIsEligible_(url, base)) | 104 if (this.checkUrlPathIsEligible_(url, base)) |
123 return true; | 105 return true; |
124 } | 106 } |
125 } | 107 } |
126 return false; | 108 return false; |
127 }, | 109 }, |
128 | 110 |
129 /** | 111 /** |
130 * Locates the current active tab in the current focused window and | 112 * Locates the current active tab in the current focused window and |
131 * performs a callback with the tab as the parameter. | 113 * performs a callback with the tab as the parameter. |
132 * @param {function(?Tab)} callback Function to call with the | 114 * @param {function(?Tab)} callback Function to call with the |
133 * active tab or null if not found. The function's |this| will be set to | 115 * active tab or null if not found. The function's |this| will be set to |
134 * this object. | 116 * this object. |
135 * @private | 117 * @private |
136 */ | 118 */ |
137 findCurrentTab_: function(callback) { | 119 findCurrentTab_: function(callback) { |
138 chrome.windows.getAll( | 120 chrome.windows.getAll({'populate': true}, function(windows) { |
139 {'populate': true}, | 121 for (var i = 0; i < windows.length; ++i) { |
140 function(windows) { | 122 if (!windows[i].focused) |
141 for (var i = 0; i < windows.length; ++i) { | 123 continue; |
142 if (!windows[i].focused) | |
143 continue; | |
144 | 124 |
145 for (var j = 0; j < windows[i].tabs.length; ++j) { | 125 for (var j = 0; j < windows[i].tabs.length; ++j) { |
146 var tab = windows[i].tabs[j]; | 126 var tab = windows[i].tabs[j]; |
147 if (tab.active) { | 127 if (tab.active) { |
148 callback.call(this, tab); | 128 callback.call(this, tab); |
149 return; | 129 return; |
150 } | |
151 } | |
152 } | 130 } |
153 callback.call(this, null); | 131 } |
154 }.bind(this)); | 132 } |
| 133 callback.call(this, null); |
| 134 }.bind(this)); |
155 }, | 135 }, |
156 | 136 |
157 /** | 137 /** |
158 * This function is called when a tab is activated (comes into focus). | 138 * This function is called when a tab is activated (comes into focus). |
159 * @param {Tab} tab Current active tab. | 139 * @param {Tab} tab Current active tab. |
160 * @private | 140 * @private |
161 */ | 141 */ |
162 activateTab_: function(tab) { | 142 activateTab_: function(tab) { |
163 if (!tab) { | 143 if (!tab) { |
164 this.stopHotwording_(); | 144 this.stopHotwording_(); |
(...skipping 10 matching lines...) Expand all Loading... |
175 /** | 155 /** |
176 * Prepare a new or updated tab by injecting the content script. | 156 * Prepare a new or updated tab by injecting the content script. |
177 * @param {!Tab} tab Newly updated or created tab. | 157 * @param {!Tab} tab Newly updated or created tab. |
178 * @private | 158 * @private |
179 */ | 159 */ |
180 prepareTab_: function(tab) { | 160 prepareTab_: function(tab) { |
181 if (!this.isEligibleUrl_(tab.url)) | 161 if (!this.isEligibleUrl_(tab.url)) |
182 return; | 162 return; |
183 | 163 |
184 chrome.tabs.executeScript( | 164 chrome.tabs.executeScript( |
185 tab.id, | 165 tab.id, {'file': 'audio_client.js'}, function(results) { |
186 {'file': 'audio_client.js'}, | |
187 function(results) { | |
188 if (chrome.runtime.lastError) { | 166 if (chrome.runtime.lastError) { |
189 // Ignore this error. For new tab pages, even though the URL is | 167 // Ignore this error. For new tab pages, even though the URL is |
190 // reported to be chrome://newtab/, the actual URL is a | 168 // reported to be chrome://newtab/, the actual URL is a |
191 // country-specific google domain. Since we don't have permission | 169 // country-specific google domain. Since we don't have permission |
192 // to inject on every page, an error will happen when the user is | 170 // to inject on every page, an error will happen when the user is |
193 // in an unsupported country. | 171 // in an unsupported country. |
194 // | 172 // |
195 // The property still needs to be accessed so that the error | 173 // The property still needs to be accessed so that the error |
196 // condition is cleared. If it isn't, exectureScript will log an | 174 // condition is cleared. If it isn't, exectureScript will log an |
197 // error the next time it is called. | 175 // error the next time it is called. |
(...skipping 69 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
267 | 245 |
268 /** | 246 /** |
269 * Handles a content script attempting to connect. | 247 * Handles a content script attempting to connect. |
270 * @param {!Port} port Communications port from the client. | 248 * @param {!Port} port Communications port from the client. |
271 * @private | 249 * @private |
272 */ | 250 */ |
273 handleConnect_: function(port) { | 251 handleConnect_: function(port) { |
274 if (port.name != hotword.constants.CLIENT_PORT_NAME) | 252 if (port.name != hotword.constants.CLIENT_PORT_NAME) |
275 return; | 253 return; |
276 | 254 |
277 var tab = /** @type {!Tab} */(port.sender.tab); | 255 var tab = /** @type {!Tab} */ (port.sender.tab); |
278 // An existing port from the same tab might already exist. But that port | 256 // An existing port from the same tab might already exist. But that port |
279 // may be from the previous page, so just overwrite the port. | 257 // may be from the previous page, so just overwrite the port. |
280 this.portMap_[tab.id] = port; | 258 this.portMap_[tab.id] = port; |
281 port.onDisconnect.addListener(function() { | 259 port.onDisconnect.addListener(function() { |
282 this.handleClientDisconnect_(port); | 260 this.handleClientDisconnect_(port); |
283 }.bind(this)); | 261 }.bind(this)); |
284 port.onMessage.addListener(function(msg) { | 262 port.onMessage.addListener(function(msg) { |
285 this.handleMessage_(msg, port.sender, port.postMessage); | 263 this.handleMessage_(msg, port.sender, port.postMessage); |
286 }.bind(this)); | 264 }.bind(this)); |
287 }, | 265 }, |
(...skipping 63 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
351 this.sendClient_(CommandToPage.HOTWORD_VOICE_TRIGGER, tab.id); | 329 this.sendClient_(CommandToPage.HOTWORD_VOICE_TRIGGER, tab.id); |
352 }); | 330 }); |
353 }, | 331 }, |
354 | 332 |
355 /** | 333 /** |
356 * Starts hotwording. | 334 * Starts hotwording. |
357 * @private | 335 * @private |
358 */ | 336 */ |
359 startHotwording_: function() { | 337 startHotwording_: function() { |
360 this.stateManager_.startSession( | 338 this.stateManager_.startSession( |
361 hotword.constants.SessionSource.NTP, | 339 hotword.constants.SessionSource.NTP, function() { |
362 function() { | |
363 this.sendAllClients_(CommandToPage.HOTWORD_STARTED); | 340 this.sendAllClients_(CommandToPage.HOTWORD_STARTED); |
364 }.bind(this), | 341 }.bind(this), this.hotwordTriggered_.bind(this)); |
365 this.hotwordTriggered_.bind(this)); | |
366 }, | 342 }, |
367 | 343 |
368 /** | 344 /** |
369 * Starts hotwording if the currently active tab is eligible for hotwording | 345 * Starts hotwording if the currently active tab is eligible for hotwording |
370 * (e.g. google.com). | 346 * (e.g. google.com). |
371 * @private | 347 * @private |
372 */ | 348 */ |
373 startHotwordingIfEligible_: function() { | 349 startHotwordingIfEligible_: function() { |
374 this.findCurrentTab_(function(tab) { | 350 this.findCurrentTab_(function(tab) { |
375 if (!tab) { | 351 if (!tab) { |
(...skipping 61 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
437 * @return {boolean} Whether to maintain the port open to call sendResponse. | 413 * @return {boolean} Whether to maintain the port open to call sendResponse. |
438 * @private | 414 * @private |
439 */ | 415 */ |
440 handleMessageFromPage_: function(request, sender, sendResponse) { | 416 handleMessageFromPage_: function(request, sender, sendResponse) { |
441 switch (request.type) { | 417 switch (request.type) { |
442 case CommandFromPage.PAGE_WAKEUP: | 418 case CommandFromPage.PAGE_WAKEUP: |
443 if (sender.tab && this.isEligibleUrl_(sender.tab.url)) { | 419 if (sender.tab && this.isEligibleUrl_(sender.tab.url)) { |
444 chrome.hotwordPrivate.getStatus( | 420 chrome.hotwordPrivate.getStatus( |
445 true /* getOptionalFields */, | 421 true /* getOptionalFields */, |
446 this.statusDone_.bind( | 422 this.statusDone_.bind( |
447 this, | 423 this, request.tab || sender.tab || {incognito: true}, |
448 request.tab || sender.tab || {incognito: true}, | |
449 sendResponse)); | 424 sendResponse)); |
450 return true; | 425 return true; |
451 } | 426 } |
452 | 427 |
453 // Do not show the opt-in promo for ineligible urls. | 428 // Do not show the opt-in promo for ineligible urls. |
454 this.sendResponse_({'doNotShowOptinMessage': true}, sendResponse); | 429 this.sendResponse_({'doNotShowOptinMessage': true}, sendResponse); |
455 break; | 430 break; |
456 case CommandFromPage.CLICKED_OPTIN: | 431 case CommandFromPage.CLICKED_OPTIN: |
457 chrome.hotwordPrivate.setEnabled(true); | 432 chrome.hotwordPrivate.setEnabled(true); |
458 break; | 433 break; |
(...skipping 61 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
520 chrome.runtime.onConnect.addListener(this.connectListener_); | 495 chrome.runtime.onConnect.addListener(this.connectListener_); |
521 chrome.tabs.onCreated.addListener(this.tabCreatedListener_); | 496 chrome.tabs.onCreated.addListener(this.tabCreatedListener_); |
522 chrome.tabs.onUpdated.addListener(this.tabUpdatedListener_); | 497 chrome.tabs.onUpdated.addListener(this.tabUpdatedListener_); |
523 chrome.tabs.onActivated.addListener(this.tabActivatedListener_); | 498 chrome.tabs.onActivated.addListener(this.tabActivatedListener_); |
524 chrome.windows.onFocusChanged.addListener( | 499 chrome.windows.onFocusChanged.addListener( |
525 this.windowFocusChangedListener_); | 500 this.windowFocusChangedListener_); |
526 chrome.hotwordPrivate.onMicrophoneStateChanged.addListener( | 501 chrome.hotwordPrivate.onMicrophoneStateChanged.addListener( |
527 this.microphoneStateChangedListener_); | 502 this.microphoneStateChangedListener_); |
528 if (chrome.runtime.onMessage.hasListener(this.messageListener_)) | 503 if (chrome.runtime.onMessage.hasListener(this.messageListener_)) |
529 return; | 504 return; |
530 chrome.runtime.onMessageExternal.addListener( | 505 chrome.runtime.onMessageExternal.addListener(this.messageListener_); |
531 this.messageListener_); | |
532 }, | 506 }, |
533 | 507 |
534 /** | 508 /** |
535 * Remove event listeners. | 509 * Remove event listeners. |
536 * @private | 510 * @private |
537 */ | 511 */ |
538 removeListeners_: function() { | 512 removeListeners_: function() { |
539 chrome.runtime.onConnect.removeListener(this.connectListener_); | 513 chrome.runtime.onConnect.removeListener(this.connectListener_); |
540 chrome.tabs.onCreated.removeListener(this.tabCreatedListener_); | 514 chrome.tabs.onCreated.removeListener(this.tabCreatedListener_); |
541 chrome.tabs.onUpdated.removeListener(this.tabUpdatedListener_); | 515 chrome.tabs.onUpdated.removeListener(this.tabUpdatedListener_); |
(...skipping 14 matching lines...) Expand all Loading... |
556 if (this.stateManager_.isSometimesOnEnabled()) { | 530 if (this.stateManager_.isSometimesOnEnabled()) { |
557 this.setupListeners_(); | 531 this.setupListeners_(); |
558 } else { | 532 } else { |
559 this.removeListeners_(); | 533 this.removeListeners_(); |
560 this.stopHotwording_(); | 534 this.stopHotwording_(); |
561 this.disconnectAllClients_(); | 535 this.disconnectAllClients_(); |
562 } | 536 } |
563 } | 537 } |
564 }; | 538 }; |
565 | 539 |
566 return { | 540 return {PageAudioManager: PageAudioManager}; |
567 PageAudioManager: PageAudioManager | |
568 }; | |
569 }); | 541 }); |
OLD | NEW |