summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--chrome/browser/resources/net_internals/index.html22
-rw-r--r--chrome/browser/resources/net_internals/logsview.js176
-rw-r--r--chrome/browser/resources/net_internals/main.css49
-rw-r--r--chrome/browser/resources/net_internals/main.js29
-rw-r--r--chrome/browser/ui/webui/net_internals_ui.cc190
5 files changed, 463 insertions, 3 deletions
diff --git a/chrome/browser/resources/net_internals/index.html b/chrome/browser/resources/net_internals/index.html
index b79ea03..fc23e98 100644
--- a/chrome/browser/resources/net_internals/index.html
+++ b/chrome/browser/resources/net_internals/index.html
@@ -31,6 +31,7 @@ found in the LICENSE file.
<script src="spdyview.js"></script>
<script src="serviceprovidersview.js"></script>
<script src="httpthrottlingview.js"></script>
+ <script src="logsview.js"></script>
</head>
<body onload="onLoaded()">
<!-- Tab switcher for main categories. -->
@@ -48,6 +49,7 @@ found in the LICENSE file.
<li><a href="#serviceProviders" id=serviceProvidersTab style="display: none;">SPIs</a></li>
<li><a href="#tests" id=testTab>Tests</a></li>
<li><a href="#hsts" id=hstsTab>HSTS</a></li>
+ <li><a href="#logs" id=logsTab style="display: none;">Logs</a></li>
</ul>
<div style="clear: both;"></div>
</div>
@@ -473,5 +475,25 @@ function displayHelpForBugDump() {
</div>
<div id=detailsLogBox></div>
<div id=detailsTimelineBox></div>
+
+ <!-- ============================ Logs View ============================ -->
+ <div id=logsTabContent style="display: none;">
+ <h4>Network Log Data</h4>
+ <button id=logsGlobalShowBtn class=logsGlobalButton>Show all...</button>
+ <button id=logsGlobalHideBtn class=logsGlobalButton>Hide all...</button>
+ <button id=logsRefreshBtn class=logsGlobalButton>Refresh logs...</button>
+ <div style="clear: both"></div>
+ <table width=100% class=styledTable>
+ <thead>
+ <tr id=logTableHeaderRow>
+ <th width=10%>Log Name</th>
+ <th width=8%></th>
+ <th width=82%>Log</th>
+ </tr>
+ </thead>
+ <tbody id=logTable>
+ </tbody>
+ </table>
+ </div>
</body>
</html>
diff --git a/chrome/browser/resources/net_internals/logsview.js b/chrome/browser/resources/net_internals/logsview.js
new file mode 100644
index 0000000..032d4577
--- /dev/null
+++ b/chrome/browser/resources/net_internals/logsview.js
@@ -0,0 +1,176 @@
+// Copyright (c) 2011 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+/**
+ * This view displays network related log data and is specific fo ChromeOS.
+ * We get log data from chrome by filtering system logs for network related
+ * keywords. Logs are not fetched until we actually need them.
+ *
+ * @constructor
+ */
+function LogsView(mainBoxId, tableId, globalShowButtonId, globalHideButtonId,
+ refreshLogsButtonId) {
+ var tableDiv = document.getElementById(tableId);
+ this.rows = [];
+ this.PopulateTable(tableDiv, this.logFilterList);
+ document.getElementById(globalShowButtonId).addEventListener('click',
+ this.onGlobalChangeVisibleClick_.bind(this, true));
+ document.getElementById(globalHideButtonId).addEventListener('click',
+ this.onGlobalChangeVisibleClick_.bind(this, false));
+ document.getElementById(refreshLogsButtonId).addEventListener('click',
+ this.onLogsRefresh_.bind(this));
+ DivView.call(this, mainBoxId);
+};
+
+inherits(LogsView, DivView);
+
+/**
+ * Contains log keys we are interested in.
+ */
+LogsView.prototype.logFilterList = [
+ {
+ key:'syslog',
+ },
+ {
+ key:'ui_log',
+ },
+ {
+ key:'chrome_system_log',
+ },
+ {
+ key:'chrome_log',
+ },
+];
+
+/**
+ * Called during View's initialization. Creates the row of a table logs will be
+ * shown in. Each row has 4 cells.
+ *
+ * First cell's content will be set to |logKey|, second will contain a button
+ * that will be used to show or hide third cell, which will contain the filtered
+ * log.
+ * |logKey| also tells us which log we are getting data from.
+ */
+LogsView.prototype.CreateTableRow = function(logKey) {
+ var row = document.createElement('tr');
+
+ var cells = [];
+ for (var i = 0; i < 3; i++) {
+ var rowCell = document.createElement('td');
+ cells.push(rowCell);
+ row.appendChild(rowCell);
+ }
+ // Log key cell.
+ cells[0].className = 'logCellText';
+ cells[0].textContent = logKey;
+ // Cell log is displayed in. Log content is in div element that is initially
+ // hidden and empty.
+ cells[2].className = 'logCellText';
+ var logDiv = document.createElement('div');
+ logDiv.textContent = '';
+ logDiv.className = 'logCellLog';
+ logDiv.id = 'logsView.logCell.' + this.rows.length;
+ cells[2].appendChild(logDiv);
+
+ // Button that we use to show or hide div element with log content. Logs are
+ // not visible initially, so we initialize button accordingly.
+ var expandButton = document.createElement('button');
+ expandButton.textContent = 'Show...';
+ expandButton.className = 'logButton';
+ expandButton.addEventListener('click',
+ this.onButtonClicked_.bind(this, row));
+
+ // Cell that contains show/hide button.
+ cells[1].appendChild(expandButton);
+ cells[1].className = 'logTableButtonColumn';
+
+ // Initially, log is not visible.
+ row.className = 'logRowCollapsed';
+
+ // We will need those to process row buttons' onclick events.
+ row.logKey = logKey;
+ row.expandButton = expandButton;
+ row.logDiv = logDiv;
+ row.logVisible = false;
+ this.rows.push(row);
+
+ return row;
+};
+
+/**
+ * Initializes |tableDiv| to represent data from |logList| which should be of
+ * type LogsView.logFilterList.
+ */
+LogsView.prototype.PopulateTable = function(tableDiv, logList) {
+ for (var i = 0; i < logList.length; i++) {
+ var logSource = this.CreateTableRow(logList[i].key);
+ tableDiv.appendChild(logSource);
+ }
+};
+
+/**
+ * Processes clicks on buttons that show or hide log contents in log row.
+ * Row containing the clicked button is given to the method since it contains
+ * all data we need to process the click (unlike button object itself).
+ */
+LogsView.prototype.onButtonClicked_ = function(containingRow) {
+ if (!containingRow.logVisible) {
+ containingRow.className = 'logRowExpanded';
+ containingRow.expandButton.textContent = 'Hide...';
+ var logDiv = containingRow.logDiv;
+ if (logDiv.textContent == '') {
+ logDiv.textContent = 'Getting logs...';
+ // Callback will be executed by g_browser.
+ g_browser.getSystemLog(containingRow.logKey,
+ containingRow.logDiv.id);
+ }
+ } else {
+ containingRow.className = 'logRowCollapsed';
+ containingRow.expandButton.textContent = 'Show...';
+ }
+ containingRow.logVisible = !containingRow.logVisible;
+};
+
+/**
+ * Processes click on one of the buttons that are used to show or hide all logs
+ * we care about.
+ */
+LogsView.prototype.onGlobalChangeVisibleClick_ = function(isShowAll) {
+ for (var row in this.rows) {
+ if (isShowAll != this.rows[row].logVisible) {
+ this.onButtonClicked_(this.rows[row]);
+ }
+ }
+};
+
+/**
+ * Processes click event on the button we use to refresh fetched logs. we get
+ * the newest logs from libcros, and refresh content of the visible log cells.
+ */
+LogsView.prototype.onLogsRefresh_ = function() {
+ g_browser.refreshSystemLogs();
+
+ var visibleLogRows = [];
+ var hiddenLogRows = [];
+ for (var row in this.rows) {
+ if (this.rows[row].logVisible) {
+ visibleLogRows.push(this.rows[row]);
+ } else {
+ hiddenLogRows.push(this.rows[row]);
+ }
+ }
+
+ // We have to refresh text content in visible rows.
+ for (row in visibleLogRows) {
+ visibleLogRows[row].logDiv.textContent = 'Getting logs...';
+ g_browser.getSystemLog(visibleLogRows[row].logKey,
+ visibleLogRows[row].logDiv.id);
+ }
+
+ // In hidden rows we just clear potential log text, so we know we have to get
+ // new contents when we show the row next time.
+ for (row in hiddenLogRows) {
+ hiddenLogRows[row].logDiv.textContent = '';
+ }
+};
diff --git a/chrome/browser/resources/net_internals/main.css b/chrome/browser/resources/net_internals/main.css
index f7c575c..609a808 100644
--- a/chrome/browser/resources/net_internals/main.css
+++ b/chrome/browser/resources/net_internals/main.css
@@ -109,6 +109,55 @@ body {
color: red;
}
+.logCellEmpty {
+ background-color: gray;
+}
+
+.logCellText, .logCellLog {
+ background-color: white;
+}
+
+.logRowCollapsed .logCellLog {
+ display: none;
+}
+
+.logRowExpanded .logCellLog {
+ display: table-cell;
+}
+
+.logTableButtonColumn {
+ text-align: center;
+}
+
+.logButton {
+ width: 85px;
+}
+
+.logsGlobalButton {
+ width: 135px;
+ float: left;
+ margin: 5px;
+ margin-top: 0;
+}
+
+#logTable td {
+ padding: 0 5px;
+ padding-top: 3px;
+ vertical-align: top;
+ line-height: 17px;
+ font-family: 'Courier New', monospace;
+ white-space: pre;
+ font-size: 12px;
+}
+
+#logTableHeaderRow th {
+ text-align: left;
+}
+
+#logsTabContent {
+ padding: 5px;
+}
+
.tabSwitcher {
margin-top: 10px;
margin-left: 10px;
diff --git a/chrome/browser/resources/net_internals/main.js b/chrome/browser/resources/net_internals/main.js
index 9f500df..cffcc4e 100644
--- a/chrome/browser/resources/net_internals/main.js
+++ b/chrome/browser/resources/net_internals/main.js
@@ -126,6 +126,15 @@ function onLoaded() {
var httpThrottlingView = new HttpThrottlingView(
'httpThrottlingTabContent', 'enableHttpThrottlingCheckbox');
+ var logsView;
+ if (g_browser.isChromeOS()) {
+ logsView = new LogsView('logsTabContent',
+ 'logTable',
+ 'logsGlobalShowBtn',
+ 'logsGlobalHideBtn',
+ 'logsRefreshBtn');
+ }
+
// Create a view which lets you tab between the different sub-views.
var categoryTabSwitcher = new TabSwitcherView('categoryTabHandles');
g_browser.setTabSwitcher(categoryTabSwitcher);
@@ -143,6 +152,8 @@ function onLoaded() {
categoryTabSwitcher.addTab('testTab', testView, false);
categoryTabSwitcher.addTab('hstsTab', hstsView, false);
categoryTabSwitcher.addTab('httpThrottlingTab', httpThrottlingView, false);
+ if (g_browser.isChromeOS())
+ categoryTabSwitcher.addTab('logsTab', logsView, false);
// Build a map from the anchor name of each tab handle to its "tab ID".
// We will consider navigations to the #hash as a switch tab request.
@@ -292,6 +303,10 @@ BrowserBridge.prototype.isPlatformWindows = function() {
return /Win/.test(navigator.platform);
};
+BrowserBridge.prototype.isChromeOS = function() {
+ return /CrOS/.test(navigator.userAgent);
+};
+
BrowserBridge.prototype.sendGetProxySettings = function() {
// The browser will call receivedProxySettings on completion.
chrome.send('getProxySettings');
@@ -383,7 +398,15 @@ BrowserBridge.prototype.enableHttpThrottling = function(enable) {
BrowserBridge.prototype.loadLogFile = function() {
chrome.send('loadLogFile');
-}
+};
+
+BrowserBridge.prototype.refreshSystemLogs = function() {
+ chrome.send('refreshSystemLogs');
+};
+
+BrowserBridge.prototype.getSystemLog = function(log_key, cellId) {
+ chrome.send('getSystemLog', [log_key, cellId]);
+};
//------------------------------------------------------------------------------
// Messages received from the browser
@@ -610,6 +633,10 @@ BrowserBridge.prototype.loadedLogFile = function(logFileContents) {
}
}
+BrowserBridge.prototype.getSystemLogCallback = function(result) {
+ document.getElementById(result.cellId).textContent = result.log;
+}
+
//------------------------------------------------------------------------------
/**
diff --git a/chrome/browser/ui/webui/net_internals_ui.cc b/chrome/browser/ui/webui/net_internals_ui.cc
index a802c77..090ea98 100644
--- a/chrome/browser/ui/webui/net_internals_ui.cc
+++ b/chrome/browser/ui/webui/net_internals_ui.cc
@@ -5,6 +5,7 @@
#include "chrome/browser/ui/webui/net_internals_ui.h"
#include <algorithm>
+#include <list>
#include <string>
#include <utility>
#include <vector>
@@ -61,6 +62,10 @@
#include "ui/base/l10n/l10n_util.h"
#include "ui/base/resource/resource_bundle.h"
+#ifdef OS_CHROMEOS
+#include "chrome/browser/chromeos/cros/cros_library.h"
+#include "chrome/browser/chromeos/cros/syslogs_library.h"
+#endif
#ifdef OS_WIN
#include "chrome/browser/net/service_providers_win.h"
#endif
@@ -172,6 +177,10 @@ class NetInternalsMessageHandler
// Javascript message handlers.
void OnRendererReady(const ListValue* list);
void OnEnableHttpThrottling(const ListValue* list);
+#ifdef OS_CHROMEOS
+ void OnRefreshSystemLogs(const ListValue* list);
+ void OnGetSystemLog(const ListValue* list);
+#endif
// SelectFileDialog::Listener implementation
virtual void FileSelected(const FilePath& path, int index, void* params);
@@ -204,6 +213,61 @@ class NetInternalsMessageHandler
const FilePath path_;
};
+#ifdef OS_CHROMEOS
+ // Class that is used for getting network related ChromeOS logs.
+ // Logs are fetched from ChromeOS libcros on user request, and only when we
+ // don't yet have a copy of logs. If a copy is present, we send back data from
+ // it, else we save request and answer to it when we get logs from libcros.
+ // If needed, we also send request for system logs to libcros.
+ // Logs refresh has to be done explicitly, by deleting old logs and then
+ // loading them again.
+ class SystemLogsGetter {
+ public:
+ SystemLogsGetter(NetInternalsMessageHandler* handler,
+ chromeos::SyslogsLibrary* syslog_lib);
+ ~SystemLogsGetter();
+
+ // Deletes logs copy we currently have, and resets logs_requested and
+ // logs_received flags.
+ void DeleteSystemLogs();
+ // Starts log fetching. If logs copy is present, requested logs are sent
+ // back.
+ // If syslogs load request hasn't been sent to libcros yet, we do that now,
+ // and postpone sending response.
+ // Request data is specified by args:
+ // $1 : key of the log we are interested in.
+ // $2 : string used to identify request.
+ void RequestSystemLog(const ListValue* args);
+ // Requests logs from libcros, but only if we don't have a copy.
+ void LoadSystemLogs();
+ // Processes callback from libcros containing system logs. Postponed
+ // request responses are sent.
+ void OnSystemLogsLoaded(chromeos::LogDictionaryType* sys_info,
+ std::string* ignored_content);
+
+ private:
+ // Struct we save postponed log request in.
+ struct SystemLogRequest {
+ std::string log_key;
+ std::string cell_id;
+ };
+
+ // Processes request.
+ void SendLogs(const SystemLogRequest& request);
+
+ NetInternalsMessageHandler* handler_;
+ chromeos::SyslogsLibrary* syslogs_library_;
+ // List of postponed requests.
+ std::list<SystemLogRequest> requests_;
+ scoped_ptr<chromeos::LogDictionaryType> logs_;
+ bool logs_received_;
+ bool logs_requested_;
+ CancelableRequestConsumer consumer_;
+ // Libcros request handle.
+ CancelableRequestProvider::Handle syslogs_request_id_;
+ };
+#endif
+
// The pref member about whether HTTP throttling is enabled, which needs to
// be accessed on the UI thread.
BooleanPrefMember http_throttling_enabled_;
@@ -215,6 +279,11 @@ class NetInternalsMessageHandler
// This is the "real" message handler, which lives on the IO thread.
scoped_refptr<IOThreadImpl> proxy_;
+#ifdef OS_CHROMEOS
+ // Class that handles getting and filtering system logs.
+ scoped_ptr<SystemLogsGetter> syslogs_getter_;
+#endif
+
// Used for loading log files.
scoped_refptr<SelectFileDialog> select_log_file_dialog_;
@@ -291,7 +360,6 @@ class NetInternalsMessageHandler::IOThreadImpl
#ifdef OS_WIN
void OnGetServiceProviders(const ListValue* list);
#endif
-
void OnSetLogLevel(const ListValue* list);
// ChromeNetLog::ThreadSafeObserver implementation:
@@ -482,6 +550,10 @@ WebUIMessageHandler* NetInternalsMessageHandler::Attach(WebUI* web_ui) {
proxy_ = new IOThreadImpl(this->AsWeakPtr(), g_browser_process->io_thread(),
web_ui->GetProfile()->GetRequestContext());
+#ifdef OS_CHROMEOS
+ syslogs_getter_.reset(new SystemLogsGetter(this,
+ chromeos::CrosLibrary::Get()->GetSyslogsLibrary()));
+#endif
renderer_ready_io_callback_.reset(
proxy_->CreateCallback(&IOThreadImpl::OnRendererReady));
@@ -582,7 +654,14 @@ void NetInternalsMessageHandler::RegisterMessages() {
"getServiceProviders",
proxy_->CreateCallback(&IOThreadImpl::OnGetServiceProviders));
#endif
-
+#ifdef OS_CHROMEOS
+ web_ui_->RegisterMessageCallback(
+ "refreshSystemLogs",
+ NewCallback(this, &NetInternalsMessageHandler::OnRefreshSystemLogs));
+ web_ui_->RegisterMessageCallback(
+ "getSystemLog",
+ NewCallback(this, &NetInternalsMessageHandler::OnGetSystemLog));
+#endif
web_ui_->RegisterMessageCallback(
"setLogLevel",
proxy_->CreateCallback(&IOThreadImpl::OnSetLogLevel));
@@ -660,6 +739,100 @@ void NetInternalsMessageHandler::ReadLogFileTask::Run() {
new StringValue(file_contents));
}
+#ifdef OS_CHROMEOS
+////////////////////////////////////////////////////////////////////////////////
+//
+// NetInternalsMessageHandler::SystemLogsGetter
+//
+////////////////////////////////////////////////////////////////////////////////
+
+NetInternalsMessageHandler::SystemLogsGetter::SystemLogsGetter(
+ NetInternalsMessageHandler* handler,
+ chromeos::SyslogsLibrary* syslog_lib)
+ : handler_(handler),
+ syslogs_library_(syslog_lib),
+ logs_(NULL),
+ logs_received_(false),
+ logs_requested_(false) {
+ if (!syslogs_library_)
+ LOG(ERROR) << "System logs library not loaded";
+}
+
+NetInternalsMessageHandler::SystemLogsGetter::~SystemLogsGetter() {
+ DeleteSystemLogs();
+}
+
+void NetInternalsMessageHandler::SystemLogsGetter::DeleteSystemLogs() {
+ if (syslogs_library_ && logs_requested_ && !logs_received_) {
+ syslogs_library_->CancelRequest(syslogs_request_id_);
+ }
+ logs_requested_ = false;
+ logs_received_ = false;
+ logs_.reset();
+}
+
+void NetInternalsMessageHandler::SystemLogsGetter::RequestSystemLog(
+ const ListValue* args) {
+ if (!logs_requested_) {
+ DCHECK(!logs_received_);
+ LoadSystemLogs();
+ }
+ SystemLogRequest log_request;
+ args->GetString(0, &log_request.log_key);
+ args->GetString(1, &log_request.cell_id);
+
+ if (logs_received_) {
+ SendLogs(log_request);
+ } else {
+ requests_.push_back(log_request);
+ }
+}
+
+void NetInternalsMessageHandler::SystemLogsGetter::LoadSystemLogs() {
+ if (logs_requested_ || !syslogs_library_)
+ return;
+ logs_requested_ = true;
+ syslogs_request_id_ = syslogs_library_->RequestSyslogs(
+ false, // compress logs.
+ chromeos::SyslogsLibrary::SYSLOGS_NETWORK,
+ &consumer_,
+ NewCallback(
+ this,
+ &NetInternalsMessageHandler::SystemLogsGetter::OnSystemLogsLoaded));
+}
+
+void NetInternalsMessageHandler::SystemLogsGetter::OnSystemLogsLoaded(
+ chromeos::LogDictionaryType* sys_info, std::string* ignored_content) {
+ DCHECK(!ignored_content);
+ logs_.reset(sys_info);
+ logs_received_ = true;
+ for (std::list<SystemLogRequest>::iterator request_it = requests_.begin();
+ request_it != requests_.end();
+ ++request_it) {
+ SendLogs(*request_it);
+ }
+ requests_.clear();
+}
+
+void NetInternalsMessageHandler::SystemLogsGetter::SendLogs(
+ const SystemLogRequest& request) {
+ scoped_ptr<DictionaryValue> result(new DictionaryValue());
+ chromeos::LogDictionaryType::iterator log_it = logs_->find(request.log_key);
+ if (log_it != logs_->end()) {
+ if (!log_it->second.empty()) {
+ result->SetString("log", log_it->second);
+ } else {
+ result->SetString("log", "<no relevant lines found>");
+ }
+ } else {
+ result->SetString("log", "<invalid log name>");
+ }
+ result->SetString("cellId", request.cell_id);
+
+ handler_->CallJavascriptFunction(L"g_browser.getSystemLogCallback",
+ result.get());
+}
+#endif
////////////////////////////////////////////////////////////////////////////////
//
// NetInternalsMessageHandler::IOThreadImpl
@@ -1322,6 +1495,19 @@ void NetInternalsMessageHandler::IOThreadImpl::OnGetServiceProviders(
}
#endif
+#ifdef OS_CHROMEOS
+void NetInternalsMessageHandler::OnRefreshSystemLogs(const ListValue* list) {
+ DCHECK(syslogs_getter_.get());
+ syslogs_getter_->DeleteSystemLogs();
+ syslogs_getter_->LoadSystemLogs();
+}
+
+void NetInternalsMessageHandler::OnGetSystemLog(const ListValue* list) {
+ DCHECK(syslogs_getter_.get());
+ syslogs_getter_->RequestSystemLog(list);
+}
+#endif
+
void NetInternalsMessageHandler::IOThreadImpl::OnSetLogLevel(
const ListValue* list) {
int log_level;