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) */
515f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)var MAXIMUM_POLLING_PERIOD_SECONDS = 30 * 60;  // 30 minutes
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 */
57010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles)var INITIAL_OPTIN_RECHECK_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 */
64010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles)var MAXIMUM_OPTIN_RECHECK_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);
208010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles)wrapper.instrumentChromeApiFunction('storage.onChanged.addListener', 0);
209424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles)wrapper.instrumentChromeApiFunction('runtime.onInstalled.addListener', 0);
210424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles)wrapper.instrumentChromeApiFunction('runtime.onStartup.addListener', 0);
211424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles)wrapper.instrumentChromeApiFunction('tabs.create', 1);
212c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)
21390dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)var updateCardsAttempts = buildAttemptManager(
21490dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)    'cards-update',
215a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)    requestCards,
21690dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)    INITIAL_POLLING_PERIOD_SECONDS,
21790dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)    MAXIMUM_POLLING_PERIOD_SECONDS);
218010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles)var optInPollAttempts = buildAttemptManager(
219effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch    'optin',
220010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles)    pollOptedInNoImmediateRecheck,
221010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles)    INITIAL_POLLING_PERIOD_SECONDS,
222010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles)    MAXIMUM_POLLING_PERIOD_SECONDS);
223010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles)var optInRecheckAttempts = buildAttemptManager(
224010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles)    'optin-recheck',
225010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles)    pollOptedInWithRecheck,
226010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles)    INITIAL_OPTIN_RECHECK_PERIOD_SECONDS,
227010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles)    MAXIMUM_OPTIN_RECHECK_PERIOD_SECONDS);
22890dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)var dismissalAttempts = buildAttemptManager(
22990dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)    'dismiss',
23090dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)    retryPendingDismissals,
23190dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)    INITIAL_RETRY_DISMISS_PERIOD_SECONDS,
23290dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)    MAXIMUM_RETRY_DISMISS_PERIOD_SECONDS);
233558790d6acca3451cf3a6b497803a5f07d0bec58Ben Murdochvar cardSet = buildCardSet();
23490dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)
235a36e5920737c6adbddd3e43b760e5de8431db6e0Torne (Richard Coles)var authenticationManager = buildAuthenticationManager();
236a36e5920737c6adbddd3e43b760e5de8431db6e0Torne (Richard Coles)
237c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)/**
238a36e5920737c6adbddd3e43b760e5de8431db6e0Torne (Richard Coles) * Google Now UMA event identifier.
239c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles) * @enum {number}
240c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles) */
241a36e5920737c6adbddd3e43b760e5de8431db6e0Torne (Richard Coles)var GoogleNowEvent = {
242c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  REQUEST_FOR_CARDS_TOTAL: 0,
243c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  REQUEST_FOR_CARDS_SUCCESS: 1,
244c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  CARDS_PARSE_SUCCESS: 2,
245c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  DISMISS_REQUEST_TOTAL: 3,
246c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  DISMISS_REQUEST_SUCCESS: 4,
24790dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)  LOCATION_REQUEST: 5,
248a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)  DELETED_LOCATION_UPDATE: 6,
249eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch  EXTENSION_START: 7,
2504e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)  DELETED_SHOW_WELCOME_TOAST: 8,
251a36e5920737c6adbddd3e43b760e5de8431db6e0Torne (Richard Coles)  STOPPED: 9,
2524e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)  DELETED_USER_SUPPRESSED: 10,
253effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch  SIGNED_OUT: 11,
254effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch  NOTIFICATION_DISABLED: 12,
255effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch  GOOGLE_NOW_DISABLED: 13,
256effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch  EVENTS_TOTAL: 14  // EVENTS_TOTAL is not an event; all new events need to be
257a36e5920737c6adbddd3e43b760e5de8431db6e0Torne (Richard Coles)                    // added before it.
258c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)};
259c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)
260c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)/**
261a36e5920737c6adbddd3e43b760e5de8431db6e0Torne (Richard Coles) * Records a Google Now Event.
262a36e5920737c6adbddd3e43b760e5de8431db6e0Torne (Richard Coles) * @param {GoogleNowEvent} event Event identifier.
263c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles) */
264c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)function recordEvent(event) {
265c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  var metricDescription = {
266c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)    metricName: 'GoogleNow.Event',
267c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)    type: 'histogram-linear',
268c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)    min: 1,
269a36e5920737c6adbddd3e43b760e5de8431db6e0Torne (Richard Coles)    max: GoogleNowEvent.EVENTS_TOTAL,
270a36e5920737c6adbddd3e43b760e5de8431db6e0Torne (Richard Coles)    buckets: GoogleNowEvent.EVENTS_TOTAL + 1
271c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  };
272c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)
273c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  chrome.metricsPrivate.recordValue(metricDescription, event);
274c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)}
275c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)
2762a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)/**
277effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch * Records a notification clicked event.
278effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch * @param {number|undefined} cardTypeId Card type ID.
279effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch */
280effb81e5f8246d0db0270817048dc992db66e9fbBen Murdochfunction recordNotificationClick(cardTypeId) {
281effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch  if (cardTypeId !== undefined) {
282effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch    chrome.metricsPrivate.recordSparseValue(
283effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch        'GoogleNow.Card.Clicked', cardTypeId);
284effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch  }
285effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch}
286effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch
287effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch/**
288effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch * Records a button clicked event.
289effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch * @param {number|undefined} cardTypeId Card type ID.
290effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch * @param {number} buttonIndex Button Index
291effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch */
292effb81e5f8246d0db0270817048dc992db66e9fbBen Murdochfunction recordButtonClick(cardTypeId, buttonIndex) {
293effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch  if (cardTypeId !== undefined) {
294effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch    chrome.metricsPrivate.recordSparseValue(
295effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch        'GoogleNow.Card.Button.Clicked' + buttonIndex, cardTypeId);
296effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch  }
297effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch}
298effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch
299effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch/**
300a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) * Checks the result of the HTTP Request and updates the authentication
301a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) * manager on any failure.
302a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) * @param {string} token Authentication token to validate against an
303a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) *     XMLHttpRequest.
304a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) * @return {function(XMLHttpRequest)} Function that validates the token with the
305a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) *     supplied XMLHttpRequest.
306eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch */
307a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)function checkAuthenticationStatus(token) {
308a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)  return function(request) {
309a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)    if (request.status == HTTP_FORBIDDEN ||
310a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)        request.status == HTTP_UNAUTHORIZED) {
311a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)      authenticationManager.removeToken(token);
312eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch    }
313a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)  }
314a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)}
315eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch
316a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)/**
317a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) * Builds and sends an authenticated request to the notification server.
318a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) * @param {string} method Request method.
319a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) * @param {string} handlerName Server handler to send the request to.
320a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) * @param {string=} opt_contentType Value for the Content-type header.
321a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) * @return {Promise} A promise to issue a request to the server.
322c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch *     The promise rejects if the response is not within the HTTP 200 range.
323a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) */
324a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)function requestFromServer(method, handlerName, opt_contentType) {
325a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)  return authenticationManager.getAuthToken().then(function(token) {
326a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)    var request = buildServerRequest(method, handlerName, opt_contentType);
327eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch    request.setRequestHeader('Authorization', 'Bearer ' + token);
328c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch    var requestPromise = new Promise(function(resolve, reject) {
329a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)      request.addEventListener('loadend', function() {
330c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch        if ((200 <= request.status) && (request.status < 300)) {
331c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch          resolve(request);
332c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch        } else {
333c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch          reject(request);
334c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch        }
335a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)      }, false);
336a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)      request.send();
337eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch    });
338c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch    requestPromise.catch(checkAuthenticationStatus(token));
339a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)    return requestPromise;
340eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch  });
341eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch}
342eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch
343eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch/**
344c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch * Shows the notification groups as notification cards.
3455d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) * @param {Object.<string, StoredNotificationGroup>} notificationGroups Map from
3465d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) *     group name to group information.
347c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch * @param {function(ReceivedNotification)=} opt_onCardShown Optional parameter
348a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles) *     called when each card is shown.
349c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch * @return {Promise} A promise to show the notification groups as cards.
3502a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) */
351c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdochfunction showNotificationGroups(notificationGroups, opt_onCardShown) {
352a02191e04bc25c4935f804f2c080ae28663d096dBen Murdoch  /** @type {Object.<ChromeNotificationId, CombinedCard>} */
353c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch  var cards = combineCardsFromGroups(notificationGroups);
354c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch  console.log('showNotificationGroups ' + JSON.stringify(cards));
355ba5b9a6411cb1792fd21f0a078d7a25cd1ceec16Ben Murdoch
356c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch  return new Promise(function(resolve) {
357c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch    instrumented.notifications.getAll(function(notifications) {
358c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch      console.log('showNotificationGroups-getAll ' +
359c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch          JSON.stringify(notifications));
360c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch      notifications = notifications || {};
361c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch
362c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch      // Mark notifications that didn't receive an update as having received
363c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch      // an empty update.
364c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch      for (var chromeNotificationId in notifications) {
365c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch        cards[chromeNotificationId] = cards[chromeNotificationId] || [];
366c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch      }
367ba5b9a6411cb1792fd21f0a078d7a25cd1ceec16Ben Murdoch
368a02191e04bc25c4935f804f2c080ae28663d096dBen Murdoch      /** @type {Object.<ChromeNotificationId, NotificationDataEntry>} */
369c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch      var notificationsData = {};
370c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch
371c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch      // Create/update/delete notifications.
372c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch      for (var chromeNotificationId in cards) {
373c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch        notificationsData[chromeNotificationId] = cardSet.update(
374c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch            chromeNotificationId,
375c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch            cards[chromeNotificationId],
376c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch            notificationGroups,
377c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch            opt_onCardShown);
378c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch      }
379c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch      chrome.storage.local.set({notificationsData: notificationsData});
380c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch      resolve();
381c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch    });
382a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)  });
3832a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)}
3842a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
3852a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)/**
386a36e5920737c6adbddd3e43b760e5de8431db6e0Torne (Richard Coles) * Removes all cards and card state on Google Now close down.
387a36e5920737c6adbddd3e43b760e5de8431db6e0Torne (Richard Coles) */
388a36e5920737c6adbddd3e43b760e5de8431db6e0Torne (Richard Coles)function removeAllCards() {
389a36e5920737c6adbddd3e43b760e5de8431db6e0Torne (Richard Coles)  console.log('removeAllCards');
390a36e5920737c6adbddd3e43b760e5de8431db6e0Torne (Richard Coles)
391a36e5920737c6adbddd3e43b760e5de8431db6e0Torne (Richard Coles)  // TODO(robliao): Once Google Now clears its own checkbox in the
392a36e5920737c6adbddd3e43b760e5de8431db6e0Torne (Richard Coles)  // notifications center and bug 260376 is fixed, the below clearing
393a36e5920737c6adbddd3e43b760e5de8431db6e0Torne (Richard Coles)  // code is no longer necessary.
394ba5b9a6411cb1792fd21f0a078d7a25cd1ceec16Ben Murdoch  instrumented.notifications.getAll(function(notifications) {
3953551c9c881056c480085172ff9840cab31610854Torne (Richard Coles)    notifications = notifications || {};
3964e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)    for (var chromeNotificationId in notifications) {
3974e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)      instrumented.notifications.clear(chromeNotificationId, function() {});
398a36e5920737c6adbddd3e43b760e5de8431db6e0Torne (Richard Coles)    }
3994e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)    chrome.storage.local.remove(['notificationsData', 'notificationGroups']);
4004e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)  });
4014e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)}
4024e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)
4034e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)/**
404a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles) * Adds a card group into a set of combined cards.
405a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles) * @param {Object.<ChromeNotificationId, CombinedCard>} combinedCards Map from
406a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles) *     chromeNotificationId to a combined card.
407a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles) *     This is an input/output parameter.
408a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles) * @param {StoredNotificationGroup} storedGroup Group to combine into the
409a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles) *     combined card set.
4104e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles) */
411a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)function combineGroup(combinedCards, storedGroup) {
412a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)  for (var i = 0; i < storedGroup.cards.length; i++) {
413a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)    /** @type {ReceivedNotification} */
414a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)    var receivedNotification = storedGroup.cards[i];
415a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)
416a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)    /** @type {UncombinedNotification} */
417a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)    var uncombinedNotification = {
418a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)      receivedNotification: receivedNotification,
419a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)      showTime: receivedNotification.trigger.showTimeSec &&
420a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)                (storedGroup.cardsTimestamp +
421a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)                 receivedNotification.trigger.showTimeSec * MS_IN_SECOND),
422a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)      hideTime: storedGroup.cardsTimestamp +
423a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)                receivedNotification.trigger.hideTimeSec * MS_IN_SECOND
4244e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)    };
4254e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)
426a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)    var combinedCard =
427a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)        combinedCards[receivedNotification.chromeNotificationId] || [];
428a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)    combinedCard.push(uncombinedNotification);
429a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)    combinedCards[receivedNotification.chromeNotificationId] = combinedCard;
4304e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)  }
4314e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)}
4324e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)
4334e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)/**
434010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles) * Calculates the soonest poll time from a map of groups as an absolute time.
435a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles) * @param {Object.<string, StoredNotificationGroup>} groups Map from group name
436a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles) *     to group information.
437010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles) * @return {number} The next poll time based off of the groups.
4384e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles) */
439010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles)function calculateNextPollTimeMilliseconds(groups) {
440010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles)  var nextPollTime = null;
441010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles)
442010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles)  for (var groupName in groups) {
443010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles)    var group = groups[groupName];
444010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles)    if (group.nextPollTime !== undefined) {
445010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles)      nextPollTime = nextPollTime == null ?
446010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles)          group.nextPollTime : Math.min(group.nextPollTime, nextPollTime);
4471e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles)    }
448010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles)  }
4494e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)
450010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles)  // At least one of the groups must have nextPollTime.
451010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles)  verify(nextPollTime != null, 'calculateNextPollTime: nextPollTime is null');
452010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles)  return nextPollTime;
453010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles)}
4544e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)
455010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles)/**
456010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles) * Schedules next cards poll.
457010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles) * @param {Object.<string, StoredNotificationGroup>} groups Map from group name
458010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles) *     to group information.
459010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles) */
460010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles)function scheduleNextCardsPoll(groups) {
461010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles)  var nextPollTimeMs = calculateNextPollTimeMilliseconds(groups);
462010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles)
463010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles)  var nextPollDelaySeconds = Math.max(
464010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles)      (nextPollTimeMs - Date.now()) / MS_IN_SECOND,
465010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles)      MINIMUM_POLLING_PERIOD_SECONDS);
466010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles)  updateCardsAttempts.start(nextPollDelaySeconds);
467010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles)}
468010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles)
469010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles)/**
470010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles) * Schedules the next opt-in check poll.
471010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles) */
472010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles)function scheduleOptInCheckPoll() {
473010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles)  instrumented.metricsPrivate.getVariationParams(
474010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles)      'GoogleNow', function(params) {
475010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles)    var optinPollPeriodSeconds =
476010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles)        parseInt(params && params.optinPollPeriodSeconds, 10) ||
477010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles)        DEFAULT_OPTIN_CHECK_PERIOD_SECONDS;
478010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles)    optInPollAttempts.start(optinPollPeriodSeconds);
479010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles)  });
4804e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)}
4814e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)
4824e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)/**
483c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch * Combines notification groups into a set of Chrome notifications.
484a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles) * @param {Object.<string, StoredNotificationGroup>} notificationGroups Map from
485a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles) *     group name to group information.
486c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch * @return {Object.<ChromeNotificationId, CombinedCard>} Cards to show.
4874e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles) */
488c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdochfunction combineCardsFromGroups(notificationGroups) {
489c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch  console.log('combineCardsFromGroups ' + JSON.stringify(notificationGroups));
490a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)  /** @type {Object.<ChromeNotificationId, CombinedCard>} */
491a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)  var combinedCards = {};
4924e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)
4934e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)  for (var groupName in notificationGroups)
494a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)    combineGroup(combinedCards, notificationGroups[groupName]);
4954e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)
496c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch  return combinedCards;
4974e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)}
4984e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)
4994e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)/**
500c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch * Processes a server response for consumption by showNotificationGroups.
5015d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) * @param {ServerResponse} response Server response.
502c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch * @return {Promise} A promise to process the server response and provide
503c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch *     updated groups. Rejects if the server response shouldn't be processed.
5044e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles) */
505c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdochfunction processServerResponse(response) {
5065d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  console.log('processServerResponse ' + JSON.stringify(response));
5074e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)
5085d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  if (response.googleNowDisabled) {
5090f1bc08d4cfcc34181b0b5cbf065c40f687bf740Torne (Richard Coles)    chrome.storage.local.set({googleNowEnabled: false});
510a02191e04bc25c4935f804f2c080ae28663d096dBen Murdoch    // Stop processing now. The state change will clear the cards.
511a02191e04bc25c4935f804f2c080ae28663d096dBen Murdoch    return Promise.reject();
5120f1bc08d4cfcc34181b0b5cbf065c40f687bf740Torne (Richard Coles)  }
5130f1bc08d4cfcc34181b0b5cbf065c40f687bf740Torne (Richard Coles)
5145d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  var receivedGroups = response.groups;
5154e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)
516c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch  return fillFromChromeLocalStorage({
517a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)    /** @type {Object.<string, StoredNotificationGroup>} */
518a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)    notificationGroups: {},
519a02191e04bc25c4935f804f2c080ae28663d096dBen Murdoch    /** @type {Object.<ServerNotificationId, number>} */
520a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)    recentDismissals: {}
521a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)  }).then(function(items) {
522a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)    console.log('processServerResponse-get ' + JSON.stringify(items));
523a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)
524a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)    // Build a set of non-expired recent dismissals. It will be used for
525a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)    // client-side filtering of cards.
526a02191e04bc25c4935f804f2c080ae28663d096dBen Murdoch    /** @type {Object.<ServerNotificationId, number>} */
527a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)    var updatedRecentDismissals = {};
528a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)    var now = Date.now();
529a02191e04bc25c4935f804f2c080ae28663d096dBen Murdoch    for (var serverNotificationId in items.recentDismissals) {
530a02191e04bc25c4935f804f2c080ae28663d096dBen Murdoch      var dismissalAge = now - items.recentDismissals[serverNotificationId];
531a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)      if (dismissalAge < DISMISS_RETENTION_TIME_MS) {
532a02191e04bc25c4935f804f2c080ae28663d096dBen Murdoch        updatedRecentDismissals[serverNotificationId] =
533a02191e04bc25c4935f804f2c080ae28663d096dBen Murdoch            items.recentDismissals[serverNotificationId];
534a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)      }
535a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)    }
5364e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)
537a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)    // Populate groups with corresponding cards.
538a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)    if (response.notifications) {
539a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)      for (var i = 0; i < response.notifications.length; ++i) {
540a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)        /** @type {ReceivedNotification} */
541a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)        var card = response.notifications[i];
542a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)        if (!(card.notificationId in updatedRecentDismissals)) {
543a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)          var group = receivedGroups[card.groupName];
544a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)          group.cards = group.cards || [];
545a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)          group.cards.push(card);
546a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)        }
547a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)      }
548a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)    }
5494e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)
550a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)    // Build updated set of groups.
551a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)    var updatedGroups = {};
552a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)
553a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)    for (var groupName in receivedGroups) {
554a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)      var receivedGroup = receivedGroups[groupName];
555a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)      var storedGroup = items.notificationGroups[groupName] || {
556a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)        cards: [],
557a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)        cardsTimestamp: undefined,
558a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)        nextPollTime: undefined,
559a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)        rank: undefined
560a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)      };
561a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)
562a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)      if (receivedGroup.requested)
563a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)        receivedGroup.cards = receivedGroup.cards || [];
564a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)
565a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)      if (receivedGroup.cards) {
566a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)        // If the group contains a cards update, all its fields will get new
567a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)        // values.
568a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)        storedGroup.cards = receivedGroup.cards;
569a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)        storedGroup.cardsTimestamp = now;
570a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)        storedGroup.rank = receivedGroup.rank;
571a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)        storedGroup.nextPollTime = undefined;
572a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)        // The code below assigns nextPollTime a defined value if
573a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)        // nextPollSeconds is specified in the received group.
574a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)        // If the group's cards are not updated, and nextPollSeconds is
575a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)        // unspecified, this method doesn't change group's nextPollTime.
576a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)      }
5774e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)
578a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)      // 'nextPollSeconds' may be sent even for groups that don't contain
579a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)      // cards updates.
580a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)      if (receivedGroup.nextPollSeconds !== undefined) {
581a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)        storedGroup.nextPollTime =
582a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)            now + receivedGroup.nextPollSeconds * MS_IN_SECOND;
583a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)      }
5844e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)
585a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)      updatedGroups[groupName] = storedGroup;
586a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)    }
5874e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)
588010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles)    scheduleNextCardsPoll(updatedGroups);
589c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch    return {
590c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch      updatedGroups: updatedGroups,
591c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch      recentDismissals: updatedRecentDismissals
592c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch    };
593a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)  });
594a36e5920737c6adbddd3e43b760e5de8431db6e0Torne (Richard Coles)}
595a36e5920737c6adbddd3e43b760e5de8431db6e0Torne (Richard Coles)
596a36e5920737c6adbddd3e43b760e5de8431db6e0Torne (Richard Coles)/**
5975d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) * Update the Explanatory Total Cards Shown Count.
5980f1bc08d4cfcc34181b0b5cbf065c40f687bf740Torne (Richard Coles) */
5995d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)function countExplanatoryCard() {
6005d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  localStorage['explanatoryCardsShown']++;
6010f1bc08d4cfcc34181b0b5cbf065c40f687bf740Torne (Richard Coles)}
6020f1bc08d4cfcc34181b0b5cbf065c40f687bf740Torne (Richard Coles)
6030f1bc08d4cfcc34181b0b5cbf065c40f687bf740Torne (Richard Coles)/**
604c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch * Determines if cards should have an explanation link.
605c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch * @return {boolean} true if an explanatory card should be shown.
606c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch */
607c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdochfunction shouldShowExplanatoryCard() {
608c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch  var isBelowThreshold =
609c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch      localStorage['explanatoryCardsShown'] < EXPLANATORY_CARDS_LINK_THRESHOLD;
610c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch  return isBelowThreshold;
611c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch}
612c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch
613c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch/**
6148bcbed890bc3ce4d7a057a8f32cab53fa534672eTorne (Richard Coles) * Requests notification cards from the server for specified groups.
6158bcbed890bc3ce4d7a057a8f32cab53fa534672eTorne (Richard Coles) * @param {Array.<string>} groupNames Names of groups that need to be refreshed.
616c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch * @return {Promise} A promise to request the specified notification groups.
6178bcbed890bc3ce4d7a057a8f32cab53fa534672eTorne (Richard Coles) */
618c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdochfunction requestNotificationGroupsFromServer(groupNames) {
619c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch  console.log(
620c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch      'requestNotificationGroupsFromServer from ' + NOTIFICATION_CARDS_URL +
6218bcbed890bc3ce4d7a057a8f32cab53fa534672eTorne (Richard Coles)      ', groupNames=' + JSON.stringify(groupNames));
6228bcbed890bc3ce4d7a057a8f32cab53fa534672eTorne (Richard Coles)
6238bcbed890bc3ce4d7a057a8f32cab53fa534672eTorne (Richard Coles)  recordEvent(GoogleNowEvent.REQUEST_FOR_CARDS_TOTAL);
6248bcbed890bc3ce4d7a057a8f32cab53fa534672eTorne (Richard Coles)
6258bcbed890bc3ce4d7a057a8f32cab53fa534672eTorne (Richard Coles)  var requestParameters = '?timeZoneOffsetMs=' +
6260f1bc08d4cfcc34181b0b5cbf065c40f687bf740Torne (Richard Coles)    (-new Date().getTimezoneOffset() * MS_IN_MINUTE);
6270f1bc08d4cfcc34181b0b5cbf065c40f687bf740Torne (Richard Coles)
628c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch  if (shouldShowExplanatoryCard()) {
6295d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    requestParameters += '&cardExplanation=true';
6300f1bc08d4cfcc34181b0b5cbf065c40f687bf740Torne (Richard Coles)  }
6318bcbed890bc3ce4d7a057a8f32cab53fa534672eTorne (Richard Coles)
6328bcbed890bc3ce4d7a057a8f32cab53fa534672eTorne (Richard Coles)  groupNames.forEach(function(groupName) {
6338bcbed890bc3ce4d7a057a8f32cab53fa534672eTorne (Richard Coles)    requestParameters += ('&requestTypes=' + groupName);
6348bcbed890bc3ce4d7a057a8f32cab53fa534672eTorne (Richard Coles)  });
6358bcbed890bc3ce4d7a057a8f32cab53fa534672eTorne (Richard Coles)
636a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)  requestParameters += '&uiLocale=' + navigator.language;
6378bcbed890bc3ce4d7a057a8f32cab53fa534672eTorne (Richard Coles)
638c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch  console.log(
639c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch      'requestNotificationGroupsFromServer: request=' + requestParameters);
6400f1bc08d4cfcc34181b0b5cbf065c40f687bf740Torne (Richard Coles)
641c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch  return requestFromServer('GET', 'notifications' + requestParameters).then(
642a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)    function(request) {
643c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch      console.log(
644c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch          'requestNotificationGroupsFromServer-received ' + request.status);
645a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)      if (request.status == HTTP_OK) {
646a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)        recordEvent(GoogleNowEvent.REQUEST_FOR_CARDS_SUCCESS);
647c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch        return JSON.parse(request.responseText);
648a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)      }
649a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)    });
6500f1bc08d4cfcc34181b0b5cbf065c40f687bf740Torne (Richard Coles)}
6510f1bc08d4cfcc34181b0b5cbf065c40f687bf740Torne (Richard Coles)
6520f1bc08d4cfcc34181b0b5cbf065c40f687bf740Torne (Richard Coles)/**
653010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles) * Performs an opt-in poll without an immediate recheck.
654010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles) * If the response is not opted-in, schedule an opt-in check poll.
655010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles) */
656010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles)function pollOptedInNoImmediateRecheck() {
657010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles)  requestAndUpdateOptedIn()
658010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles)      .then(function(optedIn) {
659010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles)        if (!optedIn) {
660010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles)          // Request a repoll if we're not opted in.
661010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles)          return Promise.reject();
662010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles)        }
663010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles)      })
664010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles)      .catch(function() {
665010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles)        scheduleOptInCheckPoll();
666010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles)      });
667010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles)}
668010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles)
669010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles)/**
670c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch * Requests the account opted-in state from the server and updates any
671c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch * state as necessary.
672c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch * @return {Promise} A promise to request and update the opted-in state.
673010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles) *     The promise resolves with the opt-in state.
6740f1bc08d4cfcc34181b0b5cbf065c40f687bf740Torne (Richard Coles) */
675c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdochfunction requestAndUpdateOptedIn() {
6760f1bc08d4cfcc34181b0b5cbf065c40f687bf740Torne (Richard Coles)  console.log('requestOptedIn from ' + NOTIFICATION_CARDS_URL);
6770f1bc08d4cfcc34181b0b5cbf065c40f687bf740Torne (Richard Coles)
678c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch  return requestFromServer('GET', 'settings/optin').then(function(request) {
6790f1bc08d4cfcc34181b0b5cbf065c40f687bf740Torne (Richard Coles)    console.log(
680a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)        'requestOptedIn-received ' + request.status + ' ' + request.response);
6810f1bc08d4cfcc34181b0b5cbf065c40f687bf740Torne (Richard Coles)    if (request.status == HTTP_OK) {
682a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)      var parsedResponse = JSON.parse(request.responseText);
683c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch      return parsedResponse.value;
684c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch    }
685c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch  }).then(function(optedIn) {
686010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles)    chrome.storage.local.set({googleNowEnabled: optedIn});
687010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles)    return optedIn;
6888bcbed890bc3ce4d7a057a8f32cab53fa534672eTorne (Richard Coles)  });
6898bcbed890bc3ce4d7a057a8f32cab53fa534672eTorne (Richard Coles)}
6908bcbed890bc3ce4d7a057a8f32cab53fa534672eTorne (Richard Coles)
6918bcbed890bc3ce4d7a057a8f32cab53fa534672eTorne (Richard Coles)/**
692c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch * Determines the groups that need to be requested right now.
693c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch * @return {Promise} A promise to determine the groups to request.
6942a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) */
695c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdochfunction getGroupsToRequest() {
696c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch  return fillFromChromeLocalStorage({
697a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)    /** @type {Object.<string, StoredNotificationGroup>} */
698c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch    notificationGroups: {}
699a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)  }).then(function(items) {
700c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch    console.log('getGroupsToRequest-storage-get ' + JSON.stringify(items));
7018bcbed890bc3ce4d7a057a8f32cab53fa534672eTorne (Richard Coles)    var groupsToRequest = [];
702a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)    var now = Date.now();
7032a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
704a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)    for (var groupName in items.notificationGroups) {
705a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)      var group = items.notificationGroups[groupName];
706a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)      if (group.nextPollTime !== undefined && group.nextPollTime <= now)
707a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)        groupsToRequest.push(groupName);
708eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch    }
709c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch    return groupsToRequest;
710c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch  });
711c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch}
7124e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)
713c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch/**
714c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch * Requests notification cards from the server.
715c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch * @return {Promise} A promise to request the notification cards.
716c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch *     Rejects if the cards won't be requested.
717c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch */
718c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdochfunction requestNotificationCards() {
719c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch  console.log('requestNotificationCards');
720010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles)  return getGroupsToRequest()
721c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch      .then(requestNotificationGroupsFromServer)
722c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch      .then(processServerResponse)
723c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch      .then(function(processedResponse) {
724c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch        var onCardShown =
725c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch            shouldShowExplanatoryCard() ? countExplanatoryCard : undefined;
726c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch        return showNotificationGroups(
727c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch            processedResponse.updatedGroups, onCardShown).then(function() {
728c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch              chrome.storage.local.set({
729c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch                notificationGroups: processedResponse.updatedGroups,
730c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch                recentDismissals: processedResponse.updatedRecentDismissals
731c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch              });
732c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch              recordEvent(GoogleNowEvent.CARDS_PARSE_SUCCESS);
733c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch            }
734c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch          );
7350f1bc08d4cfcc34181b0b5cbf065c40f687bf740Torne (Richard Coles)      });
7362a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)}
7372a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
7382a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)/**
7395f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) * Determines if an immediate retry should occur based off of the given groups.
7405f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) * The NOR group is expected most often and less latency sensitive, so we will
7415f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) * simply wait MAXIMUM_POLLING_PERIOD_SECONDS before trying again.
7425f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) * @param {Array.<string>} groupNames Names of groups that need to be refreshed.
7435f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) * @return {boolean} Whether a retry should occur.
7445f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) */
7455f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)function shouldScheduleRetryFromGroupList(groupNames) {
7465f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)  return (groupNames.length != 1) || (groupNames[0] !== 'NOR');
7475f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)}
7485f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)
7495f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)/**
750a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) * Requests and shows notification cards.
7512a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) */
752a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)function requestCards() {
753a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)  console.log('requestCards @' + new Date());
754a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)  // LOCATION_REQUEST is a legacy histogram value when we requested location.
755a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)  // This corresponds to the extension attempting to request for cards.
756a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)  // We're keeping the name the same to keep our histograms in order.
757a36e5920737c6adbddd3e43b760e5de8431db6e0Torne (Richard Coles)  recordEvent(GoogleNowEvent.LOCATION_REQUEST);
7584e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)  tasks.add(UPDATE_CARDS_TASK_NAME, function() {
759a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)    console.log('requestCards-task-begin');
760a36e5920737c6adbddd3e43b760e5de8431db6e0Torne (Richard Coles)    updateCardsAttempts.isRunning(function(running) {
761a36e5920737c6adbddd3e43b760e5de8431db6e0Torne (Richard Coles)      if (running) {
762010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles)        // The cards are requested only if there are no unsent dismissals.
763010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles)        processPendingDismissals()
764010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles)            .then(requestNotificationCards)
7655f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)            .catch(function() {
7665f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)              return getGroupsToRequest().then(function(groupsToRequest) {
7675f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)                if (shouldScheduleRetryFromGroupList(groupsToRequest)) {
7685f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)                  updateCardsAttempts.scheduleRetry();
7695f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)                }
7705f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)              });
7715f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)            });
772a36e5920737c6adbddd3e43b760e5de8431db6e0Torne (Richard Coles)      }
7732a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    });
7742a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  });
7752a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)}
7762a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
7772a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)/**
778c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles) * Sends a server request to dismiss a card.
779a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles) * @param {ChromeNotificationId} chromeNotificationId chrome.notifications ID of
780a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles) *     the card.
781c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles) * @param {number} dismissalTimeMs Time of the user's dismissal of the card in
782c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles) *     milliseconds since epoch.
7834e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles) * @param {DismissalData} dismissalData Data to build a dismissal request.
784a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) * @return {Promise} A promise to request the card dismissal, rejects on error.
785c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles) */
786c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)function requestCardDismissal(
787a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)    chromeNotificationId, dismissalTimeMs, dismissalData) {
788a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)  console.log('requestDismissingCard ' + chromeNotificationId +
789a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)      ' from ' + NOTIFICATION_CARDS_URL +
790a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)      ', dismissalData=' + JSON.stringify(dismissalData));
791ca12bfac764ba476d6cd062bf1dde12cc64c3f40Ben Murdoch
792ca12bfac764ba476d6cd062bf1dde12cc64c3f40Ben Murdoch  var dismissalAge = Date.now() - dismissalTimeMs;
793ca12bfac764ba476d6cd062bf1dde12cc64c3f40Ben Murdoch
794ca12bfac764ba476d6cd062bf1dde12cc64c3f40Ben Murdoch  if (dismissalAge > MAXIMUM_DISMISSAL_AGE_MS) {
79523730a6e56a168d1879203e4b3819bb36e3d8f1fTorne (Richard Coles)    return Promise.resolve();
796ca12bfac764ba476d6cd062bf1dde12cc64c3f40Ben Murdoch  }
797ca12bfac764ba476d6cd062bf1dde12cc64c3f40Ben Murdoch
798a36e5920737c6adbddd3e43b760e5de8431db6e0Torne (Richard Coles)  recordEvent(GoogleNowEvent.DISMISS_REQUEST_TOTAL);
7994e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)
800a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)  var requestParameters = 'notifications/' + dismissalData.notificationId +
8014e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)      '?age=' + dismissalAge +
8024e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)      '&chromeNotificationId=' + chromeNotificationId;
8034e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)
8044e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)  for (var paramField in dismissalData.parameters)
805a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)    requestParameters += ('&' + paramField +
806a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)    '=' + dismissalData.parameters[paramField]);
8074e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)
808a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)  console.log('requestCardDismissal: requestParameters=' + requestParameters);
8094e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)
810a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)  return requestFromServer('DELETE', requestParameters).then(function(request) {
811c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)    console.log('requestDismissingCard-onloadend ' + request.status);
8124e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)    if (request.status == HTTP_NOCONTENT)
813a36e5920737c6adbddd3e43b760e5de8431db6e0Torne (Richard Coles)      recordEvent(GoogleNowEvent.DISMISS_REQUEST_SUCCESS);
814c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)
815ca12bfac764ba476d6cd062bf1dde12cc64c3f40Ben Murdoch    // A dismissal doesn't require further retries if it was successful or
816ca12bfac764ba476d6cd062bf1dde12cc64c3f40Ben Murdoch    // doesn't have a chance for successful completion.
817c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch    return (request.status == HTTP_NOCONTENT) ?
818c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch           Promise.resolve() :
819c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch           Promise.reject();
820c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch  }).catch(function(request) {
8216e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)    request = (typeof request === 'object') ? request : {};
822c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch    return (request.status == HTTP_BAD_REQUEST ||
823c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch           request.status == HTTP_METHOD_NOT_ALLOWED) ?
824c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch           Promise.resolve() :
825c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch           Promise.reject();
826eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch  });
827c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)}
828c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)
829c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)/**
830c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles) * Tries to send dismiss requests for all pending dismissals.
831a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) * @return {Promise} A promise to process the pending dismissals.
832a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) *     The promise is rejected if a problem was encountered.
833c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles) */
834a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)function processPendingDismissals() {
835a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)  return fillFromChromeLocalStorage({
836a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)    /** @type {Array.<PendingDismissal>} */
837a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)    pendingDismissals: [],
838a02191e04bc25c4935f804f2c080ae28663d096dBen Murdoch    /** @type {Object.<ServerNotificationId, number>} */
839a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)    recentDismissals: {}
840a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)  }).then(function(items) {
841a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)    console.log(
842a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)        'processPendingDismissals-storage-get ' + JSON.stringify(items));
843c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)
844a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)    var dismissalsChanged = false;
845c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)
846a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)    function onFinish(success) {
847a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)      if (dismissalsChanged) {
848a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)        chrome.storage.local.set({
849a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)          pendingDismissals: items.pendingDismissals,
850a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)          recentDismissals: items.recentDismissals
851a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)        });
852a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)      }
853a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)      return success ? Promise.resolve() : Promise.reject();
854a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)    }
855a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)
856a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)    function doProcessDismissals() {
857a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)      if (items.pendingDismissals.length == 0) {
858a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)        dismissalAttempts.stop();
859a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)        return onFinish(true);
860a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)      }
861a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)
862a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)      // Send dismissal for the first card, and if successful, repeat
863a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)      // recursively with the rest.
864a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)      /** @type {PendingDismissal} */
865a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)      var dismissal = items.pendingDismissals[0];
866a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)      return requestCardDismissal(
867a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)          dismissal.chromeNotificationId,
868a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)          dismissal.time,
869a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)          dismissal.dismissalData).then(function() {
870a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)            dismissalsChanged = true;
871a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)            items.pendingDismissals.splice(0, 1);
872a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)            items.recentDismissals[dismissal.dismissalData.notificationId] =
873a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)                Date.now();
874a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)            return doProcessDismissals();
875a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)          }).catch(function() {
876a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)            return onFinish(false);
877a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)          });
878a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)    }
879a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)
880a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)    return doProcessDismissals();
881a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)  });
882c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)}
883c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)
884c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)/**
885c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles) * Submits a task to send pending dismissals.
886c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles) */
887c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)function retryPendingDismissals() {
8884e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)  tasks.add(RETRY_DISMISS_TASK_NAME, function() {
889010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles)    processPendingDismissals().catch(dismissalAttempts.scheduleRetry);
890c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  });
891c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)}
892c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)
893c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)/**
8940f1bc08d4cfcc34181b0b5cbf065c40f687bf740Torne (Richard Coles) * Opens a URL in a new tab.
8950f1bc08d4cfcc34181b0b5cbf065c40f687bf740Torne (Richard Coles) * @param {string} url URL to open.
8960f1bc08d4cfcc34181b0b5cbf065c40f687bf740Torne (Richard Coles) */
8970f1bc08d4cfcc34181b0b5cbf065c40f687bf740Torne (Richard Coles)function openUrl(url) {
8980f1bc08d4cfcc34181b0b5cbf065c40f687bf740Torne (Richard Coles)  instrumented.tabs.create({url: url}, function(tab) {
8990f1bc08d4cfcc34181b0b5cbf065c40f687bf740Torne (Richard Coles)    if (tab)
9000f1bc08d4cfcc34181b0b5cbf065c40f687bf740Torne (Richard Coles)      chrome.windows.update(tab.windowId, {focused: true});
9010f1bc08d4cfcc34181b0b5cbf065c40f687bf740Torne (Richard Coles)    else
9020f1bc08d4cfcc34181b0b5cbf065c40f687bf740Torne (Richard Coles)      chrome.windows.create({url: url, focused: true});
9030f1bc08d4cfcc34181b0b5cbf065c40f687bf740Torne (Richard Coles)  });
9040f1bc08d4cfcc34181b0b5cbf065c40f687bf740Torne (Richard Coles)}
9050f1bc08d4cfcc34181b0b5cbf065c40f687bf740Torne (Richard Coles)
9060f1bc08d4cfcc34181b0b5cbf065c40f687bf740Torne (Richard Coles)/**
9072a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) * Opens URL corresponding to the clicked part of the notification.
908a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles) * @param {ChromeNotificationId} chromeNotificationId chrome.notifications ID of
909a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles) *     the card.
910effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch * @param {function(NotificationDataEntry): (string|undefined)} selector
911effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch *     Function that extracts the url for the clicked area from the
912effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch *     notification data entry.
9132a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) */
9144e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)function onNotificationClicked(chromeNotificationId, selector) {
915a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)  fillFromChromeLocalStorage({
916a02191e04bc25c4935f804f2c080ae28663d096dBen Murdoch    /** @type {Object.<ChromeNotificationId, NotificationDataEntry>} */
917a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)    notificationsData: {}
918a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)  }).then(function(items) {
919a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)    /** @type {(NotificationDataEntry|undefined)} */
920effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch    var notificationDataEntry = items.notificationsData[chromeNotificationId];
921effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch    if (!notificationDataEntry)
922868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)      return;
9232a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
924effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch    var url = selector(notificationDataEntry);
9253551c9c881056c480085172ff9840cab31610854Torne (Richard Coles)    if (!url)
926868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)      return;
927868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)
9280f1bc08d4cfcc34181b0b5cbf065c40f687bf740Torne (Richard Coles)    openUrl(url);
9292a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  });
9302a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)}
9312a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
9322a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)/**
9332a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) * Callback for chrome.notifications.onClosed event.
934a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles) * @param {ChromeNotificationId} chromeNotificationId chrome.notifications ID of
935a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles) *     the card.
9362a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) * @param {boolean} byUser Whether the notification was closed by the user.
9372a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) */
9384e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)function onNotificationClosed(chromeNotificationId, byUser) {
9392a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  if (!byUser)
9402a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    return;
9412a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
942eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch  // At this point we are guaranteed that the notification is a now card.
943c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  chrome.metricsPrivate.recordUserAction('GoogleNow.Dismissed');
944c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)
9454e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)  tasks.add(DISMISS_CARD_TASK_NAME, function() {
94690dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)    dismissalAttempts.start();
947c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)
948a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)    fillFromChromeLocalStorage({
949a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)      /** @type {Array.<PendingDismissal>} */
950a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)      pendingDismissals: [],
951a02191e04bc25c4935f804f2c080ae28663d096dBen Murdoch      /** @type {Object.<ChromeNotificationId, NotificationDataEntry>} */
952a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)      notificationsData: {},
953a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)      /** @type {Object.<string, StoredNotificationGroup>} */
954a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)      notificationGroups: {}
955a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)    }).then(function(items) {
956a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)      /** @type {NotificationDataEntry} */
957a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)      var notificationData =
958a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)          items.notificationsData[chromeNotificationId] ||
959a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)          {
960a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)            timestamp: Date.now(),
961a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)            combinedCard: []
962a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)          };
963a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)
964a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)      var dismissalResult =
965a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)          cardSet.onDismissal(
966a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)              chromeNotificationId,
967a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)              notificationData,
968a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)              items.notificationGroups);
969a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)
970a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)      for (var i = 0; i < dismissalResult.dismissals.length; i++) {
971a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)        /** @type {PendingDismissal} */
972a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)        var dismissal = {
973a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)          chromeNotificationId: chromeNotificationId,
974a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)          time: Date.now(),
975a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)          dismissalData: dismissalResult.dismissals[i]
976a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)        };
977a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)        items.pendingDismissals.push(dismissal);
978a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)      }
979a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)
980a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)      items.notificationsData[chromeNotificationId] =
981a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)          dismissalResult.notificationData;
982a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)
983a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)      chrome.storage.local.set(items);
984a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)
985a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)      processPendingDismissals();
986a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)    });
9872a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  });
9882a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)}
9892a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
9902a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)/**
991a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) * Initializes the polling system to start fetching cards.
9922a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) */
993eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdochfunction startPollingCards() {
994a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)  console.log('startPollingCards');
995a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)  // Create an update timer for a case when for some reason requesting
996a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)  // cards gets stuck.
99790dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)  updateCardsAttempts.start(MAXIMUM_POLLING_PERIOD_SECONDS);
998a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)  requestCards();
9992a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)}
10002a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
1001eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch/**
1002a36e5920737c6adbddd3e43b760e5de8431db6e0Torne (Richard Coles) * Stops all machinery in the polling system.
1003a36e5920737c6adbddd3e43b760e5de8431db6e0Torne (Richard Coles) */
1004a36e5920737c6adbddd3e43b760e5de8431db6e0Torne (Richard Coles)function stopPollingCards() {
1005a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)  console.log('stopPollingCards');
1006a36e5920737c6adbddd3e43b760e5de8431db6e0Torne (Richard Coles)  updateCardsAttempts.stop();
1007a02191e04bc25c4935f804f2c080ae28663d096dBen Murdoch  // Since we're stopping everything, clear all runtime storage.
1008c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch  // We don't clear localStorage since those values are still relevant
1009c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch  // across Google Now start-stop events.
1010c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch  chrome.storage.local.clear();
1011a36e5920737c6adbddd3e43b760e5de8431db6e0Torne (Richard Coles)}
1012a36e5920737c6adbddd3e43b760e5de8431db6e0Torne (Richard Coles)
1013a36e5920737c6adbddd3e43b760e5de8431db6e0Torne (Richard Coles)/**
1014eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch * Initializes the event page on install or on browser startup.
1015eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch */
1016eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdochfunction initialize() {
1017a36e5920737c6adbddd3e43b760e5de8431db6e0Torne (Richard Coles)  recordEvent(GoogleNowEvent.EXTENSION_START);
1018a36e5920737c6adbddd3e43b760e5de8431db6e0Torne (Richard Coles)  onStateChange();
1019a36e5920737c6adbddd3e43b760e5de8431db6e0Torne (Richard Coles)}
1020a36e5920737c6adbddd3e43b760e5de8431db6e0Torne (Richard Coles)
1021a36e5920737c6adbddd3e43b760e5de8431db6e0Torne (Richard Coles)/**
1022a02191e04bc25c4935f804f2c080ae28663d096dBen Murdoch * Starts or stops the main pipeline for polling cards.
1023a36e5920737c6adbddd3e43b760e5de8431db6e0Torne (Richard Coles) * @param {boolean} shouldPollCardsRequest true to start and
1024a36e5920737c6adbddd3e43b760e5de8431db6e0Torne (Richard Coles) *     false to stop polling cards.
1025a36e5920737c6adbddd3e43b760e5de8431db6e0Torne (Richard Coles) */
10264e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)function setShouldPollCards(shouldPollCardsRequest) {
1027a36e5920737c6adbddd3e43b760e5de8431db6e0Torne (Richard Coles)  updateCardsAttempts.isRunning(function(currentValue) {
1028a36e5920737c6adbddd3e43b760e5de8431db6e0Torne (Richard Coles)    if (shouldPollCardsRequest != currentValue) {
1029a36e5920737c6adbddd3e43b760e5de8431db6e0Torne (Richard Coles)      console.log('Action Taken setShouldPollCards=' + shouldPollCardsRequest);
1030a36e5920737c6adbddd3e43b760e5de8431db6e0Torne (Richard Coles)      if (shouldPollCardsRequest)
1031a36e5920737c6adbddd3e43b760e5de8431db6e0Torne (Richard Coles)        startPollingCards();
1032a36e5920737c6adbddd3e43b760e5de8431db6e0Torne (Richard Coles)      else
1033a36e5920737c6adbddd3e43b760e5de8431db6e0Torne (Richard Coles)        stopPollingCards();
1034ba5b9a6411cb1792fd21f0a078d7a25cd1ceec16Ben Murdoch    } else {
1035ba5b9a6411cb1792fd21f0a078d7a25cd1ceec16Ben Murdoch      console.log(
1036ba5b9a6411cb1792fd21f0a078d7a25cd1ceec16Ben Murdoch          'Action Ignored setShouldPollCards=' + shouldPollCardsRequest);
1037a36e5920737c6adbddd3e43b760e5de8431db6e0Torne (Richard Coles)    }
1038a36e5920737c6adbddd3e43b760e5de8431db6e0Torne (Richard Coles)  });
1039a36e5920737c6adbddd3e43b760e5de8431db6e0Torne (Richard Coles)}
1040a36e5920737c6adbddd3e43b760e5de8431db6e0Torne (Richard Coles)
1041a36e5920737c6adbddd3e43b760e5de8431db6e0Torne (Richard Coles)/**
1042010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles) * Starts or stops the optin check.
1043010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles) * @param {boolean} shouldPollOptInStatus true to start and false to stop
1044010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles) *     polling the optin status.
1045010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles) */
1046010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles)function setShouldPollOptInStatus(shouldPollOptInStatus) {
1047010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles)  optInPollAttempts.isRunning(function(currentValue) {
1048010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles)    if (shouldPollOptInStatus != currentValue) {
1049010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles)      console.log(
1050010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles)          'Action Taken setShouldPollOptInStatus=' + shouldPollOptInStatus);
1051010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles)      if (shouldPollOptInStatus) {
1052010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles)        pollOptedInNoImmediateRecheck();
1053010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles)      } else {
1054010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles)        optInPollAttempts.stop();
1055010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles)      }
1056010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles)    } else {
1057010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles)      console.log(
1058010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles)          'Action Ignored setShouldPollOptInStatus=' + shouldPollOptInStatus);
1059010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles)    }
1060010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles)  });
1061010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles)}
1062010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles)
1063010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles)/**
10643551c9c881056c480085172ff9840cab31610854Torne (Richard Coles) * Enables or disables the Google Now background permission.
10653551c9c881056c480085172ff9840cab31610854Torne (Richard Coles) * @param {boolean} backgroundEnable true to run in the background.
10663551c9c881056c480085172ff9840cab31610854Torne (Richard Coles) *     false to not run in the background.
10673551c9c881056c480085172ff9840cab31610854Torne (Richard Coles) */
10684e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)function setBackgroundEnable(backgroundEnable) {
10693551c9c881056c480085172ff9840cab31610854Torne (Richard Coles)  instrumented.permissions.contains({permissions: ['background']},
10703551c9c881056c480085172ff9840cab31610854Torne (Richard Coles)      function(hasPermission) {
10713551c9c881056c480085172ff9840cab31610854Torne (Richard Coles)        if (backgroundEnable != hasPermission) {
10723551c9c881056c480085172ff9840cab31610854Torne (Richard Coles)          console.log('Action Taken setBackgroundEnable=' + backgroundEnable);
10733551c9c881056c480085172ff9840cab31610854Torne (Richard Coles)          if (backgroundEnable)
10744e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)            chrome.permissions.request({permissions: ['background']});
10753551c9c881056c480085172ff9840cab31610854Torne (Richard Coles)          else
10764e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)            chrome.permissions.remove({permissions: ['background']});
10773551c9c881056c480085172ff9840cab31610854Torne (Richard Coles)        } else {
10783551c9c881056c480085172ff9840cab31610854Torne (Richard Coles)          console.log('Action Ignored setBackgroundEnable=' + backgroundEnable);
10793551c9c881056c480085172ff9840cab31610854Torne (Richard Coles)        }
10803551c9c881056c480085172ff9840cab31610854Torne (Richard Coles)      });
10813551c9c881056c480085172ff9840cab31610854Torne (Richard Coles)}
10823551c9c881056c480085172ff9840cab31610854Torne (Richard Coles)
10833551c9c881056c480085172ff9840cab31610854Torne (Richard Coles)/**
1084effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch * Record why this extension would not poll for cards.
1085effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch * @param {boolean} signedIn true if the user is signed in.
1086effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch * @param {boolean} notificationEnabled true if
1087effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch *     Google Now for Chrome is allowed to show notifications.
1088effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch * @param {boolean} googleNowEnabled true if
1089effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch *     the Google Now is enabled for the user.
1090effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch */
1091effb81e5f8246d0db0270817048dc992db66e9fbBen Murdochfunction recordEventIfNoCards(signedIn, notificationEnabled, googleNowEnabled) {
1092effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch  if (!signedIn) {
1093effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch    recordEvent(GoogleNowEvent.SIGNED_OUT);
1094effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch  } else if (!notificationEnabled) {
1095effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch    recordEvent(GoogleNowEvent.NOTIFICATION_DISABLED);
1096effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch  } else if (!googleNowEnabled) {
1097effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch    recordEvent(GoogleNowEvent.GOOGLE_NOW_DISABLED);
1098effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch  }
1099effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch}
1100effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch
1101effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch/**
1102a36e5920737c6adbddd3e43b760e5de8431db6e0Torne (Richard Coles) * Does the actual work of deciding what Google Now should do
1103a36e5920737c6adbddd3e43b760e5de8431db6e0Torne (Richard Coles) * based off of the current state of Chrome.
1104a36e5920737c6adbddd3e43b760e5de8431db6e0Torne (Richard Coles) * @param {boolean} signedIn true if the user is signed in.
11050f1bc08d4cfcc34181b0b5cbf065c40f687bf740Torne (Richard Coles) * @param {boolean} canEnableBackground true if
11060f1bc08d4cfcc34181b0b5cbf065c40f687bf740Torne (Richard Coles) *     the background permission can be requested.
11071e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles) * @param {boolean} notificationEnabled true if
11081e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles) *     Google Now for Chrome is allowed to show notifications.
11090f1bc08d4cfcc34181b0b5cbf065c40f687bf740Torne (Richard Coles) * @param {boolean} googleNowEnabled true if
11100f1bc08d4cfcc34181b0b5cbf065c40f687bf740Torne (Richard Coles) *     the Google Now is enabled for the user.
1111a36e5920737c6adbddd3e43b760e5de8431db6e0Torne (Richard Coles) */
1112a36e5920737c6adbddd3e43b760e5de8431db6e0Torne (Richard Coles)function updateRunningState(
1113a36e5920737c6adbddd3e43b760e5de8431db6e0Torne (Richard Coles)    signedIn,
11140f1bc08d4cfcc34181b0b5cbf065c40f687bf740Torne (Richard Coles)    canEnableBackground,
11150f1bc08d4cfcc34181b0b5cbf065c40f687bf740Torne (Richard Coles)    notificationEnabled,
11160f1bc08d4cfcc34181b0b5cbf065c40f687bf740Torne (Richard Coles)    googleNowEnabled) {
1117a36e5920737c6adbddd3e43b760e5de8431db6e0Torne (Richard Coles)  console.log(
1118a36e5920737c6adbddd3e43b760e5de8431db6e0Torne (Richard Coles)      'State Update signedIn=' + signedIn + ' ' +
11190f1bc08d4cfcc34181b0b5cbf065c40f687bf740Torne (Richard Coles)      'canEnableBackground=' + canEnableBackground + ' ' +
11200f1bc08d4cfcc34181b0b5cbf065c40f687bf740Torne (Richard Coles)      'notificationEnabled=' + notificationEnabled + ' ' +
11210f1bc08d4cfcc34181b0b5cbf065c40f687bf740Torne (Richard Coles)      'googleNowEnabled=' + googleNowEnabled);
11224e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)
1123a36e5920737c6adbddd3e43b760e5de8431db6e0Torne (Richard Coles)  var shouldPollCards = false;
1124010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles)  var shouldPollOptInStatus = false;
11253551c9c881056c480085172ff9840cab31610854Torne (Richard Coles)  var shouldSetBackground = false;
1126a36e5920737c6adbddd3e43b760e5de8431db6e0Torne (Richard Coles)
11271e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles)  if (signedIn && notificationEnabled) {
1128010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles)    shouldPollCards = googleNowEnabled;
1129010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles)    shouldPollOptInStatus = !googleNowEnabled;
1130a02191e04bc25c4935f804f2c080ae28663d096dBen Murdoch    shouldSetBackground = canEnableBackground && googleNowEnabled;
1131a36e5920737c6adbddd3e43b760e5de8431db6e0Torne (Richard Coles)  } else {
1132a36e5920737c6adbddd3e43b760e5de8431db6e0Torne (Richard Coles)    recordEvent(GoogleNowEvent.STOPPED);
1133a36e5920737c6adbddd3e43b760e5de8431db6e0Torne (Richard Coles)  }
1134a36e5920737c6adbddd3e43b760e5de8431db6e0Torne (Richard Coles)
1135effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch  recordEventIfNoCards(signedIn, notificationEnabled, googleNowEnabled);
1136effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch
1137a36e5920737c6adbddd3e43b760e5de8431db6e0Torne (Richard Coles)  console.log(
11383551c9c881056c480085172ff9840cab31610854Torne (Richard Coles)      'Requested Actions shouldSetBackground=' + shouldSetBackground + ' ' +
1139a02191e04bc25c4935f804f2c080ae28663d096dBen Murdoch      'setShouldPollCards=' + shouldPollCards + ' ' +
1140010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles)      'shouldPollOptInStatus=' + shouldPollOptInStatus);
1141a36e5920737c6adbddd3e43b760e5de8431db6e0Torne (Richard Coles)
11424e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)  setBackgroundEnable(shouldSetBackground);
11434e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)  setShouldPollCards(shouldPollCards);
1144010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles)  setShouldPollOptInStatus(shouldPollOptInStatus);
1145010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles)  if (!shouldPollCards) {
1146a02191e04bc25c4935f804f2c080ae28663d096dBen Murdoch    removeAllCards();
1147a02191e04bc25c4935f804f2c080ae28663d096dBen Murdoch  }
1148a36e5920737c6adbddd3e43b760e5de8431db6e0Torne (Richard Coles)}
1149a36e5920737c6adbddd3e43b760e5de8431db6e0Torne (Richard Coles)
1150a36e5920737c6adbddd3e43b760e5de8431db6e0Torne (Richard Coles)/**
1151a36e5920737c6adbddd3e43b760e5de8431db6e0Torne (Richard Coles) * Coordinates the behavior of Google Now for Chrome depending on
1152a36e5920737c6adbddd3e43b760e5de8431db6e0Torne (Richard Coles) * Chrome and extension state.
1153a36e5920737c6adbddd3e43b760e5de8431db6e0Torne (Richard Coles) */
1154a36e5920737c6adbddd3e43b760e5de8431db6e0Torne (Richard Coles)function onStateChange() {
11554e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)  tasks.add(STATE_CHANGED_TASK_NAME, function() {
11565d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    Promise.all([
1157a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)        authenticationManager.isSignedIn(),
11585d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)        canEnableBackground(),
11595d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)        isNotificationsEnabled(),
11605d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)        isGoogleNowEnabled()])
11615d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)        .then(function(results) {
11625d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)          updateRunningState.apply(null, results);
11635d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)        });
11645d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  });
11655d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)}
11665d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)
11675d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)/**
11685d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) * Determines if background mode should be requested.
11695d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) * @return {Promise} A promise to determine if background can be enabled.
11705d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) */
11715d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)function canEnableBackground() {
11725d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  return new Promise(function(resolve) {
11735d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    instrumented.metricsPrivate.getVariationParams(
11745d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)        'GoogleNow',
11755d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)        function(response) {
11765d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)          resolve(!response || (response.canEnableBackground != 'false'));
11775d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)        });
11785d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  });
11795d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)}
11805d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)
11815d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)/**
11825d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) * Checks if Google Now is enabled in the notifications center.
11835d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) * @return {Promise} A promise to determine if Google Now is enabled
11845d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) *     in the notifications center.
11855d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) */
11865d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)function isNotificationsEnabled() {
11875d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  return new Promise(function(resolve) {
11885d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    instrumented.notifications.getPermissionLevel(function(level) {
11895d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)      resolve(level == 'granted');
11905d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    });
11915d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  });
11925d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)}
11935d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)
11945d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)/**
11955d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) * Gets the previous Google Now opt-in state.
11965d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) * @return {Promise} A promise to determine the previous Google Now
11975d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) *     opt-in state.
11985d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) */
11995d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)function isGoogleNowEnabled() {
1200a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)  return fillFromChromeLocalStorage({googleNowEnabled: false})
1201a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)      .then(function(items) {
1202a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)        return items.googleNowEnabled;
1203a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)      });
1204eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch}
1205eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch
1206effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch/**
1207effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch * Polls the optin state.
1208effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch * Sometimes we get the response to the opted in result too soon during
1209effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch * push messaging. We'll recheck the optin state a few times before giving up.
1210effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch */
1211010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles)function pollOptedInWithRecheck() {
1212effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch  /**
1213effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch   * Cleans up any state used to recheck the opt-in poll.
1214effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch   */
1215effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch  function clearPollingState() {
1216effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch    localStorage.removeItem('optedInCheckCount');
1217010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles)    optInRecheckAttempts.stop();
1218effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch  }
1219effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch
1220c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch  if (localStorage.optedInCheckCount === undefined) {
1221c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch    localStorage.optedInCheckCount = 0;
1222010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles)    optInRecheckAttempts.start();
1223c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch  }
1224c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch
1225c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch  console.log(new Date() +
1226c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch      ' checkOptedIn Attempt ' + localStorage.optedInCheckCount);
1227c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch
1228010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles)  requestAndUpdateOptedIn().then(function(optedIn) {
1229010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles)    if (optedIn) {
1230010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles)      clearPollingState();
1231010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles)      return Promise.resolve();
1232010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles)    } else {
1233010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles)      // If we're not opted in, reject to retry.
1234010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles)      return Promise.reject();
1235010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles)    }
1236c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch  }).catch(function() {
1237effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch    if (localStorage.optedInCheckCount < 5) {
1238effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch      localStorage.optedInCheckCount++;
1239010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles)      optInRecheckAttempts.scheduleRetry();
1240effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch    } else {
1241effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch      clearPollingState();
1242effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch    }
1243c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch  });
1244effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch}
1245effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch
1246ba5b9a6411cb1792fd21f0a078d7a25cd1ceec16Ben Murdochinstrumented.runtime.onInstalled.addListener(function(details) {
1247c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  console.log('onInstalled ' + JSON.stringify(details));
1248c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  if (details.reason != 'chrome_update') {
12492a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    initialize();
1250c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  }
12512a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)});
12522a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
1253ba5b9a6411cb1792fd21f0a078d7a25cd1ceec16Ben Murdochinstrumented.runtime.onStartup.addListener(function() {
1254c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  console.log('onStartup');
12554e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)
12564e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)  // Show notifications received by earlier polls. Doing this as early as
12574e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)  // possible to reduce latency of showing first notifications. This mimics how
12584e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)  // persistent notifications will work.
12594e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)  tasks.add(SHOW_ON_START_TASK_NAME, function() {
1260a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)    fillFromChromeLocalStorage({
1261a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)      /** @type {Object.<string, StoredNotificationGroup>} */
1262a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)      notificationGroups: {}
1263a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)    }).then(function(items) {
1264a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)      console.log('onStartup-get ' + JSON.stringify(items));
12654e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)
1266c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch      showNotificationGroups(items.notificationGroups).then(function() {
12675d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)        chrome.storage.local.set(items);
12685d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)      });
12694e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)    });
12704e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)  });
12714e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)
12722a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  initialize();
12732a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)});
12742a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
1275a36e5920737c6adbddd3e43b760e5de8431db6e0Torne (Richard Coles)authenticationManager.addListener(function() {
1276a36e5920737c6adbddd3e43b760e5de8431db6e0Torne (Richard Coles)  console.log('signIn State Change');
1277a36e5920737c6adbddd3e43b760e5de8431db6e0Torne (Richard Coles)  onStateChange();
1278a36e5920737c6adbddd3e43b760e5de8431db6e0Torne (Richard Coles)});
1279a36e5920737c6adbddd3e43b760e5de8431db6e0Torne (Richard Coles)
1280ba5b9a6411cb1792fd21f0a078d7a25cd1ceec16Ben Murdochinstrumented.notifications.onClicked.addListener(
12814e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)    function(chromeNotificationId) {
1282c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)      chrome.metricsPrivate.recordUserAction('GoogleNow.MessageClicked');
1283effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch      onNotificationClicked(chromeNotificationId,
1284effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch          function(notificationDataEntry) {
1285effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch            var actionUrls = notificationDataEntry.actionUrls;
1286effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch            var url = actionUrls && actionUrls.messageUrl;
1287effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch            if (url) {
1288effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch              recordNotificationClick(notificationDataEntry.cardTypeId);
1289effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch            }
1290effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch            return url;
1291effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch          });
1292effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch        });
12932a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
1294ba5b9a6411cb1792fd21f0a078d7a25cd1ceec16Ben Murdochinstrumented.notifications.onButtonClicked.addListener(
12954e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)    function(chromeNotificationId, buttonIndex) {
12964e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)      chrome.metricsPrivate.recordUserAction(
12974e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)          'GoogleNow.ButtonClicked' + buttonIndex);
1298effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch      onNotificationClicked(chromeNotificationId,
1299effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch          function(notificationDataEntry) {
1300effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch            var actionUrls = notificationDataEntry.actionUrls;
1301effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch            var url = actionUrls.buttonUrls[buttonIndex];
1302effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch            if (url) {
1303effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch              recordButtonClick(notificationDataEntry.cardTypeId, buttonIndex);
1304effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch            } else {
1305effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch              verify(false, 'onButtonClicked: no url for a button');
130646d4c2bc3267f3f028f39e7e311b0f89aba2e4fdTorne (Richard Coles)              console.log(
130746d4c2bc3267f3f028f39e7e311b0f89aba2e4fdTorne (Richard Coles)                  'buttonIndex=' + buttonIndex + ' ' +
130846d4c2bc3267f3f028f39e7e311b0f89aba2e4fdTorne (Richard Coles)                  'chromeNotificationId=' + chromeNotificationId + ' ' +
130946d4c2bc3267f3f028f39e7e311b0f89aba2e4fdTorne (Richard Coles)                  'notificationDataEntry=' +
131046d4c2bc3267f3f028f39e7e311b0f89aba2e4fdTorne (Richard Coles)                  JSON.stringify(notificationDataEntry));
1311effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch            }
1312effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch            return url;
1313effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch          });
1314effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch        });
13152a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
1316ba5b9a6411cb1792fd21f0a078d7a25cd1ceec16Ben Murdochinstrumented.notifications.onClosed.addListener(onNotificationClosed);
1317c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)
13181e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles)instrumented.notifications.onPermissionLevelChanged.addListener(
13191e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles)    function(permissionLevel) {
13201e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles)      console.log('Notifications permissionLevel Change');
13211e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles)      onStateChange();
13221e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles)    });
13231e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles)
13240f1bc08d4cfcc34181b0b5cbf065c40f687bf740Torne (Richard Coles)instrumented.notifications.onShowSettings.addListener(function() {
13250f1bc08d4cfcc34181b0b5cbf065c40f687bf740Torne (Richard Coles)  openUrl(SETTINGS_URL);
13260f1bc08d4cfcc34181b0b5cbf065c40f687bf740Torne (Richard Coles)});
13270f1bc08d4cfcc34181b0b5cbf065c40f687bf740Torne (Richard Coles)
1328010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles)// Handles state change notifications for the Google Now enabled bit.
1329010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles)instrumented.storage.onChanged.addListener(function(changes, areaName) {
1330010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles)  if (areaName === 'local') {
1331010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles)    if ('googleNowEnabled' in changes) {
1332010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles)      onStateChange();
1333010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles)    }
1334010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles)  }
1335010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles)});
1336010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles)
13378bcbed890bc3ce4d7a057a8f32cab53fa534672eTorne (Richard Coles)instrumented.pushMessaging.onMessage.addListener(function(message) {
13388bcbed890bc3ce4d7a057a8f32cab53fa534672eTorne (Richard Coles)  // message.payload will be '' when the extension first starts.
13398bcbed890bc3ce4d7a057a8f32cab53fa534672eTorne (Richard Coles)  // Each time after signing in, we'll get latest payload for all channels.
13408bcbed890bc3ce4d7a057a8f32cab53fa534672eTorne (Richard Coles)  // So, we need to poll the server only when the payload is non-empty and has
13418bcbed890bc3ce4d7a057a8f32cab53fa534672eTorne (Richard Coles)  // changed.
13428bcbed890bc3ce4d7a057a8f32cab53fa534672eTorne (Richard Coles)  console.log('pushMessaging.onMessage ' + JSON.stringify(message));
13431e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles)  if (message.payload.indexOf('REQUEST_CARDS') == 0) {
13448bcbed890bc3ce4d7a057a8f32cab53fa534672eTorne (Richard Coles)    tasks.add(ON_PUSH_MESSAGE_START_TASK_NAME, function() {
1345a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)      // Accept promise rejection on failure since it's safer to do nothing,
1346a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)      // preventing polling the server when the payload really didn't change.
1347a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)      fillFromChromeLocalStorage({
1348a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)        lastPollNowPayloads: {},
1349a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)        /** @type {Object.<string, StoredNotificationGroup>} */
1350a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)        notificationGroups: {}
1351a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)      }, PromiseRejection.ALLOW).then(function(items) {
13521e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles)        if (items.lastPollNowPayloads[message.subchannelId] !=
13531e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles)            message.payload) {
13541e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles)          items.lastPollNowPayloads[message.subchannelId] = message.payload;
13558bcbed890bc3ce4d7a057a8f32cab53fa534672eTorne (Richard Coles)
13560f1bc08d4cfcc34181b0b5cbf065c40f687bf740Torne (Richard Coles)          items.notificationGroups['PUSH' + message.subchannelId] = {
13570f1bc08d4cfcc34181b0b5cbf065c40f687bf740Torne (Richard Coles)            cards: [],
13580f1bc08d4cfcc34181b0b5cbf065c40f687bf740Torne (Richard Coles)            nextPollTime: Date.now()
13590f1bc08d4cfcc34181b0b5cbf065c40f687bf740Torne (Richard Coles)          };
13600f1bc08d4cfcc34181b0b5cbf065c40f687bf740Torne (Richard Coles)
13610f1bc08d4cfcc34181b0b5cbf065c40f687bf740Torne (Richard Coles)          chrome.storage.local.set({
13620f1bc08d4cfcc34181b0b5cbf065c40f687bf740Torne (Richard Coles)            lastPollNowPayloads: items.lastPollNowPayloads,
13630f1bc08d4cfcc34181b0b5cbf065c40f687bf740Torne (Richard Coles)            notificationGroups: items.notificationGroups
13648bcbed890bc3ce4d7a057a8f32cab53fa534672eTorne (Richard Coles)          });
13650f1bc08d4cfcc34181b0b5cbf065c40f687bf740Torne (Richard Coles)
1366010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles)          pollOptedInWithRecheck();
13678bcbed890bc3ce4d7a057a8f32cab53fa534672eTorne (Richard Coles)        }
13688bcbed890bc3ce4d7a057a8f32cab53fa534672eTorne (Richard Coles)      });
13698bcbed890bc3ce4d7a057a8f32cab53fa534672eTorne (Richard Coles)    });
13708bcbed890bc3ce4d7a057a8f32cab53fa534672eTorne (Richard Coles)  }
137190dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)});
1372