1// Copyright (c) 2012 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
5/**
6 * This view displays network related log data and is specific fo ChromeOS.
7 * We get log data from chrome by filtering system logs for network related
8 * keywords. Logs are not fetched until we actually need them.
9 */
10var LogsView = (function() {
11  'use strict';
12
13  // Special classes (defined in logs_view.css).
14  var LOG_ROW_COLLAPSED_CLASSNAME = 'logs-view-log-row-collapsed';
15  var LOG_ROW_EXPANDED_CLASSNAME = 'logs-view-log-row-expanded';
16  var LOG_CELL_TEXT_CLASSNAME = 'logs-view-log-cell-text';
17  var LOG_CELL_LOG_CLASSNAME = 'logs-view-log-cell-log';
18  var LOG_TABLE_BUTTON_COLUMN_CLASSNAME = 'logs-view-log-table-button-column';
19  var LOG_BUTTON_CLASSNAME = 'logs-view-log-button';
20
21  // We inherit from DivView.
22  var superClass = DivView;
23
24  /**
25   * @constructor
26   */
27  function LogsView() {
28    assertFirstConstructorCall(LogsView);
29
30    // Call superclass's constructor.
31    superClass.call(this, LogsView.MAIN_BOX_ID);
32
33    var tableDiv = $(LogsView.TABLE_ID);
34    this.rows = [];
35    this.populateTable(tableDiv, LOG_FILTER_LIST);
36    $(LogsView.GLOBAL_SHOW_BUTTON_ID).addEventListener('click',
37        this.onGlobalChangeVisibleClick_.bind(this, true));
38    $(LogsView.GLOBAL_HIDE_BUTTON_ID).addEventListener('click',
39        this.onGlobalChangeVisibleClick_.bind(this, false));
40    $(LogsView.REFRESH_LOGS_BUTTON_ID).addEventListener('click',
41        this.onLogsRefresh_.bind(this));
42  }
43
44  LogsView.TAB_ID = 'tab-handle-logs';
45  LogsView.TAB_NAME = 'Logs';
46  LogsView.TAB_HASH = '#logs';
47
48  // IDs for special HTML elements in logs_view.html
49  LogsView.MAIN_BOX_ID = 'logs-view-tab-content';
50  LogsView.TABLE_ID = 'logs-view-log-table';
51  LogsView.GLOBAL_SHOW_BUTTON_ID = 'logs-view-global-show-btn';
52  LogsView.GLOBAL_HIDE_BUTTON_ID = 'logs-view-global-hide-btn';
53  LogsView.REFRESH_LOGS_BUTTON_ID = 'logs-view-refresh-btn';
54
55  cr.addSingletonGetter(LogsView);
56
57  /**
58   * Contains log keys we are interested in.
59   */
60  var LOG_FILTER_LIST = [
61    {
62      key: 'syslog',
63    },
64    {
65      key: 'ui_log',
66    },
67    {
68      key: 'chrome_system_log',
69    },
70    {
71      key: 'chrome_log',
72    }
73  ];
74
75  LogsView.prototype = {
76    // Inherit the superclass's methods.
77    __proto__: superClass.prototype,
78
79    /**
80     * Called during View's initialization. Creates the row of a table logs will
81     * be shown in. Each row has 4 cells.
82     *
83     * First cell's content will be set to |logKey|, second will contain a
84     * button that will be used to show or hide third cell, which will contain
85     * the filtered log.
86     * |logKey| also tells us which log we are getting data from.
87     */
88    createTableRow: function(logKey) {
89      var row = document.createElement('tr');
90
91      var cells = [];
92      for (var i = 0; i < 3; i++) {
93        var rowCell = document.createElement('td');
94        cells.push(rowCell);
95        row.appendChild(rowCell);
96      }
97      // Log key cell.
98      cells[0].className = LOG_CELL_TEXT_CLASSNAME;
99      cells[0].textContent = logKey;
100      // Cell log is displayed in. Log content is in div element that is
101      // initially hidden and empty.
102      cells[2].className = LOG_CELL_TEXT_CLASSNAME;
103      var logDiv = document.createElement('div');
104      logDiv.textContent = '';
105      logDiv.className = LOG_CELL_LOG_CLASSNAME;
106      logDiv.id = 'logs-view.log-cell.' + this.rows.length;
107      cells[2].appendChild(logDiv);
108
109      // Button that we use to show or hide div element with log content. Logs
110      // are not visible initially, so we initialize button accordingly.
111      var expandButton = document.createElement('button');
112      expandButton.textContent = 'Show...';
113      expandButton.className = LOG_BUTTON_CLASSNAME;
114      expandButton.addEventListener('click',
115                                    this.onButtonClicked_.bind(this, row));
116
117      // Cell that contains show/hide button.
118      cells[1].appendChild(expandButton);
119      cells[1].className = LOG_TABLE_BUTTON_COLUMN_CLASSNAME;
120
121      // Initially, log is not visible.
122      row.className = LOG_ROW_COLLAPSED_CLASSNAME;
123
124      // We will need those to process row buttons' onclick events.
125      row.logKey = logKey;
126      row.expandButton = expandButton;
127      row.logDiv = logDiv;
128      row.logVisible = false;
129      this.rows.push(row);
130
131      return row;
132    },
133
134    /**
135     * Initializes |tableDiv| to represent data from |logList| which should be
136     * of type LOG_FILTER_LIST.
137     */
138    populateTable: function(tableDiv, logList) {
139      for (var i = 0; i < logList.length; i++) {
140        var logSource = this.createTableRow(logList[i].key);
141        tableDiv.appendChild(logSource);
142      }
143    },
144
145    /**
146     * Processes clicks on buttons that show or hide log contents in log row.
147     * Row containing the clicked button is given to the method since it
148     * contains all data we need to process the click (unlike button object
149     * itself).
150     */
151    onButtonClicked_: function(containingRow) {
152      if (!containingRow.logVisible) {
153        containingRow.className = LOG_ROW_EXPANDED_CLASSNAME;
154        containingRow.expandButton.textContent = 'Hide...';
155        var logDiv = containingRow.logDiv;
156        if (logDiv.textContent == '') {
157          logDiv.textContent = 'Getting logs...';
158          // Callback will be executed by g_browser.
159          g_browser.getSystemLog(containingRow.logKey,
160                                 containingRow.logDiv.id);
161        }
162      } else {
163        containingRow.className = LOG_ROW_COLLAPSED_CLASSNAME;
164        containingRow.expandButton.textContent = 'Show...';
165      }
166      containingRow.logVisible = !containingRow.logVisible;
167    },
168
169    /**
170     * Processes click on one of the buttons that are used to show or hide all
171     * logs we care about.
172     */
173    onGlobalChangeVisibleClick_: function(isShowAll) {
174      for (var row in this.rows) {
175        if (isShowAll != this.rows[row].logVisible) {
176          this.onButtonClicked_(this.rows[row]);
177        }
178      }
179    },
180
181    /**
182     * Processes click event on the button we use to refresh fetched logs. We
183     * get the newest logs from libcros, and refresh content of the visible log
184     * cells.
185     */
186    onLogsRefresh_: function() {
187      g_browser.refreshSystemLogs();
188
189      var visibleLogRows = [];
190      var hiddenLogRows = [];
191      for (var row in this.rows) {
192        if (this.rows[row].logVisible) {
193          visibleLogRows.push(this.rows[row]);
194        } else {
195          hiddenLogRows.push(this.rows[row]);
196        }
197      }
198
199      // We have to refresh text content in visible rows.
200      for (row in visibleLogRows) {
201        visibleLogRows[row].logDiv.textContent = 'Getting logs...';
202        g_browser.getSystemLog(visibleLogRows[row].logKey,
203                               visibleLogRows[row].logDiv.id);
204      }
205
206      // In hidden rows we just clear potential log text, so we know we have to
207      // get new contents when we show the row next time.
208      for (row in hiddenLogRows) {
209        hiddenLogRows[row].logDiv.textContent = '';
210      }
211    }
212  };
213
214  return LogsView;
215})();
216