diff options
-rw-r--r-- | chrome/browser/resources/net_internals/index.html | 22 | ||||
-rw-r--r-- | chrome/browser/resources/net_internals/logsview.js | 176 | ||||
-rw-r--r-- | chrome/browser/resources/net_internals/main.css | 49 | ||||
-rw-r--r-- | chrome/browser/resources/net_internals/main.js | 29 | ||||
-rw-r--r-- | chrome/browser/ui/webui/net_internals_ui.cc | 190 |
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; |