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): Use background permission to show notifications even when all | 20 // TODO(vadimt): Use background permission to show notifications even when all |
| 21 // browser windows are closed. | 21 // browser windows are closed. |
| 22 // TODO(vadimt): Decide what to do in incognito mode. | 22 // TODO(vadimt): Decide what to do in incognito mode. |
| 23 // TODO(vadimt): Honor the flag the enables Google Now integration. | 23 // TODO(vadimt): Honor the flag the enables Google Now integration. |
| 24 // TODO(vadimt): Figure out the final values of the constants. | 24 // TODO(vadimt): Figure out the final values of the constants. |
| 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 malformed server responses. |
| 27 // malformed server responses. | |
| 28 | 27 |
| 29 /** | 28 /** |
| 30 * Standard response code for successful HTTP requests. This is the only success | 29 * Standard response code for successful HTTP requests. This is the only success |
| 31 * code the server will send. | 30 * code the server will send. |
| 32 */ | 31 */ |
| 33 var HTTP_OK = 200; | 32 var HTTP_OK = 200; |
| 34 | 33 |
| 35 var HTTP_BAD_REQUEST = 400; | 34 var HTTP_BAD_REQUEST = 400; |
| 36 var HTTP_UNAUTHORIZED = 401; | 35 var HTTP_UNAUTHORIZED = 401; |
| 37 var HTTP_FORBIDDEN = 403; | 36 var HTTP_FORBIDDEN = 403; |
| (...skipping 213 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 251 if (typeof parsedResponse.next_poll_seconds != 'number') { | 250 if (typeof parsedResponse.next_poll_seconds != 'number') { |
| 252 callback(); | 251 callback(); |
| 253 return; | 252 return; |
| 254 } | 253 } |
| 255 | 254 |
| 256 tasks.debugSetStepName('parseAndShowNotificationCards-storage-get'); | 255 tasks.debugSetStepName('parseAndShowNotificationCards-storage-get'); |
| 257 instrumented.storage.local.get(['notificationsData', 'recentDismissals'], | 256 instrumented.storage.local.get(['notificationsData', 'recentDismissals'], |
| 258 function(items) { | 257 function(items) { |
| 259 console.log('parseAndShowNotificationCards-get ' + | 258 console.log('parseAndShowNotificationCards-get ' + |
| 260 JSON.stringify(items)); | 259 JSON.stringify(items)); |
| 261 items.notificationsData = items.notificationsData || {}; | 260 items.notificationsData = items.notificationsData || {}; |
|
robliao
2013/08/22 00:01:56
Is a check here necessary too?
vadimt
2013/08/22 01:24:28
Yes, but this would cause conflicts with other CLs
| |
| 262 items.recentDismissals = items.recentDismissals || {}; | 261 items.recentDismissals = items.recentDismissals || {}; |
| 263 | 262 |
| 264 tasks.debugSetStepName( | 263 tasks.debugSetStepName( |
| 265 'parseAndShowNotificationCards-notifications-getAll'); | 264 'parseAndShowNotificationCards-notifications-getAll'); |
| 266 instrumented.notifications.getAll(function(notifications) { | 265 instrumented.notifications.getAll(function(notifications) { |
| 267 console.log('parseAndShowNotificationCards-getAll ' + | 266 console.log('parseAndShowNotificationCards-getAll ' + |
| 268 JSON.stringify(notifications)); | 267 JSON.stringify(notifications)); |
| 269 // TODO(vadimt): Figure out what to do when notifications are | 268 // TODO(vadimt): Figure out what to do when notifications are |
| 270 // disabled for our extension. | 269 // disabled for our extension. |
| 271 notifications = notifications || {}; | 270 notifications = notifications || {}; |
| (...skipping 67 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 339 * For example, this occurs when the geolocation preference is unchecked in the | 338 * For example, this occurs when the geolocation preference is unchecked in the |
| 340 * content settings. | 339 * content settings. |
| 341 */ | 340 */ |
| 342 function removeAllCards() { | 341 function removeAllCards() { |
| 343 console.log('removeAllCards'); | 342 console.log('removeAllCards'); |
| 344 | 343 |
| 345 // TODO(robliao): Once Google Now clears its own checkbox in the | 344 // TODO(robliao): Once Google Now clears its own checkbox in the |
| 346 // notifications center and bug 260376 is fixed, the below clearing | 345 // notifications center and bug 260376 is fixed, the below clearing |
| 347 // code is no longer necessary. | 346 // code is no longer necessary. |
| 348 instrumented.notifications.getAll(function(notifications) { | 347 instrumented.notifications.getAll(function(notifications) { |
| 348 notifications = notifications || {}; | |
| 349 for (var notificationId in notifications) { | 349 for (var notificationId in notifications) { |
| 350 chrome.notifications.clear(notificationId, function() {}); | 350 chrome.notifications.clear(notificationId, function() {}); |
| 351 } | 351 } |
| 352 chrome.storage.local.set({notificationsData: {}}); | 352 chrome.storage.local.set({notificationsData: {}}); |
| 353 }); | 353 }); |
| 354 } | 354 } |
| 355 | 355 |
| 356 /** | 356 /** |
| 357 * Requests notification cards from the server. | 357 * Requests notification cards from the server. |
| 358 * @param {Location} position Location of this computer. | 358 * @param {Location} position Location of this computer. |
| (...skipping 39 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 398 }); | 398 }); |
| 399 } | 399 } |
| 400 | 400 |
| 401 /** | 401 /** |
| 402 * Starts getting location for a cards update. | 402 * Starts getting location for a cards update. |
| 403 */ | 403 */ |
| 404 function requestLocation() { | 404 function requestLocation() { |
| 405 console.log('requestLocation'); | 405 console.log('requestLocation'); |
| 406 recordEvent(GoogleNowEvent.LOCATION_REQUEST); | 406 recordEvent(GoogleNowEvent.LOCATION_REQUEST); |
| 407 // TODO(vadimt): Figure out location request options. | 407 // TODO(vadimt): Figure out location request options. |
| 408 chrome.metricsPrivate.getVariationParams('GoogleNow', function(params) { | 408 instrumented.metricsPrivate.getVariationParams('GoogleNow', function(params) { |
| 409 var minDistanceInMeters = | 409 var minDistanceInMeters = |
| 410 parseInt(params && params.minDistanceInMeters, 10) || | 410 parseInt(params && params.minDistanceInMeters, 10) || |
| 411 100; | 411 100; |
| 412 var minTimeInMilliseconds = | 412 var minTimeInMilliseconds = |
| 413 parseInt(params && params.minTimeInMilliseconds, 10) || | 413 parseInt(params && params.minTimeInMilliseconds, 10) || |
| 414 180000; // 3 minutes. | 414 180000; // 3 minutes. |
| 415 | 415 |
| 416 chrome.location.watchLocation(LOCATION_WATCH_NAME, { | 416 chrome.location.watchLocation(LOCATION_WATCH_NAME, { |
| 417 minDistanceInMeters: minDistanceInMeters, | 417 minDistanceInMeters: minDistanceInMeters, |
| 418 minTimeInMilliseconds: minTimeInMilliseconds | 418 minTimeInMilliseconds: minTimeInMilliseconds |
| (...skipping 92 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 511 * Tries to send dismiss requests for all pending dismissals. | 511 * Tries to send dismiss requests for all pending dismissals. |
| 512 * @param {function(boolean)} callbackBoolean Completion callback with 'success' | 512 * @param {function(boolean)} callbackBoolean Completion callback with 'success' |
| 513 * parameter. Success means that no pending dismissals are left. | 513 * parameter. Success means that no pending dismissals are left. |
| 514 */ | 514 */ |
| 515 function processPendingDismissals(callbackBoolean) { | 515 function processPendingDismissals(callbackBoolean) { |
| 516 tasks.debugSetStepName('processPendingDismissals-storage-get'); | 516 tasks.debugSetStepName('processPendingDismissals-storage-get'); |
| 517 instrumented.storage.local.get(['pendingDismissals', 'recentDismissals'], | 517 instrumented.storage.local.get(['pendingDismissals', 'recentDismissals'], |
| 518 function(items) { | 518 function(items) { |
| 519 console.log('processPendingDismissals-storage-get ' + | 519 console.log('processPendingDismissals-storage-get ' + |
| 520 JSON.stringify(items)); | 520 JSON.stringify(items)); |
| 521 items.pendingDismissals = items.pendingDismissals || []; | 521 items.pendingDismissals = items.pendingDismissals || []; |
|
robliao
2013/08/22 00:01:56
Should we check items here?
vadimt
2013/08/22 01:24:28
Conflicts, see above.
| |
| 522 items.recentDismissals = items.recentDismissals || {}; | 522 items.recentDismissals = items.recentDismissals || {}; |
| 523 | 523 |
| 524 var dismissalsChanged = false; | 524 var dismissalsChanged = false; |
| 525 | 525 |
| 526 function onFinish(success) { | 526 function onFinish(success) { |
| 527 if (dismissalsChanged) { | 527 if (dismissalsChanged) { |
| 528 chrome.storage.local.set({ | 528 chrome.storage.local.set({ |
| 529 pendingDismissals: items.pendingDismissals, | 529 pendingDismissals: items.pendingDismissals, |
| 530 recentDismissals: items.recentDismissals | 530 recentDismissals: items.recentDismissals |
| 531 }); | 531 }); |
| (...skipping 43 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 575 } | 575 } |
| 576 | 576 |
| 577 /** | 577 /** |
| 578 * Opens URL corresponding to the clicked part of the notification. | 578 * Opens URL corresponding to the clicked part of the notification. |
| 579 * @param {string} notificationId Unique identifier of the notification. | 579 * @param {string} notificationId Unique identifier of the notification. |
| 580 * @param {function(Object): string} selector Function that extracts the url for | 580 * @param {function(Object): string} selector Function that extracts the url for |
| 581 * the clicked area from the button action URLs info. | 581 * the clicked area from the button action URLs info. |
| 582 */ | 582 */ |
| 583 function onNotificationClicked(notificationId, selector) { | 583 function onNotificationClicked(notificationId, selector) { |
| 584 instrumented.storage.local.get('notificationsData', function(items) { | 584 instrumented.storage.local.get('notificationsData', function(items) { |
| 585 items.notificationsData = items.notificationsData || {}; | 585 if (!items || !items.notificationsData) |
| 586 return; | |
| 586 | 587 |
| 587 var notificationData = items.notificationsData[notificationId]; | 588 var notificationData = items.notificationsData[notificationId]; |
|
skare_
2013/08/21 23:12:32
no action required but consider
var nd = items &&
rgustafson
2013/08/22 00:46:14
+1, will return below anyways.
vadimt
2013/08/22 01:24:28
Done.
| |
| 588 | 589 |
| 589 if (!notificationData) { | 590 if (!notificationData) { |
| 590 // 'notificationsData' in storage may not match the actual list of | 591 // 'notificationsData' in storage may not match the actual list of |
| 591 // notifications. | 592 // notifications. |
| 592 return; | 593 return; |
| 593 } | 594 } |
| 594 | 595 |
| 595 var actionUrls = notificationData.actionUrls; | 596 var actionUrls = notificationData.actionUrls; |
| 596 if (typeof actionUrls != 'object') { | 597 if (typeof actionUrls != 'object') { |
| 597 return; | 598 return; |
| 598 } | 599 } |
| 599 | 600 |
| 600 var url = selector(actionUrls); | 601 var url = selector(actionUrls); |
| 601 | 602 if (!url) |
|
rgustafson
2013/08/22 00:46:14
This is verified already in the selector. if state
vadimt
2013/08/22 01:24:28
Not in selector in 'instrumented.notifications.onC
| |
| 602 if (typeof url != 'string') | |
| 603 return; | 603 return; |
| 604 | 604 |
| 605 instrumented.tabs.create({url: url}, function(tab) { | 605 instrumented.tabs.create({url: url}, function(tab) { |
| 606 if (tab) | 606 if (tab) |
| 607 chrome.windows.update(tab.windowId, {focused: true}); | 607 chrome.windows.update(tab.windowId, {focused: true}); |
| 608 else | 608 else |
| 609 chrome.windows.create({url: url, focused: true}); | 609 chrome.windows.create({url: url, focused: true}); |
| 610 }); | 610 }); |
| 611 }); | 611 }); |
| 612 } | 612 } |
| (...skipping 39 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 652 tasks.add(DISMISS_CARD_TASK_NAME, function(callback) { | 652 tasks.add(DISMISS_CARD_TASK_NAME, function(callback) { |
| 653 dismissalAttempts.start(); | 653 dismissalAttempts.start(); |
| 654 | 654 |
| 655 // Deleting the notification in case it was re-added while this task was | 655 // Deleting the notification in case it was re-added while this task was |
| 656 // scheduled, waiting for execution. | 656 // scheduled, waiting for execution. |
| 657 cardSet.clear(notificationId); | 657 cardSet.clear(notificationId); |
| 658 | 658 |
| 659 tasks.debugSetStepName('onNotificationClosed-storage-get'); | 659 tasks.debugSetStepName('onNotificationClosed-storage-get'); |
| 660 instrumented.storage.local.get(['pendingDismissals', 'notificationsData'], | 660 instrumented.storage.local.get(['pendingDismissals', 'notificationsData'], |
| 661 function(items) { | 661 function(items) { |
| 662 items.pendingDismissals = items.pendingDismissals || []; | 662 items.pendingDismissals = items.pendingDismissals || []; |
|
robliao
2013/08/22 00:01:56
Check?
vadimt
2013/08/22 01:24:28
Conflicts.
| |
| 663 items.notificationsData = items.notificationsData || {}; | 663 items.notificationsData = items.notificationsData || {}; |
| 664 | 664 |
| 665 var notificationData = items.notificationsData[notificationId]; | 665 var notificationData = items.notificationsData[notificationId]; |
| 666 | 666 |
| 667 var dismissal = { | 667 var dismissal = { |
| 668 notificationId: notificationId, | 668 notificationId: notificationId, |
| 669 time: Date.now(), | 669 time: Date.now(), |
| 670 parameters: notificationData && notificationData.dismissalParameters | 670 parameters: notificationData && notificationData.dismissalParameters |
| 671 }; | 671 }; |
| 672 items.pendingDismissals.push(dismissal); | 672 items.pendingDismissals.push(dismissal); |
| (...skipping 200 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 873 'GoogleNow', | 873 'GoogleNow', |
| 874 function(response) { | 874 function(response) { |
| 875 var enableBackground = | 875 var enableBackground = |
| 876 (!response || (response.enableBackground != 'false')); | 876 (!response || (response.enableBackground != 'false')); |
| 877 tasks.debugSetStepName( | 877 tasks.debugSetStepName( |
| 878 'onStateChange-get-googleGeolocationAccessEnabledPref'); | 878 'onStateChange-get-googleGeolocationAccessEnabledPref'); |
| 879 instrumented. | 879 instrumented. |
| 880 preferencesPrivate. | 880 preferencesPrivate. |
| 881 googleGeolocationAccessEnabled. | 881 googleGeolocationAccessEnabled. |
| 882 get({}, function(prefValue) { | 882 get({}, function(prefValue) { |
| 883 var geolocationEnabled = !!prefValue.value; | 883 var geolocationEnabled = !!prefValue.value; |
|
robliao
2013/08/22 00:01:56
Is any Chrome API subject to returning undefined?
vadimt
2013/08/22 01:24:28
No. Only those that are explicitly implemented to
robliao
2013/08/22 18:01:13
As written, it cannot fail.
On 2013/08/22 01:24:28
| |
| 884 tasks.debugSetStepName( | 884 tasks.debugSetStepName( |
| 885 'onStateChange-get-userRespondedToToast'); | 885 'onStateChange-get-userRespondedToToast'); |
| 886 instrumented.storage.local.get( | 886 instrumented.storage.local.get( |
| 887 'userRespondedToToast', | 887 'userRespondedToToast', |
| 888 function(items) { | 888 function(items) { |
| 889 var userRespondedToToast = !!items.userRespondedToToast; | 889 var userRespondedToToast = |
| 890 !items || !!items.userRespondedToToast; | |
|
rgustafson
2013/08/22 00:46:14
Could this get the user in a weird state if there
vadimt
2013/08/22 01:24:28
Deep comment again. If we are polling, the final s
rgustafson
2013/08/22 19:07:32
We'll hide the toast and disable ourselves for jus
vadimt
2013/08/22 19:34:55
https://code.google.com/p/chromium/issues/detail?i
| |
| 890 updateRunningState( | 891 updateRunningState( |
| 891 signedIn, | 892 signedIn, |
| 892 geolocationEnabled, | 893 geolocationEnabled, |
| 893 userRespondedToToast, | 894 userRespondedToToast, |
| 894 enableBackground, | 895 enableBackground, |
| 895 callback); | 896 callback); |
| 896 }); | 897 }); |
| 897 }); | 898 }); |
| 898 }); | 899 }); |
| 899 }); | 900 }); |
| (...skipping 67 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 967 }); | 968 }); |
| 968 | 969 |
| 969 instrumented.notifications.onButtonClicked.addListener( | 970 instrumented.notifications.onButtonClicked.addListener( |
| 970 function(notificationId, buttonIndex) { | 971 function(notificationId, buttonIndex) { |
| 971 if (notificationId == WELCOME_TOAST_NOTIFICATION_ID) { | 972 if (notificationId == WELCOME_TOAST_NOTIFICATION_ID) { |
| 972 onToastNotificationClicked(buttonIndex); | 973 onToastNotificationClicked(buttonIndex); |
| 973 } else { | 974 } else { |
| 974 chrome.metricsPrivate.recordUserAction( | 975 chrome.metricsPrivate.recordUserAction( |
| 975 'GoogleNow.ButtonClicked' + buttonIndex); | 976 'GoogleNow.ButtonClicked' + buttonIndex); |
| 976 onNotificationClicked(notificationId, function(actionUrls) { | 977 onNotificationClicked(notificationId, function(actionUrls) { |
| 977 if (!Array.isArray(actionUrls.buttonUrls)) | 978 verify(actionUrls.buttonUrls[buttonIndex], |
|
rgustafson
2013/08/22 00:46:14
pull actionUrls.buttonUrls[buttonIndex] out into u
vadimt
2013/08/22 01:24:28
Done.
| |
| 978 return undefined; | 979 'onButtonClicked: no url for a button'); |
| 979 | |
| 980 return actionUrls.buttonUrls[buttonIndex]; | 980 return actionUrls.buttonUrls[buttonIndex]; |
| 981 }); | 981 }); |
| 982 } | 982 } |
| 983 }); | 983 }); |
| 984 | 984 |
| 985 instrumented.notifications.onClosed.addListener(onNotificationClosed); | 985 instrumented.notifications.onClosed.addListener(onNotificationClosed); |
| 986 | 986 |
| 987 instrumented.location.onLocationUpdate.addListener(function(position) { | 987 instrumented.location.onLocationUpdate.addListener(function(position) { |
| 988 recordEvent(GoogleNowEvent.LOCATION_UPDATE); | 988 recordEvent(GoogleNowEvent.LOCATION_UPDATE); |
| 989 updateNotificationsCards(position); | 989 updateNotificationsCards(position); |
| 990 }); | 990 }); |
| 991 | 991 |
| 992 instrumented.omnibox.onInputEntered.addListener(function(text) { | 992 instrumented.omnibox.onInputEntered.addListener(function(text) { |
| 993 localStorage['server_url'] = NOTIFICATION_CARDS_URL = text; | 993 localStorage['server_url'] = NOTIFICATION_CARDS_URL = text; |
| 994 initialize(); | 994 initialize(); |
| 995 }); | 995 }); |
| OLD | NEW |