15821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// Copyright (c) 2012 The Chromium Authors. All rights reserved.
25821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// Use of this source code is governed by a BSD-style license that can be
35821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// found in the LICENSE file.
45821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
5effb81e5f8246d0db0270817048dc992db66e9fbBen Murdochcr.define('chrome.sync.about_tab', function() {
6effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch  // Contains the latest snapshot of sync about info.
7effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch  chrome.sync.aboutInfo = {};
8effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch
9c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch  function highlightIfChanged(node, oldVal, newVal) {
10c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch    function clearHighlight() {
11c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch      this.removeAttribute('highlighted');
12c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch    }
13c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch
14c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch    var oldStr = oldVal.toString();
15c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch    var newStr = newVal.toString();
16c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch    if (oldStr != '' && oldStr != newStr) {
17c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch      // Note the addListener function does not end up creating duplicate
18c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch      // listeners.  There can be only one listener per event at a time.
19c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch      // Reference: https://developer.mozilla.org/en/DOM/element.addEventListener
20c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch      node.addEventListener('webkitAnimationEnd', clearHighlight, false);
21c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch      node.setAttribute('highlighted', '');
22c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch    }
23c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch  }
24c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch
25effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch  function refreshAboutInfo(aboutInfo) {
26effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch    chrome.sync.aboutInfo = aboutInfo;
27effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch    var aboutInfoDiv = $('about-info');
28effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch    jstProcess(new JsEvalContext(aboutInfo), aboutInfoDiv);
29effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch  }
30effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch
31effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch  function onAboutInfoUpdatedEvent(e) {
32effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch    refreshAboutInfo(e.details);
33effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch  }
34effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch
350529e5d033099cbfc42635f6f6183833b09dff6eBen Murdoch  /**
360529e5d033099cbfc42635f6f6183833b09dff6eBen Murdoch   * Helper to determine if an element is scrolled to its bottom limit.
370529e5d033099cbfc42635f6f6183833b09dff6eBen Murdoch   * @param {Element} elem element to check
380529e5d033099cbfc42635f6f6183833b09dff6eBen Murdoch   * @return {boolean} true if the element is scrolled to the bottom
390529e5d033099cbfc42635f6f6183833b09dff6eBen Murdoch   */
400529e5d033099cbfc42635f6f6183833b09dff6eBen Murdoch  function isScrolledToBottom(elem) {
410529e5d033099cbfc42635f6f6183833b09dff6eBen Murdoch    return elem.scrollHeight - elem.scrollTop == elem.clientHeight;
420529e5d033099cbfc42635f6f6183833b09dff6eBen Murdoch  }
430529e5d033099cbfc42635f6f6183833b09dff6eBen Murdoch
440529e5d033099cbfc42635f6f6183833b09dff6eBen Murdoch  /**
450529e5d033099cbfc42635f6f6183833b09dff6eBen Murdoch   * Helper to scroll an element to its bottom limit.
460529e5d033099cbfc42635f6f6183833b09dff6eBen Murdoch   * @param {Element} elem element to be scrolled
470529e5d033099cbfc42635f6f6183833b09dff6eBen Murdoch   */
480529e5d033099cbfc42635f6f6183833b09dff6eBen Murdoch  function scrollToBottom(elem) {
490529e5d033099cbfc42635f6f6183833b09dff6eBen Murdoch    elem.scrollTop = elem.scrollHeight - elem.clientHeight;
500529e5d033099cbfc42635f6f6183833b09dff6eBen Murdoch  }
510529e5d033099cbfc42635f6f6183833b09dff6eBen Murdoch
52effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch  /** Container for accumulated sync protocol events. */
53effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch  var protocolEvents = [];
54effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch
55e5d81f57cb97b3b6b7fccc9c5610d21eb81db09dBen Murdoch  /** We may receive re-delivered events.  Keep a record of ones we've seen. */
56e5d81f57cb97b3b6b7fccc9c5610d21eb81db09dBen Murdoch  var knownEventTimestamps = {};
57e5d81f57cb97b3b6b7fccc9c5610d21eb81db09dBen Murdoch
58effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch  /**
59effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch   * Callback for incoming protocol events.
60effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch   * @param {Event} e The protocol event.
61effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch   */
62effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch  function onReceivedProtocolEvent(e) {
63effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch    var details = e.details;
64e5d81f57cb97b3b6b7fccc9c5610d21eb81db09dBen Murdoch
65e5d81f57cb97b3b6b7fccc9c5610d21eb81db09dBen Murdoch    // Return early if we've seen this event before.  Assumes that timestamps
66e5d81f57cb97b3b6b7fccc9c5610d21eb81db09dBen Murdoch    // are sufficiently high resolution to uniquely identify an event.
67e5d81f57cb97b3b6b7fccc9c5610d21eb81db09dBen Murdoch    if (knownEventTimestamps.hasOwnProperty(details.time)) {
68e5d81f57cb97b3b6b7fccc9c5610d21eb81db09dBen Murdoch      return;
69e5d81f57cb97b3b6b7fccc9c5610d21eb81db09dBen Murdoch    }
70e5d81f57cb97b3b6b7fccc9c5610d21eb81db09dBen Murdoch
71e5d81f57cb97b3b6b7fccc9c5610d21eb81db09dBen Murdoch    knownEventTimestamps[details.time] = true;
72effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch    protocolEvents.push(details);
73effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch
740529e5d033099cbfc42635f6f6183833b09dff6eBen Murdoch    var trafficContainer = $('traffic-event-container');
750529e5d033099cbfc42635f6f6183833b09dff6eBen Murdoch
760529e5d033099cbfc42635f6f6183833b09dff6eBen Murdoch    // Scroll to the bottom if we were already at the bottom.  Otherwise, leave
770529e5d033099cbfc42635f6f6183833b09dff6eBen Murdoch    // the scrollbar alone.
780529e5d033099cbfc42635f6f6183833b09dff6eBen Murdoch    var shouldScrollDown = isScrolledToBottom(trafficContainer);
790529e5d033099cbfc42635f6f6183833b09dff6eBen Murdoch
80effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch    var context = new JsEvalContext({ events: protocolEvents });
810529e5d033099cbfc42635f6f6183833b09dff6eBen Murdoch    jstProcess(context, trafficContainer);
820529e5d033099cbfc42635f6f6183833b09dff6eBen Murdoch
830529e5d033099cbfc42635f6f6183833b09dff6eBen Murdoch    if (shouldScrollDown)
840529e5d033099cbfc42635f6f6183833b09dff6eBen Murdoch      scrollToBottom(trafficContainer);
85effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch  }
86effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch
87effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch  /**
88effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch   * Initializes state and callbacks for the protocol event log UI.
89effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch   */
90effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch  function initProtocolEventLog() {
91effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch    chrome.sync.events.addEventListener(
92effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch        'onProtocolEvent', onReceivedProtocolEvent);
93effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch
94effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch    // Make the prototype jscontent element disappear.
95effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch    jstProcess({}, $('traffic-event-container'));
96effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch  }
97effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch
98effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch  /**
99e5d81f57cb97b3b6b7fccc9c5610d21eb81db09dBen Murdoch   * Initializes listeners for status dump and import UI.
100effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch   */
101e5d81f57cb97b3b6b7fccc9c5610d21eb81db09dBen Murdoch  function initStatusDumpButton() {
102effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch    $('status-data').hidden = true;
103effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch
104effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch    var dumpStatusButton = $('dump-status');
105effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch    dumpStatusButton.addEventListener('click', function(event) {
106effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch      var aboutInfo = chrome.sync.aboutInfo;
107effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch      if (!$('include-ids').checked) {
108effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch        aboutInfo.details = chrome.sync.aboutInfo.details.filter(function(el) {
109effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch          return !el.is_sensitive;
110effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch        });
111effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch      }
112effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch      var data = '';
113effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch      data += new Date().toString() + '\n';
114effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch      data += '======\n';
115effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch      data += 'Status\n';
116effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch      data += '======\n';
117effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch      data += JSON.stringify(aboutInfo, null, 2) + '\n';
118effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch
119effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch      $('status-text').value = data;
120effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch      $('status-data').hidden = false;
121effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch    });
122effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch
123effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch    var importStatusButton = $('import-status');
124effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch    importStatusButton.addEventListener('click', function(event) {
125effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch      $('status-data').hidden = false;
126effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch      if ($('status-text').value.length == 0) {
127effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch        $('status-text').value =
128effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch            'Paste sync status dump here then click import.';
129effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch        return;
130effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch      }
131effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch
132effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch      // First remove any characters before the '{'.
133effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch      var data = $('status-text').value;
134effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch      var firstBrace = data.indexOf('{');
135effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch      if (firstBrace < 0) {
136effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch        $('status-text').value = 'Invalid sync status dump.';
137effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch        return;
138effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch      }
139effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch      data = data.substr(firstBrace);
140effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch
141effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch      // Remove listeners to prevent sync events from overwriting imported data.
142effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch      chrome.sync.events.removeEventListener(
143effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch          'onAboutInfoUpdated',
144effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch          onAboutInfoUpdatedEvent);
145effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch
146effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch      var aboutInfo = JSON.parse(data);
147effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch      refreshAboutInfo(aboutInfo);
148effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch    });
149e5d81f57cb97b3b6b7fccc9c5610d21eb81db09dBen Murdoch  }
150effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch
151e5d81f57cb97b3b6b7fccc9c5610d21eb81db09dBen Murdoch  /**
152e5d81f57cb97b3b6b7fccc9c5610d21eb81db09dBen Murdoch   * Toggles the given traffic event entry div's "expanded" state.
1530529e5d033099cbfc42635f6f6183833b09dff6eBen Murdoch   * @param {MouseEvent} e the click event that triggered the toggle.
154e5d81f57cb97b3b6b7fccc9c5610d21eb81db09dBen Murdoch   */
1550529e5d033099cbfc42635f6f6183833b09dff6eBen Murdoch  function expandListener(e) {
1560529e5d033099cbfc42635f6f6183833b09dff6eBen Murdoch    e.target.classList.toggle('traffic-event-entry-expanded');
157e5d81f57cb97b3b6b7fccc9c5610d21eb81db09dBen Murdoch  }
158e5d81f57cb97b3b6b7fccc9c5610d21eb81db09dBen Murdoch
159e5d81f57cb97b3b6b7fccc9c5610d21eb81db09dBen Murdoch  /**
160e5d81f57cb97b3b6b7fccc9c5610d21eb81db09dBen Murdoch   * Attaches a listener to the given traffic event entry div.
161e5d81f57cb97b3b6b7fccc9c5610d21eb81db09dBen Murdoch   * @param {HTMLElement} element the element to attach the listener to.
162e5d81f57cb97b3b6b7fccc9c5610d21eb81db09dBen Murdoch   */
163e5d81f57cb97b3b6b7fccc9c5610d21eb81db09dBen Murdoch  function addExpandListener(element) {
164e5d81f57cb97b3b6b7fccc9c5610d21eb81db09dBen Murdoch    element.addEventListener('click', expandListener, false);
165e5d81f57cb97b3b6b7fccc9c5610d21eb81db09dBen Murdoch  }
166e5d81f57cb97b3b6b7fccc9c5610d21eb81db09dBen Murdoch
167e5d81f57cb97b3b6b7fccc9c5610d21eb81db09dBen Murdoch  function onLoad() {
168e5d81f57cb97b3b6b7fccc9c5610d21eb81db09dBen Murdoch    initStatusDumpButton();
169effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch    initProtocolEventLog();
170e5d81f57cb97b3b6b7fccc9c5610d21eb81db09dBen Murdoch
171e5d81f57cb97b3b6b7fccc9c5610d21eb81db09dBen Murdoch    chrome.sync.events.addEventListener(
172e5d81f57cb97b3b6b7fccc9c5610d21eb81db09dBen Murdoch        'onAboutInfoUpdated',
173e5d81f57cb97b3b6b7fccc9c5610d21eb81db09dBen Murdoch        onAboutInfoUpdatedEvent);
174e5d81f57cb97b3b6b7fccc9c5610d21eb81db09dBen Murdoch
175e5d81f57cb97b3b6b7fccc9c5610d21eb81db09dBen Murdoch    // Register to receive a stream of event notifications.
176e5d81f57cb97b3b6b7fccc9c5610d21eb81db09dBen Murdoch    chrome.sync.registerForEvents();
177e5d81f57cb97b3b6b7fccc9c5610d21eb81db09dBen Murdoch
178e5d81f57cb97b3b6b7fccc9c5610d21eb81db09dBen Murdoch    // Request an about info update event to initialize the page.
179e5d81f57cb97b3b6b7fccc9c5610d21eb81db09dBen Murdoch    chrome.sync.requestUpdatedAboutInfo();
180effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch  }
181f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)
182effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch  return {
183effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch    onLoad: onLoad,
184c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch    addExpandListener: addExpandListener,
185c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch    highlightIfChanged: highlightIfChanged
186effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch  };
187effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch});
1885821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
189effb81e5f8246d0db0270817048dc992db66e9fbBen Murdochdocument.addEventListener(
190effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch    'DOMContentLoaded', chrome.sync.about_tab.onLoad, false);
191