| 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 ba36c2a3cba29f137581bce2d718b519e44ecb6e..29b5ff7b0caacb8c934b665347e9107d2a6f9946 100644
|
| --- a/chrome/browser/resources/google_now/background.js
|
| +++ b/chrome/browser/resources/google_now/background.js
|
| @@ -30,12 +30,16 @@
|
| * code the server will send.
|
| */
|
| var HTTP_OK = 200;
|
| +var HTTP_NOCONTENT = 204;
|
|
|
| var HTTP_BAD_REQUEST = 400;
|
| var HTTP_UNAUTHORIZED = 401;
|
| var HTTP_FORBIDDEN = 403;
|
| var HTTP_METHOD_NOT_ALLOWED = 405;
|
|
|
| +var MS_IN_SECOND = 1000;
|
| +var MS_IN_MINUTE = 60 * 1000;
|
| +
|
| /**
|
| * Initial period for polling for Google Now Notifications cards to use when the
|
| * period from the server is not available.
|
| @@ -43,6 +47,11 @@ var HTTP_METHOD_NOT_ALLOWED = 405;
|
| var INITIAL_POLLING_PERIOD_SECONDS = 5 * 60; // 5 minutes
|
|
|
| /**
|
| + * Mininal period for polling for Google Now Notifications cards.
|
| + */
|
| +var MINIMUM_POLLING_PERIOD_SECONDS = 5 * 60; // 5 minutes
|
| +
|
| +/**
|
| * Maximal period for polling for Google Now Notifications cards to use when the
|
| * period from the server is not available.
|
| */
|
| @@ -87,6 +96,34 @@ var WELCOME_TOAST_NOTIFICATION_ID = 'enable-now-toast';
|
| var ToastButtonIndex = {YES: 0, NO: 1};
|
|
|
| /**
|
| + * Notification as it's sent by the server.
|
| + *
|
| + * @typedef {{
|
| + * notificationId: string,
|
| + * chromeNotificationId: string,
|
| + * trigger: Object=,
|
| + * version: number,
|
| + * chromeNotificationOptions: Object,
|
| + * actionUrls: Object=,
|
| + * dismissal: Object
|
| + * }}
|
| + */
|
| +var UnmergedNotification;
|
| +
|
| +/**
|
| + * Notification group as the client stores it. |cardsTimestamp| and |rank| are
|
| + * defined if |cards| is non-empty.
|
| + *
|
| + * @typedef {{
|
| + * cards: Array.<UnmergedNotification>,
|
| + * cardsTimestamp: number=,
|
| + * nextPollTime: number,
|
| + * rank: number=
|
| + * }}
|
| + */
|
| +var StorageGroup;
|
| +
|
| +/**
|
| * Checks if a new task can't be scheduled when another task is already
|
| * scheduled.
|
| * @param {string} newTaskName Name of the new task.
|
| @@ -118,6 +155,7 @@ var tasks = buildTaskManager(areTasksConflicting);
|
| // Add error processing to API calls.
|
| wrapper.instrumentChromeApiFunction('location.onLocationUpdate.addListener', 0);
|
| wrapper.instrumentChromeApiFunction('metricsPrivate.getVariationParams', 1);
|
| +wrapper.instrumentChromeApiFunction('notifications.clear', 1);
|
| wrapper.instrumentChromeApiFunction('notifications.create', 2);
|
| wrapper.instrumentChromeApiFunction('notifications.update', 2);
|
| wrapper.instrumentChromeApiFunction('notifications.getAll', 0);
|
| @@ -197,7 +235,6 @@ function recordEvent(event) {
|
| * parameter.
|
| */
|
| function setAuthorization(request, callbackBoolean) {
|
| - tasks.debugSetStepName('setAuthorization-isSignedIn');
|
| authenticationManager.isSignedIn(function(token) {
|
| if (!token) {
|
| callbackBoolean(false);
|
| @@ -211,7 +248,6 @@ function setAuthorization(request, callbackBoolean) {
|
| request.onloadend = wrapper.wrapCallback(function(event) {
|
| if (request.status == HTTP_FORBIDDEN ||
|
| request.status == HTTP_UNAUTHORIZED) {
|
| - tasks.debugSetStepName('setAuthorization-removeToken');
|
| authenticationManager.removeToken(token, function() {
|
| originalOnLoadEnd(event);
|
| });
|
| @@ -225,45 +261,22 @@ function setAuthorization(request, callbackBoolean) {
|
| }
|
|
|
| /**
|
| - * Parses JSON response from the notification server, show notifications and
|
| - * schedule next update.
|
| - * @param {string} response Server response.
|
| - * @param {function()} callback Completion callback.
|
| + * Shows parsed and merged cards as notifications.
|
| + * @param {Object.<string, MergedCard>} cards Map from chromeNotificationId to
|
| + * the merged card, containing cards to show.
|
| */
|
| -function parseAndShowNotificationCards(response, callback) {
|
| - console.log('parseAndShowNotificationCards ' + response);
|
| - try {
|
| - var parsedResponse = JSON.parse(response);
|
| - } catch (error) {
|
| - console.error('parseAndShowNotificationCards parse error: ' + error);
|
| - callback();
|
| - return;
|
| - }
|
| +function showNotificationCards(cards) {
|
| + console.log('showNotificationCards ' + JSON.stringify(cards));
|
|
|
| - var cards = parsedResponse.cards;
|
| -
|
| - if (!(cards instanceof Array)) {
|
| - callback();
|
| - return;
|
| - }
|
| -
|
| - if (typeof parsedResponse.next_poll_seconds != 'number') {
|
| - callback();
|
| - return;
|
| - }
|
| -
|
| - tasks.debugSetStepName('parseAndShowNotificationCards-storage-get');
|
| instrumented.storage.local.get(['notificationsData', 'recentDismissals'],
|
| function(items) {
|
| - console.log('parseAndShowNotificationCards-get ' +
|
| + console.log('showNotificationCards-get ' +
|
| JSON.stringify(items));
|
| items.notificationsData = items.notificationsData || {};
|
| items.recentDismissals = items.recentDismissals || {};
|
|
|
| - tasks.debugSetStepName(
|
| - 'parseAndShowNotificationCards-notifications-getAll');
|
| instrumented.notifications.getAll(function(notifications) {
|
| - console.log('parseAndShowNotificationCards-getAll ' +
|
| + console.log('showNotificationCards-getAll ' +
|
| JSON.stringify(notifications));
|
| // TODO(vadimt): Figure out what to do when notifications are
|
| // disabled for our extension.
|
| @@ -273,62 +286,47 @@ function parseAndShowNotificationCards(response, callback) {
|
| // client-side filtering of cards.
|
| var updatedRecentDismissals = {};
|
| var currentTimeMs = Date.now();
|
| - for (var notificationId in items.recentDismissals) {
|
| - if (currentTimeMs - items.recentDismissals[notificationId] <
|
| + for (var chromeNotificationId in items.recentDismissals) {
|
| + if (currentTimeMs - items.recentDismissals[chromeNotificationId] <
|
| DISMISS_RETENTION_TIME_MS) {
|
| - updatedRecentDismissals[notificationId] =
|
| - items.recentDismissals[notificationId];
|
| - }
|
| - }
|
| -
|
| - // Mark existing notifications that received an update in this server
|
| - // response.
|
| - var updatedNotifications = {};
|
| -
|
| - for (var i = 0; i < cards.length; ++i) {
|
| - var notificationId = cards[i].notificationId;
|
| - if (!(notificationId in updatedRecentDismissals) &&
|
| - notificationId in notifications) {
|
| - updatedNotifications[notificationId] = true;
|
| + updatedRecentDismissals[chromeNotificationId] =
|
| + items.recentDismissals[chromeNotificationId];
|
| + delete cards[chromeNotificationId];
|
| }
|
| }
|
|
|
| // Delete notifications that didn't receive an update.
|
| - for (var notificationId in notifications) {
|
| - console.log('parseAndShowNotificationCards-delete-check ' +
|
| - notificationId);
|
| - if (!(notificationId in updatedNotifications)) {
|
| - console.log('parseAndShowNotificationCards-delete ' +
|
| - notificationId);
|
| - cardSet.clear(notificationId);
|
| + for (var chromeNotificationId in notifications) {
|
| + console.log('showNotificationCards-delete-check ' +
|
| + chromeNotificationId);
|
| + if (!(chromeNotificationId in cards)) {
|
| + console.log(
|
| + 'showNotificationCards-delete ' + chromeNotificationId);
|
| + cardSet.clear(chromeNotificationId);
|
| }
|
| }
|
|
|
| - recordEvent(GoogleNowEvent.CARDS_PARSE_SUCCESS);
|
| -
|
| // Create/update notifications and store their new properties.
|
| var newNotificationsData = {};
|
| - for (var i = 0; i < cards.length; ++i) {
|
| - var card = cards[i];
|
| - if (!(card.notificationId in updatedRecentDismissals)) {
|
| - var notificationData =
|
| - items.notificationsData[card.notificationId];
|
| - var previousVersion = notifications[card.notificationId] &&
|
| - notificationData &&
|
| - notificationData.cardCreateInfo &&
|
| - notificationData.cardCreateInfo.version;
|
| - newNotificationsData[card.notificationId] =
|
| - cardSet.update(card, previousVersion);
|
| - }
|
| + for (var chromeNotificationId in cards) {
|
| + var notificationData =
|
| + items.notificationsData[chromeNotificationId];
|
| + var previousVersion = notifications[chromeNotificationId] &&
|
| + notificationData &&
|
| + notificationData.cardCreateInfo &&
|
| + notificationData.cardCreateInfo.version;
|
| + newNotificationsData[chromeNotificationId] = cardSet.update(
|
| + chromeNotificationId,
|
| + cards[chromeNotificationId],
|
| + previousVersion);
|
| }
|
|
|
| - updateCardsAttempts.start(parsedResponse.next_poll_seconds);
|
| + recordEvent(GoogleNowEvent.CARDS_PARSE_SUCCESS);
|
|
|
| chrome.storage.local.set({
|
| notificationsData: newNotificationsData,
|
| recentDismissals: updatedRecentDismissals
|
| });
|
| - callback();
|
| });
|
| });
|
| }
|
| @@ -346,55 +344,226 @@ function removeAllCards() {
|
| // code is no longer necessary.
|
| instrumented.notifications.getAll(function(notifications) {
|
| notifications = notifications || {};
|
| - for (var notificationId in notifications) {
|
| - chrome.notifications.clear(notificationId, function() {});
|
| + for (var chromeNotificationId in notifications) {
|
| + instrumented.notifications.clear(chromeNotificationId, function() {});
|
| }
|
| - chrome.storage.local.set({notificationsData: {}});
|
| + chrome.storage.local.remove(['notificationsData', 'notificationGroups']);
|
| + });
|
| +}
|
| +
|
| +/**
|
| + * Merges an unmerged notification into a merged card with same ID.
|
| + * @param {MergedCard=} mergedCard Existing merged card or undefined if a merged
|
| + * card doesn't exist (i.e. we see this ID for the first time while
|
| + * merging).
|
| + * @param {UnmergedNotification} unmergedNotification Notification as it was
|
| + * received from the server.
|
| + * @param {number} notificationTimestamp The moment the unmerged card was
|
| + * received.
|
| + * @param {number} notificationGroupRank Rank of the group of the unmerged card.
|
| + * @return {MergedCard} Result of merging |unmergedNotification| into
|
| + * |mergedCard|.
|
| + */
|
| +function mergeCards(
|
| + mergedCard,
|
| + unmergedNotification,
|
| + notificationTimestamp,
|
| + notificationGroupRank) {
|
| + var result = mergedCard || {dismissals: []};
|
| +
|
| + var priority = mergedCard ?
|
| + Math.max(
|
| + mergedCard.notification.priority,
|
| + unmergedNotification.chromeNotificationOptions.priority) :
|
| + unmergedNotification.chromeNotificationOptions.priority;
|
| +
|
| + if (!mergedCard || notificationGroupRank > mergedCard.groupRank) {
|
| + result.groupRank = notificationGroupRank;
|
| + var showTime = unmergedNotification.trigger &&
|
| + unmergedNotification.trigger.showTimeSec &&
|
| + (notificationTimestamp +
|
| + unmergedNotification.trigger.showTimeSec * MS_IN_SECOND);
|
| + var hideTime = unmergedNotification.trigger &&
|
| + unmergedNotification.trigger.hideTimeSec &&
|
| + (notificationTimestamp +
|
| + unmergedNotification.trigger.hideTimeSec * MS_IN_SECOND);
|
| + result.trigger = {
|
| + showTime: showTime,
|
| + hideTime: hideTime
|
| + };
|
| + }
|
| +
|
| + if (!mergedCard || notificationTimestamp > mergedCard.timestamp) {
|
| + result.timestamp = notificationTimestamp;
|
| + result.notification = unmergedNotification.chromeNotificationOptions;
|
| + result.actionUrls = unmergedNotification.actionUrls;
|
| + result.version = unmergedNotification.version;
|
| + }
|
| +
|
| + result.notification.priority = priority;
|
| + var dismissalData = {
|
| + notificationId: unmergedNotification.notificationId,
|
| + parameters: unmergedNotification.dismissal
|
| + };
|
| + result.dismissals.push(dismissalData);
|
| +
|
| + return result;
|
| +}
|
| +
|
| +/**
|
| + * Merges a card group into a set of merged cards.
|
| + * @param {Object.<string, MergedCard>} mergedCards Map from
|
| + * chromeNotificationId to a merged card.
|
| + * This is an input/output parameter.
|
| + * @param {StorageGroup} storageGroup Group to merge into the merged card set.
|
| + */
|
| +function mergeGroup(mergedCards, storageGroup) {
|
| + for (var i = 0; i < storageGroup.cards.length; i++) {
|
| + var card = storageGroup.cards[i];
|
| + mergedCards[card.chromeNotificationId] = mergeCards(
|
| + mergedCards[card.chromeNotificationId],
|
| + card,
|
| + storageGroup.cardsTimestamp,
|
| + storageGroup.rank);
|
| + }
|
| +}
|
| +
|
| +/**
|
| + * Schedules next cards poll.
|
| + * @param {Object.<string, StorageGroup>} groups Map from group name to group
|
| + * information.
|
| + */
|
| +function scheduleNextPoll(groups) {
|
| + var nextPollTime = null;
|
| +
|
| + for (var groupName in groups) {
|
| + var group = groups[groupName];
|
| + nextPollTime = nextPollTime == null ?
|
| + group.nextPollTime : Math.min(group.nextPollTime, nextPollTime);
|
| + }
|
| +
|
| + verify(nextPollTime != null, 'scheduleNextPoll: nextPollTime is null');
|
| +
|
| + var nextPollDelaySeconds = Math.max(
|
| + (nextPollTime - Date.now()) / MS_IN_SECOND,
|
| + MINIMUM_POLLING_PERIOD_SECONDS);
|
| + updateCardsAttempts.start(nextPollDelaySeconds);
|
| +}
|
| +
|
| +/**
|
| + * Parses JSON response from the notification server, shows notifications and
|
| + * schedules next update.
|
| + * @param {string} response Server response.
|
| + */
|
| +function parseAndShowNotificationCards(response) {
|
| + console.log('parseAndShowNotificationCards ' + response);
|
| + var parsedResponse = JSON.parse(response);
|
| +
|
| + var groups = parsedResponse.groups;
|
| +
|
| + // Populate groups with corresponding cards.
|
| + if (parsedResponse.notifications) {
|
| + for (var i = 0; i != parsedResponse.notifications.length; ++i) {
|
| + var card = parsedResponse.notifications[i];
|
| + var group = groups[card.groupName];
|
| + group.cards = group.cards || [];
|
| + group.cards.push(card);
|
| + }
|
| + }
|
| +
|
| + instrumented.storage.local.get('notificationGroups', function(items) {
|
| + console.log('parseAndShowNotificationCards-get ' + JSON.stringify(items));
|
| + items.notificationGroups = items.notificationGroups || {};
|
| +
|
| + var now = Date.now();
|
| +
|
| + // Build updated set of groups and merge cards from all groups into one set.
|
| + var updatedGroups = {};
|
| + var mergedCards = {};
|
| +
|
| + for (var groupName in groups) {
|
| + var receivedGroup = groups[groupName];
|
| + var storageGroup = items.notificationGroups[groupName] || {
|
| + cards: [],
|
| + cardsTimestamp: undefined,
|
| + nextPollTime: now,
|
| + rank: undefined
|
| + };
|
| +
|
| + if (receivedGroup.requested)
|
| + receivedGroup.cards = receivedGroup.cards || [];
|
| +
|
| + if (receivedGroup.cards) {
|
| + storageGroup.cards = receivedGroup.cards;
|
| + storageGroup.cardsTimestamp = now;
|
| + storageGroup.rank = receivedGroup.rank;
|
| + }
|
| +
|
| + if (receivedGroup.nextPollSeconds !== undefined) {
|
| + storageGroup.nextPollTime =
|
| + now + receivedGroup.nextPollSeconds * MS_IN_SECOND;
|
| + }
|
| +
|
| + updatedGroups[groupName] = storageGroup;
|
| +
|
| + mergeGroup(mergedCards, storageGroup);
|
| + }
|
| +
|
| + scheduleNextPoll(updatedGroups);
|
| +
|
| + chrome.storage.local.set({notificationGroups: updatedGroups});
|
| +
|
| + showNotificationCards(mergedCards);
|
| });
|
| }
|
|
|
| /**
|
| * Requests notification cards from the server.
|
| * @param {Location} position Location of this computer.
|
| - * @param {function()} callback Completion callback.
|
| */
|
| -function requestNotificationCards(position, callback) {
|
| +function requestNotificationCards(position) {
|
| console.log('requestNotificationCards ' + JSON.stringify(position) +
|
| ' from ' + NOTIFICATION_CARDS_URL);
|
|
|
| - if (!NOTIFICATION_CARDS_URL) {
|
| - callback();
|
| + if (!NOTIFICATION_CARDS_URL)
|
| return;
|
| - }
|
|
|
| recordEvent(GoogleNowEvent.REQUEST_FOR_CARDS_TOTAL);
|
|
|
| - // TODO(vadimt): Should we use 'q' as the parameter name?
|
| - var requestParameters =
|
| - 'q=' + position.coords.latitude +
|
| - ',' + position.coords.longitude +
|
| - ',' + position.coords.accuracy;
|
| + instrumented.storage.local.get('notificationGroups', function(items) {
|
| + console.log('requestNotificationCards-storage-get ' +
|
| + JSON.stringify(items));
|
|
|
| - var request = buildServerRequest('notifications',
|
| - 'application/x-www-form-urlencoded');
|
| + var requestParameters = '?timeZoneOffsetMs=' +
|
| + (-new Date().getTimezoneOffset() * MS_IN_MINUTE);
|
|
|
| - request.onloadend = function(event) {
|
| - console.log('requestNotificationCards-onloadend ' + request.status);
|
| - if (request.status == HTTP_OK) {
|
| - recordEvent(GoogleNowEvent.REQUEST_FOR_CARDS_SUCCESS);
|
| - parseAndShowNotificationCards(request.response, callback);
|
| - } else {
|
| - callback();
|
| - }
|
| - };
|
| + if (items.notificationGroups) {
|
| + var now = Date.now();
|
|
|
| - setAuthorization(request, function(success) {
|
| - if (success) {
|
| - tasks.debugSetStepName('requestNotificationCards-send-request');
|
| - request.send(requestParameters);
|
| - } else {
|
| - callback();
|
| + for (var groupName in items.notificationGroups) {
|
| + var group = items.notificationGroups[groupName];
|
| + if (group.nextPollTime <= now)
|
| + requestParameters += ('&requestTypes=' + groupName);
|
| + }
|
| }
|
| +
|
| + console.log('requestNotificationCards: request=' + requestParameters);
|
| +
|
| + var request = buildServerRequest('GET',
|
| + 'notifications' + requestParameters);
|
| +
|
| + request.onloadend = function(event) {
|
| + console.log('requestNotificationCards-onloadend ' + request.status);
|
| + if (request.status == HTTP_OK) {
|
| + recordEvent(GoogleNowEvent.REQUEST_FOR_CARDS_SUCCESS);
|
| + parseAndShowNotificationCards(request.response);
|
| + }
|
| + };
|
| +
|
| + setAuthorization(request, function(success) {
|
| + if (success)
|
| + request.send();
|
| + });
|
| });
|
| }
|
|
|
| @@ -436,7 +605,7 @@ function stopRequestLocation() {
|
| function updateNotificationsCards(position) {
|
| console.log('updateNotificationsCards ' + JSON.stringify(position) +
|
| ' @' + new Date());
|
| - tasks.add(UPDATE_CARDS_TASK_NAME, function(callback) {
|
| + tasks.add(UPDATE_CARDS_TASK_NAME, function() {
|
| console.log('updateNotificationsCards-task-begin');
|
| updateCardsAttempts.isRunning(function(running) {
|
| if (running) {
|
| @@ -444,9 +613,7 @@ function updateNotificationsCards(position) {
|
| processPendingDismissals(function(success) {
|
| if (success) {
|
| // The cards are requested only if there are no unsent dismissals.
|
| - requestNotificationCards(position, callback);
|
| - } else {
|
| - callback();
|
| + requestNotificationCards(position);
|
| }
|
| });
|
| });
|
| @@ -457,16 +624,16 @@ function updateNotificationsCards(position) {
|
|
|
| /**
|
| * Sends a server request to dismiss a card.
|
| - * @param {string} notificationId Unique identifier of the card.
|
| + * @param {string} chromeNotificationId chrome.notifications ID of the card.
|
| * @param {number} dismissalTimeMs Time of the user's dismissal of the card in
|
| * milliseconds since epoch.
|
| - * @param {Object} dismissalParameters Dismissal parameters.
|
| + * @param {DismissalData} dismissalData Data to build a dismissal request.
|
| * @param {function(boolean)} callbackBoolean Completion callback with 'done'
|
| * parameter.
|
| */
|
| function requestCardDismissal(
|
| - notificationId, dismissalTimeMs, dismissalParameters, callbackBoolean) {
|
| - console.log('requestDismissingCard ' + notificationId + ' from ' +
|
| + chromeNotificationId, dismissalTimeMs, dismissalData, callbackBoolean) {
|
| + console.log('requestDismissingCard ' + chromeNotificationId + ' from ' +
|
| NOTIFICATION_CARDS_URL);
|
|
|
| var dismissalAge = Date.now() - dismissalTimeMs;
|
| @@ -477,33 +644,35 @@ function requestCardDismissal(
|
| }
|
|
|
| recordEvent(GoogleNowEvent.DISMISS_REQUEST_TOTAL);
|
| - var request = buildServerRequest('dismiss', 'application/json');
|
| +
|
| + var request = 'notifications/' + dismissalData.notificationId +
|
| + '?age=' + dismissalAge +
|
| + '&chromeNotificationId=' + chromeNotificationId;
|
| +
|
| + for (var paramField in dismissalData.parameters)
|
| + request += ('&' + paramField + '=' + dismissalData.parameters[paramField]);
|
| +
|
| + console.log('requestCardDismissal: request=' + request);
|
| +
|
| + var request = buildServerRequest('DELETE', request);
|
| request.onloadend = function(event) {
|
| console.log('requestDismissingCard-onloadend ' + request.status);
|
| - if (request.status == HTTP_OK)
|
| + if (request.status == HTTP_NOCONTENT)
|
| recordEvent(GoogleNowEvent.DISMISS_REQUEST_SUCCESS);
|
|
|
| // A dismissal doesn't require further retries if it was successful or
|
| // doesn't have a chance for successful completion.
|
| - var done = request.status == HTTP_OK ||
|
| + var done = request.status == HTTP_NOCONTENT ||
|
| request.status == HTTP_BAD_REQUEST ||
|
| request.status == HTTP_METHOD_NOT_ALLOWED;
|
| callbackBoolean(done);
|
| };
|
|
|
| setAuthorization(request, function(success) {
|
| - if (success) {
|
| - tasks.debugSetStepName('requestCardDismissal-send-request');
|
| -
|
| - var dismissalObject = {
|
| - id: notificationId,
|
| - age: dismissalAge,
|
| - dismissal: dismissalParameters
|
| - };
|
| - request.send(JSON.stringify(dismissalObject));
|
| - } else {
|
| + if (success)
|
| + request.send();
|
| + else
|
| callbackBoolean(false);
|
| - }
|
| });
|
| }
|
|
|
| @@ -513,7 +682,6 @@ function requestCardDismissal(
|
| * parameter. Success means that no pending dismissals are left.
|
| */
|
| function processPendingDismissals(callbackBoolean) {
|
| - tasks.debugSetStepName('processPendingDismissals-storage-get');
|
| instrumented.storage.local.get(['pendingDismissals', 'recentDismissals'],
|
| function(items) {
|
| console.log('processPendingDismissals-storage-get ' +
|
| @@ -544,14 +712,15 @@ function processPendingDismissals(callbackBoolean) {
|
| // recursively with the rest.
|
| var dismissal = items.pendingDismissals[0];
|
| requestCardDismissal(
|
| - dismissal.notificationId,
|
| + dismissal.chromeNotificationId,
|
| dismissal.time,
|
| - dismissal.parameters,
|
| + dismissal.dismissalData,
|
| function(done) {
|
| if (done) {
|
| dismissalsChanged = true;
|
| items.pendingDismissals.splice(0, 1);
|
| - items.recentDismissals[dismissal.notificationId] = Date.now();
|
| + items.recentDismissals[dismissal.chromeNotificationId] =
|
| + Date.now();
|
| doProcessDismissals();
|
| } else {
|
| onFinish(false);
|
| @@ -567,34 +736,29 @@ function processPendingDismissals(callbackBoolean) {
|
| * Submits a task to send pending dismissals.
|
| */
|
| function retryPendingDismissals() {
|
| - tasks.add(RETRY_DISMISS_TASK_NAME, function(callback) {
|
| + tasks.add(RETRY_DISMISS_TASK_NAME, function() {
|
| dismissalAttempts.planForNext(function() {
|
| - processPendingDismissals(function(success) { callback(); });
|
| + processPendingDismissals(function(success) {});
|
| });
|
| });
|
| }
|
|
|
| /**
|
| * Opens URL corresponding to the clicked part of the notification.
|
| - * @param {string} notificationId Unique identifier of the notification.
|
| + * @param {string} chromeNotificationId chrome.notifications ID of the card.
|
| * @param {function(Object): string} selector Function that extracts the url for
|
| * the clicked area from the button action URLs info.
|
| */
|
| -function onNotificationClicked(notificationId, selector) {
|
| +function onNotificationClicked(chromeNotificationId, selector) {
|
| instrumented.storage.local.get('notificationsData', function(items) {
|
| var notificationData = items &&
|
| items.notificationsData &&
|
| - items.notificationsData[notificationId];
|
| + items.notificationsData[chromeNotificationId];
|
|
|
| if (!notificationData)
|
| return;
|
|
|
| - var actionUrls = notificationData.actionUrls;
|
| - if (typeof actionUrls != 'object') {
|
| - return;
|
| - }
|
| -
|
| - var url = selector(actionUrls);
|
| + var url = selector(notificationData.actionUrls);
|
| if (!url)
|
| return;
|
|
|
| @@ -627,14 +791,14 @@ function onToastNotificationClicked(buttonIndex) {
|
|
|
| /**
|
| * Callback for chrome.notifications.onClosed event.
|
| - * @param {string} notificationId Unique identifier of the notification.
|
| + * @param {string} chromeNotificationId chrome.notifications ID of the card.
|
| * @param {boolean} byUser Whether the notification was closed by the user.
|
| */
|
| -function onNotificationClosed(notificationId, byUser) {
|
| +function onNotificationClosed(chromeNotificationId, byUser) {
|
| if (!byUser)
|
| return;
|
|
|
| - if (notificationId == WELCOME_TOAST_NOTIFICATION_ID) {
|
| + if (chromeNotificationId == WELCOME_TOAST_NOTIFICATION_ID) {
|
| // Even though they only closed the notification without clicking no, treat
|
| // it as though they clicked No anwyay, and don't show the toast again.
|
| chrome.metricsPrivate.recordUserAction('GoogleNow.WelcomeToastDismissed');
|
| @@ -645,31 +809,35 @@ function onNotificationClosed(notificationId, byUser) {
|
| // At this point we are guaranteed that the notification is a now card.
|
| chrome.metricsPrivate.recordUserAction('GoogleNow.Dismissed');
|
|
|
| - tasks.add(DISMISS_CARD_TASK_NAME, function(callback) {
|
| + tasks.add(DISMISS_CARD_TASK_NAME, function() {
|
| dismissalAttempts.start();
|
|
|
| // Deleting the notification in case it was re-added while this task was
|
| // scheduled, waiting for execution.
|
| - cardSet.clear(notificationId);
|
| + cardSet.clear(chromeNotificationId);
|
|
|
| - tasks.debugSetStepName('onNotificationClosed-storage-get');
|
| - instrumented.storage.local.get(['pendingDismissals', 'notificationsData'],
|
| - function(items) {
|
| - items.pendingDismissals = items.pendingDismissals || [];
|
| - items.notificationsData = items.notificationsData || {};
|
| + instrumented.storage.local.get(
|
| + ['pendingDismissals', 'notificationsData'], function(items) {
|
| + items.pendingDismissals = items.pendingDismissals || [];
|
| + items.notificationsData = items.notificationsData || {};
|
|
|
| - var notificationData = items.notificationsData[notificationId];
|
| + var notificationData = items.notificationsData[chromeNotificationId];
|
|
|
| + if (notificationData && notificationData.dismissals) {
|
| + for (var i = 0; i < notificationData.dismissals.length; i++) {
|
| var dismissal = {
|
| - notificationId: notificationId,
|
| + chromeNotificationId: chromeNotificationId,
|
| time: Date.now(),
|
| - parameters: notificationData && notificationData.dismissalParameters
|
| + dismissalData: notificationData.dismissals[i]
|
| };
|
| items.pendingDismissals.push(dismissal);
|
| - chrome.storage.local.set(
|
| - {pendingDismissals: items.pendingDismissals});
|
| - processPendingDismissals(function(success) { callback(); });
|
| - });
|
| + }
|
| +
|
| + chrome.storage.local.set({pendingDismissals: items.pendingDismissals});
|
| + }
|
| +
|
| + processPendingDismissals(function(success) {});
|
| + });
|
| });
|
| }
|
|
|
| @@ -916,14 +1084,14 @@ function showWelcomeToast() {
|
| buttons: buttons
|
| };
|
| instrumented.notifications.create(WELCOME_TOAST_NOTIFICATION_ID, options,
|
| - function(notificationId) {});
|
| + function(chromeNotificationId) {});
|
| }
|
|
|
| /**
|
| * Hides the welcome toast.
|
| */
|
| function hideWelcomeToast() {
|
| - chrome.notifications.clear(
|
| + instrumented.notifications.clear(
|
| WELCOME_TOAST_NOTIFICATION_ID,
|
| function() {});
|
| }
|
| @@ -956,21 +1124,21 @@ authenticationManager.addListener(function() {
|
| });
|
|
|
| instrumented.notifications.onClicked.addListener(
|
| - function(notificationId) {
|
| + function(chromeNotificationId) {
|
| chrome.metricsPrivate.recordUserAction('GoogleNow.MessageClicked');
|
| - onNotificationClicked(notificationId, function(actionUrls) {
|
| - return actionUrls.messageUrl;
|
| + onNotificationClicked(chromeNotificationId, function(actionUrls) {
|
| + return actionUrls && actionUrls.messageUrl;
|
| });
|
| });
|
|
|
| instrumented.notifications.onButtonClicked.addListener(
|
| - function(notificationId, buttonIndex) {
|
| - if (notificationId == WELCOME_TOAST_NOTIFICATION_ID) {
|
| + function(chromeNotificationId, buttonIndex) {
|
| + if (chromeNotificationId == WELCOME_TOAST_NOTIFICATION_ID) {
|
| onToastNotificationClicked(buttonIndex);
|
| } else {
|
| chrome.metricsPrivate.recordUserAction(
|
| 'GoogleNow.ButtonClicked' + buttonIndex);
|
| - onNotificationClicked(notificationId, function(actionUrls) {
|
| + onNotificationClicked(chromeNotificationId, function(actionUrls) {
|
| var url = actionUrls.buttonUrls[buttonIndex];
|
| verify(url, 'onButtonClicked: no url for a button');
|
| return url;
|
|
|