1// Copyright 2013 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
5var g_main_view = null;
6
7/**
8 * This class is the root view object of the page.
9 */
10var MainView = (function() {
11  'use strict';
12
13  /**
14   * @constructor
15   */
16  function MainView() {
17    $('button-update').onclick = function() {
18      chrome.send('update');
19    };
20  };
21
22  MainView.prototype = {
23    /**
24     * Receiving notification to display memory snapshot.
25     * @param {Object}  Information about memory in JSON format.
26     */
27    onSetSnapshot: function(browser) {
28      $('json').textContent = JSON.stringify(browser);
29      $('json').style.display = 'block';
30
31      $('os-value').textContent = browser['os'] + ' (' +
32          browser['os_version'] + ')';
33      $('uptime-value').textContent =
34          secondsToHMS(Math.floor(browser['uptime'] / 1000));
35
36      this.updateSnapshot(browser['processes']);
37      this.updateExtensions(browser['extensions']);
38    },
39
40    /**
41     * Update process information table.
42     * @param {Object} processes information about memory.
43     */
44    updateSnapshot: function(processes) {
45      // Remove existing processes.
46      var size = $('snapshot-view').getElementsByClassName('process').length;
47      for (var i = 0; i < size; ++i) {
48        $('snapshot-view').deleteRow(-1);
49      }
50
51      var template = $('process-template').childNodes;
52      // Add processes.
53      for (var p in processes) {
54        var process = processes[p];
55
56        var row = $('snapshot-view').insertRow(-1);
57        // We skip |template[0]|, because it is a (invalid) Text object.
58        for (var i = 1; i < template.length; ++i) {
59          var value = '---';
60          switch (template[i].className) {
61          case 'process-id':
62            value = process['pid'];
63            break;
64          case 'process-info':
65            value = process['type'];
66            if (process['type'].match(/^Tab/) && 'history' in process) {
67              // Append each tab's history.
68              for (var j = 0; j < process['history'].length; ++j) {
69                value += '<dl><dt>History ' + j + ':' +
70                    JoinLinks(process['history'][j]) + '</dl>';
71              }
72            } else {
73              value += '<br>' + process['titles'].join('<br>');
74            }
75            break;
76          case 'process-memory-private':
77            value = process['memory_private'];
78            break;
79          case 'process-memory-v8':
80            if (process['v8_alloc'] !== undefined) {
81              value = process['v8_used'] + '<br>/ ' + process['v8_alloc'];
82            }
83            break;
84          }
85          var col = row.insertCell(-1);
86          col.innerHTML = value;
87          col.className = template[i].className;
88        }
89        row.setAttribute('class', 'process');
90      }
91    },
92
93    /**
94     * Update extension information table.
95     * @param {Object} extensions information about memory.
96     */
97    updateExtensions: function(extensions) {
98      // Remove existing information.
99      var size =
100          $('extension-view').getElementsByClassName('extension').length;
101      for (var i = 0; i < size; ++i) {
102        $('extension-view').deleteRow(-1);
103      }
104
105      var template = $('extension-template').childNodes;
106      for (var id in extensions) {
107        var extension = extensions[id];
108
109        var row = $('extension-view').insertRow(-1);
110        // We skip |template[0]|, because it is a (invalid) Text object.
111        for (var i = 1; i < template.length; ++i) {
112          var value = '---';
113          switch (template[i].className) {
114          case 'extension-id':
115            value = extension['pid'];
116            break;
117          case 'extension-info':
118            value = extension['titles'].join('<br>');
119            break;
120          case 'extension-memory':
121            value = extension['memory_private'];
122            break;
123          }
124          var col = row.insertCell(-1);
125          col.innerHTML = value;
126          col.className = template[i].className;
127        }
128        row.setAttribute('class', 'extension');
129      }
130    }
131  };
132
133  function JoinLinks(tab) {
134    var line = '';
135    for (var l in tab['history']) {
136      var history = tab['history'][l];
137      var title = (history['title'] == '') ? history['url'] : history['title'];
138      var url = '<a href="' + history['url'] + '">' + HTMLEscape(title) +
139          '</a> (' + secondsToHMS(history['time']) + ' ago)';
140      if (l == tab['index']) {
141        url = '<strong>' + url + '</strong>';
142      }
143      line += '<dd>' + url;
144    }
145    return line;
146  };
147
148  /**
149   * Produces a readable string int the format '<HH> hours <MM> min. <SS> sec.'
150   * representing the amount of time provided as the number of seconds.
151   * @param {number} totalSeconds The total amount of seconds.
152   * @return {string} The formatted HH hours/hours MM min. SS sec. string
153   */
154  function secondsToHMS(totalSeconds) {
155    totalSeconds = Number(totalSeconds);
156    var hour = Math.floor(totalSeconds / 3600);
157    var min = Math.floor(totalSeconds % 3600 / 60);
158    var sec = Math.floor(totalSeconds % 60);
159    return (hour > 0 ? (hour + (hour > 1 ? ' hours ' : ' hour ')) : '') +
160           (min > 0 ? (min + ' min. ') : '') +
161           (sec + ' sec. ');
162  }
163
164  return MainView;
165})();
166
167/**
168 * Initialize everything once we have access to chrome://memory-internals.
169 */
170document.addEventListener('DOMContentLoaded', function() {
171  g_main_view = new MainView();
172});
173