Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(760)

Unified Diff: chrome/browser/resources/google_now/background.js

Issue 12316075: Preventing race conditions in Google Now extension (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src
Patch Set: Rebase/switch to notifications.clear Created 7 years, 10 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
« no previous file with comments | « no previous file | no next file » | no next file with comments »
Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
Index: chrome/browser/resources/google_now/background.js
diff --git a/chrome/browser/resources/google_now/background.js b/chrome/browser/resources/google_now/background.js
index 78f647d503e8838c397035ffc0ffb832bdb6b289..93c05165a79d32bbe60524cefe521e93eccb1708 100644
--- a/chrome/browser/resources/google_now/background.js
+++ b/chrome/browser/resources/google_now/background.js
@@ -1,11 +1,11 @@
-// Copyright (c) 2013 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-'use strict';
-
-/**
- * @fileoverview The event page for Google Now for Chrome implementation.
+// Copyright (c) 2013 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+'use strict';
+
+/**
+ * @fileoverview The event page for Google Now for Chrome implementation.
* The Google Now event page gets Google Now cards from the server and shows
* them as Chrome notifications.
* The service performs periodic updating of Google Now cards.
@@ -13,46 +13,222 @@
* 1. Obtaining the location of the machine;
* 2. Making a server request based on that location;
* 3. Showing the received cards as notifications.
- */
-
-// TODO(vadimt): Use background permission to show notifications even when all
-// browser windows are closed.
-// TODO(vadimt): Remove the C++ implementation.
-// TODO(vadimt): Decide what to do in incognito mode.
-// TODO(vadimt): Gather UMAs.
-// TODO(vadimt): Honor the flag the enables Google Now integration.
-// TODO(vadimt): Figure out the final values of the constants.
-// TODO(vadimt): Report errors to the user.
-
-// TODO(vadimt): Figure out the server name. Use it in the manifest and for
-// TODO(vadimt): Consider processing errors for all storage.set calls.
-// NOTIFICATION_CARDS_URL. Meanwhile, to use the feature, you need to manually
-// edit NOTIFICATION_CARDS_URL before building Chrome.
+ */
+
+// TODO(vadimt): Use background permission to show notifications even when all
+// browser windows are closed.
+// TODO(vadimt): Remove the C++ implementation.
+// TODO(vadimt): Decide what to do in incognito mode.
+// TODO(vadimt): Gather UMAs.
+// TODO(vadimt): Honor the flag the enables Google Now integration.
+// TODO(vadimt): Figure out the final values of the constants.
+// TODO(vadimt): Report errors to the user.
+
+// TODO(vadimt): Figure out the server name. Use it in the manifest and for
+// NOTIFICATION_CARDS_URL. Meanwhile, to use the feature, you need to manually
+// edit NOTIFICATION_CARDS_URL before building Chrome.
+// TODO(vadimt): Consider processing errors for all storage.set calls.
+
/**
* URL to retrieve notification cards.
- */
-var NOTIFICATION_CARDS_URL = '';
-
+ */
+var NOTIFICATION_CARDS_URL = '';
+
/**
- * Standard response code for successful HTTP requests. This is the only success
- * code the server will send.
- */
-var HTTP_OK = 200;
-
+ * Standard response code for successful HTTP requests. This is the only success
+ * code the server will send.
+ */
+var HTTP_OK = 200;
+
/**
- * Initial period for polling for Google Now Notifications cards to use when the
- * period from the server is not available.
- */
-var INITIAL_POLLING_PERIOD_SECONDS = 300; // 5 minutes
-
+ * Initial period for polling for Google Now Notifications cards to use when the
+ * period from the server is not available.
+ */
+var INITIAL_POLLING_PERIOD_SECONDS = 300; // 5 minutes
+
/**
- * Maximal period for polling for Google Now Notifications cards to use when the
- * period from the server is not available.
- */
-var MAXIMUM_POLLING_PERIOD_SECONDS = 3600; // 1 hour
-
-var storage = chrome.storage.local;
-
+ * Maximal period for polling for Google Now Notifications cards to use when the
+ * period from the server is not available.
+ */
+var MAXIMUM_POLLING_PERIOD_SECONDS = 3600; // 1 hour
+
+/**
+ * Names for tasks that can be created by the extension.
+ * @enum {string}
+ */
+var TaskName = {
+ UPDATE_CARDS: 'update-cards',
+ DISMISS_CARD: 'dismiss-card',
+ CARD_CLICKED: 'card-clicked'
+};
+
+var UPDATE_NOTIFICATIONS_ALARM_NAME = 'UPDATE';
+
+var storage = chrome.storage.local;
+
+/////////////////////////////////////////////////////////////////////////
+////// ABSOLUTELY NOT FORGET TO MOVE ASSERT AND TASK MANAGER INTO SEPARATE FILE
+////// IN THIS CL !!!!!!!!!!!!!
+////////////////////////////////////////////////////////////////////////
+
+/**
+ * Check for internal errors.
+ * @param {boolean} condition Condition that must be true.
+ * @param {string} message Diagnostic message for the case when the condition is
+ * false.
+ */
+function assert(condition, message) {
+ // TODO(vadimt): Send UMAs instead of showing alert.
+ if (!condition) {
+ var errorText = 'ASSERT: ' + message;
+ console.error(errorText);
+ alert(errorText);
skare_ 2013/03/02 00:07:05 console.assert()?
skare_ 2013/03/02 00:07:05 I didn't think alert() from a background page work
vadimt 2013/03/06 21:04:38 Then no one will notice it. I'd prefer these impor
vadimt 2013/03/06 21:04:38 We checked that it works, remember :) ?
skare_ 2013/03/06 22:35:57 [ok] yes, but that was after this set of comments
vadimt 2013/03/06 22:56:35 No doubt about that.
skare_ 2013/03/07 23:25:13 bringing this comment back from the dead. Would co
vadimt 2013/03/08 21:11:58 Renamed assert to verify. This assert is for thin
rgustafson 2013/03/11 18:11:04 If it's stuff that should never happen and this wi
skare_ 2013/03/11 18:37:12 re: the alert() line, would it be ok to not show a
vadimt 2013/03/11 21:31:53 The assert shouldn't happen in a different sense :
vadimt 2013/03/11 21:31:53 RE: re: the alert() line Per offline talk, leaving
+ }
+}
+
+/**
+ * Build the closure that manages tasks (mutually exclusive chains of events).
skare_ 2013/03/02 00:07:05 you could omit "build the closure" and just say e.
vadimt 2013/03/06 21:04:38 Done.
+ * @return {Object} Set of methods to control tasks.
+ */
+function TaskManager() {
+ /**
+ * Name of the alarm that triggers the error saying that the event page cannot
+ * unload.
+ */
+ var CANNOT_UNLOAD_ALARM_NAME = 'CANNOT-UNLOAD';
+
+ /**
+ * Maximal time we expect the event page to stay loaded after starting a task.
+ */
+ var MAXIMUM_LOADED_TIME_MINUTES = 5;
+
+ /**
+ * Queue of scheduled tasks. The first element, if present, corresponds to the
+ * currently running task.
skare_ 2013/03/07 23:25:13 @type this -- {Array.<Task>} // where you'd typede
vadimt 2013/03/08 21:11:58 Coool
+ */
+ var queue = [];
+
+ /**
+ * Name of the current step of the currently running task if present,
+ * otherwise, null. For diagnostics only.
+ * It's set when the task is started and before each asynchronous operation.
+ */
+ var stepName = null;
+
+ /**
+ * Start the first queued task.
+ */
+ function startFirst() {
rgustafson 2013/03/04 22:31:27 startFirstTask() makes this more self explanatory
vadimt 2013/03/06 21:04:38 The idea is that these are members of TaskManager,
+ // Set alarm to verify that the event page will unload in a reasonable time.
+ chrome.alarms.create(CANNOT_UNLOAD_ALARM_NAME,
+ {delayInMinutes: MAXIMUM_LOADED_TIME_MINUTES});
+
+ // Start the oldest queued task, but not remove it from the queue.
skare_ 2013/03/02 00:07:05 nit: s/not/don't
vadimt 2013/03/06 21:04:38 Done.
+ assert(stepName == null, 'tasks.startFirst: stepName is not null');
+ var entry = queue[0];
skare_ 2013/03/02 00:07:05 maybe bail out if queue is empty here?
rgustafson 2013/03/04 22:31:27 This seems to be called only when there is a first
vadimt 2013/03/06 21:04:38 The method will be called only when the queue is n
vadimt 2013/03/06 21:04:38 Done.
vadimt 2013/03/06 21:04:38 Done.
skare_ 2013/03/06 22:35:57 I still like bailing out on an empty queue (or lik
skare_ 2013/03/07 23:25:13 [bringing this into latest round of comments too]
vadimt 2013/03/08 21:11:58 Per the offline talk, seems we are proceeding as i
skare_ 2013/03/11 18:37:12 so long as whatever the assert() is replaced with
vadimt 2013/03/11 21:31:53 Added to verify(): // TODO(vadimt): Make sure the
+ stepName = entry.name + '-initial';
+ entry.task();
+ }
+
+ /**
+ * Check if a new task can be added to a queue that contains an existing task.
+ * @param {string} newTaskName Name of the new task.
+ * @param {string} queuedTaskName Name of the task in the queue.
+ * @return {boolean} Whether the new task doesn't conflict with the existing
skare_ 2013/03/02 00:07:05 maybe invert logic -- isCompatible -> tasksConflic
vadimt 2013/03/06 21:04:38 Done.
+ * task.
+ */
+ function isCompatible(newTaskName, queuedTaskName) {
+ if (newTaskName == TaskName.UPDATE_CARDS &&
+ queuedTaskName == TaskName.UPDATE_CARDS) {
+ // If a card update is requested while an old update is still in the
+ // queue, we don't need the new update.
+ return false;
+ }
+
+ return true;
+ }
+
+ /**
+ * Check if a new task can be added to the task queue.
skare_ 2013/03/02 00:07:05 nit (throughout comments in the file): this is in
vadimt 2013/03/06 21:04:38 Done.
+ * @param {TaskName} taskName Name of the new task.
+ * @return {boolean} Whether the new task can be added.
+ */
+ function canQueue(taskName) {
+ for (var i = 0; i < queue.length; ++i)
+ if (!isCompatible(taskName, queue[i].name))
+ return false;
+
+ return true;
+ }
+
+ /**
+ * Add a new task. If another task is not running, run the task immediately.
+ * If any task in the queue is not compatible with the task, ignore the new
+ * task. Otherwise, store the task for future execution.
+ * @param {TaskName} taskName Name of the task.
+ * @param {function()} task Code of the task.
+ */
+ function submit(taskName, task) {
+ if (!canQueue(taskName))
+ return;
+
+ queue.push({name: taskName, task: task});
+
+ if (queue.length == 1) {
+ startFirst();
skare_ 2013/03/02 00:07:05 it's ok as used now now, but you're ok with the fa
vadimt 2013/03/06 21:04:38 Yes, I think that it's better than starting asynch
+ }
+ }
+
+ /**
+ * Complete the current task and start the next queued task if available.
+ */
+ function finish() {
skare_ 2013/03/02 00:07:05 high-level comment: a lot of JS patterns end up ha
vadimt 2013/03/06 21:04:38 I've considered this. I thought that this would be
skare_ 2013/03/06 22:35:57 feel free to get other people to comment here but
vadimt 2013/03/06 22:56:35 Done.
+ assert(queue.length >= 1, 'tasks.finish: The task queue is empty.');
+ queue.shift();
+ stepName = null;
+
+ if (queue.length >= 1)
+ startFirst();
+ }
+
+ /**
+ * Associate a name with the current step of the task. Used for diagnostics
+ * only. A task is a chain of asynchronous events; setStep should be called
+ * before starting any asynchronous operation.
+ * @param {string} step Name of new step.
+ */
+ function setStep(step) {
rgustafson 2013/03/04 22:31:27 Is there a reason you don't use this inside the Ta
vadimt 2013/03/06 21:04:38 One day, for example, I may want to add a "stepNam
skare_ 2013/03/06 22:35:57 more general question -- could state name be instr
vadimt 2013/03/06 22:56:35 The plan about this is: in the release, we'll prob
skare_ 2013/03/07 23:25:13 would UMA error counters be slightly more granular
vadimt 2013/03/08 21:11:58 May be, yes, may be, no. We'll have to decide then
rgustafson 2013/03/11 18:11:05 Are we really expecting our event page being impro
skare_ 2013/03/11 18:37:12 re: 'parent name' I meant hypothetically myTask.se
skare_ 2013/03/11 19:46:26 ignore setStep() naming comment; resolved in next
vadimt 2013/03/11 21:31:53 If I knew situations that cannot be caught with te
vadimt 2013/03/11 21:31:53 RE: re: 'parent name' I meant hypothetically In th
vadimt 2013/03/11 21:31:53 (In a gloom voice:) Too late. Already renamed to d
+ stepName = step;
+ }
+
+ chrome.alarms.onAlarm.addListener(function(alarm) {
+ if (alarm.name == CANNOT_UNLOAD_ALARM_NAME) {
+ // Error if the event page wasn't unloaded after a reasonable timeout
+ // since starting the last task.
+ // TODO(vadimt): Uncomment the assert once this bug is fixed:
+ // crbug.com/177563
+ // assert(false, 'Event page didn\'t unload, queue = ' +
+ // JSON.stringify(tasks) + ', step = ' + stepName + ' (ignore this assert
+ // if devtools is attached).');
+ }
+ });
+
+ chrome.runtime.onSuspend.addListener(function() {
+ chrome.alarms.clear(CANNOT_UNLOAD_ALARM_NAME);
+ assert(queue.length == 0 && stepName == null,
+ 'Incomplete task when unloading event page, queue = ' +
+ JSON.stringify(queue) + ', step = ' + stepName);
+ });
+
+ return {
+ submit: submit,
+ setStep: setStep,
+ finish: finish
+ };
+}
+
+var tasks = TaskManager();
+
/**
* Show a notification and remember information associated with it.
* @param {Object} card Google Now card represented as a set of parameters for
@@ -66,7 +242,7 @@ function createNotification(card, notificationsUrlInfo) {
chrome.experimental.notification.create(
card.notificationId,
card.notification,
- function(assignedNotificationId) {});
+ function() {});
notificationsUrlInfo[card.notificationId] = card.actionUrls;
}
@@ -75,133 +251,146 @@ function createNotification(card, notificationsUrlInfo) {
* Parse JSON response from the notification server, show notifications and
* schedule next update.
* @param {string} response Server response.
- */
-function parseAndShowNotificationCards(response) {
- try {
- var parsedResponse = JSON.parse(response);
- } catch (error) {
- // TODO(vadimt): Report errors to the user.
- return;
- }
-
- var cards = parsedResponse.cards;
-
- if (!(cards instanceof Array)) {
- // TODO(vadimt): Report errors to the user.
- return;
- }
-
- if (typeof parsedResponse.expiration_timestamp_seconds != 'number') {
- // TODO(vadimt): Report errors to the user.
- return;
- }
-
- storage.get('activeNotifications', function(items) {
- // Mark existing notifications that received an update in this server
- // response.
- for (var i = 0; i < cards.length; ++i) {
- var notificationId = cards[i].notificationId;
- if (notificationId in items.activeNotifications)
- items.activeNotifications[notificationId].hasUpdate = true;
- }
-
- // Delete notifications that didn't receive an update.
- for (var notificationId in items.activeNotifications)
- if (!items.activeNotifications[notificationId].hasUpdate) {
- chrome.experimental.notification.clear(
- notificationId,
- function(wasDeleted) {});
- }
-
- // Create/update notifications and store their new properties.
- var notificationsUrlInfo = {};
-
- for (var i = 0; i < cards.length; ++i) {
- try {
- createNotification(cards[i], notificationsUrlInfo);
- } catch (error) {
- // TODO(vadimt): Report errors to the user.
- }
- }
- storage.set({activeNotifications: notificationsUrlInfo});
-
- scheduleNextUpdate(parsedResponse.expiration_timestamp_seconds);
-
- // Now that we got a valid response from the server, reset the retry period
- // to the initial value. This retry period will be used the next time we
- // fail to get the server-provided period.
- storage.set({retryDelaySeconds: INITIAL_POLLING_PERIOD_SECONDS});
- });
-}
-
+ */
+function parseAndShowNotificationCards(response) {
+ try {
+ var parsedResponse = JSON.parse(response);
+ } catch (error) {
+ // TODO(vadimt): Report errors to the user.
+ return;
+ }
+
+ var cards = parsedResponse.cards;
+
+ if (!(cards instanceof Array)) {
+ // TODO(vadimt): Report errors to the user.
+ return;
+ }
+
+ if (typeof parsedResponse.expiration_timestamp_seconds != 'number') {
+ // TODO(vadimt): Report errors to the user.
+ return;
+ }
+
+ tasks.setStep('parseAndShowNotificationCards-get-active-notifications');
+ storage.get('activeNotifications', function(items) {
+ // Mark existing notifications that received an update in this server
+ // response.
+ for (var i = 0; i < cards.length; ++i) {
+ var notificationId = cards[i].notificationId;
+ if (notificationId in items.activeNotifications)
+ items.activeNotifications[notificationId].hasUpdate = true;
+ }
+
+ // Delete notifications that didn't receive an update.
+ for (var notificationId in items.activeNotifications)
+ if (!items.activeNotifications[notificationId].hasUpdate) {
+ chrome.experimental.notification.clear(
+ notificationId,
+ function() {});
+ }
+
+ // Create/update notifications and store their new properties.
+ var notificationsUrlInfo = {};
+
+ for (var i = 0; i < cards.length; ++i) {
+ try {
+ createNotification(cards[i], notificationsUrlInfo);
+ } catch (error) {
+ // TODO(vadimt): Report errors to the user.
+ }
+ }
+ storage.set({activeNotifications: notificationsUrlInfo});
+
+ scheduleNextUpdate(parsedResponse.expiration_timestamp_seconds);
+
+ // Now that we got a valid response from the server, reset the retry period
+ // to the initial value. This retry period will be used the next time we
+ // fail to get the server-provided period.
+ storage.set({retryDelaySeconds: INITIAL_POLLING_PERIOD_SECONDS});
+ tasks.finish();
+ });
+}
+
/**
* Request notification cards from the server.
* @param {string} requestParameters Query string for the request.
- */
-function requestNotificationCards(requestParameters) {
- // TODO(vadimt): Figure out how to send user's identity to the server.
- var request = new XMLHttpRequest();
-
+ */
+function requestNotificationCards(requestParameters) {
+ // TODO(vadimt): Figure out how to send user's identity to the server.
+ var request = new XMLHttpRequest();
+
request.responseType = 'text';
- request.onload = function(event) {
- if (request.status == HTTP_OK)
- parseAndShowNotificationCards(request.response);
- }
-
- request.open(
+ request.onload = function() {
+ if (request.status == HTTP_OK)
+ parseAndShowNotificationCards(request.response);
+ else
+ tasks.finish();
+ }
+
+ request.onerror = function() {
rgustafson 2013/03/04 22:31:27 Are there any other callbacks we need to handle li
vadimt 2013/03/06 21:04:38 Done.
+ tasks.finish();
+ }
+
+ request.open(
'GET',
NOTIFICATION_CARDS_URL + '/notifications' + requestParameters,
true);
+ tasks.setStep('requestNotificationCards-send-request');
request.send();
-}
-
+}
+
/**
* Request notification cards from the server when we have geolocation.
* @param {Geoposition} position Location of this computer.
- */
-function requestNotificationCardsWithLocation(position) {
- // TODO(vadimt): Should we use 'q' as the parameter name?
+ */
+function requestNotificationCardsWithLocation(position) {
+ // TODO(vadimt): Should we use 'q' as the parameter name?
var requestParameters =
'?q=' + position.coords.latitude +
',' + position.coords.longitude +
',' + position.coords.accuracy;
requestNotificationCards(requestParameters);
-}
-
+}
+
/**
* Request notification cards from the server when we don't have geolocation.
* @param {PositionError} positionError Position error.
- */
+ */
function requestNotificationCardsWithoutLocation(positionError) {
requestNotificationCards('');
-}
-
+}
+
/**
* Obtain new location; request and show notification cards based on this
* location.
- */
-function updateNotificationsCards() {
- storage.get('retryDelaySeconds', function(items) {
- // Immediately schedule the update after the current retry period. Then,
- // we'll use update time from the server if available.
- scheduleNextUpdate(items.retryDelaySeconds);
-
- // TODO(vadimt): Consider interrupting waiting for the next update if we
- // detect that the network conditions have changed. Also, decide whether the
- // exponential backoff is needed both when we are offline and when there are
- // failures on the server side.
- var newRetryDelaySeconds =
- Math.min(items.retryDelaySeconds * 2 * (1 + 0.2 * Math.random()),
- MAXIMUM_POLLING_PERIOD_SECONDS);
- storage.set({retryDelaySeconds: newRetryDelaySeconds});
-
- navigator.geolocation.getCurrentPosition(
- requestNotificationCardsWithLocation,
- requestNotificationCardsWithoutLocation);
- });
-}
-
+ */
+function updateNotificationsCards() {
+ tasks.submit(TaskName.UPDATE_CARDS, function() {
+ tasks.setStep('updateNotificationsCards-get-retryDelaySeconds');
+ storage.get('retryDelaySeconds', function(items) {
+ // Immediately schedule the update after the current retry period. Then,
+ // we'll use update time from the server if available.
+ scheduleNextUpdate(items.retryDelaySeconds);
+
+ // TODO(vadimt): Consider interrupting waiting for the next update if we
+ // detect that the network conditions have changed. Also, decide whether
+ // the exponential backoff is needed both when we are offline and when
+ // there are failures on the server side.
+ var newRetryDelaySeconds =
+ Math.min(items.retryDelaySeconds * 2 * (1 + 0.2 * Math.random()),
+ MAXIMUM_POLLING_PERIOD_SECONDS);
+ storage.set({retryDelaySeconds: newRetryDelaySeconds});
+
+ tasks.setStep('updateNotificationsCards-get-location');
+ navigator.geolocation.getCurrentPosition(
+ requestNotificationCardsWithLocation,
+ requestNotificationCardsWithoutLocation);
+ });
+ });
+}
+
/**
* Opens URL corresponding to the clicked part of the notification.
* @param {string} notificationId Unique identifier of the notification.
@@ -209,70 +398,89 @@ function updateNotificationsCards() {
* the clicked area from the button action URLs info.
*/
function onNotificationClicked(notificationId, selector) {
- storage.get('activeNotifications', function(items) {
- var actionUrls = items.activeNotifications[notificationId];
- if (typeof actionUrls != 'object') {
- // TODO(vadimt): report an error.
- return;
- }
+ tasks.submit(TaskName.CARD_CLICKED, function() {
+ tasks.setStep('onNotificationClicked-get-activeNotifications');
+ storage.get('activeNotifications', function(items) {
+ var actionUrls = items.activeNotifications[notificationId];
+ if (typeof actionUrls != 'object') {
+ // TODO(vadimt): report an error.
+ tasks.finish();
+ return;
+ }
- var url = selector(actionUrls);
+ var url = selector(actionUrls);
- if (typeof url != 'string') {
- // TODO(vadimt): report an error.
- return;
- }
+ if (typeof url != 'string') {
+ // TODO(vadimt): report an error.
+ tasks.finish();
+ return;
+ }
- chrome.tabs.create({url: url});
- });
+ chrome.tabs.create({url: url});
+ tasks.finish();
+ });
+ });
}
/**
* Callback for chrome.experimental.notification.onClosed event.
* @param {string} notificationId Unique identifier of the notification.
* @param {boolean} byUser Whether the notification was closed by the user.
- */
-function onNotificationClosed(notificationId, byUser) {
- if (byUser) {
- // TODO(vadimt): Analyze possible race conditions between request for cards
- // and dismissal.
- // Send a dismiss request to the server.
- var requestParameters = '?id=' + notificationId;
- var request = new XMLHttpRequest();
- request.responseType = 'text';
- // TODO(vadimt): If the request fails, for example, because there is no
- // internet connection, do retry with exponential backoff.
- request.open(
- 'GET',
- NOTIFICATION_CARDS_URL + '/dismiss' + requestParameters,
- true);
- request.send();
- }
-}
-
+ */
+function onNotificationClosed(notificationId, byUser) {
+ if (byUser) {
+ tasks.submit(TaskName.DISMISS_CARD, function() {
+ // Deleting the notification in case it was re-added while this task was
+ // waiting in the queue.
+ chrome.experimental.notification.clear(
rgustafson 2013/03/04 22:31:27 This is going to have a notification pop up for a
vadimt 2013/03/06 21:04:38 The reasoning for not doing this is that I expect
rgustafson 2013/03/11 18:11:05 I'm okay with this for now. Eventually, I think th
skare_ 2013/03/11 18:37:12 especially if there's ever the ability to have the
+ notificationId,
+ function() {});
+
+ // Send a dismiss request to the server.
+ var requestParameters = '?id=' + notificationId;
+ var request = new XMLHttpRequest();
+ request.responseType = 'text';
+ request.onloadend = function() {
+ tasks.finish();
+ }
+ // TODO(vadimt): If the request fails, for example, because there is no
+ // internet connection, do retry with exponential backoff.
+ request.open(
+ 'GET',
+ NOTIFICATION_CARDS_URL + '/dismiss' + requestParameters,
+ true);
+ tasks.setStep('onNotificationClosed-send-request');
+ request.send();
+ });
+ }
+}
+
/**
* Schedule next update for notification cards.
* @param {int} delaySeconds Length of time in seconds after which the alarm
* event should fire.
- */
-function scheduleNextUpdate(delaySeconds) {
- // Schedule an alarm after the specified delay. 'periodInMinutes' is for the
- // case when we fail to re-register the alarm.
- chrome.alarms.create({
- delayInMinutes: delaySeconds / 60,
- periodInMinutes: MAXIMUM_POLLING_PERIOD_SECONDS / 60
- });
-}
-
+ */
+function scheduleNextUpdate(delaySeconds) {
+ // Schedule an alarm after the specified delay. 'periodInMinutes' is for the
+ // case when we fail to re-register the alarm.
+ var alarmInfo = {
+ delayInMinutes: delaySeconds / 60,
+ periodInMinutes: MAXIMUM_POLLING_PERIOD_SECONDS / 60
+ };
+
+ chrome.alarms.create(UPDATE_NOTIFICATIONS_ALARM_NAME, alarmInfo);
+}
+
/**
* Initialize the event page on install or on browser startup.
- */
-function initialize() {
+ */
+function initialize() {
var initialStorage = {
activeNotifications: {},
retryDelaySeconds: INITIAL_POLLING_PERIOD_SECONDS
};
- storage.set(initialStorage, updateNotificationsCards);
+ storage.set(initialStorage);
+ updateNotificationsCards();
rgustafson 2013/03/04 22:31:27 Couldn't retryDelaySeconds not be set when update
vadimt 2013/03/06 21:04:38 I had a long conversation with extensions guys who
rgustafson 2013/03/11 18:11:05 What exactly was their promise? A set followed by
vadimt 2013/03/11 21:31:53 Literally: If get is executed after set(X), 'get'
}
chrome.runtime.onInstalled.addListener(function(details) {
@@ -285,7 +493,8 @@ chrome.runtime.onStartup.addListener(function() {
});
chrome.alarms.onAlarm.addListener(function(alarm) {
- updateNotificationsCards();
+ if (alarm.name == UPDATE_NOTIFICATIONS_ALARM_NAME)
+ updateNotificationsCards();
});
chrome.experimental.notification.onClicked.addListener(
« no previous file with comments | « no previous file | no next file » | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698