• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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