background.js revision a02191e04bc25c4935f804f2c080ae28663d096d
12a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)// Copyright (c) 2013 The Chromium Authors. All rights reserved.
22a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)// Use of this source code is governed by a BSD-style license that can be
32a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)// found in the LICENSE file.
42a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
5868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)'use strict';
62a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
72a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)/**
82a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) * @fileoverview The event page for Google Now for Chrome implementation.
92a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) * The Google Now event page gets Google Now cards from the server and shows
102a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) * them as Chrome notifications.
112a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) * The service performs periodic updating of Google Now cards.
12c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles) * Each updating of the cards includes 4 steps:
13a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) * 1. Processing requests for cards dismissals that are not yet sent to the
14a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) *    server.
15a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) * 2. Making a server request.
16a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) * 3. Showing the received cards as notifications.
172a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) */
182a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
19c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch// TODO(robliao): Decide what to do in incognito mode.
202a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
212a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)/**
222a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) * Standard response code for successful HTTP requests. This is the only success
232a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) * code the server will send.
242a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) */
252a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)var HTTP_OK = 200;
264e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)var HTTP_NOCONTENT = 204;
272a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
28ca12bfac764ba476d6cd062bf1dde12cc64c3f40Ben Murdochvar HTTP_BAD_REQUEST = 400;
29eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdochvar HTTP_UNAUTHORIZED = 401;
30eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdochvar HTTP_FORBIDDEN = 403;
31ca12bfac764ba476d6cd062bf1dde12cc64c3f40Ben Murdochvar HTTP_METHOD_NOT_ALLOWED = 405;
32eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch
334e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)var MS_IN_SECOND = 1000;
344e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)var MS_IN_MINUTE = 60 * 1000;
354e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)
362a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)/**
372a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) * Initial period for polling for Google Now Notifications cards to use when the
382a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) * period from the server is not available.
392a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) */
40c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)var INITIAL_POLLING_PERIOD_SECONDS = 5 * 60;  // 5 minutes
412a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
422a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)/**
434e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles) * Mininal period for polling for Google Now Notifications cards.
444e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles) */
454e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)var MINIMUM_POLLING_PERIOD_SECONDS = 5 * 60;  // 5 minutes
464e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)
474e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)/**
482a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) * Maximal period for polling for Google Now Notifications cards to use when the
492a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) * period from the server is not available.
502a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) */
51c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)var MAXIMUM_POLLING_PERIOD_SECONDS = 60 * 60;  // 1 hour
522a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
53c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)/**
54effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch * Initial period for polling for Google Now optin notification after push
55effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch * messaging indicates Google Now is enabled.
56effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch */
57effb81e5f8246d0db0270817048dc992db66e9fbBen Murdochvar INITIAL_OPTIN_POLLING_PERIOD_SECONDS = 60;  // 1 minute
58effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch
59effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch/**
60effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch * Maximum period for polling for Google Now optin notification after push
61effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch * messaging indicates Google Now is enabled. It is expected that the alarm
62effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch * will be stopped after this.
63effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch */
64effb81e5f8246d0db0270817048dc992db66e9fbBen Murdochvar MAXIMUM_OPTIN_POLLING_PERIOD_SECONDS = 16 * 60;  // 16 minutes
65effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch
66effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch/**
6790dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles) * Initial period for retrying the server request for dismissing cards.
68c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles) */
6990dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)var INITIAL_RETRY_DISMISS_PERIOD_SECONDS = 60;  // 1 minute
70c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)
7190dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)/**
7290dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles) * Maximum period for retrying the server request for dismissing cards.
7390dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles) */
7490dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)var MAXIMUM_RETRY_DISMISS_PERIOD_SECONDS = 60 * 60;  // 1 hour
75c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)
76c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)/**
77ca12bfac764ba476d6cd062bf1dde12cc64c3f40Ben Murdoch * Time we keep retrying dismissals.
78ca12bfac764ba476d6cd062bf1dde12cc64c3f40Ben Murdoch */
79ca12bfac764ba476d6cd062bf1dde12cc64c3f40Ben Murdochvar MAXIMUM_DISMISSAL_AGE_MS = 24 * 60 * 60 * 1000; // 1 day
80ca12bfac764ba476d6cd062bf1dde12cc64c3f40Ben Murdoch
81ca12bfac764ba476d6cd062bf1dde12cc64c3f40Ben Murdoch/**
82c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles) * Time we keep dismissals after successful server dismiss requests.
83c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles) */
84c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)var DISMISS_RETENTION_TIME_MS = 20 * 60 * 1000;  // 20 minutes
85c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)
862a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)/**
870f1bc08d4cfcc34181b0b5cbf065c40f687bf740Torne (Richard Coles) * Default period for checking whether the user is opted in to Google Now.
880f1bc08d4cfcc34181b0b5cbf065c40f687bf740Torne (Richard Coles) */
890f1bc08d4cfcc34181b0b5cbf065c40f687bf740Torne (Richard Coles)var DEFAULT_OPTIN_CHECK_PERIOD_SECONDS = 60 * 60 * 24 * 7; // 1 week
900f1bc08d4cfcc34181b0b5cbf065c40f687bf740Torne (Richard Coles)
910f1bc08d4cfcc34181b0b5cbf065c40f687bf740Torne (Richard Coles)/**
920f1bc08d4cfcc34181b0b5cbf065c40f687bf740Torne (Richard Coles) * URL to open when the user clicked on a link for the our notification
930f1bc08d4cfcc34181b0b5cbf065c40f687bf740Torne (Richard Coles) * settings.
940f1bc08d4cfcc34181b0b5cbf065c40f687bf740Torne (Richard Coles) */
950f1bc08d4cfcc34181b0b5cbf065c40f687bf740Torne (Richard Coles)var SETTINGS_URL = 'https://support.google.com/chrome/?p=ib_google_now_welcome';
960f1bc08d4cfcc34181b0b5cbf065c40f687bf740Torne (Richard Coles)
970f1bc08d4cfcc34181b0b5cbf065c40f687bf740Torne (Richard Coles)/**
985d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) * Number of cards that need an explanatory link.
990f1bc08d4cfcc34181b0b5cbf065c40f687bf740Torne (Richard Coles) */
1005d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)var EXPLANATORY_CARDS_LINK_THRESHOLD = 4;
1010f1bc08d4cfcc34181b0b5cbf065c40f687bf740Torne (Richard Coles)
1020f1bc08d4cfcc34181b0b5cbf065c40f687bf740Torne (Richard Coles)/**
1032a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) * Names for tasks that can be created by the extension.
1042a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) */
1052a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)var UPDATE_CARDS_TASK_NAME = 'update-cards';
1062a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)var DISMISS_CARD_TASK_NAME = 'dismiss-card';
107c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)var RETRY_DISMISS_TASK_NAME = 'retry-dismiss';
108a36e5920737c6adbddd3e43b760e5de8431db6e0Torne (Richard Coles)var STATE_CHANGED_TASK_NAME = 'state-changed';
1094e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)var SHOW_ON_START_TASK_NAME = 'show-cards-on-start';
1108bcbed890bc3ce4d7a057a8f32cab53fa534672eTorne (Richard Coles)var ON_PUSH_MESSAGE_START_TASK_NAME = 'on-push-message';
111c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)
1124e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)/**
113a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles) * Group as received from the server.
1144e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles) *
1154e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles) * @typedef {{
116a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles) *   nextPollSeconds: (string|undefined),
117a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles) *   rank: (number|undefined),
118a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles) *   requested: (boolean|undefined)
1194e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles) * }}
1204e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles) */
121a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)var ReceivedGroup;
122a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)
123a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)/**
124a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles) * Server response with notifications and groups.
125a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles) *
126a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles) * @typedef {{
127a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles) *   googleNowDisabled: (boolean|undefined),
128a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles) *   groups: Object.<string, ReceivedGroup>,
129a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles) *   notifications: Array.<ReceivedNotification>
130a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles) * }}
131a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles) */
132a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)var ServerResponse;
133eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch
134eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch/**
1354e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles) * Notification group as the client stores it. |cardsTimestamp| and |rank| are
1361e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles) * defined if |cards| is non-empty. |nextPollTime| is undefined if the server
1371e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles) * (1) never sent 'nextPollSeconds' for the group or
1381e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles) * (2) didn't send 'nextPollSeconds' with the last group update containing a
1391e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles) *     cards update and all the times after that.
1404e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles) *
1414e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles) * @typedef {{
142a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles) *   cards: Array.<ReceivedNotification>,
143a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles) *   cardsTimestamp: (number|undefined),
144a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles) *   nextPollTime: (number|undefined),
145a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles) *   rank: (number|undefined)
1464e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles) * }}
147eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch */
148a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)var StoredNotificationGroup;
149a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)
150a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)/**
151a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles) * Pending (not yet successfully sent) dismissal for a received notification.
152a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles) * |time| is the moment when the user requested dismissal.
153a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles) *
154a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles) * @typedef {{
155a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles) *   chromeNotificationId: ChromeNotificationId,
156a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles) *   time: number,
157a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles) *   dismissalData: DismissalData
158a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles) * }}
159a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles) */
160a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)var PendingDismissal;
161eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch
162eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch/**
1632a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) * Checks if a new task can't be scheduled when another task is already
1642a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) * scheduled.
1652a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) * @param {string} newTaskName Name of the new task.
166c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles) * @param {string} scheduledTaskName Name of the scheduled task.
1672a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) * @return {boolean} Whether the new task conflicts with the existing task.
1682a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) */
169c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)function areTasksConflicting(newTaskName, scheduledTaskName) {
1702a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  if (newTaskName == UPDATE_CARDS_TASK_NAME &&
171c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)      scheduledTaskName == UPDATE_CARDS_TASK_NAME) {
1722a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    // If a card update is requested while an old update is still scheduled, we
1732a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    // don't need the new update.
1742a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    return true;
1752a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  }
1762a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
177c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  if (newTaskName == RETRY_DISMISS_TASK_NAME &&
178c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)      (scheduledTaskName == UPDATE_CARDS_TASK_NAME ||
179c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)       scheduledTaskName == DISMISS_CARD_TASK_NAME ||
180c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)       scheduledTaskName == RETRY_DISMISS_TASK_NAME)) {
181c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)    // No need to schedule retry-dismiss action if another action that tries to
182c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)    // send dismissals is scheduled.
183c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)    return true;
184c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  }
185c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)
1862a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  return false;
1872a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)}
1882a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
1892a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)var tasks = buildTaskManager(areTasksConflicting);
1902a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
191c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)// Add error processing to API calls.
192424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles)wrapper.instrumentChromeApiFunction('metricsPrivate.getVariationParams', 1);
1934e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)wrapper.instrumentChromeApiFunction('notifications.clear', 1);
194424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles)wrapper.instrumentChromeApiFunction('notifications.create', 2);
1951e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles)wrapper.instrumentChromeApiFunction('notifications.getPermissionLevel', 0);
196424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles)wrapper.instrumentChromeApiFunction('notifications.update', 2);
197424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles)wrapper.instrumentChromeApiFunction('notifications.getAll', 0);
198424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles)wrapper.instrumentChromeApiFunction(
199ba5b9a6411cb1792fd21f0a078d7a25cd1ceec16Ben Murdoch    'notifications.onButtonClicked.addListener', 0);
200424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles)wrapper.instrumentChromeApiFunction('notifications.onClicked.addListener', 0);
201424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles)wrapper.instrumentChromeApiFunction('notifications.onClosed.addListener', 0);
202424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles)wrapper.instrumentChromeApiFunction(
2031e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles)    'notifications.onPermissionLevelChanged.addListener', 0);
2041e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles)wrapper.instrumentChromeApiFunction(
2050f1bc08d4cfcc34181b0b5cbf065c40f687bf740Torne (Richard Coles)    'notifications.onShowSettings.addListener', 0);
206424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles)wrapper.instrumentChromeApiFunction('permissions.contains', 1);
2078bcbed890bc3ce4d7a057a8f32cab53fa534672eTorne (Richard Coles)wrapper.instrumentChromeApiFunction('pushMessaging.onMessage.addListener', 0);
208424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles)wrapper.instrumentChromeApiFunction('runtime.onInstalled.addListener', 0);
209424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles)wrapper.instrumentChromeApiFunction('runtime.onStartup.addListener', 0);
210424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles)wrapper.instrumentChromeApiFunction('tabs.create', 1);
211c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)
21290dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)var updateCardsAttempts = buildAttemptManager(
21390dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)    'cards-update',
214a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)    requestCards,
21590dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)    INITIAL_POLLING_PERIOD_SECONDS,
21690dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)    MAXIMUM_POLLING_PERIOD_SECONDS);
217effb81e5f8246d0db0270817048dc992db66e9fbBen Murdochvar optInCheckAttempts = buildAttemptManager(
218effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch    'optin',
219effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch    pollOptedIn,
220effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch    INITIAL_OPTIN_POLLING_PERIOD_SECONDS,
221effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch    MAXIMUM_OPTIN_POLLING_PERIOD_SECONDS);
22290dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)var dismissalAttempts = buildAttemptManager(
22390dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)    'dismiss',
22490dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)    retryPendingDismissals,
22590dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)    INITIAL_RETRY_DISMISS_PERIOD_SECONDS,
22690dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)    MAXIMUM_RETRY_DISMISS_PERIOD_SECONDS);
227558790d6acca3451cf3a6b497803a5f07d0bec58Ben Murdochvar cardSet = buildCardSet();
22890dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)
229a36e5920737c6adbddd3e43b760e5de8431db6e0Torne (Richard Coles)var authenticationManager = buildAuthenticationManager();
230a36e5920737c6adbddd3e43b760e5de8431db6e0Torne (Richard Coles)
231c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)/**
232a36e5920737c6adbddd3e43b760e5de8431db6e0Torne (Richard Coles) * Google Now UMA event identifier.
233c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles) * @enum {number}
234c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles) */
235a36e5920737c6adbddd3e43b760e5de8431db6e0Torne (Richard Coles)var GoogleNowEvent = {
236c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  REQUEST_FOR_CARDS_TOTAL: 0,
237c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  REQUEST_FOR_CARDS_SUCCESS: 1,
238c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  CARDS_PARSE_SUCCESS: 2,
239c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  DISMISS_REQUEST_TOTAL: 3,
240c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  DISMISS_REQUEST_SUCCESS: 4,
24190dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)  LOCATION_REQUEST: 5,
242a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)  DELETED_LOCATION_UPDATE: 6,
243eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch  EXTENSION_START: 7,
2444e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)  DELETED_SHOW_WELCOME_TOAST: 8,
245a36e5920737c6adbddd3e43b760e5de8431db6e0Torne (Richard Coles)  STOPPED: 9,
2464e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)  DELETED_USER_SUPPRESSED: 10,
247effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch  SIGNED_OUT: 11,
248effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch  NOTIFICATION_DISABLED: 12,
249effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch  GOOGLE_NOW_DISABLED: 13,
250effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch  EVENTS_TOTAL: 14  // EVENTS_TOTAL is not an event; all new events need to be
251a36e5920737c6adbddd3e43b760e5de8431db6e0Torne (Richard Coles)                    // added before it.
252c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)};
253c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)
254c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)/**
255a36e5920737c6adbddd3e43b760e5de8431db6e0Torne (Richard Coles) * Records a Google Now Event.
256a36e5920737c6adbddd3e43b760e5de8431db6e0Torne (Richard Coles) * @param {GoogleNowEvent} event Event identifier.
257c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles) */
258c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)function recordEvent(event) {
259c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  var metricDescription = {
260c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)    metricName: 'GoogleNow.Event',
261c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)    type: 'histogram-linear',
262c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)    min: 1,
263a36e5920737c6adbddd3e43b760e5de8431db6e0Torne (Richard Coles)    max: GoogleNowEvent.EVENTS_TOTAL,
264a36e5920737c6adbddd3e43b760e5de8431db6e0Torne (Richard Coles)    buckets: GoogleNowEvent.EVENTS_TOTAL + 1
265c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  };
266c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)
267c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  chrome.metricsPrivate.recordValue(metricDescription, event);
268c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)}
269c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)
2702a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)/**
271effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch * Records a notification clicked event.
272effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch * @param {number|undefined} cardTypeId Card type ID.
273effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch */
274effb81e5f8246d0db0270817048dc992db66e9fbBen Murdochfunction recordNotificationClick(cardTypeId) {
275effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch  if (cardTypeId !== undefined) {
276effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch    chrome.metricsPrivate.recordSparseValue(
277effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch        'GoogleNow.Card.Clicked', cardTypeId);
278effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch  }
279effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch}
280effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch
281effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch/**
282effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch * Records a button clicked event.
283effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch * @param {number|undefined} cardTypeId Card type ID.
284effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch * @param {number} buttonIndex Button Index
285effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch */
286effb81e5f8246d0db0270817048dc992db66e9fbBen Murdochfunction recordButtonClick(cardTypeId, buttonIndex) {
287effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch  if (cardTypeId !== undefined) {
288effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch    chrome.metricsPrivate.recordSparseValue(
289effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch        'GoogleNow.Card.Button.Clicked' + buttonIndex, cardTypeId);
290effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch  }
291effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch}
292effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch
293effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch/**
294a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) * Checks the result of the HTTP Request and updates the authentication
295a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) * manager on any failure.
296a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) * @param {string} token Authentication token to validate against an
297a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) *     XMLHttpRequest.
298a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) * @return {function(XMLHttpRequest)} Function that validates the token with the
299a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) *     supplied XMLHttpRequest.
300eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch */
301a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)function checkAuthenticationStatus(token) {
302a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)  return function(request) {
303a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)    if (request.status == HTTP_FORBIDDEN ||
304a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)        request.status == HTTP_UNAUTHORIZED) {
305a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)      authenticationManager.removeToken(token);
306eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch    }
307a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)  }
308a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)}
309eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch
310a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)/**
311a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) * Builds and sends an authenticated request to the notification server.
312a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) * @param {string} method Request method.
313a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) * @param {string} handlerName Server handler to send the request to.
314a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) * @param {string=} opt_contentType Value for the Content-type header.
315a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) * @return {Promise} A promise to issue a request to the server.
316c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch *     The promise rejects if the response is not within the HTTP 200 range.
317a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) */
318a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)function requestFromServer(method, handlerName, opt_contentType) {
319a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)  return authenticationManager.getAuthToken().then(function(token) {
320a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)    var request = buildServerRequest(method, handlerName, opt_contentType);
321eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch    request.setRequestHeader('Authorization', 'Bearer ' + token);
322c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch    var requestPromise = new Promise(function(resolve, reject) {
323a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)      request.addEventListener('loadend', function() {
324c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch        if ((200 <= request.status) && (request.status < 300)) {
325c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch          resolve(request);
326c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch        } else {
327c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch          reject(request);
328c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch        }
329a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)      }, false);
330a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)      request.send();
331eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch    });
332c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch    requestPromise.catch(checkAuthenticationStatus(token));
333a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)    return requestPromise;
334eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch  });
335eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch}
336eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch
337eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch/**
338c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch * Shows the notification groups as notification cards.
3395d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) * @param {Object.<string, StoredNotificationGroup>} notificationGroups Map from
3405d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) *     group name to group information.
341c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch * @param {function(ReceivedNotification)=} opt_onCardShown Optional parameter
342a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles) *     called when each card is shown.
343c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch * @return {Promise} A promise to show the notification groups as cards.
3442a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) */
345c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdochfunction showNotificationGroups(notificationGroups, opt_onCardShown) {
346a02191e04bc25c4935f804f2c080ae28663d096dBen Murdoch  /** @type {Object.<ChromeNotificationId, CombinedCard>} */
347c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch  var cards = combineCardsFromGroups(notificationGroups);
348c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch  console.log('showNotificationGroups ' + JSON.stringify(cards));
349ba5b9a6411cb1792fd21f0a078d7a25cd1ceec16Ben Murdoch
350c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch  return new Promise(function(resolve) {
351c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch    instrumented.notifications.getAll(function(notifications) {
352c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch      console.log('showNotificationGroups-getAll ' +
353c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch          JSON.stringify(notifications));
354c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch      notifications = notifications || {};
355c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch
356c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch      // Mark notifications that didn't receive an update as having received
357c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch      // an empty update.
358c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch      for (var chromeNotificationId in notifications) {
359c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch        cards[chromeNotificationId] = cards[chromeNotificationId] || [];
360c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch      }
361ba5b9a6411cb1792fd21f0a078d7a25cd1ceec16Ben Murdoch
362a02191e04bc25c4935f804f2c080ae28663d096dBen Murdoch      /** @type {Object.<ChromeNotificationId, NotificationDataEntry>} */
363c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch      var notificationsData = {};
364c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch
365c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch      // Create/update/delete notifications.
366c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch      for (var chromeNotificationId in cards) {
367c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch        notificationsData[chromeNotificationId] = cardSet.update(
368c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch            chromeNotificationId,
369c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch            cards[chromeNotificationId],
370c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch            notificationGroups,
371c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch            opt_onCardShown);
372c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch      }
373c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch      chrome.storage.local.set({notificationsData: notificationsData});
374c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch      resolve();
375c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch    });
376a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)  });
3772a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)}
3782a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
3792a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)/**
380a36e5920737c6adbddd3e43b760e5de8431db6e0Torne (Richard Coles) * Removes all cards and card state on Google Now close down.
381a36e5920737c6adbddd3e43b760e5de8431db6e0Torne (Richard Coles) */
382a36e5920737c6adbddd3e43b760e5de8431db6e0Torne (Richard Coles)function removeAllCards() {
383a36e5920737c6adbddd3e43b760e5de8431db6e0Torne (Richard Coles)  console.log('removeAllCards');
384a36e5920737c6adbddd3e43b760e5de8431db6e0Torne (Richard Coles)
385a36e5920737c6adbddd3e43b760e5de8431db6e0Torne (Richard Coles)  // TODO(robliao): Once Google Now clears its own checkbox in the
386a36e5920737c6adbddd3e43b760e5de8431db6e0Torne (Richard Coles)  // notifications center and bug 260376 is fixed, the below clearing
387a36e5920737c6adbddd3e43b760e5de8431db6e0Torne (Richard Coles)  // code is no longer necessary.
388ba5b9a6411cb1792fd21f0a078d7a25cd1ceec16Ben Murdoch  instrumented.notifications.getAll(function(notifications) {
3893551c9c881056c480085172ff9840cab31610854Torne (Richard Coles)    notifications = notifications || {};
3904e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)    for (var chromeNotificationId in notifications) {
3914e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)      instrumented.notifications.clear(chromeNotificationId, function() {});
392a36e5920737c6adbddd3e43b760e5de8431db6e0Torne (Richard Coles)    }
3934e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)    chrome.storage.local.remove(['notificationsData', 'notificationGroups']);
3944e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)  });
3954e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)}
3964e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)
3974e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)/**
398a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles) * Adds a card group into a set of combined cards.
399a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles) * @param {Object.<ChromeNotificationId, CombinedCard>} combinedCards Map from
400a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles) *     chromeNotificationId to a combined card.
401a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles) *     This is an input/output parameter.
402a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles) * @param {StoredNotificationGroup} storedGroup Group to combine into the
403a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles) *     combined card set.
4044e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles) */
405a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)function combineGroup(combinedCards, storedGroup) {
406a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)  for (var i = 0; i < storedGroup.cards.length; i++) {
407a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)    /** @type {ReceivedNotification} */
408a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)    var receivedNotification = storedGroup.cards[i];
409a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)
410a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)    /** @type {UncombinedNotification} */
411a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)    var uncombinedNotification = {
412a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)      receivedNotification: receivedNotification,
413a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)      showTime: receivedNotification.trigger.showTimeSec &&
414a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)                (storedGroup.cardsTimestamp +
415a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)                 receivedNotification.trigger.showTimeSec * MS_IN_SECOND),
416a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)      hideTime: storedGroup.cardsTimestamp +
417a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)                receivedNotification.trigger.hideTimeSec * MS_IN_SECOND
4184e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)    };
4194e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)
420a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)    var combinedCard =
421a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)        combinedCards[receivedNotification.chromeNotificationId] || [];
422a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)    combinedCard.push(uncombinedNotification);
423a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)    combinedCards[receivedNotification.chromeNotificationId] = combinedCard;
4244e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)  }
4254e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)}
4264e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)
4274e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)/**
4284e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles) * Schedules next cards poll.
429a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles) * @param {Object.<string, StoredNotificationGroup>} groups Map from group name
430a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles) *     to group information.
4310f1bc08d4cfcc34181b0b5cbf065c40f687bf740Torne (Richard Coles) * @param {boolean} isOptedIn True if the user is opted in to Google Now.
4324e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles) */
4330f1bc08d4cfcc34181b0b5cbf065c40f687bf740Torne (Richard Coles)function scheduleNextPoll(groups, isOptedIn) {
4340f1bc08d4cfcc34181b0b5cbf065c40f687bf740Torne (Richard Coles)  if (isOptedIn) {
4350f1bc08d4cfcc34181b0b5cbf065c40f687bf740Torne (Richard Coles)    var nextPollTime = null;
4360f1bc08d4cfcc34181b0b5cbf065c40f687bf740Torne (Richard Coles)
4370f1bc08d4cfcc34181b0b5cbf065c40f687bf740Torne (Richard Coles)    for (var groupName in groups) {
4380f1bc08d4cfcc34181b0b5cbf065c40f687bf740Torne (Richard Coles)      var group = groups[groupName];
4390f1bc08d4cfcc34181b0b5cbf065c40f687bf740Torne (Richard Coles)      if (group.nextPollTime !== undefined) {
4400f1bc08d4cfcc34181b0b5cbf065c40f687bf740Torne (Richard Coles)        nextPollTime = nextPollTime == null ?
4410f1bc08d4cfcc34181b0b5cbf065c40f687bf740Torne (Richard Coles)            group.nextPollTime : Math.min(group.nextPollTime, nextPollTime);
4420f1bc08d4cfcc34181b0b5cbf065c40f687bf740Torne (Richard Coles)      }
4431e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles)    }
4444e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)
4450f1bc08d4cfcc34181b0b5cbf065c40f687bf740Torne (Richard Coles)    // At least one of the groups must have nextPollTime.
4460f1bc08d4cfcc34181b0b5cbf065c40f687bf740Torne (Richard Coles)    verify(nextPollTime != null, 'scheduleNextPoll: nextPollTime is null');
4474e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)
4480f1bc08d4cfcc34181b0b5cbf065c40f687bf740Torne (Richard Coles)    var nextPollDelaySeconds = Math.max(
4490f1bc08d4cfcc34181b0b5cbf065c40f687bf740Torne (Richard Coles)        (nextPollTime - Date.now()) / MS_IN_SECOND,
4500f1bc08d4cfcc34181b0b5cbf065c40f687bf740Torne (Richard Coles)        MINIMUM_POLLING_PERIOD_SECONDS);
4510f1bc08d4cfcc34181b0b5cbf065c40f687bf740Torne (Richard Coles)    updateCardsAttempts.start(nextPollDelaySeconds);
4520f1bc08d4cfcc34181b0b5cbf065c40f687bf740Torne (Richard Coles)  } else {
4530f1bc08d4cfcc34181b0b5cbf065c40f687bf740Torne (Richard Coles)    instrumented.metricsPrivate.getVariationParams(
4540f1bc08d4cfcc34181b0b5cbf065c40f687bf740Torne (Richard Coles)        'GoogleNow', function(params) {
4550f1bc08d4cfcc34181b0b5cbf065c40f687bf740Torne (Richard Coles)      var optinPollPeriodSeconds =
4560f1bc08d4cfcc34181b0b5cbf065c40f687bf740Torne (Richard Coles)          parseInt(params && params.optinPollPeriodSeconds, 10) ||
4570f1bc08d4cfcc34181b0b5cbf065c40f687bf740Torne (Richard Coles)          DEFAULT_OPTIN_CHECK_PERIOD_SECONDS;
4580f1bc08d4cfcc34181b0b5cbf065c40f687bf740Torne (Richard Coles)      updateCardsAttempts.start(optinPollPeriodSeconds);
4590f1bc08d4cfcc34181b0b5cbf065c40f687bf740Torne (Richard Coles)    });
4600f1bc08d4cfcc34181b0b5cbf065c40f687bf740Torne (Richard Coles)  }
4614e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)}
4624e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)
4634e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)/**
464c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch * Combines notification groups into a set of Chrome notifications.
465a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles) * @param {Object.<string, StoredNotificationGroup>} notificationGroups Map from
466a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles) *     group name to group information.
467c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch * @return {Object.<ChromeNotificationId, CombinedCard>} Cards to show.
4684e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles) */
469c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdochfunction combineCardsFromGroups(notificationGroups) {
470c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch  console.log('combineCardsFromGroups ' + JSON.stringify(notificationGroups));
471a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)  /** @type {Object.<ChromeNotificationId, CombinedCard>} */
472a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)  var combinedCards = {};
4734e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)
4744e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)  for (var groupName in notificationGroups)
475a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)    combineGroup(combinedCards, notificationGroups[groupName]);
4764e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)
477c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch  return combinedCards;
4784e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)}
4794e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)
4804e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)/**
481c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch * Processes a server response for consumption by showNotificationGroups.
4825d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) * @param {ServerResponse} response Server response.
483c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch * @return {Promise} A promise to process the server response and provide
484c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch *     updated groups. Rejects if the server response shouldn't be processed.
4854e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles) */
486c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdochfunction processServerResponse(response) {
4875d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  console.log('processServerResponse ' + JSON.stringify(response));
4884e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)
4895d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  if (response.googleNowDisabled) {
4900f1bc08d4cfcc34181b0b5cbf065c40f687bf740Torne (Richard Coles)    chrome.storage.local.set({googleNowEnabled: false});
491c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch    // TODO(robliao): Remove the line below once the server stops sending groups
4920f1bc08d4cfcc34181b0b5cbf065c40f687bf740Torne (Richard Coles)    // with 'googleNowDisabled' responses.
4935d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    response.groups = {};
4940f1bc08d4cfcc34181b0b5cbf065c40f687bf740Torne (Richard Coles)    // Google Now was enabled; now it's disabled. This is a state change.
4950f1bc08d4cfcc34181b0b5cbf065c40f687bf740Torne (Richard Coles)    onStateChange();
496a02191e04bc25c4935f804f2c080ae28663d096dBen Murdoch    // Start the Google Now Disabled polling period.
497a02191e04bc25c4935f804f2c080ae28663d096dBen Murdoch    scheduleNextPoll({}, false);
498a02191e04bc25c4935f804f2c080ae28663d096dBen Murdoch    // Stop processing now. The state change will clear the cards.
499a02191e04bc25c4935f804f2c080ae28663d096dBen Murdoch    return Promise.reject();
5000f1bc08d4cfcc34181b0b5cbf065c40f687bf740Torne (Richard Coles)  }
5010f1bc08d4cfcc34181b0b5cbf065c40f687bf740Torne (Richard Coles)
5025d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  var receivedGroups = response.groups;
5034e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)
504c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch  return fillFromChromeLocalStorage({
505a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)    /** @type {Object.<string, StoredNotificationGroup>} */
506a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)    notificationGroups: {},
507a02191e04bc25c4935f804f2c080ae28663d096dBen Murdoch    /** @type {Object.<ServerNotificationId, number>} */
508a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)    recentDismissals: {}
509a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)  }).then(function(items) {
510a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)    console.log('processServerResponse-get ' + JSON.stringify(items));
511a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)
512a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)    // Build a set of non-expired recent dismissals. It will be used for
513a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)    // client-side filtering of cards.
514a02191e04bc25c4935f804f2c080ae28663d096dBen Murdoch    /** @type {Object.<ServerNotificationId, number>} */
515a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)    var updatedRecentDismissals = {};
516a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)    var now = Date.now();
517a02191e04bc25c4935f804f2c080ae28663d096dBen Murdoch    for (var serverNotificationId in items.recentDismissals) {
518a02191e04bc25c4935f804f2c080ae28663d096dBen Murdoch      var dismissalAge = now - items.recentDismissals[serverNotificationId];
519a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)      if (dismissalAge < DISMISS_RETENTION_TIME_MS) {
520a02191e04bc25c4935f804f2c080ae28663d096dBen Murdoch        updatedRecentDismissals[serverNotificationId] =
521a02191e04bc25c4935f804f2c080ae28663d096dBen Murdoch            items.recentDismissals[serverNotificationId];
522a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)      }
523a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)    }
5244e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)
525a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)    // Populate groups with corresponding cards.
526a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)    if (response.notifications) {
527a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)      for (var i = 0; i < response.notifications.length; ++i) {
528a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)        /** @type {ReceivedNotification} */
529a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)        var card = response.notifications[i];
530a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)        if (!(card.notificationId in updatedRecentDismissals)) {
531a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)          var group = receivedGroups[card.groupName];
532a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)          group.cards = group.cards || [];
533a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)          group.cards.push(card);
534a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)        }
535a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)      }
536a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)    }
5374e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)
538a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)    // Build updated set of groups.
539a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)    var updatedGroups = {};
540a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)
541a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)    for (var groupName in receivedGroups) {
542a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)      var receivedGroup = receivedGroups[groupName];
543a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)      var storedGroup = items.notificationGroups[groupName] || {
544a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)        cards: [],
545a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)        cardsTimestamp: undefined,
546a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)        nextPollTime: undefined,
547a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)        rank: undefined
548a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)      };
549a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)
550a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)      if (receivedGroup.requested)
551a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)        receivedGroup.cards = receivedGroup.cards || [];
552a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)
553a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)      if (receivedGroup.cards) {
554a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)        // If the group contains a cards update, all its fields will get new
555a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)        // values.
556a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)        storedGroup.cards = receivedGroup.cards;
557a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)        storedGroup.cardsTimestamp = now;
558a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)        storedGroup.rank = receivedGroup.rank;
559a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)        storedGroup.nextPollTime = undefined;
560a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)        // The code below assigns nextPollTime a defined value if
561a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)        // nextPollSeconds is specified in the received group.
562a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)        // If the group's cards are not updated, and nextPollSeconds is
563a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)        // unspecified, this method doesn't change group's nextPollTime.
564a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)      }
5654e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)
566a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)      // 'nextPollSeconds' may be sent even for groups that don't contain
567a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)      // cards updates.
568a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)      if (receivedGroup.nextPollSeconds !== undefined) {
569a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)        storedGroup.nextPollTime =
570a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)            now + receivedGroup.nextPollSeconds * MS_IN_SECOND;
571a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)      }
5724e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)
573a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)      updatedGroups[groupName] = storedGroup;
574a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)    }
5754e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)
576a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)    scheduleNextPoll(updatedGroups, !response.googleNowDisabled);
577c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch    return {
578c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch      updatedGroups: updatedGroups,
579c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch      recentDismissals: updatedRecentDismissals
580c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch    };
581a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)  });
582a36e5920737c6adbddd3e43b760e5de8431db6e0Torne (Richard Coles)}
583a36e5920737c6adbddd3e43b760e5de8431db6e0Torne (Richard Coles)
584a36e5920737c6adbddd3e43b760e5de8431db6e0Torne (Richard Coles)/**
5855d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) * Update the Explanatory Total Cards Shown Count.
5860f1bc08d4cfcc34181b0b5cbf065c40f687bf740Torne (Richard Coles) */
5875d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)function countExplanatoryCard() {
5885d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  localStorage['explanatoryCardsShown']++;
5890f1bc08d4cfcc34181b0b5cbf065c40f687bf740Torne (Richard Coles)}
5900f1bc08d4cfcc34181b0b5cbf065c40f687bf740Torne (Richard Coles)
5910f1bc08d4cfcc34181b0b5cbf065c40f687bf740Torne (Richard Coles)/**
592c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch * Determines if cards should have an explanation link.
593c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch * @return {boolean} true if an explanatory card should be shown.
594c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch */
595c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdochfunction shouldShowExplanatoryCard() {
596c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch  var isBelowThreshold =
597c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch      localStorage['explanatoryCardsShown'] < EXPLANATORY_CARDS_LINK_THRESHOLD;
598c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch  return isBelowThreshold;
599c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch}
600c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch
601c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch/**
6028bcbed890bc3ce4d7a057a8f32cab53fa534672eTorne (Richard Coles) * Requests notification cards from the server for specified groups.
6038bcbed890bc3ce4d7a057a8f32cab53fa534672eTorne (Richard Coles) * @param {Array.<string>} groupNames Names of groups that need to be refreshed.
604c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch * @return {Promise} A promise to request the specified notification groups.
6058bcbed890bc3ce4d7a057a8f32cab53fa534672eTorne (Richard Coles) */
606c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdochfunction requestNotificationGroupsFromServer(groupNames) {
607c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch  console.log(
608c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch      'requestNotificationGroupsFromServer from ' + NOTIFICATION_CARDS_URL +
6098bcbed890bc3ce4d7a057a8f32cab53fa534672eTorne (Richard Coles)      ', groupNames=' + JSON.stringify(groupNames));
6108bcbed890bc3ce4d7a057a8f32cab53fa534672eTorne (Richard Coles)
6118bcbed890bc3ce4d7a057a8f32cab53fa534672eTorne (Richard Coles)  recordEvent(GoogleNowEvent.REQUEST_FOR_CARDS_TOTAL);
6128bcbed890bc3ce4d7a057a8f32cab53fa534672eTorne (Richard Coles)
6138bcbed890bc3ce4d7a057a8f32cab53fa534672eTorne (Richard Coles)  var requestParameters = '?timeZoneOffsetMs=' +
6140f1bc08d4cfcc34181b0b5cbf065c40f687bf740Torne (Richard Coles)    (-new Date().getTimezoneOffset() * MS_IN_MINUTE);
6150f1bc08d4cfcc34181b0b5cbf065c40f687bf740Torne (Richard Coles)
616c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch  if (shouldShowExplanatoryCard()) {
6175d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    requestParameters += '&cardExplanation=true';
6180f1bc08d4cfcc34181b0b5cbf065c40f687bf740Torne (Richard Coles)  }
6198bcbed890bc3ce4d7a057a8f32cab53fa534672eTorne (Richard Coles)
6208bcbed890bc3ce4d7a057a8f32cab53fa534672eTorne (Richard Coles)  groupNames.forEach(function(groupName) {
6218bcbed890bc3ce4d7a057a8f32cab53fa534672eTorne (Richard Coles)    requestParameters += ('&requestTypes=' + groupName);
6228bcbed890bc3ce4d7a057a8f32cab53fa534672eTorne (Richard Coles)  });
6238bcbed890bc3ce4d7a057a8f32cab53fa534672eTorne (Richard Coles)
624a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)  requestParameters += '&uiLocale=' + navigator.language;
6258bcbed890bc3ce4d7a057a8f32cab53fa534672eTorne (Richard Coles)
626c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch  console.log(
627c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch      'requestNotificationGroupsFromServer: request=' + requestParameters);
6280f1bc08d4cfcc34181b0b5cbf065c40f687bf740Torne (Richard Coles)
629c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch  return requestFromServer('GET', 'notifications' + requestParameters).then(
630a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)    function(request) {
631c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch      console.log(
632c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch          'requestNotificationGroupsFromServer-received ' + request.status);
633a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)      if (request.status == HTTP_OK) {
634a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)        recordEvent(GoogleNowEvent.REQUEST_FOR_CARDS_SUCCESS);
635c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch        return JSON.parse(request.responseText);
636a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)      }
637a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)    });
6380f1bc08d4cfcc34181b0b5cbf065c40f687bf740Torne (Richard Coles)}
6390f1bc08d4cfcc34181b0b5cbf065c40f687bf740Torne (Richard Coles)
6400f1bc08d4cfcc34181b0b5cbf065c40f687bf740Torne (Richard Coles)/**
641c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch * Requests the account opted-in state from the server and updates any
642c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch * state as necessary.
643c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch * @return {Promise} A promise to request and update the opted-in state.
644c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch *     The promise resolves if the opt-in state is true.
6450f1bc08d4cfcc34181b0b5cbf065c40f687bf740Torne (Richard Coles) */
646c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdochfunction requestAndUpdateOptedIn() {
6470f1bc08d4cfcc34181b0b5cbf065c40f687bf740Torne (Richard Coles)  console.log('requestOptedIn from ' + NOTIFICATION_CARDS_URL);
6480f1bc08d4cfcc34181b0b5cbf065c40f687bf740Torne (Richard Coles)
649c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch  return requestFromServer('GET', 'settings/optin').then(function(request) {
6500f1bc08d4cfcc34181b0b5cbf065c40f687bf740Torne (Richard Coles)    console.log(
651a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)        'requestOptedIn-received ' + request.status + ' ' + request.response);
6520f1bc08d4cfcc34181b0b5cbf065c40f687bf740Torne (Richard Coles)    if (request.status == HTTP_OK) {
653a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)      var parsedResponse = JSON.parse(request.responseText);
654c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch      return parsedResponse.value;
655c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch    }
656c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch  }).then(function(optedIn) {
657c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch    if (optedIn) {
658c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch      chrome.storage.local.set({googleNowEnabled: true});
659c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch      // Google Now was disabled, now it's enabled. This is a state change.
660c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch      onStateChange();
661c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch      return Promise.resolve();
662c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch    } else {
663c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch      scheduleNextPoll({}, false);
664c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch      return Promise.reject();
6658bcbed890bc3ce4d7a057a8f32cab53fa534672eTorne (Richard Coles)    }
6668bcbed890bc3ce4d7a057a8f32cab53fa534672eTorne (Richard Coles)  });
6678bcbed890bc3ce4d7a057a8f32cab53fa534672eTorne (Richard Coles)}
6688bcbed890bc3ce4d7a057a8f32cab53fa534672eTorne (Richard Coles)
6698bcbed890bc3ce4d7a057a8f32cab53fa534672eTorne (Richard Coles)/**
670c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch * Determines the groups that need to be requested right now.
671c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch * @return {Promise} A promise to determine the groups to request.
6722a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) */
673c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdochfunction getGroupsToRequest() {
674c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch  return fillFromChromeLocalStorage({
675a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)    /** @type {Object.<string, StoredNotificationGroup>} */
676c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch    notificationGroups: {}
677a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)  }).then(function(items) {
678c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch    console.log('getGroupsToRequest-storage-get ' + JSON.stringify(items));
6798bcbed890bc3ce4d7a057a8f32cab53fa534672eTorne (Richard Coles)    var groupsToRequest = [];
680a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)    var now = Date.now();
6812a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
682a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)    for (var groupName in items.notificationGroups) {
683a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)      var group = items.notificationGroups[groupName];
684a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)      if (group.nextPollTime !== undefined && group.nextPollTime <= now)
685a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)        groupsToRequest.push(groupName);
686eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch    }
687c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch    return groupsToRequest;
688c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch  });
689c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch}
6904e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)
691c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch/**
692c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch * Requests notification cards from the server.
693c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch * @return {Promise} A promise to request the notification cards.
694c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch *     Rejects if the cards won't be requested.
695c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch */
696c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdochfunction requestNotificationCards() {
697c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch  console.log('requestNotificationCards');
698c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch
699c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch  return isGoogleNowEnabled()
700c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch      .then(function(googleNowEnabled) {
701c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch        return googleNowEnabled ? Promise.resolve() : requestAndUpdateOptedIn();
702c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch      })
703c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch      .then(getGroupsToRequest)
704c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch      .then(requestNotificationGroupsFromServer)
705c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch      .then(processServerResponse)
706c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch      .then(function(processedResponse) {
707c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch        var onCardShown =
708c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch            shouldShowExplanatoryCard() ? countExplanatoryCard : undefined;
709c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch        return showNotificationGroups(
710c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch            processedResponse.updatedGroups, onCardShown).then(function() {
711c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch              chrome.storage.local.set({
712c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch                notificationGroups: processedResponse.updatedGroups,
713c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch                recentDismissals: processedResponse.updatedRecentDismissals
714c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch              });
715c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch              recordEvent(GoogleNowEvent.CARDS_PARSE_SUCCESS);
716c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch            }
717c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch          );
7180f1bc08d4cfcc34181b0b5cbf065c40f687bf740Torne (Richard Coles)      });
7192a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)}
7202a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
7212a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)/**
722a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) * Requests and shows notification cards.
7232a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) */
724a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)function requestCards() {
725a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)  console.log('requestCards @' + new Date());
726a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)  // LOCATION_REQUEST is a legacy histogram value when we requested location.
727a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)  // This corresponds to the extension attempting to request for cards.
728a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)  // We're keeping the name the same to keep our histograms in order.
729a36e5920737c6adbddd3e43b760e5de8431db6e0Torne (Richard Coles)  recordEvent(GoogleNowEvent.LOCATION_REQUEST);
7304e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)  tasks.add(UPDATE_CARDS_TASK_NAME, function() {
731a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)    console.log('requestCards-task-begin');
732a36e5920737c6adbddd3e43b760e5de8431db6e0Torne (Richard Coles)    updateCardsAttempts.isRunning(function(running) {
733a36e5920737c6adbddd3e43b760e5de8431db6e0Torne (Richard Coles)      if (running) {
734a36e5920737c6adbddd3e43b760e5de8431db6e0Torne (Richard Coles)        updateCardsAttempts.planForNext(function() {
735a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)          // The cards are requested only if there are no unsent dismissals.
736a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)          processPendingDismissals().then(requestNotificationCards);
737a36e5920737c6adbddd3e43b760e5de8431db6e0Torne (Richard Coles)        });
738a36e5920737c6adbddd3e43b760e5de8431db6e0Torne (Richard Coles)      }
7392a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    });
7402a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  });
7412a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)}
7422a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
7432a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)/**
744c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles) * Sends a server request to dismiss a card.
745a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles) * @param {ChromeNotificationId} chromeNotificationId chrome.notifications ID of
746a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles) *     the card.
747c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles) * @param {number} dismissalTimeMs Time of the user's dismissal of the card in
748c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles) *     milliseconds since epoch.
7494e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles) * @param {DismissalData} dismissalData Data to build a dismissal request.
750a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) * @return {Promise} A promise to request the card dismissal, rejects on error.
751c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles) */
752c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)function requestCardDismissal(
753a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)    chromeNotificationId, dismissalTimeMs, dismissalData) {
754a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)  console.log('requestDismissingCard ' + chromeNotificationId +
755a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)      ' from ' + NOTIFICATION_CARDS_URL +
756a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)      ', dismissalData=' + JSON.stringify(dismissalData));
757ca12bfac764ba476d6cd062bf1dde12cc64c3f40Ben Murdoch
758ca12bfac764ba476d6cd062bf1dde12cc64c3f40Ben Murdoch  var dismissalAge = Date.now() - dismissalTimeMs;
759ca12bfac764ba476d6cd062bf1dde12cc64c3f40Ben Murdoch
760ca12bfac764ba476d6cd062bf1dde12cc64c3f40Ben Murdoch  if (dismissalAge > MAXIMUM_DISMISSAL_AGE_MS) {
76123730a6e56a168d1879203e4b3819bb36e3d8f1fTorne (Richard Coles)    return Promise.resolve();
762ca12bfac764ba476d6cd062bf1dde12cc64c3f40Ben Murdoch  }
763ca12bfac764ba476d6cd062bf1dde12cc64c3f40Ben Murdoch
764a36e5920737c6adbddd3e43b760e5de8431db6e0Torne (Richard Coles)  recordEvent(GoogleNowEvent.DISMISS_REQUEST_TOTAL);
7654e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)
766a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)  var requestParameters = 'notifications/' + dismissalData.notificationId +
7674e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)      '?age=' + dismissalAge +
7684e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)      '&chromeNotificationId=' + chromeNotificationId;
7694e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)
7704e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)  for (var paramField in dismissalData.parameters)
771a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)    requestParameters += ('&' + paramField +
772a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)    '=' + dismissalData.parameters[paramField]);
7734e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)
774a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)  console.log('requestCardDismissal: requestParameters=' + requestParameters);
7754e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)
776a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)  return requestFromServer('DELETE', requestParameters).then(function(request) {
777c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)    console.log('requestDismissingCard-onloadend ' + request.status);
7784e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)    if (request.status == HTTP_NOCONTENT)
779a36e5920737c6adbddd3e43b760e5de8431db6e0Torne (Richard Coles)      recordEvent(GoogleNowEvent.DISMISS_REQUEST_SUCCESS);
780c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)
781ca12bfac764ba476d6cd062bf1dde12cc64c3f40Ben Murdoch    // A dismissal doesn't require further retries if it was successful or
782ca12bfac764ba476d6cd062bf1dde12cc64c3f40Ben Murdoch    // doesn't have a chance for successful completion.
783c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch    return (request.status == HTTP_NOCONTENT) ?
784c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch           Promise.resolve() :
785c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch           Promise.reject();
786c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch  }).catch(function(request) {
787c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch    return (request.status == HTTP_BAD_REQUEST ||
788c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch           request.status == HTTP_METHOD_NOT_ALLOWED) ?
789c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch           Promise.resolve() :
790c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch           Promise.reject();
791eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch  });
792c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)}
793c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)
794c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)/**
795c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles) * Tries to send dismiss requests for all pending dismissals.
796a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) * @return {Promise} A promise to process the pending dismissals.
797a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) *     The promise is rejected if a problem was encountered.
798c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles) */
799a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)function processPendingDismissals() {
800a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)  return fillFromChromeLocalStorage({
801a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)    /** @type {Array.<PendingDismissal>} */
802a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)    pendingDismissals: [],
803a02191e04bc25c4935f804f2c080ae28663d096dBen Murdoch    /** @type {Object.<ServerNotificationId, number>} */
804a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)    recentDismissals: {}
805a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)  }).then(function(items) {
806a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)    console.log(
807a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)        'processPendingDismissals-storage-get ' + JSON.stringify(items));
808c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)
809a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)    var dismissalsChanged = false;
810c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)
811a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)    function onFinish(success) {
812a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)      if (dismissalsChanged) {
813a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)        chrome.storage.local.set({
814a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)          pendingDismissals: items.pendingDismissals,
815a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)          recentDismissals: items.recentDismissals
816a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)        });
817a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)      }
818a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)      return success ? Promise.resolve() : Promise.reject();
819a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)    }
820a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)
821a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)    function doProcessDismissals() {
822a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)      if (items.pendingDismissals.length == 0) {
823a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)        dismissalAttempts.stop();
824a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)        return onFinish(true);
825a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)      }
826a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)
827a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)      // Send dismissal for the first card, and if successful, repeat
828a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)      // recursively with the rest.
829a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)      /** @type {PendingDismissal} */
830a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)      var dismissal = items.pendingDismissals[0];
831a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)      return requestCardDismissal(
832a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)          dismissal.chromeNotificationId,
833a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)          dismissal.time,
834a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)          dismissal.dismissalData).then(function() {
835a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)            dismissalsChanged = true;
836a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)            items.pendingDismissals.splice(0, 1);
837a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)            items.recentDismissals[dismissal.dismissalData.notificationId] =
838a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)                Date.now();
839a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)            return doProcessDismissals();
840a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)          }).catch(function() {
841a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)            return onFinish(false);
842a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)          });
843a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)    }
844a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)
845a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)    return doProcessDismissals();
846a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)  });
847c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)}
848c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)
849c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)/**
850c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles) * Submits a task to send pending dismissals.
851c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles) */
852c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)function retryPendingDismissals() {
8534e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)  tasks.add(RETRY_DISMISS_TASK_NAME, function() {
85490dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)    dismissalAttempts.planForNext(function() {
855a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)      processPendingDismissals();
85690dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)     });
857c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  });
858c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)}
859c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)
860c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)/**
8610f1bc08d4cfcc34181b0b5cbf065c40f687bf740Torne (Richard Coles) * Opens a URL in a new tab.
8620f1bc08d4cfcc34181b0b5cbf065c40f687bf740Torne (Richard Coles) * @param {string} url URL to open.
8630f1bc08d4cfcc34181b0b5cbf065c40f687bf740Torne (Richard Coles) */
8640f1bc08d4cfcc34181b0b5cbf065c40f687bf740Torne (Richard Coles)function openUrl(url) {
8650f1bc08d4cfcc34181b0b5cbf065c40f687bf740Torne (Richard Coles)  instrumented.tabs.create({url: url}, function(tab) {
8660f1bc08d4cfcc34181b0b5cbf065c40f687bf740Torne (Richard Coles)    if (tab)
8670f1bc08d4cfcc34181b0b5cbf065c40f687bf740Torne (Richard Coles)      chrome.windows.update(tab.windowId, {focused: true});
8680f1bc08d4cfcc34181b0b5cbf065c40f687bf740Torne (Richard Coles)    else
8690f1bc08d4cfcc34181b0b5cbf065c40f687bf740Torne (Richard Coles)      chrome.windows.create({url: url, focused: true});
8700f1bc08d4cfcc34181b0b5cbf065c40f687bf740Torne (Richard Coles)  });
8710f1bc08d4cfcc34181b0b5cbf065c40f687bf740Torne (Richard Coles)}
8720f1bc08d4cfcc34181b0b5cbf065c40f687bf740Torne (Richard Coles)
8730f1bc08d4cfcc34181b0b5cbf065c40f687bf740Torne (Richard Coles)/**
8742a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) * Opens URL corresponding to the clicked part of the notification.
875a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles) * @param {ChromeNotificationId} chromeNotificationId chrome.notifications ID of
876a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles) *     the card.
877effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch * @param {function(NotificationDataEntry): (string|undefined)} selector
878effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch *     Function that extracts the url for the clicked area from the
879effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch *     notification data entry.
8802a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) */
8814e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)function onNotificationClicked(chromeNotificationId, selector) {
882a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)  fillFromChromeLocalStorage({
883a02191e04bc25c4935f804f2c080ae28663d096dBen Murdoch    /** @type {Object.<ChromeNotificationId, NotificationDataEntry>} */
884a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)    notificationsData: {}
885a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)  }).then(function(items) {
886a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)    /** @type {(NotificationDataEntry|undefined)} */
887effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch    var notificationDataEntry = items.notificationsData[chromeNotificationId];
888effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch    if (!notificationDataEntry)
889868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)      return;
8902a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
891effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch    var url = selector(notificationDataEntry);
8923551c9c881056c480085172ff9840cab31610854Torne (Richard Coles)    if (!url)
893868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)      return;
894868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)
8950f1bc08d4cfcc34181b0b5cbf065c40f687bf740Torne (Richard Coles)    openUrl(url);
8962a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  });
8972a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)}
8982a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
8992a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)/**
9002a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) * Callback for chrome.notifications.onClosed event.
901a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles) * @param {ChromeNotificationId} chromeNotificationId chrome.notifications ID of
902a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles) *     the card.
9032a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) * @param {boolean} byUser Whether the notification was closed by the user.
9042a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) */
9054e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)function onNotificationClosed(chromeNotificationId, byUser) {
9062a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  if (!byUser)
9072a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    return;
9082a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
909eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch  // At this point we are guaranteed that the notification is a now card.
910c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  chrome.metricsPrivate.recordUserAction('GoogleNow.Dismissed');
911c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)
9124e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)  tasks.add(DISMISS_CARD_TASK_NAME, function() {
91390dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)    dismissalAttempts.start();
914c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)
915a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)    fillFromChromeLocalStorage({
916a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)      /** @type {Array.<PendingDismissal>} */
917a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)      pendingDismissals: [],
918a02191e04bc25c4935f804f2c080ae28663d096dBen Murdoch      /** @type {Object.<ChromeNotificationId, NotificationDataEntry>} */
919a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)      notificationsData: {},
920a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)      /** @type {Object.<string, StoredNotificationGroup>} */
921a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)      notificationGroups: {}
922a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)    }).then(function(items) {
923a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)      /** @type {NotificationDataEntry} */
924a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)      var notificationData =
925a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)          items.notificationsData[chromeNotificationId] ||
926a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)          {
927a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)            timestamp: Date.now(),
928a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)            combinedCard: []
929a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)          };
930a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)
931a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)      var dismissalResult =
932a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)          cardSet.onDismissal(
933a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)              chromeNotificationId,
934a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)              notificationData,
935a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)              items.notificationGroups);
936a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)
937a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)      for (var i = 0; i < dismissalResult.dismissals.length; i++) {
938a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)        /** @type {PendingDismissal} */
939a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)        var dismissal = {
940a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)          chromeNotificationId: chromeNotificationId,
941a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)          time: Date.now(),
942a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)          dismissalData: dismissalResult.dismissals[i]
943a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)        };
944a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)        items.pendingDismissals.push(dismissal);
945a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)      }
946a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)
947a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)      items.notificationsData[chromeNotificationId] =
948a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)          dismissalResult.notificationData;
949a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)
950a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)      chrome.storage.local.set(items);
951a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)
952a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)      processPendingDismissals();
953a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)    });
9542a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  });
9552a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)}
9562a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
9572a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)/**
958a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) * Initializes the polling system to start fetching cards.
9592a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) */
960eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdochfunction startPollingCards() {
961a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)  console.log('startPollingCards');
962a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)  // Create an update timer for a case when for some reason requesting
963a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)  // cards gets stuck.
96490dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)  updateCardsAttempts.start(MAXIMUM_POLLING_PERIOD_SECONDS);
965a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)  requestCards();
9662a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)}
9672a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
968eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch/**
969a36e5920737c6adbddd3e43b760e5de8431db6e0Torne (Richard Coles) * Stops all machinery in the polling system.
970a36e5920737c6adbddd3e43b760e5de8431db6e0Torne (Richard Coles) */
971a36e5920737c6adbddd3e43b760e5de8431db6e0Torne (Richard Coles)function stopPollingCards() {
972a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)  console.log('stopPollingCards');
973a36e5920737c6adbddd3e43b760e5de8431db6e0Torne (Richard Coles)  updateCardsAttempts.stop();
974a02191e04bc25c4935f804f2c080ae28663d096dBen Murdoch  // Since we're stopping everything, clear all runtime storage.
975c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch  // We don't clear localStorage since those values are still relevant
976c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch  // across Google Now start-stop events.
977c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch  chrome.storage.local.clear();
978a36e5920737c6adbddd3e43b760e5de8431db6e0Torne (Richard Coles)}
979a36e5920737c6adbddd3e43b760e5de8431db6e0Torne (Richard Coles)
980a36e5920737c6adbddd3e43b760e5de8431db6e0Torne (Richard Coles)/**
981eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch * Initializes the event page on install or on browser startup.
982eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch */
983eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdochfunction initialize() {
984a36e5920737c6adbddd3e43b760e5de8431db6e0Torne (Richard Coles)  recordEvent(GoogleNowEvent.EXTENSION_START);
985a36e5920737c6adbddd3e43b760e5de8431db6e0Torne (Richard Coles)  onStateChange();
986a36e5920737c6adbddd3e43b760e5de8431db6e0Torne (Richard Coles)}
987a36e5920737c6adbddd3e43b760e5de8431db6e0Torne (Richard Coles)
988a36e5920737c6adbddd3e43b760e5de8431db6e0Torne (Richard Coles)/**
989a02191e04bc25c4935f804f2c080ae28663d096dBen Murdoch * Starts or stops the main pipeline for polling cards.
990a36e5920737c6adbddd3e43b760e5de8431db6e0Torne (Richard Coles) * @param {boolean} shouldPollCardsRequest true to start and
991a36e5920737c6adbddd3e43b760e5de8431db6e0Torne (Richard Coles) *     false to stop polling cards.
992a36e5920737c6adbddd3e43b760e5de8431db6e0Torne (Richard Coles) */
9934e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)function setShouldPollCards(shouldPollCardsRequest) {
994a36e5920737c6adbddd3e43b760e5de8431db6e0Torne (Richard Coles)  updateCardsAttempts.isRunning(function(currentValue) {
995a36e5920737c6adbddd3e43b760e5de8431db6e0Torne (Richard Coles)    if (shouldPollCardsRequest != currentValue) {
996a36e5920737c6adbddd3e43b760e5de8431db6e0Torne (Richard Coles)      console.log('Action Taken setShouldPollCards=' + shouldPollCardsRequest);
997a36e5920737c6adbddd3e43b760e5de8431db6e0Torne (Richard Coles)      if (shouldPollCardsRequest)
998a36e5920737c6adbddd3e43b760e5de8431db6e0Torne (Richard Coles)        startPollingCards();
999a36e5920737c6adbddd3e43b760e5de8431db6e0Torne (Richard Coles)      else
1000a36e5920737c6adbddd3e43b760e5de8431db6e0Torne (Richard Coles)        stopPollingCards();
1001ba5b9a6411cb1792fd21f0a078d7a25cd1ceec16Ben Murdoch    } else {
1002ba5b9a6411cb1792fd21f0a078d7a25cd1ceec16Ben Murdoch      console.log(
1003ba5b9a6411cb1792fd21f0a078d7a25cd1ceec16Ben Murdoch          'Action Ignored setShouldPollCards=' + shouldPollCardsRequest);
1004a36e5920737c6adbddd3e43b760e5de8431db6e0Torne (Richard Coles)    }
1005a36e5920737c6adbddd3e43b760e5de8431db6e0Torne (Richard Coles)  });
1006a36e5920737c6adbddd3e43b760e5de8431db6e0Torne (Richard Coles)}
1007a36e5920737c6adbddd3e43b760e5de8431db6e0Torne (Richard Coles)
1008a36e5920737c6adbddd3e43b760e5de8431db6e0Torne (Richard Coles)/**
10093551c9c881056c480085172ff9840cab31610854Torne (Richard Coles) * Enables or disables the Google Now background permission.
10103551c9c881056c480085172ff9840cab31610854Torne (Richard Coles) * @param {boolean} backgroundEnable true to run in the background.
10113551c9c881056c480085172ff9840cab31610854Torne (Richard Coles) *     false to not run in the background.
10123551c9c881056c480085172ff9840cab31610854Torne (Richard Coles) */
10134e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)function setBackgroundEnable(backgroundEnable) {
10143551c9c881056c480085172ff9840cab31610854Torne (Richard Coles)  instrumented.permissions.contains({permissions: ['background']},
10153551c9c881056c480085172ff9840cab31610854Torne (Richard Coles)      function(hasPermission) {
10163551c9c881056c480085172ff9840cab31610854Torne (Richard Coles)        if (backgroundEnable != hasPermission) {
10173551c9c881056c480085172ff9840cab31610854Torne (Richard Coles)          console.log('Action Taken setBackgroundEnable=' + backgroundEnable);
10183551c9c881056c480085172ff9840cab31610854Torne (Richard Coles)          if (backgroundEnable)
10194e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)            chrome.permissions.request({permissions: ['background']});
10203551c9c881056c480085172ff9840cab31610854Torne (Richard Coles)          else
10214e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)            chrome.permissions.remove({permissions: ['background']});
10223551c9c881056c480085172ff9840cab31610854Torne (Richard Coles)        } else {
10233551c9c881056c480085172ff9840cab31610854Torne (Richard Coles)          console.log('Action Ignored setBackgroundEnable=' + backgroundEnable);
10243551c9c881056c480085172ff9840cab31610854Torne (Richard Coles)        }
10253551c9c881056c480085172ff9840cab31610854Torne (Richard Coles)      });
10263551c9c881056c480085172ff9840cab31610854Torne (Richard Coles)}
10273551c9c881056c480085172ff9840cab31610854Torne (Richard Coles)
10283551c9c881056c480085172ff9840cab31610854Torne (Richard Coles)/**
1029effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch * Record why this extension would not poll for cards.
1030effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch * @param {boolean} signedIn true if the user is signed in.
1031effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch * @param {boolean} notificationEnabled true if
1032effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch *     Google Now for Chrome is allowed to show notifications.
1033effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch * @param {boolean} googleNowEnabled true if
1034effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch *     the Google Now is enabled for the user.
1035effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch */
1036effb81e5f8246d0db0270817048dc992db66e9fbBen Murdochfunction recordEventIfNoCards(signedIn, notificationEnabled, googleNowEnabled) {
1037effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch  if (!signedIn) {
1038effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch    recordEvent(GoogleNowEvent.SIGNED_OUT);
1039effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch  } else if (!notificationEnabled) {
1040effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch    recordEvent(GoogleNowEvent.NOTIFICATION_DISABLED);
1041effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch  } else if (!googleNowEnabled) {
1042effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch    recordEvent(GoogleNowEvent.GOOGLE_NOW_DISABLED);
1043effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch  }
1044effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch}
1045effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch
1046effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch/**
1047a36e5920737c6adbddd3e43b760e5de8431db6e0Torne (Richard Coles) * Does the actual work of deciding what Google Now should do
1048a36e5920737c6adbddd3e43b760e5de8431db6e0Torne (Richard Coles) * based off of the current state of Chrome.
1049a36e5920737c6adbddd3e43b760e5de8431db6e0Torne (Richard Coles) * @param {boolean} signedIn true if the user is signed in.
10500f1bc08d4cfcc34181b0b5cbf065c40f687bf740Torne (Richard Coles) * @param {boolean} canEnableBackground true if
10510f1bc08d4cfcc34181b0b5cbf065c40f687bf740Torne (Richard Coles) *     the background permission can be requested.
10521e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles) * @param {boolean} notificationEnabled true if
10531e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles) *     Google Now for Chrome is allowed to show notifications.
10540f1bc08d4cfcc34181b0b5cbf065c40f687bf740Torne (Richard Coles) * @param {boolean} googleNowEnabled true if
10550f1bc08d4cfcc34181b0b5cbf065c40f687bf740Torne (Richard Coles) *     the Google Now is enabled for the user.
1056a36e5920737c6adbddd3e43b760e5de8431db6e0Torne (Richard Coles) */
1057a36e5920737c6adbddd3e43b760e5de8431db6e0Torne (Richard Coles)function updateRunningState(
1058a36e5920737c6adbddd3e43b760e5de8431db6e0Torne (Richard Coles)    signedIn,
10590f1bc08d4cfcc34181b0b5cbf065c40f687bf740Torne (Richard Coles)    canEnableBackground,
10600f1bc08d4cfcc34181b0b5cbf065c40f687bf740Torne (Richard Coles)    notificationEnabled,
10610f1bc08d4cfcc34181b0b5cbf065c40f687bf740Torne (Richard Coles)    googleNowEnabled) {
1062a36e5920737c6adbddd3e43b760e5de8431db6e0Torne (Richard Coles)  console.log(
1063a36e5920737c6adbddd3e43b760e5de8431db6e0Torne (Richard Coles)      'State Update signedIn=' + signedIn + ' ' +
10640f1bc08d4cfcc34181b0b5cbf065c40f687bf740Torne (Richard Coles)      'canEnableBackground=' + canEnableBackground + ' ' +
10650f1bc08d4cfcc34181b0b5cbf065c40f687bf740Torne (Richard Coles)      'notificationEnabled=' + notificationEnabled + ' ' +
10660f1bc08d4cfcc34181b0b5cbf065c40f687bf740Torne (Richard Coles)      'googleNowEnabled=' + googleNowEnabled);
10674e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)
1068a36e5920737c6adbddd3e43b760e5de8431db6e0Torne (Richard Coles)  var shouldPollCards = false;
10693551c9c881056c480085172ff9840cab31610854Torne (Richard Coles)  var shouldSetBackground = false;
1070a02191e04bc25c4935f804f2c080ae28663d096dBen Murdoch  var shouldClearCards = true;
1071a36e5920737c6adbddd3e43b760e5de8431db6e0Torne (Richard Coles)
10721e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles)  if (signedIn && notificationEnabled) {
1073a02191e04bc25c4935f804f2c080ae28663d096dBen Murdoch    shouldClearCards = !googleNowEnabled;
1074a02191e04bc25c4935f804f2c080ae28663d096dBen Murdoch    shouldSetBackground = canEnableBackground && googleNowEnabled;
1075a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)    shouldPollCards = true;
1076a36e5920737c6adbddd3e43b760e5de8431db6e0Torne (Richard Coles)  } else {
1077a36e5920737c6adbddd3e43b760e5de8431db6e0Torne (Richard Coles)    recordEvent(GoogleNowEvent.STOPPED);
1078a36e5920737c6adbddd3e43b760e5de8431db6e0Torne (Richard Coles)  }
1079a36e5920737c6adbddd3e43b760e5de8431db6e0Torne (Richard Coles)
1080effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch  recordEventIfNoCards(signedIn, notificationEnabled, googleNowEnabled);
1081effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch
1082a36e5920737c6adbddd3e43b760e5de8431db6e0Torne (Richard Coles)  console.log(
10833551c9c881056c480085172ff9840cab31610854Torne (Richard Coles)      'Requested Actions shouldSetBackground=' + shouldSetBackground + ' ' +
1084a02191e04bc25c4935f804f2c080ae28663d096dBen Murdoch      'setShouldPollCards=' + shouldPollCards + ' ' +
1085a02191e04bc25c4935f804f2c080ae28663d096dBen Murdoch      'shouldClearCards=' + shouldClearCards);
1086a36e5920737c6adbddd3e43b760e5de8431db6e0Torne (Richard Coles)
10874e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)  setBackgroundEnable(shouldSetBackground);
10884e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)  setShouldPollCards(shouldPollCards);
1089a02191e04bc25c4935f804f2c080ae28663d096dBen Murdoch  if (shouldClearCards) {
1090a02191e04bc25c4935f804f2c080ae28663d096dBen Murdoch    removeAllCards();
1091a02191e04bc25c4935f804f2c080ae28663d096dBen Murdoch  }
1092a36e5920737c6adbddd3e43b760e5de8431db6e0Torne (Richard Coles)}
1093a36e5920737c6adbddd3e43b760e5de8431db6e0Torne (Richard Coles)
1094a36e5920737c6adbddd3e43b760e5de8431db6e0Torne (Richard Coles)/**
1095a36e5920737c6adbddd3e43b760e5de8431db6e0Torne (Richard Coles) * Coordinates the behavior of Google Now for Chrome depending on
1096a36e5920737c6adbddd3e43b760e5de8431db6e0Torne (Richard Coles) * Chrome and extension state.
1097a36e5920737c6adbddd3e43b760e5de8431db6e0Torne (Richard Coles) */
1098a36e5920737c6adbddd3e43b760e5de8431db6e0Torne (Richard Coles)function onStateChange() {
10994e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)  tasks.add(STATE_CHANGED_TASK_NAME, function() {
11005d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    Promise.all([
1101a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)        authenticationManager.isSignedIn(),
11025d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)        canEnableBackground(),
11035d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)        isNotificationsEnabled(),
11045d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)        isGoogleNowEnabled()])
11055d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)        .then(function(results) {
11065d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)          updateRunningState.apply(null, results);
11075d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)        });
11085d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  });
11095d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)}
11105d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)
11115d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)/**
11125d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) * Determines if background mode should be requested.
11135d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) * @return {Promise} A promise to determine if background can be enabled.
11145d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) */
11155d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)function canEnableBackground() {
11165d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  return new Promise(function(resolve) {
11175d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    instrumented.metricsPrivate.getVariationParams(
11185d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)        'GoogleNow',
11195d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)        function(response) {
11205d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)          resolve(!response || (response.canEnableBackground != 'false'));
11215d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)        });
11225d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  });
11235d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)}
11245d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)
11255d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)/**
11265d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) * Checks if Google Now is enabled in the notifications center.
11275d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) * @return {Promise} A promise to determine if Google Now is enabled
11285d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) *     in the notifications center.
11295d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) */
11305d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)function isNotificationsEnabled() {
11315d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  return new Promise(function(resolve) {
11325d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    instrumented.notifications.getPermissionLevel(function(level) {
11335d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)      resolve(level == 'granted');
11345d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    });
11355d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  });
11365d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)}
11375d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)
11385d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)/**
11395d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) * Gets the previous Google Now opt-in state.
11405d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) * @return {Promise} A promise to determine the previous Google Now
11415d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) *     opt-in state.
11425d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) */
11435d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)function isGoogleNowEnabled() {
1144a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)  return fillFromChromeLocalStorage({googleNowEnabled: false})
1145a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)      .then(function(items) {
1146a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)        return items.googleNowEnabled;
1147a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)      });
1148eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch}
1149eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch
1150effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch/**
1151effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch * Polls the optin state.
1152effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch * Sometimes we get the response to the opted in result too soon during
1153effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch * push messaging. We'll recheck the optin state a few times before giving up.
1154effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch */
1155effb81e5f8246d0db0270817048dc992db66e9fbBen Murdochfunction pollOptedIn() {
1156effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch  /**
1157effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch   * Cleans up any state used to recheck the opt-in poll.
1158effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch   */
1159effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch  function clearPollingState() {
1160effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch    localStorage.removeItem('optedInCheckCount');
1161effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch    optInCheckAttempts.stop();
1162effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch  }
1163effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch
1164c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch  if (localStorage.optedInCheckCount === undefined) {
1165c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch    localStorage.optedInCheckCount = 0;
1166c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch    optInCheckAttempts.start();
1167c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch  }
1168c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch
1169c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch  console.log(new Date() +
1170c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch      ' checkOptedIn Attempt ' + localStorage.optedInCheckCount);
1171c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch
1172c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch  requestAndUpdateOptedIn().then(function() {
1173c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch    clearPollingState();
1174c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch    requestCards();
1175c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch  }).catch(function() {
1176effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch    if (localStorage.optedInCheckCount < 5) {
1177effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch      localStorage.optedInCheckCount++;
1178c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch      optInCheckAttempts.planForNext(function() {});
1179effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch    } else {
1180effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch      clearPollingState();
1181effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch    }
1182c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch  });
1183effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch}
1184effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch
1185ba5b9a6411cb1792fd21f0a078d7a25cd1ceec16Ben Murdochinstrumented.runtime.onInstalled.addListener(function(details) {
1186c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  console.log('onInstalled ' + JSON.stringify(details));
1187c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  if (details.reason != 'chrome_update') {
11882a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    initialize();
1189c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  }
11902a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)});
11912a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
1192ba5b9a6411cb1792fd21f0a078d7a25cd1ceec16Ben Murdochinstrumented.runtime.onStartup.addListener(function() {
1193c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  console.log('onStartup');
11944e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)
11954e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)  // Show notifications received by earlier polls. Doing this as early as
11964e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)  // possible to reduce latency of showing first notifications. This mimics how
11974e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)  // persistent notifications will work.
11984e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)  tasks.add(SHOW_ON_START_TASK_NAME, function() {
1199a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)    fillFromChromeLocalStorage({
1200a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)      /** @type {Object.<string, StoredNotificationGroup>} */
1201a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)      notificationGroups: {}
1202a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)    }).then(function(items) {
1203a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)      console.log('onStartup-get ' + JSON.stringify(items));
12044e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)
1205c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch      showNotificationGroups(items.notificationGroups).then(function() {
12065d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)        chrome.storage.local.set(items);
12075d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)      });
12084e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)    });
12094e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)  });
12104e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)
12112a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  initialize();
12122a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)});
12132a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
1214a36e5920737c6adbddd3e43b760e5de8431db6e0Torne (Richard Coles)authenticationManager.addListener(function() {
1215a36e5920737c6adbddd3e43b760e5de8431db6e0Torne (Richard Coles)  console.log('signIn State Change');
1216a36e5920737c6adbddd3e43b760e5de8431db6e0Torne (Richard Coles)  onStateChange();
1217a36e5920737c6adbddd3e43b760e5de8431db6e0Torne (Richard Coles)});
1218a36e5920737c6adbddd3e43b760e5de8431db6e0Torne (Richard Coles)
1219ba5b9a6411cb1792fd21f0a078d7a25cd1ceec16Ben Murdochinstrumented.notifications.onClicked.addListener(
12204e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)    function(chromeNotificationId) {
1221c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)      chrome.metricsPrivate.recordUserAction('GoogleNow.MessageClicked');
1222effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch      onNotificationClicked(chromeNotificationId,
1223effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch          function(notificationDataEntry) {
1224effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch            var actionUrls = notificationDataEntry.actionUrls;
1225effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch            var url = actionUrls && actionUrls.messageUrl;
1226effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch            if (url) {
1227effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch              recordNotificationClick(notificationDataEntry.cardTypeId);
1228effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch            }
1229effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch            return url;
1230effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch          });
1231effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch        });
12322a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
1233ba5b9a6411cb1792fd21f0a078d7a25cd1ceec16Ben Murdochinstrumented.notifications.onButtonClicked.addListener(
12344e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)    function(chromeNotificationId, buttonIndex) {
12354e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)      chrome.metricsPrivate.recordUserAction(
12364e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)          'GoogleNow.ButtonClicked' + buttonIndex);
1237effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch      onNotificationClicked(chromeNotificationId,
1238effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch          function(notificationDataEntry) {
1239effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch            var actionUrls = notificationDataEntry.actionUrls;
1240effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch            var url = actionUrls.buttonUrls[buttonIndex];
1241effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch            if (url) {
1242effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch              recordButtonClick(notificationDataEntry.cardTypeId, buttonIndex);
1243effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch            } else {
1244effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch              verify(false, 'onButtonClicked: no url for a button');
1245effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch            }
1246effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch            return url;
1247effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch          });
1248effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch        });
12492a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
1250ba5b9a6411cb1792fd21f0a078d7a25cd1ceec16Ben Murdochinstrumented.notifications.onClosed.addListener(onNotificationClosed);
1251c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)
12521e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles)instrumented.notifications.onPermissionLevelChanged.addListener(
12531e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles)    function(permissionLevel) {
12541e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles)      console.log('Notifications permissionLevel Change');
12551e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles)      onStateChange();
12561e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles)    });
12571e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles)
12580f1bc08d4cfcc34181b0b5cbf065c40f687bf740Torne (Richard Coles)instrumented.notifications.onShowSettings.addListener(function() {
12590f1bc08d4cfcc34181b0b5cbf065c40f687bf740Torne (Richard Coles)  openUrl(SETTINGS_URL);
12600f1bc08d4cfcc34181b0b5cbf065c40f687bf740Torne (Richard Coles)});
12610f1bc08d4cfcc34181b0b5cbf065c40f687bf740Torne (Richard Coles)
12628bcbed890bc3ce4d7a057a8f32cab53fa534672eTorne (Richard Coles)instrumented.pushMessaging.onMessage.addListener(function(message) {
12638bcbed890bc3ce4d7a057a8f32cab53fa534672eTorne (Richard Coles)  // message.payload will be '' when the extension first starts.
12648bcbed890bc3ce4d7a057a8f32cab53fa534672eTorne (Richard Coles)  // Each time after signing in, we'll get latest payload for all channels.
12658bcbed890bc3ce4d7a057a8f32cab53fa534672eTorne (Richard Coles)  // So, we need to poll the server only when the payload is non-empty and has
12668bcbed890bc3ce4d7a057a8f32cab53fa534672eTorne (Richard Coles)  // changed.
12678bcbed890bc3ce4d7a057a8f32cab53fa534672eTorne (Richard Coles)  console.log('pushMessaging.onMessage ' + JSON.stringify(message));
12681e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles)  if (message.payload.indexOf('REQUEST_CARDS') == 0) {
12698bcbed890bc3ce4d7a057a8f32cab53fa534672eTorne (Richard Coles)    tasks.add(ON_PUSH_MESSAGE_START_TASK_NAME, function() {
1270a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)      // Accept promise rejection on failure since it's safer to do nothing,
1271a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)      // preventing polling the server when the payload really didn't change.
1272a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)      fillFromChromeLocalStorage({
1273a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)        lastPollNowPayloads: {},
1274a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)        /** @type {Object.<string, StoredNotificationGroup>} */
1275a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)        notificationGroups: {}
1276a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)      }, PromiseRejection.ALLOW).then(function(items) {
12771e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles)        if (items.lastPollNowPayloads[message.subchannelId] !=
12781e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles)            message.payload) {
12791e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles)          items.lastPollNowPayloads[message.subchannelId] = message.payload;
12808bcbed890bc3ce4d7a057a8f32cab53fa534672eTorne (Richard Coles)
12810f1bc08d4cfcc34181b0b5cbf065c40f687bf740Torne (Richard Coles)          items.notificationGroups['PUSH' + message.subchannelId] = {
12820f1bc08d4cfcc34181b0b5cbf065c40f687bf740Torne (Richard Coles)            cards: [],
12830f1bc08d4cfcc34181b0b5cbf065c40f687bf740Torne (Richard Coles)            nextPollTime: Date.now()
12840f1bc08d4cfcc34181b0b5cbf065c40f687bf740Torne (Richard Coles)          };
12850f1bc08d4cfcc34181b0b5cbf065c40f687bf740Torne (Richard Coles)
12860f1bc08d4cfcc34181b0b5cbf065c40f687bf740Torne (Richard Coles)          chrome.storage.local.set({
12870f1bc08d4cfcc34181b0b5cbf065c40f687bf740Torne (Richard Coles)            lastPollNowPayloads: items.lastPollNowPayloads,
12880f1bc08d4cfcc34181b0b5cbf065c40f687bf740Torne (Richard Coles)            notificationGroups: items.notificationGroups
12898bcbed890bc3ce4d7a057a8f32cab53fa534672eTorne (Richard Coles)          });
12900f1bc08d4cfcc34181b0b5cbf065c40f687bf740Torne (Richard Coles)
1291effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch          pollOptedIn();
12928bcbed890bc3ce4d7a057a8f32cab53fa534672eTorne (Richard Coles)        }
12938bcbed890bc3ce4d7a057a8f32cab53fa534672eTorne (Richard Coles)      });
12948bcbed890bc3ce4d7a057a8f32cab53fa534672eTorne (Richard Coles)    });
12958bcbed890bc3ce4d7a057a8f32cab53fa534672eTorne (Richard Coles)  }
129690dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)});
1297