Chromium Code Reviews| 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. |
| 11 * The service performs periodic updating of Google Now cards. | 11 * The service performs periodic updating of Google Now cards. |
| 12 * Each updating of the cards includes 4 steps: | 12 * Each updating of the cards includes 4 steps: |
| 13 * 1. Obtaining the location of the machine; | 13 * 1. Obtaining the location of the machine; |
| 14 * 2. Processing requests for cards dismissals that are not yet sent to the | 14 * 2. Processing requests for cards dismissals that are not yet sent to the |
| 15 * server; | 15 * server; |
| 16 * 3. Making a server request based on that location; | 16 * 3. Making a server request based on that location; |
| 17 * 4. Showing the received cards as notifications. | 17 * 4. Showing the received cards as notifications. |
| 18 */ | 18 */ |
| 19 | 19 |
| 20 // TODO(vadimt): Decide what to do in incognito mode. | 20 // TODO(vadimt): Decide what to do in incognito mode. |
| 21 // TODO(vadimt): Figure out the final values of the constants. | 21 // TODO(vadimt): Figure out the final values of the constants. |
| 22 // TODO(vadimt): Remove 'console' calls. | 22 // TODO(vadimt): Remove 'console' calls. |
| 23 // TODO(vadimt): Consider sending JS stacks for malformed server responses. | |
| 24 | 23 |
| 25 /** | 24 /** |
| 26 * Standard response code for successful HTTP requests. This is the only success | 25 * Standard response code for successful HTTP requests. This is the only success |
| 27 * code the server will send. | 26 * code the server will send. |
| 28 */ | 27 */ |
| 29 var HTTP_OK = 200; | 28 var HTTP_OK = 200; |
| 30 var HTTP_NOCONTENT = 204; | 29 var HTTP_NOCONTENT = 204; |
| 31 | 30 |
| 32 var HTTP_BAD_REQUEST = 400; | 31 var HTTP_BAD_REQUEST = 400; |
| 33 var HTTP_UNAUTHORIZED = 401; | 32 var HTTP_UNAUTHORIZED = 401; |
| (...skipping 40 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 74 */ | 73 */ |
| 75 var DISMISS_RETENTION_TIME_MS = 20 * 60 * 1000; // 20 minutes | 74 var DISMISS_RETENTION_TIME_MS = 20 * 60 * 1000; // 20 minutes |
| 76 | 75 |
| 77 /** | 76 /** |
| 78 * Names for tasks that can be created by the extension. | 77 * Names for tasks that can be created by the extension. |
| 79 */ | 78 */ |
| 80 var UPDATE_CARDS_TASK_NAME = 'update-cards'; | 79 var UPDATE_CARDS_TASK_NAME = 'update-cards'; |
| 81 var DISMISS_CARD_TASK_NAME = 'dismiss-card'; | 80 var DISMISS_CARD_TASK_NAME = 'dismiss-card'; |
| 82 var RETRY_DISMISS_TASK_NAME = 'retry-dismiss'; | 81 var RETRY_DISMISS_TASK_NAME = 'retry-dismiss'; |
| 83 var STATE_CHANGED_TASK_NAME = 'state-changed'; | 82 var STATE_CHANGED_TASK_NAME = 'state-changed'; |
| 83 var SHOW_ON_START_TASK_NAME = 'show-cards-on-start'; | |
| 84 | 84 |
| 85 var LOCATION_WATCH_NAME = 'location-watch'; | 85 var LOCATION_WATCH_NAME = 'location-watch'; |
| 86 | 86 |
| 87 var WELCOME_TOAST_NOTIFICATION_ID = 'enable-now-toast'; | 87 var WELCOME_TOAST_NOTIFICATION_ID = 'enable-now-toast'; |
| 88 | 88 |
| 89 /** | 89 /** |
| 90 * The indices of the buttons that are displayed on the welcome toast. | 90 * The indices of the buttons that are displayed on the welcome toast. |
| 91 * @enum {number} | 91 * @enum {number} |
| 92 */ | 92 */ |
| 93 var ToastButtonIndex = {YES: 0, NO: 1}; | 93 var ToastButtonIndex = {YES: 0, NO: 1}; |
| (...skipping 216 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 310 var previousVersion = notifications[chromeNotificationId] && | 310 var previousVersion = notifications[chromeNotificationId] && |
| 311 notificationData && | 311 notificationData && |
| 312 notificationData.cardCreateInfo && | 312 notificationData.cardCreateInfo && |
| 313 notificationData.cardCreateInfo.version; | 313 notificationData.cardCreateInfo.version; |
| 314 newNotificationsData[chromeNotificationId] = cardSet.update( | 314 newNotificationsData[chromeNotificationId] = cardSet.update( |
| 315 chromeNotificationId, | 315 chromeNotificationId, |
| 316 cards[chromeNotificationId], | 316 cards[chromeNotificationId], |
| 317 previousVersion); | 317 previousVersion); |
| 318 } | 318 } |
| 319 | 319 |
| 320 recordEvent(GoogleNowEvent.CARDS_PARSE_SUCCESS); | |
| 321 | |
| 322 chrome.storage.local.set({ | 320 chrome.storage.local.set({ |
| 323 notificationsData: newNotificationsData, | 321 notificationsData: newNotificationsData, |
| 324 recentDismissals: updatedRecentDismissals | 322 recentDismissals: updatedRecentDismissals |
| 325 }); | 323 }); |
| 326 }); | 324 }); |
| 327 }); | 325 }); |
| 328 } | 326 } |
| 329 | 327 |
| 330 /** | 328 /** |
| 331 * Removes all cards and card state on Google Now close down. | 329 * Removes all cards and card state on Google Now close down. |
| (...skipping 108 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 440 | 438 |
| 441 verify(nextPollTime != null, 'scheduleNextPoll: nextPollTime is null'); | 439 verify(nextPollTime != null, 'scheduleNextPoll: nextPollTime is null'); |
| 442 | 440 |
| 443 var nextPollDelaySeconds = Math.max( | 441 var nextPollDelaySeconds = Math.max( |
| 444 (nextPollTime - Date.now()) / MS_IN_SECOND, | 442 (nextPollTime - Date.now()) / MS_IN_SECOND, |
| 445 MINIMUM_POLLING_PERIOD_SECONDS); | 443 MINIMUM_POLLING_PERIOD_SECONDS); |
| 446 updateCardsAttempts.start(nextPollDelaySeconds); | 444 updateCardsAttempts.start(nextPollDelaySeconds); |
| 447 } | 445 } |
| 448 | 446 |
| 449 /** | 447 /** |
| 448 * Merges notification groups into a set of Chrome notifications and shows them. | |
| 449 * @param {Object.<string, StorageGroup>} notificationGroups Map from group name | |
| 450 * to group information. | |
| 451 */ | |
| 452 function mergeAndShowNotificationCards(notificationGroups) { | |
| 453 // Merge cards from all groups into one set. | |
|
rgustafson
2013/10/15 21:14:33
nit: comment not really necessary. If you want mor
vadimt
2013/10/15 21:28:03
Done.
| |
| 454 var mergedCards = {}; | |
| 455 | |
| 456 for (var groupName in notificationGroups) | |
| 457 mergeGroup(mergedCards, notificationGroups[groupName]); | |
| 458 | |
| 459 showNotificationCards(mergedCards); | |
| 460 } | |
| 461 | |
| 462 /** | |
| 450 * Parses JSON response from the notification server, shows notifications and | 463 * Parses JSON response from the notification server, shows notifications and |
| 451 * schedules next update. | 464 * schedules next update. |
| 452 * @param {string} response Server response. | 465 * @param {string} response Server response. |
| 453 */ | 466 */ |
| 454 function parseAndShowNotificationCards(response) { | 467 function parseAndShowNotificationCards(response) { |
| 455 console.log('parseAndShowNotificationCards ' + response); | 468 console.log('parseAndShowNotificationCards ' + response); |
| 456 var parsedResponse = JSON.parse(response); | 469 var parsedResponse = JSON.parse(response); |
| 457 | 470 |
| 458 var groups = parsedResponse.groups; | 471 var receivedGroups = parsedResponse.groups; |
| 459 | 472 |
| 460 // Populate groups with corresponding cards. | 473 // Populate groups with corresponding cards. |
| 461 if (parsedResponse.notifications) { | 474 if (parsedResponse.notifications) { |
| 462 for (var i = 0; i != parsedResponse.notifications.length; ++i) { | 475 for (var i = 0; i != parsedResponse.notifications.length; ++i) { |
| 463 var card = parsedResponse.notifications[i]; | 476 var card = parsedResponse.notifications[i]; |
| 464 var group = groups[card.groupName]; | 477 var group = receivedGroups[card.groupName]; |
| 465 group.cards = group.cards || []; | 478 group.cards = group.cards || []; |
| 466 group.cards.push(card); | 479 group.cards.push(card); |
| 467 } | 480 } |
| 468 } | 481 } |
| 469 | 482 |
| 470 instrumented.storage.local.get('notificationGroups', function(items) { | 483 instrumented.storage.local.get('notificationGroups', function(items) { |
| 471 console.log('parseAndShowNotificationCards-get ' + JSON.stringify(items)); | 484 console.log('parseAndShowNotificationCards-get ' + JSON.stringify(items)); |
| 472 items = items || {}; | 485 items = items || {}; |
| 473 items.notificationGroups = items.notificationGroups || {}; | 486 items.notificationGroups = items.notificationGroups || {}; |
| 474 | 487 |
| 475 var now = Date.now(); | 488 var now = Date.now(); |
| 476 | 489 |
| 477 // Build updated set of groups and merge cards from all groups into one set. | 490 // Build updated set of groups. |
| 478 var updatedGroups = {}; | 491 for (var groupName in receivedGroups) { |
|
rgustafson
2013/10/15 21:14:33
For updatedGroups -> items.notificationGroups
Wha
vadimt
2013/10/15 21:28:03
Ah, looks like I forgot the protocol...
So, the se
rgustafson
2013/10/15 21:43:41
This is the joy of having extensibility which ther
vadimt
2013/10/15 22:05:36
Thanks; you've found a regression; I'm fixing it.
| |
| 479 var mergedCards = {}; | 492 var receivedGroup = receivedGroups[groupName]; |
| 480 | |
| 481 for (var groupName in groups) { | |
| 482 var receivedGroup = groups[groupName]; | |
| 483 var storageGroup = items.notificationGroups[groupName] || { | 493 var storageGroup = items.notificationGroups[groupName] || { |
| 484 cards: [], | 494 cards: [], |
| 485 cardsTimestamp: undefined, | 495 cardsTimestamp: undefined, |
| 486 nextPollTime: now, | 496 nextPollTime: now, |
| 487 rank: undefined | 497 rank: undefined |
| 488 }; | 498 }; |
| 489 | 499 |
| 490 if (receivedGroup.requested) | 500 if (receivedGroup.requested) |
| 491 receivedGroup.cards = receivedGroup.cards || []; | 501 receivedGroup.cards = receivedGroup.cards || []; |
| 492 | 502 |
| 493 if (receivedGroup.cards) { | 503 if (receivedGroup.cards) { |
| 494 storageGroup.cards = receivedGroup.cards; | 504 storageGroup.cards = receivedGroup.cards; |
| 495 storageGroup.cardsTimestamp = now; | 505 storageGroup.cardsTimestamp = now; |
| 496 storageGroup.rank = receivedGroup.rank; | 506 storageGroup.rank = receivedGroup.rank; |
| 497 } | 507 } |
| 498 | 508 |
| 499 if (receivedGroup.nextPollSeconds !== undefined) { | 509 if (receivedGroup.nextPollSeconds !== undefined) { |
| 500 storageGroup.nextPollTime = | 510 storageGroup.nextPollTime = |
| 501 now + receivedGroup.nextPollSeconds * MS_IN_SECOND; | 511 now + receivedGroup.nextPollSeconds * MS_IN_SECOND; |
| 502 } | 512 } |
| 503 | 513 |
| 504 updatedGroups[groupName] = storageGroup; | 514 items.notificationGroups[groupName] = storageGroup; |
| 505 | |
| 506 mergeGroup(mergedCards, storageGroup); | |
| 507 } | 515 } |
| 508 | 516 |
| 509 scheduleNextPoll(updatedGroups); | 517 scheduleNextPoll(items.notificationGroups); |
| 510 | 518 chrome.storage.local.set({notificationGroups: items.notificationGroups}); |
| 511 chrome.storage.local.set({notificationGroups: updatedGroups}); | 519 mergeAndShowNotificationCards(items.notificationGroups); |
| 512 | 520 recordEvent(GoogleNowEvent.CARDS_PARSE_SUCCESS); |
| 513 showNotificationCards(mergedCards); | |
| 514 }); | 521 }); |
| 515 } | 522 } |
| 516 | 523 |
| 517 /** | 524 /** |
| 518 * Requests notification cards from the server. | 525 * Requests notification cards from the server. |
| 519 * @param {Location} position Location of this computer. | 526 * @param {Location} position Location of this computer. |
| 520 */ | 527 */ |
| 521 function requestNotificationCards(position) { | 528 function requestNotificationCards(position) { |
| 522 console.log('requestNotificationCards ' + JSON.stringify(position) + | 529 console.log('requestNotificationCards ' + JSON.stringify(position) + |
| 523 ' from ' + NOTIFICATION_CARDS_URL); | 530 ' from ' + NOTIFICATION_CARDS_URL); |
| (...skipping 345 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 869 updateCardsAttempts.stop(); | 876 updateCardsAttempts.stop(); |
| 870 | 877 |
| 871 removeAllCards(); | 878 removeAllCards(); |
| 872 } | 879 } |
| 873 | 880 |
| 874 /** | 881 /** |
| 875 * Initializes the event page on install or on browser startup. | 882 * Initializes the event page on install or on browser startup. |
| 876 */ | 883 */ |
| 877 function initialize() { | 884 function initialize() { |
| 878 recordEvent(GoogleNowEvent.EXTENSION_START); | 885 recordEvent(GoogleNowEvent.EXTENSION_START); |
| 879 | |
| 880 // Alarms persist across chrome restarts. This is undesirable since it | |
| 881 // prevents us from starting up everything (alarms are a heuristic to | |
| 882 // determine if we are already running). To mitigate this, we will | |
| 883 // shut everything down on initialize before starting everything up. | |
| 884 stopPollingCards(); | |
| 885 onStateChange(); | 886 onStateChange(); |
| 886 } | 887 } |
| 887 | 888 |
| 888 /** | 889 /** |
| 889 * Starts or stops the polling of cards. | 890 * Starts or stops the polling of cards. |
| 890 * @param {boolean} shouldPollCardsRequest true to start and | 891 * @param {boolean} shouldPollCardsRequest true to start and |
| 891 * false to stop polling cards. | 892 * false to stop polling cards. |
| 892 */ | 893 */ |
| 893 function setShouldPollCards(shouldPollCardsRequest) { | 894 function setShouldPollCards(shouldPollCardsRequest) { |
| 894 updateCardsAttempts.isRunning(function(currentValue) { | 895 updateCardsAttempts.isRunning(function(currentValue) { |
| (...skipping 183 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 1078 | 1079 |
| 1079 instrumented.runtime.onInstalled.addListener(function(details) { | 1080 instrumented.runtime.onInstalled.addListener(function(details) { |
| 1080 console.log('onInstalled ' + JSON.stringify(details)); | 1081 console.log('onInstalled ' + JSON.stringify(details)); |
| 1081 if (details.reason != 'chrome_update') { | 1082 if (details.reason != 'chrome_update') { |
| 1082 initialize(); | 1083 initialize(); |
| 1083 } | 1084 } |
| 1084 }); | 1085 }); |
| 1085 | 1086 |
| 1086 instrumented.runtime.onStartup.addListener(function() { | 1087 instrumented.runtime.onStartup.addListener(function() { |
| 1087 console.log('onStartup'); | 1088 console.log('onStartup'); |
| 1089 | |
| 1090 // Show notifications received by earlier polls. Doing this as early as | |
| 1091 // possible to reduce latency of showing first notifications. This mimics how | |
| 1092 // persistent notifications will work. | |
| 1093 tasks.add(SHOW_ON_START_TASK_NAME, function() { | |
| 1094 instrumented.storage.local.get('notificationGroups', function(items) { | |
| 1095 console.log('onStartup-get ' + JSON.stringify(items)); | |
| 1096 items = items || {}; | |
| 1097 items.notificationGroups = items.notificationGroups || {}; | |
| 1098 | |
| 1099 mergeAndShowNotificationCards(items.notificationGroups); | |
| 1100 }); | |
| 1101 }); | |
| 1102 | |
| 1088 initialize(); | 1103 initialize(); |
| 1089 }); | 1104 }); |
| 1090 | 1105 |
| 1091 instrumented. | 1106 instrumented. |
| 1092 preferencesPrivate. | 1107 preferencesPrivate. |
| 1093 googleGeolocationAccessEnabled. | 1108 googleGeolocationAccessEnabled. |
| 1094 onChange. | 1109 onChange. |
| 1095 addListener(function(prefValue) { | 1110 addListener(function(prefValue) { |
| 1096 console.log('googleGeolocationAccessEnabled Pref onChange ' + | 1111 console.log('googleGeolocationAccessEnabled Pref onChange ' + |
| 1097 prefValue.value); | 1112 prefValue.value); |
| (...skipping 32 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 1130 | 1145 |
| 1131 instrumented.location.onLocationUpdate.addListener(function(position) { | 1146 instrumented.location.onLocationUpdate.addListener(function(position) { |
| 1132 recordEvent(GoogleNowEvent.LOCATION_UPDATE); | 1147 recordEvent(GoogleNowEvent.LOCATION_UPDATE); |
| 1133 updateNotificationsCards(position); | 1148 updateNotificationsCards(position); |
| 1134 }); | 1149 }); |
| 1135 | 1150 |
| 1136 instrumented.omnibox.onInputEntered.addListener(function(text) { | 1151 instrumented.omnibox.onInputEntered.addListener(function(text) { |
| 1137 localStorage['server_url'] = NOTIFICATION_CARDS_URL = text; | 1152 localStorage['server_url'] = NOTIFICATION_CARDS_URL = text; |
| 1138 initialize(); | 1153 initialize(); |
| 1139 }); | 1154 }); |
| OLD | NEW |