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 stored notification groups into a set of Chrome notifications and | |
| 449 * shows them. | |
| 450 */ | |
| 451 function mergeAndShowNotificationCards() { | |
| 452 instrumented.storage.local.get('notificationGroups', function(items) { | |
| 453 console.log('mergeAndShowNotificationCards-get ' + JSON.stringify(items)); | |
| 454 items = items || {}; | |
| 455 items.notificationGroups = items.notificationGroups || {}; | |
| 456 | |
| 457 // Merge cards from all groups into one set. | |
| 458 var mergedCards = {}; | |
| 459 | |
| 460 for (var groupName in items.notificationGroups) | |
| 461 mergeGroup(mergedCards, items.notificationGroups[groupName]); | |
| 462 | |
| 463 showNotificationCards(mergedCards); | |
| 464 }); | |
| 465 } | |
| 466 | |
| 467 /** | |
| 450 * Parses JSON response from the notification server, shows notifications and | 468 * Parses JSON response from the notification server, shows notifications and |
| 451 * schedules next update. | 469 * schedules next update. |
| 452 * @param {string} response Server response. | 470 * @param {string} response Server response. |
| 453 */ | 471 */ |
| 454 function parseAndShowNotificationCards(response) { | 472 function parseAndShowNotificationCards(response) { |
| 455 console.log('parseAndShowNotificationCards ' + response); | 473 console.log('parseAndShowNotificationCards ' + response); |
| 456 var parsedResponse = JSON.parse(response); | 474 var parsedResponse = JSON.parse(response); |
| 457 | 475 |
| 458 var groups = parsedResponse.groups; | 476 var receivedGroups = parsedResponse.groups; |
| 459 | 477 |
| 460 // Populate groups with corresponding cards. | 478 // Populate groups with corresponding cards. |
| 461 if (parsedResponse.notifications) { | 479 if (parsedResponse.notifications) { |
| 462 for (var i = 0; i != parsedResponse.notifications.length; ++i) { | 480 for (var i = 0; i != parsedResponse.notifications.length; ++i) { |
| 463 var card = parsedResponse.notifications[i]; | 481 var card = parsedResponse.notifications[i]; |
| 464 var group = groups[card.groupName]; | 482 var group = receivedGroups[card.groupName]; |
| 465 group.cards = group.cards || []; | 483 group.cards = group.cards || []; |
| 466 group.cards.push(card); | 484 group.cards.push(card); |
| 467 } | 485 } |
| 468 } | 486 } |
| 469 | 487 |
| 470 instrumented.storage.local.get('notificationGroups', function(items) { | 488 instrumented.storage.local.get('notificationGroups', function(items) { |
| 471 console.log('parseAndShowNotificationCards-get ' + JSON.stringify(items)); | 489 console.log('parseAndShowNotificationCards-get ' + JSON.stringify(items)); |
| 472 items = items || {}; | 490 items = items || {}; |
| 473 items.notificationGroups = items.notificationGroups || {}; | 491 items.notificationGroups = items.notificationGroups || {}; |
| 474 | 492 |
| 475 var now = Date.now(); | 493 var now = Date.now(); |
| 476 | 494 |
| 477 // Build updated set of groups and merge cards from all groups into one set. | 495 // Build updated set of groups. |
| 478 var updatedGroups = {}; | 496 for (var groupName in receivedGroups) { |
| 479 var mergedCards = {}; | 497 var receivedGroup = receivedGroups[groupName]; |
| 480 | |
| 481 for (var groupName in groups) { | |
| 482 var receivedGroup = groups[groupName]; | |
| 483 var storageGroup = items.notificationGroups[groupName] || { | 498 var storageGroup = items.notificationGroups[groupName] || { |
| 484 cards: [], | 499 cards: [], |
| 485 cardsTimestamp: undefined, | 500 cardsTimestamp: undefined, |
| 486 nextPollTime: now, | 501 nextPollTime: now, |
| 487 rank: undefined | 502 rank: undefined |
| 488 }; | 503 }; |
| 489 | 504 |
| 490 if (receivedGroup.requested) | 505 if (receivedGroup.requested) |
| 491 receivedGroup.cards = receivedGroup.cards || []; | 506 receivedGroup.cards = receivedGroup.cards || []; |
| 492 | 507 |
| 493 if (receivedGroup.cards) { | 508 if (receivedGroup.cards) { |
| 494 storageGroup.cards = receivedGroup.cards; | 509 storageGroup.cards = receivedGroup.cards; |
| 495 storageGroup.cardsTimestamp = now; | 510 storageGroup.cardsTimestamp = now; |
| 496 storageGroup.rank = receivedGroup.rank; | 511 storageGroup.rank = receivedGroup.rank; |
| 497 } | 512 } |
| 498 | 513 |
| 499 if (receivedGroup.nextPollSeconds !== undefined) { | 514 if (receivedGroup.nextPollSeconds !== undefined) { |
| 500 storageGroup.nextPollTime = | 515 storageGroup.nextPollTime = |
| 501 now + receivedGroup.nextPollSeconds * MS_IN_SECOND; | 516 now + receivedGroup.nextPollSeconds * MS_IN_SECOND; |
| 502 } | 517 } |
| 503 | 518 |
| 504 updatedGroups[groupName] = storageGroup; | 519 items.notificationGroups[groupName] = storageGroup; |
| 505 | |
| 506 mergeGroup(mergedCards, storageGroup); | |
| 507 } | 520 } |
| 508 | 521 |
| 509 scheduleNextPoll(updatedGroups); | 522 scheduleNextPoll(items.notificationGroups); |
| 510 | 523 chrome.storage.local.set({notificationGroups: items.notificationGroups}); |
| 511 chrome.storage.local.set({notificationGroups: updatedGroups}); | 524 mergeAndShowNotificationCards(); |
|
robliao
2013/10/14 21:05:31
Seems like overkill to set notificationGroups and
vadimt
2013/10/14 23:51:57
I'd not do serious code refactorings, assuming tha
robliao
2013/10/14 23:56:08
This rearrangement only affects new code, namely m
vadimt
2013/10/15 01:09:44
Done.
| |
| 512 | 525 recordEvent(GoogleNowEvent.CARDS_PARSE_SUCCESS); |
| 513 showNotificationCards(mergedCards); | |
| 514 }); | 526 }); |
| 515 } | 527 } |
| 516 | 528 |
| 517 /** | 529 /** |
| 518 * Requests notification cards from the server. | 530 * Requests notification cards from the server. |
| 519 * @param {Location} position Location of this computer. | 531 * @param {Location} position Location of this computer. |
| 520 */ | 532 */ |
| 521 function requestNotificationCards(position) { | 533 function requestNotificationCards(position) { |
| 522 console.log('requestNotificationCards ' + JSON.stringify(position) + | 534 console.log('requestNotificationCards ' + JSON.stringify(position) + |
| 523 ' from ' + NOTIFICATION_CARDS_URL); | 535 ' from ' + NOTIFICATION_CARDS_URL); |
| (...skipping 345 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 869 updateCardsAttempts.stop(); | 881 updateCardsAttempts.stop(); |
| 870 | 882 |
| 871 removeAllCards(); | 883 removeAllCards(); |
| 872 } | 884 } |
| 873 | 885 |
| 874 /** | 886 /** |
| 875 * Initializes the event page on install or on browser startup. | 887 * Initializes the event page on install or on browser startup. |
| 876 */ | 888 */ |
| 877 function initialize() { | 889 function initialize() { |
| 878 recordEvent(GoogleNowEvent.EXTENSION_START); | 890 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(); | 891 onStateChange(); |
| 886 } | 892 } |
| 887 | 893 |
| 888 /** | 894 /** |
| 889 * Starts or stops the polling of cards. | 895 * Starts or stops the polling of cards. |
| 890 * @param {boolean} shouldPollCardsRequest true to start and | 896 * @param {boolean} shouldPollCardsRequest true to start and |
| 891 * false to stop polling cards. | 897 * false to stop polling cards. |
| 892 */ | 898 */ |
| 893 function setShouldPollCards(shouldPollCardsRequest) { | 899 function setShouldPollCards(shouldPollCardsRequest) { |
| 894 updateCardsAttempts.isRunning(function(currentValue) { | 900 updateCardsAttempts.isRunning(function(currentValue) { |
| (...skipping 183 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 1078 | 1084 |
| 1079 instrumented.runtime.onInstalled.addListener(function(details) { | 1085 instrumented.runtime.onInstalled.addListener(function(details) { |
| 1080 console.log('onInstalled ' + JSON.stringify(details)); | 1086 console.log('onInstalled ' + JSON.stringify(details)); |
| 1081 if (details.reason != 'chrome_update') { | 1087 if (details.reason != 'chrome_update') { |
| 1082 initialize(); | 1088 initialize(); |
| 1083 } | 1089 } |
| 1084 }); | 1090 }); |
| 1085 | 1091 |
| 1086 instrumented.runtime.onStartup.addListener(function() { | 1092 instrumented.runtime.onStartup.addListener(function() { |
| 1087 console.log('onStartup'); | 1093 console.log('onStartup'); |
| 1094 | |
| 1095 // Show notifications received by earlier polls. | |
| 1096 tasks.add(SHOW_ON_START_TASK_NAME, mergeAndShowNotificationCards); | |
|
robliao
2013/10/14 21:05:31
This should occur after initialize.
vadimt
2013/10/14 23:51:57
Why? Imagine that persistent notifications are imp
robliao
2013/10/14 23:56:08
It is non-obvious to an outside reader why anythin
vadimt
2013/10/15 01:09:44
Done.
| |
| 1097 | |
| 1088 initialize(); | 1098 initialize(); |
| 1089 }); | 1099 }); |
| 1090 | 1100 |
| 1091 instrumented. | 1101 instrumented. |
| 1092 preferencesPrivate. | 1102 preferencesPrivate. |
| 1093 googleGeolocationAccessEnabled. | 1103 googleGeolocationAccessEnabled. |
| 1094 onChange. | 1104 onChange. |
| 1095 addListener(function(prefValue) { | 1105 addListener(function(prefValue) { |
| 1096 console.log('googleGeolocationAccessEnabled Pref onChange ' + | 1106 console.log('googleGeolocationAccessEnabled Pref onChange ' + |
| 1097 prefValue.value); | 1107 prefValue.value); |
| (...skipping 32 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 1130 | 1140 |
| 1131 instrumented.location.onLocationUpdate.addListener(function(position) { | 1141 instrumented.location.onLocationUpdate.addListener(function(position) { |
| 1132 recordEvent(GoogleNowEvent.LOCATION_UPDATE); | 1142 recordEvent(GoogleNowEvent.LOCATION_UPDATE); |
| 1133 updateNotificationsCards(position); | 1143 updateNotificationsCards(position); |
| 1134 }); | 1144 }); |
| 1135 | 1145 |
| 1136 instrumented.omnibox.onInputEntered.addListener(function(text) { | 1146 instrumented.omnibox.onInputEntered.addListener(function(text) { |
| 1137 localStorage['server_url'] = NOTIFICATION_CARDS_URL = text; | 1147 localStorage['server_url'] = NOTIFICATION_CARDS_URL = text; |
| 1138 initialize(); | 1148 initialize(); |
| 1139 }); | 1149 }); |
| OLD | NEW |