1// Copyright 2014 The Chromium Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5cr.define('chrome.invalidations', function() {
6  /**
7   * Local variable where we maintain a count of the invalidations received
8   * and of every ObjectId that has ever been updated (note that this doesn't
9   * log any invalidations ocurred prior to opening the about:invalidation
10   * page).
11   */
12  var tableObjects = {};
13
14  /**
15   * Local variable that contains the detailed information in an object form.
16   * This was done this way as to allow multiple calls to updateDetailedStatus
17   * to keep adding new items.
18   */
19  var cachedDetails = {};
20
21  function quote(str) {
22    return '\"' + str + '\"';
23  }
24
25  function nowTimeString() {
26    return '[' + new Date().getTime() + '] ';
27  }
28
29  /**
30   * Appends a string to a textarea log.
31   * @param {string} logMessage The string to be appended.
32   */
33  function appendToLog(logMessage) {
34    var invalidationsLog = $('invalidations-log');
35    invalidationsLog.value += logMessage + '\n';
36  }
37  /**
38   *  Updates the jstemplate with the latest ObjectIds, ordered by registrar.
39   */
40  function repaintTable() {
41    var keys = [];
42    for (var key in tableObjects) {
43      keys.push(key);
44    }
45    keys.sort();
46    var sortedInvalidations = [];
47    for (var i = 0; i < keys.length; i++) {
48      sortedInvalidations.push(tableObjects[keys[i]]);
49    }
50    var wrapped = { objectsidtable: sortedInvalidations };
51    jstProcess(new JsEvalContext(wrapped), $('objectsid-table-div'));
52  }
53
54  /**
55   * Shows the current state of the InvalidatorService.
56   * @param {string} newState The string to be displayed and logged.
57   * @param {number} lastChangedTime The time in epoch when the state was last
58   *     changed.
59   */
60  function updateInvalidatorState(newState, lastChangedTime) {
61    var logMessage = nowTimeString() +
62      'Invalidations service state changed to ' + quote(newState);
63
64    appendToLog(logMessage);
65    $('invalidations-state').textContent = newState + ' (since ' +
66        new Date(lastChangedTime) + ')';
67  }
68
69  /**
70   * Adds to the log the latest invalidations received
71   * @param {!Array.<!Object>} allInvalidations The array of ObjectId
72   *     that contains the invalidations received by the InvalidatorService.
73   */
74  function logInvalidations(allInvalidations) {
75    for (var i = 0; i < allInvalidations.length; i++) {
76      var inv = allInvalidations[i];
77      if (inv.hasOwnProperty('objectId')) {
78        var logMessage = nowTimeString() +
79          'Received Invalidation with type ' +
80          quote(inv.objectId.name) +
81          ' version ' +
82          quote((inv.isUnknownVersion ? 'Unknown' : inv.version)) +
83          ' with payload ' +
84          quote(inv.payload);
85
86        appendToLog(logMessage);
87        var isInvalidation = true;
88        logToTable(inv, isInvalidation);
89      }
90    }
91    repaintTable();
92  }
93
94  /**
95   * Marks a change in the table whether a new invalidation has arrived
96   * or a new ObjectId is currently being added or updated.
97   * @param {!Object} oId The ObjectId being added or updated.
98   * @param {!boolean} isInvaldation A flag that says that an invalidation
99   *     for this ObjectId has arrived or we just need to add it to the table
100   *     as it was just updated its state.
101   */
102  function logToTable(oId, isInvalidation) {
103    var registrar = oId.registrar;
104    var name = oId.objectId.name;
105    var source = oId.objectId.source;
106    var totalCount = oId.objectId.totalCount || 0;
107    var key = source + '-' + name;
108    var time = new Date();
109    var version = oId.isUnknownVersion ? '?' :
110      oId.version;
111    var payload = '';
112    if (oId.hasOwnProperty('payload'))
113      payload = oId.payload;
114    if (!(key in tableObjects)) {
115      tableObjects[key] = {
116        name: name,
117        source: source,
118        totalCount: totalCount,
119        sessionCount: 0,
120        registrar: registrar,
121        time: '',
122        version: '',
123        payload: '',
124        type: 'content'
125      };
126    }
127    // Refresh the type to be a content because it might have been
128    // greyed out.
129    tableObjects[key].type = 'content';
130    if (isInvalidation) {
131      tableObjects[key].totalCount = tableObjects[key].totalCount + 1;
132      tableObjects[key].sessionCount = tableObjects[key].sessionCount + 1;
133      tableObjects[key].time = time.toTimeString();
134      tableObjects[key].version = version;
135      tableObjects[key].payload = payload;
136    }
137  }
138
139  /**
140   * Shows the handlers that are currently registered for invalidations
141   * (but might not have objects ids registered yet).
142   * @param {!Array.<string>} allHandlers An array of Strings that are
143   *     the names of all the handlers currently registered in the
144   *     InvalidatorService.
145   */
146  function updateHandlers(allHandlers) {
147    var allHandlersFormatted = allHandlers.join(', ');
148    $('registered-handlers').textContent = allHandlersFormatted;
149    var logMessage = nowTimeString() +
150        'InvalidatorHandlers currently registered: ' + allHandlersFormatted;
151    appendToLog(logMessage);
152  }
153
154  /**
155   * Updates the table with the objects ids registered for invalidations
156   * @param {string} registrar The name of the owner of the InvalidationHandler
157   *     that is registered for invalidations
158   * @param {Array of Object} allIds An array of ObjectsIds that are currently
159   *     registered for invalidations. It is not differential (as in, whatever
160   *     is not registered now but was before, it mean it was taken out the
161   *     registered objects)
162   */
163  function updateIds(registrar, allIds) {
164    // Grey out every datatype assigned to this registrar
165    // (and reenable them later in case they are still registered).
166    for (var key in tableObjects) {
167      if (tableObjects[key]['registrar'] === registrar)
168        tableObjects[key].type = 'greyed';
169    }
170    // Reenable those ObjectsIds still registered with this registrar.
171    for (var i = 0; i < allIds.length; i++) {
172      var oId = { objectId: allIds[i], registrar: registrar };
173      var isInvalidation = false;
174      logToTable(oId, isInvalidation);
175    }
176    repaintTable();
177  }
178
179  /**
180   * Update the internal status display, merging new detailed information.
181   * @param {!Object} newDetails The dictionary containing assorted debugging
182   *      details (e.g. Network Channel information).
183   */
184  function updateDetailedStatus(newDetails) {
185    for (var key in newDetails) {
186      cachedDetails[key] = newDetails[key];
187    }
188    $('internal-display').value = JSON.stringify(cachedDetails, null, 2);
189  }
190
191  /**
192   * Function that notifies the InvalidationsMessageHandler that the UI is
193   * ready to receive real-time notifications.
194   */
195  function onLoadWork() {
196    $('request-detailed-status').onclick = function() {
197      cachedDetails = {};
198      chrome.send('requestDetailedStatus');
199    };
200    chrome.send('doneLoading');
201  }
202
203  return {
204    logInvalidations: logInvalidations,
205    onLoadWork: onLoadWork,
206    updateDetailedStatus: updateDetailedStatus,
207    updateHandlers: updateHandlers,
208    updateIds: updateIds,
209    updateInvalidatorState: updateInvalidatorState,
210  };
211});
212
213document.addEventListener('DOMContentLoaded', chrome.invalidations.onLoadWork);
214