blob: edaecaef6c9b7fa30f9d77664b49f373120ee5b6 [file] [log] [blame]
// Copyright (c) 2014 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
'use strict';
/**
* @fileoverview This extension manages communications between Chrome,
* Google.com pages and the Chrome Hotword extension.
*
* This helper extension is required due to the depoyment plan for Chrome M34:
*
* - The hotword extension will be distributed as an externally loaded
* component extension.
* - Settings for enabling and disabling the hotword extension has moved to
* Chrome settings.
* - Newtab page is served via chrome://newtab/
*
*/
/** @constructor */
var OptInManager = function() {};
/**
* @const {string}
* @private
*/
OptInManager.HOTWORD_EXTENSION_ID_ = 'bepbmhgboaologfdajaanbcjmnhjmhfn';
/**
* Test extension ID.
* @const {string}
* @private
*/
OptInManager.TEST_EXTENSION_ID_ = 'cpfhkdbjfdgdebcjlifoldbijinjfifp';
/**
* Commands sent from the page to this content script.
* @enum {string}
*/
OptInManager.CommandFromPage = {
// User has explicitly clicked 'no'.
CLICKED_NO_OPTIN: 'hcno',
// User has opted in.
CLICKED_OPTIN: 'hco',
// Audio logging is opted in.
AUDIO_LOGGING_ON: 'alon',
// Audio logging is opted out.
AUDIO_LOGGING_OFF: 'aloff',
// User visited an eligible page.
PAGE_WAKEUP: 'wu'
};
/**
* @param {Tab} tab Tab to inject.
* @param {function(HotwordStatus)} sendResponse Callback function to respond
* to sender.
* @param {HotwordStatus} hotwordStatus Status of the hotword extension.
* @private
*/
OptInManager.prototype.injectTab_ = function(
tab, sendResponse, hotwordStatus) {
var response = {'doNotShowOptinMessage': true};
if (!tab.incognito && hotwordStatus.available) {
if (!hotwordStatus.enabledSet)
response = hotwordStatus;
else if (hotwordStatus.enabled)
chrome.tabs.executeScript(tab.id, {'file': 'audio_client.js'});
}
try {
sendResponse(response);
} catch (err) {
// Suppress the exception thrown by sendResponse() when the page doesn't
// specify a response callback in the call to chrome.runtime.sendMessage().
// Unfortunately, there doesn't appear to be a way to detect one-way
// messages without explicitly saying in the message itself. This message
// is defined as a constant in extensions/renderer/messaging_bindings.cc
if (err.message == 'Attempting to use a disconnected port object')
return;
throw err;
}
};
/**
* Handles messages from the helper content script.
* @param {*} request Message from the sender.
* @param {MessageSender} sender Information about the sender.
* @param {function(HotwordStatus)} sendResponse Callback function to respond
* to sender.
* @return {boolean} Whether to maintain the port open to call sendResponse.
* @private
*/
OptInManager.prototype.handleMessage_ = function(
request, sender, sendResponse) {
switch (request.type) {
case OptInManager.CommandFromPage.PAGE_WAKEUP:
if (((sender.tab && this.isEligibleUrl(sender.tab.url)) ||
sender.id == OptInManager.HOTWORD_EXTENSION_ID_ ||
sender.id == OptInManager.TEST_EXTENSION_ID_) &&
chrome.hotwordPrivate && chrome.hotwordPrivate.getStatus) {
chrome.hotwordPrivate.getStatus(
this.injectTab_.bind(
this,
request.tab || sender.tab || {incognito: true},
sendResponse));
return true;
}
break;
case OptInManager.CommandFromPage.CLICKED_OPTIN:
if (chrome.hotwordPrivate && chrome.hotwordPrivate.setEnabled &&
chrome.hotwordPrivate.getStatus) {
chrome.hotwordPrivate.setEnabled(true);
chrome.hotwordPrivate.getStatus(
this.injectTab_.bind(this, sender.tab, sendResponse));
return true;
}
break;
// User has explicitly clicked 'no thanks'.
case OptInManager.CommandFromPage.CLICKED_NO_OPTIN:
if (chrome.hotwordPrivate && chrome.hotwordPrivate.setEnabled) {
chrome.hotwordPrivate.setEnabled(false);
}
break;
// Information regarding the audio logging preference was sent.
case OptInManager.CommandFromPage.AUDIO_LOGGING_ON:
if (chrome.hotwordPrivate &&
chrome.hotwordPrivate.setAudioLoggingEnabled) {
chrome.hotwordPrivate.setAudioLoggingEnabled(true);
}
break;
case OptInManager.CommandFromPage.AUDIO_LOGGING_OFF:
if (chrome.hotwordPrivate &&
chrome.hotwordPrivate.setAudioLoggingEnabled) {
chrome.hotwordPrivate.setAudioLoggingEnabled(false);
}
break;
default:
break;
}
return false;
};
/**
* Helper function to test URLs as being valid for running the
* hotwording extension. It's used by isEligibleUrl to make that
* function clearer.
* @param {string} url URL to check.
* @param {string} base Base URL to compare against..
* @return {boolean} True if url is an eligible hotword URL.
*/
OptInManager.prototype.checkEligibleUrl = function(url, base) {
if (!url)
return false;
if (url === base ||
url === base + '/' ||
url.indexOf(base + '/_/chrome/newtab?') === 0 || // Appcache NTP.
url.indexOf(base + '/?') === 0 ||
url.indexOf(base + '/#') === 0 ||
url.indexOf(base + '/webhp') === 0 ||
url.indexOf(base + '/search') === 0) {
return true;
}
return false;
};
/**
* Determines if a URL is eligible for hotwording. For now, the
* valid pages are the Google HP and SERP (this will include the NTP).
* @param {string} url URL to check.
* @return {boolean} True if url is an eligible hotword URL.
*/
OptInManager.prototype.isEligibleUrl = function(url) {
if (!url)
return false;
// More URLs will be added in the future so leaving this as an array.
var baseUrls = [
'chrome://newtab'
];
var baseGoogleUrls = [
'https://www.google.',
'https://encrypted.google.'
];
var tlds = [
'com',
'co.uk',
'de',
'fr',
'ru'
];
// Check URLs which do not have locale-based TLDs first.
if (this.checkEligibleUrl(url, baseUrls[0]))
return true;
// Check URLs with each type of local-based TLD.
for (var i = 0; i < baseGoogleUrls.length; i++) {
for (var j = 0; j < tlds.length; j++) {
var base = baseGoogleUrls[i] + tlds[j];
if (this.checkEligibleUrl(url, base))
return true;
}
}
return false;
};
/**
* Initializes the extension.
*/
OptInManager.prototype.initialize = function() {
// TODO(rlp): Possibly remove the next line. It's proably not used, but
// leaving for now to be safe. We should remove it once all messsage
// relaying is removed form the content scripts.
chrome.runtime.onMessage.addListener(this.handleMessage_.bind(this));
chrome.runtime.onMessageExternal.addListener(
this.handleMessage_.bind(this));
};
new OptInManager().initialize();