OLD | NEW |
1 // Copyright (c) 2013 The Chromium Authors. All rights reserved. | 1 // Copyright (c) 2013 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 The event page for Google Now for Chrome implementation. | 8 * @fileoverview The event page for Google Now for Chrome implementation. |
9 * The Google Now event page gets Google Now cards from the server and shows | 9 * The Google Now event page gets Google Now cards from the server and shows |
10 * them as Chrome notifications. | 10 * them as Chrome notifications. |
(...skipping 14 matching lines...) Expand all Loading... |
25 // TODO(vadimt): Remove 'console' calls. | 25 // TODO(vadimt): Remove 'console' calls. |
26 // TODO(vadimt): Consider sending JS stacks for chrome.* API errors and | 26 // TODO(vadimt): Consider sending JS stacks for chrome.* API errors and |
27 // malformed server responses. | 27 // malformed server responses. |
28 | 28 |
29 /** | 29 /** |
30 * Standard response code for successful HTTP requests. This is the only success | 30 * Standard response code for successful HTTP requests. This is the only success |
31 * code the server will send. | 31 * code the server will send. |
32 */ | 32 */ |
33 var HTTP_OK = 200; | 33 var HTTP_OK = 200; |
34 | 34 |
| 35 var HTTP_UNAUTHORIZED = 401; |
| 36 var HTTP_FORBIDDEN = 403; |
| 37 |
35 /** | 38 /** |
36 * Initial period for polling for Google Now Notifications cards to use when the | 39 * Initial period for polling for Google Now Notifications cards to use when the |
37 * period from the server is not available. | 40 * period from the server is not available. |
38 */ | 41 */ |
39 var INITIAL_POLLING_PERIOD_SECONDS = 5 * 60; // 5 minutes | 42 var INITIAL_POLLING_PERIOD_SECONDS = 5 * 60; // 5 minutes |
40 | 43 |
41 /** | 44 /** |
42 * Maximal period for polling for Google Now Notifications cards to use when the | 45 * Maximal period for polling for Google Now Notifications cards to use when the |
43 * period from the server is not available. | 46 * period from the server is not available. |
44 */ | 47 */ |
(...skipping 60 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
105 // send dismissals is scheduled. | 108 // send dismissals is scheduled. |
106 return true; | 109 return true; |
107 } | 110 } |
108 | 111 |
109 return false; | 112 return false; |
110 } | 113 } |
111 | 114 |
112 var tasks = buildTaskManager(areTasksConflicting); | 115 var tasks = buildTaskManager(areTasksConflicting); |
113 | 116 |
114 // Add error processing to API calls. | 117 // Add error processing to API calls. |
| 118 tasks.instrumentApiFunction(chrome.identity, 'getAuthToken', 1); |
| 119 tasks.instrumentApiFunction(chrome.identity, 'removeCachedAuthToken', 1); |
115 tasks.instrumentApiFunction(chrome.location.onLocationUpdate, 'addListener', 0); | 120 tasks.instrumentApiFunction(chrome.location.onLocationUpdate, 'addListener', 0); |
116 tasks.instrumentApiFunction(chrome.notifications, 'create', 2); | 121 tasks.instrumentApiFunction(chrome.notifications, 'create', 2); |
117 tasks.instrumentApiFunction(chrome.notifications, 'update', 2); | 122 tasks.instrumentApiFunction(chrome.notifications, 'update', 2); |
118 tasks.instrumentApiFunction(chrome.notifications, 'getAll', 0); | 123 tasks.instrumentApiFunction(chrome.notifications, 'getAll', 0); |
119 tasks.instrumentApiFunction( | 124 tasks.instrumentApiFunction( |
120 chrome.notifications.onButtonClicked, 'addListener', 0); | 125 chrome.notifications.onButtonClicked, 'addListener', 0); |
121 tasks.instrumentApiFunction(chrome.notifications.onClicked, 'addListener', 0); | 126 tasks.instrumentApiFunction(chrome.notifications.onClicked, 'addListener', 0); |
122 tasks.instrumentApiFunction(chrome.notifications.onClosed, 'addListener', 0); | 127 tasks.instrumentApiFunction(chrome.notifications.onClosed, 'addListener', 0); |
123 tasks.instrumentApiFunction(chrome.runtime.onInstalled, 'addListener', 0); | 128 tasks.instrumentApiFunction(chrome.runtime.onInstalled, 'addListener', 0); |
124 tasks.instrumentApiFunction(chrome.runtime.onStartup, 'addListener', 0); | 129 tasks.instrumentApiFunction(chrome.runtime.onStartup, 'addListener', 0); |
(...skipping 37 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
162 type: 'histogram-linear', | 167 type: 'histogram-linear', |
163 min: 1, | 168 min: 1, |
164 max: DiagnosticEvent.EVENTS_TOTAL, | 169 max: DiagnosticEvent.EVENTS_TOTAL, |
165 buckets: DiagnosticEvent.EVENTS_TOTAL + 1 | 170 buckets: DiagnosticEvent.EVENTS_TOTAL + 1 |
166 }; | 171 }; |
167 | 172 |
168 chrome.metricsPrivate.recordValue(metricDescription, event); | 173 chrome.metricsPrivate.recordValue(metricDescription, event); |
169 } | 174 } |
170 | 175 |
171 /** | 176 /** |
| 177 * Adds authorization behavior to the request. |
| 178 * @param {XMLHttpRequest} request Server request. |
| 179 * @param {function(boolean)} callbackBoolean Completion callback with 'success' |
| 180 * parameter. |
| 181 */ |
| 182 function setAuthorization(request, callbackBoolean) { |
| 183 tasks.debugSetStepName('setAuthorization-getAuthToken'); |
| 184 chrome.identity.getAuthToken({interactive: false}, function(token) { |
| 185 var errorMessage = |
| 186 chrome.runtime.lastError && chrome.runtime.lastError.message; |
| 187 console.log('setAuthorization: error=' + errorMessage + |
| 188 ', token=' + (token && 'non-empty')); |
| 189 if (chrome.runtime.lastError || !token) { |
| 190 callbackBoolean(false); |
| 191 return; |
| 192 } |
| 193 |
| 194 request.setRequestHeader('Authorization', 'Bearer ' + token); |
| 195 |
| 196 // Instrument onloadend to remove stale auth tokens. |
| 197 var originalOnLoadEnd = request.onloadend; |
| 198 request.onloadend = tasks.wrapCallback(function(event) { |
| 199 if (request.status == HTTP_FORBIDDEN || |
| 200 request.status == HTTP_UNAUTHORIZED) { |
| 201 tasks.debugSetStepName('setAuthorization-removeCachedAuthToken'); |
| 202 chrome.identity.removeCachedAuthToken({token: token}, function() { |
| 203 // After purging the token cache, call getAuthToken() again to let |
| 204 // Chrome know about the problem with the token. |
| 205 chrome.identity.getAuthToken({interactive: false}, function() {}); |
| 206 originalOnLoadEnd(event); |
| 207 }); |
| 208 } else { |
| 209 originalOnLoadEnd(event); |
| 210 } |
| 211 }); |
| 212 |
| 213 callbackBoolean(true); |
| 214 }); |
| 215 } |
| 216 |
| 217 /** |
172 * Shows a notification and remembers information associated with it. | 218 * Shows a notification and remembers information associated with it. |
173 * @param {Object} card Google Now card represented as a set of parameters for | 219 * @param {Object} card Google Now card represented as a set of parameters for |
174 * showing a Chrome notification. | 220 * showing a Chrome notification. |
175 * @param {Object} notificationsData Map from notification id to the data | 221 * @param {Object} notificationsData Map from notification id to the data |
176 * associated with a notification. | 222 * associated with a notification. |
177 * @param {number=} opt_previousVersion The version of the shown card with this | 223 * @param {number=} opt_previousVersion The version of the shown card with this |
178 * id, if it exists, undefined otherwise. | 224 * id, if it exists, undefined otherwise. |
179 */ | 225 */ |
180 function showNotification(card, notificationsData, opt_previousVersion) { | 226 function showNotification(card, notificationsData, opt_previousVersion) { |
181 console.log('showNotification ' + JSON.stringify(card) + ' ' + | 227 console.log('showNotification ' + JSON.stringify(card) + ' ' + |
(...skipping 168 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
350 } | 396 } |
351 | 397 |
352 recordEvent(DiagnosticEvent.REQUEST_FOR_CARDS_TOTAL); | 398 recordEvent(DiagnosticEvent.REQUEST_FOR_CARDS_TOTAL); |
353 | 399 |
354 // TODO(vadimt): Should we use 'q' as the parameter name? | 400 // TODO(vadimt): Should we use 'q' as the parameter name? |
355 var requestParameters = | 401 var requestParameters = |
356 'q=' + position.coords.latitude + | 402 'q=' + position.coords.latitude + |
357 ',' + position.coords.longitude + | 403 ',' + position.coords.longitude + |
358 ',' + position.coords.accuracy; | 404 ',' + position.coords.accuracy; |
359 | 405 |
360 // TODO(vadimt): Figure out how to send user's identity to the server. | |
361 var request = buildServerRequest('notifications'); | 406 var request = buildServerRequest('notifications'); |
362 | 407 |
363 request.onloadend = tasks.wrapCallback(function(event) { | 408 request.onloadend = function(event) { |
364 console.log('requestNotificationCards-onloadend ' + request.status); | 409 console.log('requestNotificationCards-onloadend ' + request.status); |
365 if (request.status == HTTP_OK) { | 410 if (request.status == HTTP_OK) { |
366 recordEvent(DiagnosticEvent.REQUEST_FOR_CARDS_SUCCESS); | 411 recordEvent(DiagnosticEvent.REQUEST_FOR_CARDS_SUCCESS); |
367 parseAndShowNotificationCards(request.response, callback); | 412 parseAndShowNotificationCards(request.response, callback); |
368 } else { | 413 } else { |
369 callback(); | 414 callback(); |
370 } | 415 } |
| 416 }; |
| 417 |
| 418 setAuthorization(request, function(success) { |
| 419 if (success) { |
| 420 tasks.debugSetStepName('requestNotificationCards-send-request'); |
| 421 request.send(requestParameters); |
| 422 } else { |
| 423 callback(); |
| 424 } |
371 }); | 425 }); |
372 | |
373 tasks.debugSetStepName('requestNotificationCards-send-request'); | |
374 request.send(requestParameters); | |
375 } | 426 } |
376 | 427 |
377 /** | 428 /** |
378 * Starts getting location for a cards update. | 429 * Starts getting location for a cards update. |
379 */ | 430 */ |
380 function requestLocation() { | 431 function requestLocation() { |
381 console.log('requestLocation'); | 432 console.log('requestLocation'); |
382 recordEvent(DiagnosticEvent.LOCATION_REQUEST); | 433 recordEvent(DiagnosticEvent.LOCATION_REQUEST); |
383 // TODO(vadimt): Figure out location request options. | 434 // TODO(vadimt): Figure out location request options. |
384 chrome.location.watchLocation(LOCATION_WATCH_NAME, {}); | 435 chrome.location.watchLocation(LOCATION_WATCH_NAME, {}); |
(...skipping 33 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
418 */ | 469 */ |
419 function requestCardDismissal( | 470 function requestCardDismissal( |
420 notificationId, dismissalTimeMs, callbackBoolean) { | 471 notificationId, dismissalTimeMs, callbackBoolean) { |
421 console.log('requestDismissingCard ' + notificationId + ' from ' + | 472 console.log('requestDismissingCard ' + notificationId + ' from ' + |
422 NOTIFICATION_CARDS_URL); | 473 NOTIFICATION_CARDS_URL); |
423 recordEvent(DiagnosticEvent.DISMISS_REQUEST_TOTAL); | 474 recordEvent(DiagnosticEvent.DISMISS_REQUEST_TOTAL); |
424 // Send a dismiss request to the server. | 475 // Send a dismiss request to the server. |
425 var requestParameters = 'id=' + notificationId + | 476 var requestParameters = 'id=' + notificationId + |
426 '&dismissalAge=' + (Date.now() - dismissalTimeMs); | 477 '&dismissalAge=' + (Date.now() - dismissalTimeMs); |
427 var request = buildServerRequest('dismiss'); | 478 var request = buildServerRequest('dismiss'); |
428 request.onloadend = tasks.wrapCallback(function(event) { | 479 request.onloadend = function(event) { |
429 console.log('requestDismissingCard-onloadend ' + request.status); | 480 console.log('requestDismissingCard-onloadend ' + request.status); |
430 if (request.status == HTTP_OK) | 481 if (request.status == HTTP_OK) |
431 recordEvent(DiagnosticEvent.DISMISS_REQUEST_SUCCESS); | 482 recordEvent(DiagnosticEvent.DISMISS_REQUEST_SUCCESS); |
432 | 483 |
433 callbackBoolean(request.status == HTTP_OK); | 484 callbackBoolean(request.status == HTTP_OK); |
| 485 }; |
| 486 |
| 487 setAuthorization(request, function(success) { |
| 488 if (success) { |
| 489 tasks.debugSetStepName('requestCardDismissal-send-request'); |
| 490 request.send(requestParameters); |
| 491 } else { |
| 492 callbackBoolean(false); |
| 493 } |
434 }); | 494 }); |
435 | |
436 tasks.debugSetStepName('requestCardDismissal-send-request'); | |
437 request.send(requestParameters); | |
438 } | 495 } |
439 | 496 |
440 /** | 497 /** |
441 * Tries to send dismiss requests for all pending dismissals. | 498 * Tries to send dismiss requests for all pending dismissals. |
442 * @param {function(boolean)} callbackBoolean Completion callback with 'success' | 499 * @param {function(boolean)} callbackBoolean Completion callback with 'success' |
443 * parameter. Success means that no pending dismissals are left. | 500 * parameter. Success means that no pending dismissals are left. |
444 */ | 501 */ |
445 function processPendingDismissals(callbackBoolean) { | 502 function processPendingDismissals(callbackBoolean) { |
446 tasks.debugSetStepName('processPendingDismissals-storage-get'); | 503 tasks.debugSetStepName('processPendingDismissals-storage-get'); |
447 storage.get(['pendingDismissals', 'recentDismissals'], function(items) { | 504 storage.get(['pendingDismissals', 'recentDismissals'], function(items) { |
(...skipping 244 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
692 | 749 |
693 chrome.location.onLocationUpdate.addListener(function(position) { | 750 chrome.location.onLocationUpdate.addListener(function(position) { |
694 recordEvent(DiagnosticEvent.LOCATION_UPDATE); | 751 recordEvent(DiagnosticEvent.LOCATION_UPDATE); |
695 updateNotificationsCards(position); | 752 updateNotificationsCards(position); |
696 }); | 753 }); |
697 | 754 |
698 chrome.omnibox.onInputEntered.addListener(function(text) { | 755 chrome.omnibox.onInputEntered.addListener(function(text) { |
699 localStorage['server_url'] = NOTIFICATION_CARDS_URL = text; | 756 localStorage['server_url'] = NOTIFICATION_CARDS_URL = text; |
700 initialize(); | 757 initialize(); |
701 }); | 758 }); |
OLD | NEW |