diff options
author | mmenke@google.com <mmenke@google.com@0039d316-1c4b-4281-b951-d872f2087c98> | 2011-01-26 00:09:07 +0000 |
---|---|---|
committer | mmenke@google.com <mmenke@google.com@0039d316-1c4b-4281-b951-d872f2087c98> | 2011-01-26 00:09:07 +0000 |
commit | 44d33b4816fa103086266bf77030e8bec2336a22 (patch) | |
tree | 00df51214d9a88102313f44af84a60b6851f7b27 | |
parent | a1be9ffd087904ffef1749db5048c3d49588fbb4 (diff) | |
download | chromium_src-44d33b4816fa103086266bf77030e8bec2336a22.zip chromium_src-44d33b4816fa103086266bf77030e8bec2336a22.tar.gz chromium_src-44d33b4816fa103086266bf77030e8bec2336a22.tar.bz2 |
Adds the ability to load JSON log files to about:net-internals.
Only works with logs created by the new "--log-net-log=file",
which writes NetLog events to the specified file, regardless
of other logging command line parameters.
Using "--log-net-log" without a file name will just write
NetLog events to VLOG(1), as before.
BUG=63687
TEST=None
Review URL: http://codereview.chromium.org/6025017
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@72567 0039d316-1c4b-4281-b951-d872f2087c98
-rw-r--r-- | chrome/browser/dom_ui/net_internals_ui.cc | 93 | ||||
-rw-r--r-- | chrome/browser/net/chrome_net_log.cc | 3 | ||||
-rw-r--r-- | chrome/browser/net/net_log_logger.cc | 22 | ||||
-rw-r--r-- | chrome/browser/net/net_log_logger.h | 17 | ||||
-rw-r--r-- | chrome/browser/resources/net_internals/dataview.js | 31 | ||||
-rw-r--r-- | chrome/browser/resources/net_internals/eventsview.js | 12 | ||||
-rw-r--r-- | chrome/browser/resources/net_internals/index.html | 44 | ||||
-rw-r--r-- | chrome/browser/resources/net_internals/main.js | 173 | ||||
-rw-r--r-- | chrome/browser/resources/net_internals/proxyview.js | 4 | ||||
-rw-r--r-- | chrome/browser/resources/net_internals/tabswitcherview.js | 7 |
10 files changed, 363 insertions, 43 deletions
diff --git a/chrome/browser/dom_ui/net_internals_ui.cc b/chrome/browser/dom_ui/net_internals_ui.cc index 403eb5e..856c2b7 100644 --- a/chrome/browser/dom_ui/net_internals_ui.cc +++ b/chrome/browser/dom_ui/net_internals_ui.cc @@ -28,6 +28,9 @@ #include "chrome/browser/net/url_fixer_upper.h" #include "chrome/browser/platform_util.h" #include "chrome/browser/profiles/profile.h" +#include "chrome/browser/shell_dialogs.h" +#include "chrome/browser/tab_contents/tab_contents.h" +#include "chrome/browser/tab_contents/tab_contents_view.h" #include "chrome/common/chrome_paths.h" #include "chrome/common/chrome_version_info.h" #include "chrome/common/jstemplate_builder.h" @@ -136,6 +139,7 @@ class NetInternalsHTMLSource : public ChromeURLDataManager::DataSource { // TODO(eroman): Can we start on the IO thread to begin with? class NetInternalsMessageHandler : public DOMMessageHandler, + public SelectFileDialog::Listener, public base::SupportsWeakPtr<NetInternalsMessageHandler> { public: NetInternalsMessageHandler(); @@ -150,12 +154,43 @@ class NetInternalsMessageHandler void CallJavascriptFunction(const std::wstring& function_name, const Value* value); + // SelectFileDialog::Listener implementation + virtual void FileSelected(const FilePath& path, int index, void* params); + virtual void FileSelectionCanceled(void* params); + + // The only callback handled on the UI thread. As it needs to access fields + // from |dom_ui_|, it can't be called on the IO thread. + void OnLoadLogFile(const ListValue* list); + private: class IOThreadImpl; + // Task run on the FILE thread to read the contents of a log file. The result + // is then passed to IOThreadImpl's CallJavascriptFunction, which sends it + // back to the web page. IOThreadImpl is used instead of the + // NetInternalsMessageHandler directly because it checks if the message + // handler has been destroyed in the meantime. + class ReadLogFileTask : public Task { + public: + ReadLogFileTask(IOThreadImpl* proxy, const FilePath& path); + + virtual void Run(); + + private: + // IOThreadImpl implements existence checks already. Simpler to reused them + // then to reimplement them. + scoped_refptr<IOThreadImpl> proxy_; + + // Path of the file to open. + const FilePath path_; + }; + // This is the "real" message handler, which lives on the IO thread. scoped_refptr<IOThreadImpl> proxy_; + // Used for loading log files. + scoped_refptr<SelectFileDialog> select_log_file_dialog_; + DISALLOW_COPY_AND_ASSIGN(NetInternalsMessageHandler); }; @@ -243,17 +278,17 @@ class NetInternalsMessageHandler::IOThreadImpl int result); virtual void OnCompletedConnectionTestSuite(); + // Helper that executes |function_name| in the attached renderer. + // The function takes ownership of |arg|. Note that this can be called from + // any thread. + void CallJavascriptFunction(const std::wstring& function_name, Value* arg); + private: class CallbackHelper; // Helper that runs |method| with |arg|, and deletes |arg| on completion. void DispatchToMessageHandler(ListValue* arg, MessageHandler method); - // Helper that executes |function_name| in the attached renderer. - // The function takes ownership of |arg|. Note that this can be called from - // any thread. - void CallJavascriptFunction(const std::wstring& function_name, Value* arg); - // Adds |entry| to the queue of pending log entries to be sent to the page via // Javascript. Must be called on the IO Thread. Also creates a delayed task // that will call PostPendingEntries, if there isn't one already. @@ -398,6 +433,8 @@ NetInternalsMessageHandler::~NetInternalsMessageHandler() { BrowserThread::PostTask(BrowserThread::IO, FROM_HERE, NewRunnableMethod(proxy_.get(), &IOThreadImpl::Detach)); } + if (select_log_file_dialog_) + select_log_file_dialog_->ListenerDestroyed(); } DOMMessageHandler* NetInternalsMessageHandler::Attach(DOMUI* dom_ui) { @@ -408,8 +445,35 @@ DOMMessageHandler* NetInternalsMessageHandler::Attach(DOMUI* dom_ui) { return result; } +void NetInternalsMessageHandler::FileSelected( + const FilePath& path, int index, void* params) { + select_log_file_dialog_.release(); + BrowserThread::PostTask( + BrowserThread::FILE, FROM_HERE, + new ReadLogFileTask(proxy_.get(), path)); +} + +void NetInternalsMessageHandler::FileSelectionCanceled(void* params) { + select_log_file_dialog_.release(); +} + +void NetInternalsMessageHandler::OnLoadLogFile(const ListValue* list) { + // Only allow a single dialog at a time. + if (select_log_file_dialog_.get()) + return; + select_log_file_dialog_ = SelectFileDialog::Create(this); + select_log_file_dialog_->SelectFile( + SelectFileDialog::SELECT_OPEN_FILE, string16(), FilePath(), NULL, 0, + FILE_PATH_LITERAL(""), + dom_ui_->tab_contents()->view()->GetTopLevelNativeWindow(), NULL); +} + void NetInternalsMessageHandler::RegisterMessages() { DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); + // Only callback handled on UI thread. + dom_ui_->RegisterMessageCallback( + "loadLogFile", + NewCallback(this, &NetInternalsMessageHandler::OnLoadLogFile)); dom_ui_->RegisterMessageCallback( "notifyReady", @@ -471,6 +535,25 @@ void NetInternalsMessageHandler::CallJavascriptFunction( //////////////////////////////////////////////////////////////////////////////// // +// NetInternalsMessageHandler::ReadLogFileTask +// +//////////////////////////////////////////////////////////////////////////////// + +NetInternalsMessageHandler::ReadLogFileTask::ReadLogFileTask( + IOThreadImpl* proxy, const FilePath& path) + : proxy_(proxy), path_(path) { +} + +void NetInternalsMessageHandler::ReadLogFileTask::Run() { + std::string file_contents; + if (!file_util::ReadFileToString(path_, &file_contents)) + return; + proxy_->CallJavascriptFunction(L"g_browser.loadedLogFile", + new StringValue(file_contents)); +} + +//////////////////////////////////////////////////////////////////////////////// +// // NetInternalsMessageHandler::IOThreadImpl // //////////////////////////////////////////////////////////////////////////////// diff --git a/chrome/browser/net/chrome_net_log.cc b/chrome/browser/net/chrome_net_log.cc index 58fe6ad..39ebe79 100644 --- a/chrome/browser/net/chrome_net_log.cc +++ b/chrome/browser/net/chrome_net_log.cc @@ -67,7 +67,8 @@ ChromeNetLog::ChromeNetLog() const CommandLine& command_line = *CommandLine::ForCurrentProcess(); if (command_line.HasSwitch(switches::kLogNetLog)) { - net_log_logger_.reset(new NetLogLogger()); + net_log_logger_.reset(new NetLogLogger( + command_line.GetSwitchValuePath(switches::kLogNetLog))); AddObserver(net_log_logger_.get()); } } diff --git a/chrome/browser/net/net_log_logger.cc b/chrome/browser/net/net_log_logger.cc index bba75ca..1468e6f 100644 --- a/chrome/browser/net/net_log_logger.cc +++ b/chrome/browser/net/net_log_logger.cc @@ -4,11 +4,19 @@ #include "chrome/browser/net/net_log_logger.h" +#include <stdio.h> + +#include "base/file_util.h" #include "base/json/json_writer.h" +#include "base/threading/thread_restrictions.h" #include "base/values.h" -NetLogLogger::NetLogLogger() +NetLogLogger::NetLogLogger(const FilePath &log_path) : ThreadSafeObserver(net::NetLog::LOG_ALL_BUT_BYTES) { + if (!log_path.empty()) { + base::ThreadRestrictions::ScopedAllowIO allow_io; + file_.Set(file_util::OpenFile(log_path, "w")); + } } NetLogLogger::~NetLogLogger() {} @@ -21,8 +29,16 @@ void NetLogLogger::OnAddEntry(net::NetLog::EventType type, scoped_ptr<Value> value(net::NetLog::EntryToDictionaryValue(type, time, source, phase, params, true)); + // Don't pretty print, so each JSON value occupies a single line, with no + // breaks (Line breaks in any text field will be escaped). Using strings + // instead of integer identifiers allows logs from older versions to be + // loaded, though a little extra parsing has to be done when loading a log. std::string json; - base::JSONWriter::Write(value.get(), true, &json); - VLOG(1) << json; + base::JSONWriter::Write(value.get(), false, &json); + if (!file_.get()) { + VLOG(1) << json; + } else { + fprintf(file_.get(), "%s\n", json.c_str()); + } } diff --git a/chrome/browser/net/net_log_logger.h b/chrome/browser/net/net_log_logger.h index 564f232..2cb88c6 100644 --- a/chrome/browser/net/net_log_logger.h +++ b/chrome/browser/net/net_log_logger.h @@ -6,14 +6,23 @@ #define CHROME_BROWSER_NET_NET_LOG_LOGGER_H_ #pragma once +#include "base/scoped_handle.h" #include "chrome/browser/net/chrome_net_log.h" +class FilePath; + // NetLogLogger watches the NetLog event stream, and sends all entries to -// VLOG(1). This is to debug errors that prevent getting to the -// about:net-internals page. +// VLOG(1) or a path specified on creation. This is to debug errors that +// prevent getting to the about:net-internals page. +// +// Relies on ChromeNetLog only calling an Observer once at a time for +// thread-safety. class NetLogLogger : public ChromeNetLog::ThreadSafeObserver { public: - NetLogLogger(); + // If |log_path| is empty or file creation fails, writes to VLOG(1). + // Otherwise, writes to |log_path|. Uses one line per entry, for + // easy parsing. + explicit NetLogLogger(const FilePath &log_path); ~NetLogLogger(); // ThreadSafeObserver implementation: @@ -24,6 +33,8 @@ class NetLogLogger : public ChromeNetLog::ThreadSafeObserver { net::NetLog::EventParameters* params); private: + ScopedStdioHandle file_; + DISALLOW_COPY_AND_ASSIGN(NetLogLogger); }; diff --git a/chrome/browser/resources/net_internals/dataview.js b/chrome/browser/resources/net_internals/dataview.js index 2535303..5db47ff 100644 --- a/chrome/browser/resources/net_internals/dataview.js +++ b/chrome/browser/resources/net_internals/dataview.js @@ -19,7 +19,12 @@ function DataView(mainBoxId, byteLoggingCheckboxId, passivelyCapturedCountId, activelyCapturedCountId, - deleteAllId) { + deleteAllId, + dumpDataDivId, + loadDataDivId, + loadLogFileId, + capturingTextSpanId, + loggingTextSpanId) { DivView.call(this, mainBoxId); this.textPre_ = document.getElementById(outputTextBoxId); @@ -40,6 +45,14 @@ function DataView(mainBoxId, document.getElementById(deleteAllId).onclick = g_browser.deleteAllEvents.bind(g_browser); + this.dumpDataDiv_ = document.getElementById(dumpDataDivId); + this.loadDataDiv_ = document.getElementById(loadDataDivId); + this.capturingTextSpan_ = document.getElementById(capturingTextSpanId); + this.loggingTextSpan_ = document.getElementById(loggingTextSpanId); + + document.getElementById(loadLogFileId).onclick = + g_browser.loadLogFile.bind(g_browser); + this.updateEventCounts_(); this.waitingForUpdate_ = false; @@ -71,6 +84,18 @@ DataView.prototype.onAllLogEntriesDeleted = function() { }; /** + * Called when either a log file is loaded or when going back to actively + * logging events. In either case, called after clearing the old entries, + * but before getting any new ones. + */ +DataView.prototype.onSetIsViewingLogFile = function(isViewingLogFile) { + setNodeDisplay(this.dumpDataDiv_, !isViewingLogFile); + setNodeDisplay(this.capturingTextSpan_, !isViewingLogFile); + setNodeDisplay(this.loggingTextSpan_, isViewingLogFile); + this.setText_(''); +}; + +/** * Updates the counters showing how many events have been captured. */ DataView.prototype.updateEventCounts_ = function() { @@ -108,6 +133,10 @@ DataView.prototype.onExportToText_ = function() { * Presents the captured data as formatted text. */ DataView.prototype.onUpdateAllCompleted = function(data) { + // It's possible for a log file to be loaded while a dump is being generated. + // When that happens, don't display the log dump, to avoid any confusion. + if (g_browser.isViewingLogFile()) + return; this.waitingForUpdate_ = false; var text = []; diff --git a/chrome/browser/resources/net_internals/eventsview.js b/chrome/browser/resources/net_internals/eventsview.js index 7af86aa..d3768ed 100644 --- a/chrome/browser/resources/net_internals/eventsview.js +++ b/chrome/browser/resources/net_internals/eventsview.js @@ -475,10 +475,20 @@ EventsView.prototype.onLogEntriesDeleted = function(sourceIds) { /** * Called whenever all log events are deleted. */ -EventsView.prototype.onAllLogEntriesDeleted = function(offset) { +EventsView.prototype.onAllLogEntriesDeleted = function() { this.initializeSourceList_(); }; +/** + * Called when either a log file is loaded or when going back to actively + * logging events. In either case, called after clearing the old entries, + * but before getting any new ones. + */ +EventsView.prototype.onSetIsViewingLogFile = function(isViewingLogFile) { + // Needed to sort new sourceless entries correctly. + this.maxReceivedSourceId_ = 0; +}; + EventsView.prototype.incrementPrefilterCount = function(offset) { this.numPrefilter_ += offset; this.invalidateFilterCounter_(); diff --git a/chrome/browser/resources/net_internals/index.html b/chrome/browser/resources/net_internals/index.html index 732c4be..0cbadb7 100644 --- a/chrome/browser/resources/net_internals/index.html +++ b/chrome/browser/resources/net_internals/index.html @@ -202,23 +202,41 @@ found in the LICENSE file. <table width=100%> <tr> <td valign=top> - <h2>Dump data</h2> - <div style="margin: 8px"> - <p><input id=securityStrippingCheckbox type=checkbox checked=yes> - Strip private information (cookies and credentials). - </p> - <p> - <a href="javascript:displayHelpForBugDump()"> - Help: How to get data for bug reports? - </a> - </p> - <button id=exportToText class=bigButton>Dump to text</button> + <div id=dataViewDumpDataDiv> + <h2>Dump data</h2> + <div style="margin: 8px"> + <p><input id=securityStrippingCheckbox type=checkbox checked=yes> + Strip private information (cookies and credentials). + </p> + <p> + <a href="javascript:displayHelpForBugDump()"> + Help: How to get data for bug reports? + </a> + </p> + <button id=exportToText class=bigButton>Dump to text</button> + </div> + </div> + <div id=dataViewLoadDataDiv> + <h2>Load data</h2> + <div style="margin: 8px"> + <p><input type=button value="Load log from file" id=dataViewLoadLogFile /></p> + <p>Only works with log files created with "--log-net-log=file_name".</p> + <p>Once a log is loaded, this page will stop collecting data, and will + only start gathering data again when the page is + <a href="javascript:history.go(0);">reloaded</a>.<BR> + </p> + </div> </div> </td> <td align=right valign=top> - <div class="capturingBox"> - <b>Capturing all events...</b> + <div class="capturingBox" id=dataViewCapturingBox> + <span id=dataViewCapturingTextSpan> + <b>Capturing all events...</b> + </span> + <span id=dataViewLoggingTextSpan style="display: none;"> + <b>Viewing loaded log file.</b> + </span> <table style="margin: 8px"> <tr> <td>Passively captured:</td> diff --git a/chrome/browser/resources/net_internals/main.js b/chrome/browser/resources/net_internals/main.js index d879c87..de8b43c 100644 --- a/chrome/browser/resources/net_internals/main.js +++ b/chrome/browser/resources/net_internals/main.js @@ -75,10 +75,12 @@ function onLoaded() { // captured data. var dataView = new DataView('dataTabContent', 'exportedDataText', 'exportToText', 'securityStrippingCheckbox', - 'byteLoggingCheckbox', - 'passivelyCapturedCount', - 'activelyCapturedCount', - 'dataViewDeleteAll'); + 'byteLoggingCheckbox', 'passivelyCapturedCount', + 'activelyCapturedCount', 'dataViewDeleteAll', + 'dataViewDumpDataDiv', 'dataViewLoadDataDiv', + 'dataViewLoadLogFile', + 'dataViewCapturingTextSpan', + 'dataViewLoggingTextSpan'); // Create a view which will display the results and controls for connection // tests. @@ -108,6 +110,7 @@ function onLoaded() { // Create a view which lets you tab between the different sub-views. var categoryTabSwitcher = new TabSwitcherView('categoryTabHandles'); + g_browser.setTabSwitcher(categoryTabSwitcher); // Populate the main tabs. categoryTabSwitcher.addTab('eventsTab', eventsView, false); @@ -144,6 +147,9 @@ function onLoaded() { // Select the initial view based on the current URL. window.onhashchange(); + // Inform observers a log file is not currently being displayed. + g_browser.setIsViewingLogFile_(false); + // Tell the browser that we are ready to start receiving log events. g_browser.sendReady(); } @@ -191,6 +197,11 @@ function BrowserBridge() { // Next unique id to be assigned to a log entry without a source. // Needed to simplify deletion, identify associated GUI elements, etc. this.nextSourcelessEventId_ = -1; + + // True when viewing a log file rather than actively logged events. + // When viewing a log file, all tabs are hidden except the event view, + // and all received events are ignored. + this.isViewingLogFile_ = false; } /* @@ -302,23 +313,19 @@ BrowserBridge.prototype.setLogLevel = function(logLevel) { chrome.send('setLogLevel', ['' + logLevel]); } +BrowserBridge.prototype.loadLogFile = function() { + chrome.send('loadLogFile'); +} + //------------------------------------------------------------------------------ // Messages received from the browser //------------------------------------------------------------------------------ BrowserBridge.prototype.receivedLogEntries = function(logEntries) { - for (var e = 0; e < logEntries.length; ++e) { - var logEntry = logEntries[e]; - - // Assign unique ID, if needed. - if (logEntry.source.id == 0) { - logEntry.source.id = this.nextSourcelessEventId_; - --this.nextSourcelessEventId_; - } - this.capturedEvents_.push(logEntry); - for (var i = 0; i < this.logObservers_.length; ++i) - this.logObservers_[i].onLogEntryAdded(logEntry); - } + // Does nothing if viewing a log file. + if (this.isViewingLogFile_) + return; + this.addLogEntries(logEntries); }; BrowserBridge.prototype.receivedLogEventTypeConstants = function(constantsMap) { @@ -439,9 +446,91 @@ BrowserBridge.prototype.receivedHttpCacheInfo = function(info) { this.pollableDataHelpers_.httpCacheInfo.update(info); }; +BrowserBridge.prototype.loadedLogFile = function(logFileContents) { + var match; + // Replace carriage returns with linebreaks and then split around linebreaks. + var lines = logFileContents.replace(/\r/g, '\n').split('\n'); + var entries = []; + var numInvalidLines = 0; + + for (var i = 0; i < lines.length; ++i) { + if (lines[i].trim().length == 0) + continue; + // Parse all valid lines, skipping any others. + try { + var entry = JSON.parse(lines[i]); + if (entry && + typeof(entry) == 'object' && + entry.phase != undefined && + entry.source != undefined && + entry.time != undefined && + entry.type != undefined) { + entries.push(entry); + continue; + } + } catch (err) { + } + ++numInvalidLines; + console.log('Unable to parse log line: ' + lines[i]); + } + + if (entries.length == 0) { + window.alert('Loading log file failed.'); + return; + } + + this.deleteAllEvents(); + + this.setIsViewingLogFile_(true); + + var validEntries = []; + for (var i = 0; i < entries.length; ++i) { + entries[i].wasPassivelyCaptured = true; + if (LogEventType[entries[i].type] != undefined && + LogSourceType[entries[i].source.type] != undefined && + LogEventPhase[entries[i].phase] != undefined) { + entries[i].type = LogEventType[entries[i].type]; + entries[i].source.type = LogSourceType[entries[i].source.type]; + entries[i].phase = LogEventPhase[entries[i].phase]; + validEntries.push(entries[i]); + } else { + // TODO(mmenke): Do something reasonable when the event type isn't + // found, which could happen when event types are + // removed or added between versions. Could also happen + // with source types, but less likely. + console.log( + 'Unrecognized values in log entry: ' + JSON.stringify(entry)); + } + } + + this.numPassivelyCapturedEvents_ = validEntries.length; + this.addLogEntries(validEntries); + + var numInvalidEntries = entries.length - validEntries.length; + if (numInvalidEntries > 0 || numInvalidLines > 0) { + window.alert( + numInvalidLines.toString() + + ' could not be parsed as JSON strings, and ' + + numInvalidEntries.toString() + + ' entries don\'t have valid data.\n\n' + + 'Unparseable lines may indicate log file corruption.\n' + + 'Entries with invalid data may be caused by version differences.\n\n' + + 'See console for more information.'); + } +} + //------------------------------------------------------------------------------ /** + * Sets the |categoryTabSwitcher_| of BrowserBridge. Since views depend on + * g_browser being initialized, have to have a BrowserBridge prior to tab + * construction. + */ +BrowserBridge.prototype.setTabSwitcher = function(categoryTabSwitcher) { + this.categoryTabSwitcher_ = categoryTabSwitcher; +}; + +/** * Adds a listener of log entries. |observer| will be called back when new log * data arrives, through: * @@ -592,6 +681,25 @@ BrowserBridge.prototype.getNumPassivelyCapturedEvents = function() { }; /** + * Sends each entry to all log observers, and updates |capturedEvents_|. + * Also assigns unique ids to log entries without a source. + */ +BrowserBridge.prototype.addLogEntries = function(logEntries) { + for (var e = 0; e < logEntries.length; ++e) { + var logEntry = logEntries[e]; + + // Assign unique ID, if needed. + if (logEntry.source.id == 0) { + logEntry.source.id = this.nextSourcelessEventId_; + --this.nextSourcelessEventId_; + } + this.capturedEvents_.push(logEntry); + for (var i = 0; i < this.logObservers_.length; ++i) + this.logObservers_[i].onLogEntryAdded(logEntry); + } +}; + +/** * Deletes captured events with source IDs in |sourceIds|. */ BrowserBridge.prototype.deleteEventsBySourceId = function(sourceIds) { @@ -626,6 +734,39 @@ BrowserBridge.prototype.deleteAllEvents = function() { }; /** + * Informs log observers whether or not future events will be from a log file. + * Hides all tabs except the events and data tabs when viewing a log file, shows + * them all otherwise. + */ +BrowserBridge.prototype.setIsViewingLogFile_ = function(isViewingLogFile) { + this.isViewingLogFile_ = isViewingLogFile; + var tabIds = this.categoryTabSwitcher_.getAllTabIds(); + + for (var i = 0; i < this.logObservers_.length; ++i) + this.logObservers_[i].onSetIsViewingLogFile(isViewingLogFile); + + // Shows/hides tabs not used when viewing a log file. + for (var i = 0; i < tabIds.length; ++i) { + if (tabIds[i] == 'eventsTab' || tabIds[i] == 'dataTab') + continue; + this.categoryTabSwitcher_.showTabHandleNode(tabIds[i], !isViewingLogFile); + } + + if (isViewingLogFile) { + var activeTab = this.categoryTabSwitcher_.findActiveTab(); + if (activeTab.id != 'eventsTab') + this.categoryTabSwitcher_.switchToTab('dataTab', null); + } +}; + +/** + * Returns true if a log file is currently being viewed. + */ +BrowserBridge.prototype.isViewingLogFile = function() { + return this.isViewingLogFile_; +}; + +/** * If |force| is true, calls all startUpdate functions. Otherwise, just * runs updates with active observers. */ diff --git a/chrome/browser/resources/net_internals/proxyview.js b/chrome/browser/resources/net_internals/proxyview.js index 0f7f68b..a6ff9a9 100644 --- a/chrome/browser/resources/net_internals/proxyview.js +++ b/chrome/browser/resources/net_internals/proxyview.js @@ -114,3 +114,7 @@ ProxyView.prototype.onLogEntriesDeleted = function(sourceIds) { ProxyView.prototype.onAllLogEntriesDeleted = function() { this.clearLog_(); }; + +ProxyView.prototype.onSetIsViewingLogFile = function(isViewingLogFile) { +}; + diff --git a/chrome/browser/resources/net_internals/tabswitcherview.js b/chrome/browser/resources/net_internals/tabswitcherview.js index 646c983..18cfa7a 100644 --- a/chrome/browser/resources/net_internals/tabswitcherview.js +++ b/chrome/browser/resources/net_internals/tabswitcherview.js @@ -136,6 +136,13 @@ TabSwitcherView.prototype.getAllTabIds = function() { return ids; }; +// Shows/hides the DOM node that is used to select the tab. Will not change +// the active tab. +TabSwitcherView.prototype.showTabHandleNode = function(id, isVisible) { + var tab = this.findTabById(id); + setNodeDisplay(tab.getTabHandleNode(), isVisible); +}; + //----------------------------------------------------------------------------- /** |