summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authormmenke@chromium.org <mmenke@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2011-07-11 21:55:11 +0000
committermmenke@chromium.org <mmenke@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2011-07-11 21:55:11 +0000
commitc5afccb09e2b865b20803d44e9fb1b7cb39ca123 (patch)
tree10bfd7813c4592ecd0844ac0a0447b0851a26ed9
parenta0057be84fa71801a949079147edb9dd8cdfb5da (diff)
downloadchromium_src-c5afccb09e2b865b20803d44e9fb1b7cb39ca123.zip
chromium_src-c5afccb09e2b865b20803d44e9fb1b7cb39ca123.tar.gz
chromium_src-c5afccb09e2b865b20803d44e9fb1b7cb39ca123.tar.bz2
Use JSON for net-internals log dumps and make them loadable.
BUG=87320 TEST=manual Review URL: http://codereview.chromium.org/7215037 git-svn-id: svn://svn.chromium.org/chrome/trunk/src@92061 0039d316-1c4b-4281-b951-d872f2087c98
-rw-r--r--chrome/browser/net/net_log_logger.cc18
-rw-r--r--chrome/browser/net/net_log_logger.h4
-rw-r--r--chrome/browser/resources/net_internals/dataview.js479
-rw-r--r--chrome/browser/resources/net_internals/detailsview.js6
-rw-r--r--chrome/browser/resources/net_internals/dnsview.js12
-rw-r--r--chrome/browser/resources/net_internals/eventsview.js9
-rw-r--r--chrome/browser/resources/net_internals/httpcacheview.js10
-rw-r--r--chrome/browser/resources/net_internals/index.html54
-rw-r--r--chrome/browser/resources/net_internals/logdumputil.js229
-rw-r--r--chrome/browser/resources/net_internals/logviewpainter.js55
-rw-r--r--chrome/browser/resources/net_internals/main.js374
-rw-r--r--chrome/browser/resources/net_internals/prerenderview.js12
-rw-r--r--chrome/browser/resources/net_internals/proxyview.js47
-rw-r--r--chrome/browser/resources/net_internals/serviceprovidersview.js20
-rw-r--r--chrome/browser/resources/net_internals/socketsview.js7
-rw-r--r--chrome/browser/resources/net_internals/spdyview.js31
-rw-r--r--chrome/browser/resources/net_internals/tabswitcherview.js7
-rw-r--r--chrome/browser/resources/net_internals/util.js16
-rw-r--r--chrome/browser/resources/net_internals/view.js19
-rw-r--r--chrome/browser/ui/webui/net_internals_ui.cc405
-rw-r--r--chrome/browser/ui/webui/net_internals_ui.h6
21 files changed, 995 insertions, 825 deletions
diff --git a/chrome/browser/net/net_log_logger.cc b/chrome/browser/net/net_log_logger.cc
index e0f67a3..6c79278 100644
--- a/chrome/browser/net/net_log_logger.cc
+++ b/chrome/browser/net/net_log_logger.cc
@@ -8,18 +8,30 @@
#include "base/file_util.h"
#include "base/json/json_writer.h"
+#include "base/memory/scoped_ptr.h"
#include "base/threading/thread_restrictions.h"
#include "base/values.h"
+#include "chrome/browser/ui/webui/net_internals_ui.h"
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"));
+
+ // Write constants to the output file. This allows loading files that have
+ // different source and event types, as they may be added and removed
+ // between Chrome versions.
+ scoped_ptr<Value> value(NetInternalsUI::GetConstants());
+ std::string json;
+ base::JSONWriter::Write(value.get(), false, &json);
+ fprintf(file_.get(), "{\"constants\": %s,\n", json.c_str());
+ fprintf(file_.get(), "\"events\": [\n");
}
}
-NetLogLogger::~NetLogLogger() {}
+NetLogLogger::~NetLogLogger() {
+}
void NetLogLogger::OnAddEntry(net::NetLog::EventType type,
const base::TimeTicks& time,
@@ -28,7 +40,7 @@ void NetLogLogger::OnAddEntry(net::NetLog::EventType type,
net::NetLog::EventParameters* params) {
scoped_ptr<Value> value(
net::NetLog::EntryToDictionaryValue(
- type, time, source, phase, params, true));
+ type, time, source, phase, params, false));
// 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
@@ -38,6 +50,6 @@ void NetLogLogger::OnAddEntry(net::NetLog::EventType type,
if (!file_.get()) {
VLOG(1) << json;
} else {
- fprintf(file_.get(), "%s\n", json.c_str());
+ 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 9f3ff75..1ad1244 100644
--- a/chrome/browser/net/net_log_logger.h
+++ b/chrome/browser/net/net_log_logger.h
@@ -15,6 +15,10 @@ class FilePath;
// VLOG(1) or a path specified on creation. This is to debug errors that
// prevent getting to the about:net-internals page.
//
+// When writing directly to a file rather than VLOG(1), the text file will
+// contain a single JSON object, with an extra comma on the end and missing
+// a terminal "]}".
+//
// Relies on ChromeNetLog only calling an Observer once at a time for
// thread-safety.
class NetLogLogger : public ChromeNetLog::ThreadSafeObserver {
diff --git a/chrome/browser/resources/net_internals/dataview.js b/chrome/browser/resources/net_internals/dataview.js
index afe49cb..dad9903 100644
--- a/chrome/browser/resources/net_internals/dataview.js
+++ b/chrome/browser/resources/net_internals/dataview.js
@@ -3,26 +3,31 @@
// found in the LICENSE file.
/**
- * This view displays options for importing/exporting the captured data. Its
- * primarily usefulness is to allow users to copy-paste their data in an easy
- * to read format for bug reports.
+ * This view displays options for importing/exporting the captured data. This
+ * view is responsible for the interface and file I/O. The BrowserBridge
+ * handles creation and loading of the data dumps.
*
- * - Has a button to generate a text report.
+ * - Has a button to generate a JSON dump.
+ *
+ * - Has a button to import a JSON dump.
*
* - Shows how many events have been captured.
* @constructor
*/
function DataView(mainBoxId,
downloadIframeId,
- exportFileButtonId,
+ saveFileButtonId,
+ dataViewSaveStatusTextId,
securityStrippingCheckboxId,
byteLoggingCheckboxId,
passivelyCapturedCountId,
activelyCapturedCountId,
deleteAllId,
dumpDataDivId,
- loadDataDivId,
+ loadedDivId,
+ loadedClientInfoTextId,
loadLogFileId,
+ dataViewLoadStatusTextId,
capturingTextSpanId,
loggingTextSpanId) {
DivView.call(this, mainBoxId);
@@ -38,8 +43,9 @@ function DataView(mainBoxId,
this.downloadIframe_ = document.getElementById(downloadIframeId);
- this.exportFileButton_ = document.getElementById(exportFileButtonId);
- this.exportFileButton_.onclick = this.onExportToText_.bind(this);
+ this.saveFileButton_ = document.getElementById(saveFileButtonId);
+ this.saveFileButton_.onclick = this.onSaveFile_.bind(this);
+ this.saveStatusText_ = document.getElementById(dataViewSaveStatusTextId);
this.activelyCapturedCountBox_ =
document.getElementById(activelyCapturedCountId);
@@ -49,13 +55,15 @@ function DataView(mainBoxId,
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);
- var loadLogFileElement = document.getElementById(loadLogFileId);
- loadLogFileElement.onchange =
- this.logFileChanged.bind(this, loadLogFileElement);
+ this.loadedDiv_ = document.getElementById(loadedDivId);
+ this.loadedClientInfoText_ = document.getElementById(loadedClientInfoTextId);
+
+ this.loadFileElement_ = document.getElementById(loadLogFileId);
+ this.loadFileElement_.onchange = this.logFileChanged.bind(this);
+ this.loadStatusText_ = document.getElementById(dataViewLoadStatusTextId);
this.updateEventCounts_();
@@ -87,14 +95,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.
+ * Called when a log file is loaded, after clearing the old log entries and
+ * loading the new ones. Hides parts of the page related to saving data, and
+ * shows those related to loading. Returns true to indicate the view should
+ * still be visible.
*/
-DataView.prototype.onSetIsViewingLogFile = function(isViewingLogFile) {
- setNodeDisplay(this.dumpDataDiv_, !isViewingLogFile);
- setNodeDisplay(this.capturingTextSpan_, !isViewingLogFile);
- setNodeDisplay(this.loggingTextSpan_, isViewingLogFile);
+DataView.prototype.onLoadLogFinish = function(data) {
+ setNodeDisplay(this.dumpDataDiv_, false);
+ setNodeDisplay(this.capturingTextSpan_, false);
+ setNodeDisplay(this.loggingTextSpan_, true);
+ setNodeDisplay(this.loadedDiv_, true);
+ this.updateLoadedClientInfo();
+ return true;
};
/**
@@ -136,9 +148,10 @@ DataView.prototype.onSecurityStrippingChanged = function() {
*
* Gets the log file from the input element and tries to read from it.
*/
-DataView.prototype.logFileChanged = function(loadLogFileElement) {
- var logFile = loadLogFileElement.files[0];
+DataView.prototype.logFileChanged = function() {
+ var logFile = this.loadFileElement_.files[0];
if (logFile) {
+ this.setLoadFileStatus('Loading log...', true);
var fileReader = new FileReader();
fileReader.onload = this.onLoadLogFile.bind(this);
@@ -150,269 +163,99 @@ DataView.prototype.logFileChanged = function(loadLogFileElement) {
/**
* Displays an error message when unable to read the selected log file.
+ * Also clears the file input control, so the same file can be reloaded.
*/
DataView.prototype.onLoadLogFileError = function(event) {
- alert('Error ' + event.target.error.code + '. Unable to load file.');
+ this.loadFileElement_.value = null;
+ this.setLoadFileStatus(
+ 'Error ' + getKeyWithValue(FileError, event.target.error.code) +
+ '. Unable to read file.',
+ false);
};
-/**
- * Tries to load the contents of the log file.
- */
DataView.prototype.onLoadLogFile = function(event) {
- g_browser.loadedLogFile(event.target.result);
+ var result = loadLogFile(event.target.result);
+ this.setLoadFileStatus(result, false);
};
-DataView.prototype.enableExportFileButton_ = function(enabled) {
- this.exportFileButton_.disabled = !enabled;
+/**
+ * Sets the load from file status text, displayed below the load file button,
+ * to |text|. Also enables or disables the save/load buttons based on the value
+ * of |isLoading|, which must be true if the load process is still ongoing, and
+ * false when the operation has stopped, regardless of success of failure.
+ * Also, when loading is done, replaces the load button so the same file can be
+ * loaded again.
+ */
+DataView.prototype.setLoadFileStatus = function(text, isLoading) {
+ this.enableLoadFileElement_(!isLoading);
+ this.enableSaveFileButton_(!isLoading);
+ this.loadStatusText_.innerText = text;
+ this.saveStatusText_.innerText = '';
+
+ if (!isLoading) {
+ // Clear the button, so the same file can be reloaded. Recreating the
+ // element seems to be the only way to do this.
+ var loadFileElementId = this.loadFileElement_.id;
+ var loadFileElementOnChange = this.loadFileElement_.onchange;
+ this.loadFileElement_.outerHTML = this.loadFileElement_.outerHTML;
+ this.loadFileElement_ = document.getElementById(loadFileElementId);
+ this.loadFileElement_.onchange = loadFileElementOnChange;
+ }
};
/**
- * If not already waiting for results from all updates, triggers all
- * updates and starts waiting for them to complete.
+ * Sets the save to file status text, displayed below the save to file button,
+ * to |text|. Also enables or disables the save/load buttons based on the value
+ * of |isSaving|, which must be true if the save process is still ongoing, and
+ * false when the operation has stopped, regardless of success of failure.
*/
-DataView.prototype.onExportToText_ = function() {
- if (this.exportFileButton_.disabled)
- return;
- this.enableExportFileButton_(false);
+DataView.prototype.setSaveFileStatus = function(text, isSaving) {
+ this.enableSaveFileButton_(!isSaving);
+ this.enableLoadFileElement_(!isSaving);
+ this.loadStatusText_.innerText = '';
+ this.saveStatusText_.innerText = text;
+};
+
+DataView.prototype.enableSaveFileButton_ = function(enabled) {
+ this.saveFileButton_.disabled = !enabled;
+};
- g_browser.updateAllInfo(this.onUpdateAllCompleted.bind(this));
+DataView.prototype.enableLoadFileElement_ = function(enabled) {
+ this.loadFileElement_.disabled = !enabled;
};
/**
- * Presents the captured data as formatted text.
+ * If not already busy loading or saving a log dump, triggers generation of
+ * gathering log dump data and starts waiting for the process to complete.
*/
-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())
+DataView.prototype.onSaveFile_ = function() {
+ if (this.saveFileButton_.disabled)
return;
- this.waitingForUpdate_ = false;
- var text = [];
-
- // Print some basic information about our environment.
- text.push('Data exported on: ' + (new Date()).toLocaleString());
- text.push('');
- text.push('Number of passively captured events: ' +
- g_browser.getNumPassivelyCapturedEvents());
- text.push('Number of actively captured events: ' +
- g_browser.getNumActivelyCapturedEvents());
- text.push('');
-
- text.push('Chrome version: ' + ClientInfo.version +
- ' (' + ClientInfo.official +
- ' ' + ClientInfo.cl +
- ') ' + ClientInfo.version_mod);
- // Third value in first set of parentheses in user-agent string.
- var platform = /\(.*?;.*?; (.*?);/.exec(navigator.userAgent);
- if (platform)
- text.push('Platform: ' + platform[1]);
- text.push('Command line: ' + ClientInfo.command_line);
-
- text.push('');
- var default_address_family = data.hostResolverInfo.default_address_family;
- text.push('Default address family: ' +
- getKeyWithValue(AddressFamily, default_address_family));
- if (default_address_family == AddressFamily.ADDRESS_FAMILY_IPV4)
- text.push(' (IPv6 disabled)');
-
- text.push('');
- text.push('----------------------------------------------');
- text.push(' Proxy settings (effective)');
- text.push('----------------------------------------------');
- text.push('');
-
- text.push(proxySettingsToString(data.proxySettings.effective));
-
- text.push('');
- text.push('----------------------------------------------');
- text.push(' Proxy settings (original)');
- text.push('----------------------------------------------');
- text.push('');
-
- text.push(proxySettingsToString(data.proxySettings.original));
-
- text.push('');
- text.push('----------------------------------------------');
- text.push(' Bad proxies cache');
- text.push('----------------------------------------------');
-
- var badProxiesList = data.badProxies;
- if (badProxiesList.length == 0) {
- text.push('');
- text.push('None');
- } else {
- for (var i = 0; i < badProxiesList.length; ++i) {
- var e = badProxiesList[i];
- text.push('');
- text.push('(' + (i+1) + ')');
- text.push('Proxy: ' + e.proxy_uri);
- text.push('Bad until: ' + this.formatExpirationTime_(e.bad_until));
- }
- }
-
- text.push('');
- text.push('----------------------------------------------');
- text.push(' Host resolver cache');
- text.push('----------------------------------------------');
- text.push('');
-
- var hostResolverCache = data.hostResolverInfo.cache;
-
- text.push('Capacity: ' + hostResolverCache.capacity);
- text.push('Time to live for successful resolves (ms): ' +
- hostResolverCache.ttl_success_ms);
- text.push('Time to live for failed resolves (ms): ' +
- hostResolverCache.ttl_failure_ms);
-
- if (hostResolverCache.entries.length > 0) {
- for (var i = 0; i < hostResolverCache.entries.length; ++i) {
- var e = hostResolverCache.entries[i];
-
- text.push('');
- text.push('(' + (i+1) + ')');
- text.push('Hostname: ' + e.hostname);
- text.push('Address family: ' +
- getKeyWithValue(AddressFamily, e.address_family));
-
- if (e.error != undefined) {
- text.push('Error: ' + e.error);
- } else {
- for (var j = 0; j < e.addresses.length; ++j) {
- text.push('Address ' + (j + 1) + ': ' + e.addresses[j]);
- }
- }
-
- text.push('Valid until: ' + this.formatExpirationTime_(e.expiration));
- var expirationDate = g_browser.convertTimeTicksToDate(e.expiration);
- text.push(' (' + expirationDate.toLocaleString() + ')');
- }
- } else {
- text.push('');
- text.push('None');
- }
-
- text.push('');
- text.push('----------------------------------------------');
- text.push(' Events');
- text.push('----------------------------------------------');
- text.push('');
-
- this.appendEventsPrintedAsText_(text);
-
- text.push('');
- text.push('----------------------------------------------');
- text.push(' Http cache stats');
- text.push('----------------------------------------------');
- text.push('');
-
- var httpCacheStats = data.httpCacheInfo.stats;
- for (var statName in httpCacheStats)
- text.push(statName + ': ' + httpCacheStats[statName]);
-
- text.push('');
- text.push('----------------------------------------------');
- text.push(' Socket pools');
- text.push('----------------------------------------------');
- text.push('');
-
- this.appendSocketPoolsAsText_(text, data.socketPoolInfo);
-
- text.push('');
- text.push('----------------------------------------------');
- text.push(' SPDY Status');
- text.push('----------------------------------------------');
- text.push('');
-
- text.push('SPDY Enabled: ' + data.spdyStatus.spdy_enabled);
- text.push('Use Alternate Protocol: ' +
- data.spdyStatus.use_alternate_protocols);
- text.push('Force SPDY Always: ' + data.spdyStatus.force_spdy_always);
- text.push('Force SPDY Over SSL: ' + data.spdyStatus.force_spdy_over_ssl);
- text.push('Next Protocols: ' + data.spdyStatus.next_protos);
-
-
- text.push('');
- text.push('----------------------------------------------');
- text.push(' SPDY Sessions');
- text.push('----------------------------------------------');
- text.push('');
-
- if (data.spdySessionInfo == null || data.spdySessionInfo.length == 0) {
- text.push('None');
- } else {
- var spdyTablePrinter =
- SpdyView.createSessionTablePrinter(data.spdySessionInfo);
- text.push(spdyTablePrinter.toText(2));
- }
-
- text.push('');
- text.push('----------------------------------------------');
- text.push(' Alternate Protocol Mappings');
- text.push('----------------------------------------------');
- text.push('');
-
- if (data.spdyAlternateProtocolMappings == null ||
- data.spdyAlternateProtocolMappings.length == 0) {
- text.push('None');
- } else {
- var spdyTablePrinter =
- SpdyView.createAlternateProtocolMappingsTablePrinter(
- data.spdyAlternateProtocolMappings);
- text.push(spdyTablePrinter.toText(2));
- }
-
- if (g_browser.isPlatformWindows()) {
- text.push('');
- text.push('----------------------------------------------');
- text.push(' Winsock layered service providers');
- text.push('----------------------------------------------');
- text.push('');
-
- var serviceProviders = data.serviceProviders;
- var layeredServiceProviders = serviceProviders.service_providers;
- for (var i = 0; i < layeredServiceProviders.length; ++i) {
- var provider = layeredServiceProviders[i];
- text.push('name: ' + provider.name);
- text.push('version: ' + provider.version);
- text.push('type: ' +
- ServiceProvidersView.getLayeredServiceProviderType(provider));
- text.push('socket_type: ' +
- ServiceProvidersView.getSocketType(provider));
- text.push('socket_protocol: ' +
- ServiceProvidersView.getProtocolType(provider));
- text.push('path: ' + provider.path);
- text.push('');
- }
-
- text.push('');
- text.push('----------------------------------------------');
- text.push(' Winsock namespace providers');
- text.push('----------------------------------------------');
- text.push('');
+ this.setSaveFileStatus('Preparing data...', true);
- var namespaceProviders = serviceProviders.namespace_providers;
- for (var i = 0; i < namespaceProviders.length; ++i) {
- var provider = namespaceProviders[i];
- text.push('name: ' + provider.name);
- text.push('version: ' + provider.version);
- text.push('type: ' +
- ServiceProvidersView.getNamespaceProviderType(provider));
- text.push('active: ' + provider.active);
- text.push('');
- }
- }
+ createLogDumpAsync(this.onLogDumpCreated.bind(this));
+};
+/**
+ * Starts the process of creating a file containing |dumpText| and downloading
+ * it.
+ */
+DataView.prototype.onLogDumpCreated = function(dumpText) {
var blobBuilder = new WebKitBlobBuilder();
- blobBuilder.append(text.join('\n'), 'native');
+ blobBuilder.append(dumpText, 'native');
var textBlob = blobBuilder.getBlob('octet/stream');
+ this.setSaveFileStatus('Creating file...', true);
+
window.webkitRequestFileSystem(
window.TEMPORARY, textBlob.size,
this.onFileSystemCreate_.bind(this, textBlob),
this.onFileError_.bind(this, 'Unable to create file system.'));
};
-// Once we have access to the file system, create a log file.
+/**
+ * Once we have access to the file system, create a log file.
+ */
DataView.prototype.onFileSystemCreate_ = function(textBlob, fileSystem) {
fileSystem.root.getFile(
'net_internals.log', {create: true},
@@ -420,16 +263,19 @@ DataView.prototype.onFileSystemCreate_ = function(textBlob, fileSystem) {
this.onFileError_.bind(this, 'Unable to create file.'));
};
-// Once the file is created, or an existing one has been opened, create a
-// writer for it.
+/**
+ * Once the file is created, or an existing one has been opened, create a
+ * writer for it.
+ */
DataView.prototype.onFileCreate_ = function(textBlob, fileEntry) {
fileEntry.createWriter(
this.onFileCreateWriter_.bind(this, textBlob, fileEntry),
this.onFileError_.bind(this, 'Unable to create writer.'));
};
-// Once the |fileWriter| has been created, truncate the file, in case it already
-// existed.
+/* Once the |fileWriter| has been created, truncate the file, in case it already
+ * existed.
+ */
DataView.prototype.onFileCreateWriter_ = function(textBlob,
fileEntry, fileWriter) {
fileWriter.onerror = this.onFileError_.bind(this, 'Truncate failed.');
@@ -438,100 +284,57 @@ DataView.prototype.onFileCreateWriter_ = function(textBlob,
fileWriter.truncate(0);
};
-// Once the file has been truncated, write |textBlob| to the file.
+/**
+ * Once the file has been truncated, write |textBlob| to the file.
+ */
DataView.prototype.onFileTruncate_ = function(textBlob, fileWriter, fileEntry) {
fileWriter.onerror = this.onFileError_.bind(this, 'Write failed.');
fileWriter.onwriteend = this.onFileWriteComplete_.bind(this, fileEntry);
fileWriter.write(textBlob);
};
-// Once the file has been written to, start the download.
+/**
+ * Once the file has been written to, start the download.
+ */
DataView.prototype.onFileWriteComplete_ = function(fileEntry) {
this.downloadIframe_.src = fileEntry.toURL();
- this.enableExportFileButton_(true);
+ this.setSaveFileStatus('Dump successful', false);
};
-// On any Javascript File API error, enable the export button and display
-// |errorText|, followed by the specific error.
+/**
+ * On any Javascript File API error, enable the save button and display
+ * |errorText|, followed by the specific error.
+ */
DataView.prototype.onFileError_ = function(errorText, error) {
- this.enableExportFileButton_(true);
- alert(errorText + ' ' + getKeyWithValue(FileError, error.code));
+ this.enableSaveFileButton_(true);
+ this.setSaveFileStatus(
+ errorText + ' ' + getKeyWithValue(FileError, error.code),
+ false);
};
-DataView.prototype.appendEventsPrintedAsText_ = function(out) {
- var allEvents = g_browser.getAllCapturedEvents();
-
- // Group the events into buckets by source ID, and buckets by source type.
- var sourceIds = [];
- var sourceIdToEventList = {};
- var sourceTypeToSourceIdList = {};
-
- // Lists used for actual output.
- var eventLists = [];
-
- for (var i = 0; i < allEvents.length; ++i) {
- var e = allEvents[i];
- var eventList = sourceIdToEventList[e.source.id];
- if (!eventList) {
- eventList = [];
- eventLists.push(eventList);
- if (e.source.type != LogSourceType.NONE)
- sourceIdToEventList[e.source.id] = eventList;
-
- // Update sourceIds
- sourceIds.push(e.source.id);
-
- // Update the sourceTypeToSourceIdList list.
- var idList = sourceTypeToSourceIdList[e.source.type];
- if (!idList) {
- idList = [];
- sourceTypeToSourceIdList[e.source.type] = idList;
- }
- idList.push(e.source.id);
- }
- eventList.push(e);
- }
-
-
- // For each source or event without a source (ordered by when the first
- // output event for that source happened).
- for (var i = 0; i < eventLists.length; ++i) {
- var eventList = eventLists[i];
- var sourceId = eventList[0].source.id;
- var sourceType = eventList[0].source.type;
-
- var startDate = g_browser.convertTimeTicksToDate(eventList[0].time);
+/**
+ * Prints some basic information about the environment when the log was made.
+ */
+DataView.prototype.updateLoadedClientInfo = function() {
+ this.loadedClientInfoText_.innerText = '';
+ if (typeof(ClientInfo) != 'object')
+ return;
- out.push('------------------------------------------');
- out.push(getKeyWithValue(LogSourceType, sourceType) +
- ' (id=' + sourceId + ')' +
- ' [start=' + startDate.toLocaleString() + ']');
- out.push('------------------------------------------');
+ var text = [];
- out.push(PrintSourceEntriesAsText(eventList));
+ // Dumps made with the command line option don't have a date.
+ if (ClientInfo.date) {
+ text.push('Data exported on: ' + ClientInfo.date);
+ text.push('');
}
-};
-DataView.prototype.appendSocketPoolsAsText_ = function(text, socketPoolInfo) {
- var socketPools = SocketPoolWrapper.createArrayFrom(socketPoolInfo);
- var tablePrinter = SocketPoolWrapper.createTablePrinter(socketPools);
- text.push(tablePrinter.toText(2));
-
- text.push('');
-
- for (var i = 0; i < socketPools.length; ++i) {
- if (socketPools[i].origPool.groups == undefined)
- continue;
- var groupTablePrinter = socketPools[i].createGroupTablePrinter();
- text.push(groupTablePrinter.toText(2));
- }
-};
+ text.push(ClientInfo.name +
+ ' ' + ClientInfo.version +
+ ' (' + ClientInfo.official +
+ ' ' + ClientInfo.cl +
+ ') ' + ClientInfo.version_mod);
+ text.push('OS Type: ' + ClientInfo.os_type);
+ text.push('Command line: ' + ClientInfo.command_line);
-/**
- * Format a time ticks count as a timestamp.
- */
-DataView.prototype.formatExpirationTime_ = function(timeTicks) {
- var d = g_browser.convertTimeTicksToDate(timeTicks);
- var isExpired = d.getTime() < (new Date()).getTime();
- return 't=' + d.getTime() + (isExpired ? ' [EXPIRED]' : '');
+ this.loadedClientInfoText_.innerText = text.join('\n');
};
diff --git a/chrome/browser/resources/net_internals/detailsview.js b/chrome/browser/resources/net_internals/detailsview.js
index e3543d4..a68a6b1 100644
--- a/chrome/browser/resources/net_internals/detailsview.js
+++ b/chrome/browser/resources/net_internals/detailsview.js
@@ -1,4 +1,4 @@
-// Copyright (c) 2010 The Chromium Authors. All rights reserved.
+// 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.
@@ -19,8 +19,8 @@ function DetailsView(tabHandlesContainerId,
this.logView_ = new DetailsLogView(logBoxId);
this.timelineView_ = new DetailsTimelineView(timelineBoxId);
- this.addTab(logTabId, this.logView_, true);
- this.addTab(timelineTabId, this.timelineView_, true);
+ this.addTab(logTabId, this.logView_, true, true);
+ this.addTab(timelineTabId, this.timelineView_, true, true);
// Default to the log view.
this.switchToTab(logTabId, null);
diff --git a/chrome/browser/resources/net_internals/dnsview.js b/chrome/browser/resources/net_internals/dnsview.js
index 8c1b6f4..b258ada 100644
--- a/chrome/browser/resources/net_internals/dnsview.js
+++ b/chrome/browser/resources/net_internals/dnsview.js
@@ -1,4 +1,4 @@
-// Copyright (c) 2010 The Chromium Authors. All rights reserved.
+// 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.
@@ -46,6 +46,10 @@ function DnsView(mainBoxId,
inherits(DnsView, DivView);
+DnsView.prototype.onLoadLogFinish = function(data) {
+ return this.onHostResolverInfoChanged(data.hostResolverInfo);
+};
+
DnsView.prototype.onHostResolverInfoChanged = function(hostResolverInfo) {
// Clear the existing values.
this.defaultFamilySpan_.innerHTML = '';
@@ -55,8 +59,8 @@ DnsView.prototype.onHostResolverInfoChanged = function(hostResolverInfo) {
this.cacheTbody_.innerHTML = '';
// No info.
- if (!hostResolverInfo)
- return;
+ if (!hostResolverInfo || !hostResolverInfo.cache)
+ return false;
var family = hostResolverInfo.default_address_family;
addTextNode(this.defaultFamilySpan_, getKeyWithValue(AddressFamily, family));
@@ -98,4 +102,6 @@ DnsView.prototype.onHostResolverInfoChanged = function(hostResolverInfo) {
var expiresCell = addNode(tr, 'td');
addTextNode(expiresCell, expiresDate.toLocaleString());
}
+
+ return true;
};
diff --git a/chrome/browser/resources/net_internals/eventsview.js b/chrome/browser/resources/net_internals/eventsview.js
index 8f682d5..85ad1a8 100644
--- a/chrome/browser/resources/net_internals/eventsview.js
+++ b/chrome/browser/resources/net_internals/eventsview.js
@@ -495,15 +495,18 @@ EventsView.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,
+ * Called when either a log file is loaded, after clearing the old entries,
* but before getting any new ones.
*/
-EventsView.prototype.onSetIsViewingLogFile = function(isViewingLogFile) {
+EventsView.prototype.onLoadLogStart = function() {
// Needed to sort new sourceless entries correctly.
this.maxReceivedSourceId_ = 0;
};
+EventsView.prototype.onLoadLogFinish = function(data) {
+ return true;
+};
+
EventsView.prototype.incrementPrefilterCount = function(offset) {
this.numPrefilter_ += offset;
this.invalidateFilterCounter_();
diff --git a/chrome/browser/resources/net_internals/httpcacheview.js b/chrome/browser/resources/net_internals/httpcacheview.js
index dd9346d..2e0e624e 100644
--- a/chrome/browser/resources/net_internals/httpcacheview.js
+++ b/chrome/browser/resources/net_internals/httpcacheview.js
@@ -1,4 +1,4 @@
-// Copyright (c) 2010 The Chromium Authors. All rights reserved.
+// 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.
@@ -17,14 +17,22 @@ function HttpCacheView(mainBoxId, statsDivId) {
inherits(HttpCacheView, DivView);
+HttpCacheView.prototype.onLoadLogFinish = function(data) {
+ return this.onHttpCacheInfoChanged(data.httpCacheInfo);
+};
+
HttpCacheView.prototype.onHttpCacheInfoChanged = function(info) {
this.statsDiv_.innerHTML = '';
+ if (!info)
+ return false;
+
// Print the statistics.
var statsUl = addNode(this.statsDiv_, 'ul');
for (var statName in info.stats) {
var li = addNode(statsUl, 'li');
addTextNode(li, statName + ': ' + info.stats[statName]);
}
+ return true;
};
diff --git a/chrome/browser/resources/net_internals/index.html b/chrome/browser/resources/net_internals/index.html
index a0a185f..8ada47b 100644
--- a/chrome/browser/resources/net_internals/index.html
+++ b/chrome/browser/resources/net_internals/index.html
@@ -15,6 +15,7 @@ found in the LICENSE file.
<script src="httpcacheview.js"></script>
<script src="testview.js"></script>
<script src="hstsview.js"></script>
+ <script src="logdumputil.js"></script>
<script src="main.js"></script>
<script src="dnsview.js"></script>
<script src="eventsview.js"></script>
@@ -59,7 +60,7 @@ found in the LICENSE file.
<div id=proxyTabContent>
<h4>
Current proxy settings
- <input type=button value="Re-apply settings" id=proxyReloadSettings />
+ <input type=button value="Re-apply settings" id=proxyReloadSettings class="hideOnLoadLog"/>
</h4>
<table><tr>
@@ -92,7 +93,7 @@ found in the LICENSE file.
<h4>
Proxies which have failed recently, and are marked as bad
- <input type=button value="Clear bad proxies" id=clearBadProxies />
+ <input type=button value="Clear bad proxies" id=clearBadProxies class="hideOnLoadLog" />
</h4>
<table class="styledTable">
<thead>
@@ -119,7 +120,7 @@ found in the LICENSE file.
<h4>
Host resolver cache
- <input type=button value="Clear host cache" id=clearHostResolverCache />
+ <input type=button value="Clear host cache" id=clearHostResolverCache class="hideOnLoadLog" />
</h4>
<ul>
<li>Capacity: <span id=hostResolverCacheCapacity></span></li>
@@ -145,9 +146,11 @@ found in the LICENSE file.
<div id=socketsTabContent>
<h4>Socket pools</h4>
<ul>
- <li><input type=button value="Close idle sockets" id=socketPoolCloseIdleButton />
+ <li class="hideOnLoadLog">
+ <input type=button value="Close idle sockets" id=socketPoolCloseIdleButton />
</li>
- <li><input type=button value="Flush socket pools" id=socketPoolFlushButton />
+ <li class="hideOnLoadLog">
+ <input type=button value="Flush socket pools" id=socketPoolFlushButton />
<span class=warningText >May break pages with active connections</span>
</li>
<li><a href='#events&q=type:SOCKET%20is:active'>View live sockets</a>
@@ -189,8 +192,10 @@ found in the LICENSE file.
</div>
</div>
<div id=httpCacheTabContent>
- <h4>Entries</h4>
- <a href="chrome://view-http-cache" target=_blank>Explore cache entries</a>
+ <div class="hideOnLoadLog">
+ <h4>Entries</h4>
+ <a href="chrome://view-http-cache" target=_blank>Explore cache entries</a>
+ </div>
<h4>Statistics</h4>
<div id=httpCacheStats>Nothing loaded yet.</div>
@@ -277,19 +282,27 @@ found in the LICENSE file.
Help: How to get data for bug reports?
</a>
</p>
- <button id=exportToFile class=bigButton>Dump to file</button>
+ <button id=dataViewSaveLogFile class=bigButton>Dump to file</button>
+ <pre id=dataViewSaveStatusText></pre>
</div>
</div>
- <div id=dataViewLoadDataDiv>
+ <div id=dataViewLoadedDiv style="display: none">
+ <h2>Data Loaded</h2>
+ <p id=dataViewLoadedClientInfoText></p>
+ </div>
+ <div>
<h2>Load data</h2>
+ <p><input type=file value="Load log from file" id=dataViewLoadLogFile /></p>
+ <pre id=dataViewLoadStatusText></pre>
<div style="margin: 8px">
- <p><input type=file value="Load log from file" id=dataViewLoadLogFile /></p>
- <p>Only works with log files created with "--log-net-log=file_name".
- "--net-log-level=#" will set the default log level used.
+ <p>Works for both log dumps generated from this page with Chrome 14
+ and later, and with log files created with
+ "--log-net-log=file_name". "--net-log-level=#" will set the
+ default log level used.
</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>
+ only start gathering data again when
+ <a href="javascript:history.go(0);">reloaded</a>.
</p>
</div>
</div>
@@ -305,7 +318,7 @@ found in the LICENSE file.
</span>
<table style="margin: 8px">
<tr>
- <td>Passively captured:</td>
+ <td>Passively&nbsp;captured:</td>
<td align=right id=passivelyCapturedCount></td>
</tr>
<tr>
@@ -313,8 +326,10 @@ found in the LICENSE file.
<td align=right id=activelyCapturedCount></td>
</tr>
</table>
- <p><input type=button value="Delete all" id=dataViewDeleteAll /></p>
- <p><input id=byteLoggingCheckbox type=checkbox>
+ <p class="hideOnLoadLog">
+ <input type=button value="Delete all" id=dataViewDeleteAll />
+ </p>
+ <p class="hideOnLoadLog"><input id=byteLoggingCheckbox type=checkbox />
Log actual bytes sent/received.
</p>
</div>
@@ -350,6 +365,8 @@ function displayHelpForBugDump() {
<li>Email the log file to the bug investigator,
<b>along with an explanation of what went wrong, including any relevant
URLs.</b>
+ You may want to compress the file before sending, as the log files can be
+ fairly large but compress well.
</li>
</ol>
@@ -360,7 +377,8 @@ function displayHelpForBugDump() {
IP addresses, URLs, and cookies.</li>
<ul>
<li>You can edit the log to obscure information if you like, but sometimes it
- is relevant to the bug.</li>
+ is relevant to the bug. If you choose do this, please make sure you can still
+ load the log file.</li>
<li>If you choose not to have cookies removed from the log, you must toggle
the checkbox before clicking the button.</li>
</ul>
diff --git a/chrome/browser/resources/net_internals/logdumputil.js b/chrome/browser/resources/net_internals/logdumputil.js
new file mode 100644
index 0000000..94501eb
--- /dev/null
+++ b/chrome/browser/resources/net_internals/logdumputil.js
@@ -0,0 +1,229 @@
+// 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.
+
+var createLogDumpAsync;
+var loadLogFile;
+
+// Start of anonymous namespace.
+(function() {
+
+/**
+ * Creates a new log dump. |events| is a list of all events, |polledData| is an
+ * object containing the results of each poll, |tabData| is an object containing
+ * data for individual tabs, and |date| is the time the dump was created, as a
+ * formatted string.
+ * Returns the new log dump as an object. |date| may be null.
+ *
+ * Log dumps are just JSON objects containing four values:
+ * |constants| needed to interpret the data. This also includes some browser
+ * state information
+ * |events| from the NetLog,
+ * |polledData| from each PollableDataHelper available on the source OS,
+ * |tabData| containing any tab-specific state that's not present in
+ * |polledData|.
+ *
+ * |polledData| and |tabData| may be empty objects, or may be missing data for
+ * tabs not present on the OS the log is from.
+ */
+function createLogDump(constants, events, polledData, tabData, date) {
+ if (g_browser.getSecurityStripping())
+ events = events.map(stripCookiesAndLoginInfo);
+
+ var logDump = {
+ 'constants': constants,
+ 'events': events,
+ 'polledData': polledData,
+ 'tabData': tabData
+ };
+
+ // Not technically client info, but it's used at the same point in the code.
+ if (date && constants.clientInfo)
+ constants.clientInfo.date = date;
+
+ return logDump;
+}
+
+/**
+ * Creates a full log dump using |polledData| and the return value of each tab's
+ * saveState function and passes it to |callback|.
+ */
+function onUpdateAllCompleted(callback, polledData) {
+ // Gather any tab-specific state information.
+ var tabData = {};
+ var categoryTabSwitcher = g_browser.categoryTabSwitcher();
+ var tabIds = categoryTabSwitcher.getAllTabIds();
+ for (var i = 0; i < tabIds.length; ++i) {
+ var view = categoryTabSwitcher.findTabById(tabIds[i]).contentView;
+ if (view.saveState)
+ tabData[tabIds[i]] = view.saveState();
+ }
+
+ var logDump = createLogDump(Constants,
+ g_browser.getAllCapturedEvents(),
+ polledData,
+ tabData,
+ (new Date()).toLocaleString());
+ callback(JSON.stringify(logDump, null, ' '));
+}
+
+/**
+ * Called to create a new log dump. Must not be called once a dump has been
+ * loaded. Once a log dump has been created, |callback| is passed the dumped
+ * text as a string.
+ */
+createLogDumpAsync = function(callback) {
+ g_browser.updateAllInfo(onUpdateAllCompleted.bind(null, callback));
+};
+
+/**
+ * Loads a full log dump. Returns a string containing a log of the load.
+ * The process goes like this:
+ * 1) Load constants. If this fails, or the version number can't be handled,
+ * abort the load. If this step succeeds, the load cannot be aborted.
+ * 2) Clear all events. Any event observers are informed of the clear as
+ * normal.
+ * 3) Call onLoadLogStart(polledData, tabData) for each view with an
+ * onLoadLogStart function. This allows tabs to clear any extra state that
+ * would affect the next step. |polledData| contains the data polled for
+ * all helpers, but |tabData| contains only the data from that specific tab.
+ * 4) Add all events from the log file.
+ * 5) Call onLoadLogFinish(polledData, tabData) for each view with an
+ * onLoadLogFinish function. The arguments are the same as in step 3. If
+ * there is no onLoadLogFinish function, it throws an exception, or it
+ * returns false instead of true, the data dump is assumed to contain no
+ * valid data for the tab, so the tab is hidden. Otherwise, the tab is
+ * shown.
+ */
+function loadLogDump(logDump) {
+ // Perform minimal validity check, and abort if it fails.
+ if (typeof(logDump) != 'object')
+ return 'Load failed. Top level JSON data is not an object.';
+
+ // String listing text summary of load errors, if any.
+ var errorString = '';
+
+ if (!g_browser.areValidConstants(logDump.constants))
+ errorString += 'Invalid constants object.\n';
+ if (typeof(logDump.events) != 'object')
+ errorString += 'NetLog events missing.\n';
+ if (typeof(logDump.constants.logFormatVersion) != 'number')
+ errorString += 'Invalid version number.\n';
+
+ if (errorString.length > 0)
+ return 'Load failed:\n\n' + errorString;
+
+ if (typeof(logDump.polledData) != 'object')
+ logDump.polledData = {};
+ if (typeof(logDump.tabData) != 'object')
+ logDump.tabData = {};
+
+ if (logDump.constants.logFormatVersion !=
+ g_browser.logFormatVersion()) {
+ return 'Unable to load different log version.' +
+ ' Found ' + logDump.constants.logFormatVersion +
+ ', Expected ' + g_browser.logFormatVersion();
+ }
+ if (!g_browser.loadConstants(logDump.constants))
+ return 'Expected constants not found in log file.';
+
+ // Prevent communication with the browser. Once the constants have been
+ // loaded, it's safer to continue trying to load the log, even in the case of
+ // bad data.
+ g_browser.onLoadLogFile();
+
+ // Delete all events. This will also update all logObservers.
+ g_browser.deleteAllEvents();
+
+ // Inform all the views that a log file is being loaded, and pass in
+ // view-specific saved state, if any.
+ var categoryTabSwitcher = g_browser.categoryTabSwitcher();
+ var tabIds = categoryTabSwitcher.getAllTabIds();
+ for (var i = 0; i < tabIds.length; ++i) {
+ var view = categoryTabSwitcher.findTabById(tabIds[i]).contentView;
+ view.onLoadLogStart(logDump.polledData, logDump.tabData[tabIds[i]]);
+ }
+
+ // Check for validity of each log entry, and then add the ones that pass.
+ // Since the events are kept around, and we can't just hide a single view
+ // on a bad event, we have more error checking for them than other data.
+ var validPassiveEvents = [];
+ var validActiveEvents = [];
+ for (var eventIndex = 0; eventIndex < logDump.events.length; ++eventIndex) {
+ var event = logDump.events[eventIndex];
+ if (typeof(event) == 'object' && typeof(event.source) == 'object' &&
+ typeof(event.time) == 'string' &&
+ getKeyWithValue(LogEventType, event.type) != '?' &&
+ getKeyWithValue(LogSourceType, event.source.type) != '?' &&
+ getKeyWithValue(LogEventPhase, event.phase) != '?') {
+ if (event.wasPassivelyCaptured) {
+ validPassiveEvents.push(event);
+ } else {
+ validActiveEvents.push(event);
+ }
+ }
+ }
+ g_browser.receivedPassiveLogEntries(validPassiveEvents);
+ g_browser.addLogEntries(validActiveEvents);
+
+ var numInvalidEvents = logDump.events.length
+ - validPassiveEvents.length
+ - validActiveEvents.length;
+ if (numInvalidEvents > 0) {
+ errorString += 'Unable to load ' + numInvalidEvents +
+ ' events, due to invalid data.\n\n';
+ }
+
+ // Update all views with data from the file. Show only those views which
+ // successfully load the data.
+ for (var i = 0; i < tabIds.length; ++i) {
+ var view = categoryTabSwitcher.findTabById(tabIds[i]).contentView;
+ var showView = false;
+ // The try block eliminates the need for checking every single value before
+ // trying to access it.
+ try {
+ if (view.onLoadLogFinish(logDump.polledData,
+ logDump.tabData[tabIds[i]])) {
+ showView = true;
+ }
+ } catch (error) {
+ }
+ categoryTabSwitcher.showTabHandleNode(tabIds[i], showView);
+ }
+
+ return errorString + 'Log loaded.';
+}
+
+/**
+ * Loads a log dump from the string |logFileContents|, which can be either a
+ * full net-internals dump, or a NetLog dump only. Returns a string containing
+ * a log of the load.
+ */
+loadLogFile = function(logFileContents) {
+ // Try and parse the log dump as a single JSON string. If this succeeds,
+ // it's most likely a full log dump. Otherwise, it may be a dump created by
+ // --log-net-log.
+ var parsedDump = null;
+ try {
+ parsedDump = JSON.parse(logFileContents);
+ } catch (error) {
+ try {
+ // We may have a --log-net-log=blah log dump. If so, remove the comma
+ // after the final good entry, and add the necessary close brackets.
+ var end = Math.max(logFileContents.lastIndexOf(',\n'),
+ logFileContents.lastIndexOf(',\r'));
+ if (end != -1)
+ parsedDump = JSON.parse(logFileContents.substring(0, end) + ']}');
+ }
+ catch (error) {
+ }
+ }
+
+ if (!parsedDump)
+ return 'Unable to parse log dump as JSON file.';
+ return loadLogDump(parsedDump);
+};
+
+// End of anonymous namespace.
+})();
+
diff --git a/chrome/browser/resources/net_internals/logviewpainter.js b/chrome/browser/resources/net_internals/logviewpainter.js
index 8199a6d..b95cb5d 100644
--- a/chrome/browser/resources/net_internals/logviewpainter.js
+++ b/chrome/browser/resources/net_internals/logviewpainter.js
@@ -12,6 +12,7 @@
var PaintLogView;
var PrintSourceEntriesAsText;
var proxySettingsToString;
+var stripCookiesAndLoginInfo;
// Start of anonymous namespace.
(function() {
@@ -24,8 +25,6 @@ PaintLogView = function(sourceEntries, node) {
}
}
-const INDENTATION_PX = 20;
-
function addSourceEntry_(node, sourceEntry) {
var div = addNode(node, 'div');
div.className = 'logSourceEntry';
@@ -180,15 +179,19 @@ function formatHexString(hexString, asciiCharsPerLine) {
function getTextForExtraParams(entry, enableSecurityStripping) {
// Format the extra parameters (use a custom formatter for certain types,
// but default to displaying as JSON).
+
+ // If security stripping is enabled, remove data as needed.
+ if (enableSecurityStripping)
+ entry = stripCookiesAndLoginInfo(entry);
+
switch (entry.type) {
case LogEventType.HTTP_TRANSACTION_SEND_REQUEST_HEADERS:
case LogEventType.HTTP_TRANSACTION_SEND_TUNNEL_HEADERS:
- return getTextForRequestHeadersExtraParam(entry, enableSecurityStripping);
+ return getTextForRequestHeadersExtraParam(entry);
case LogEventType.HTTP_TRANSACTION_READ_RESPONSE_HEADERS:
case LogEventType.HTTP_TRANSACTION_READ_TUNNEL_RESPONSE_HEADERS:
- return getTextForResponseHeadersExtraParam(entry,
- enableSecurityStripping);
+ return getTextForResponseHeadersExtraParam(entry);
case LogEventType.PROXY_CONFIG_CHANGED:
return getTextForProxyConfigChangedExtraParam(entry);
@@ -197,9 +200,7 @@ function getTextForExtraParams(entry, enableSecurityStripping) {
var out = [];
for (var k in entry.params) {
if (k == 'headers' && entry.params[k] instanceof Array) {
- out.push(
- getTextForResponseHeadersExtraParam(entry,
- enableSecurityStripping));
+ out.push(getTextForResponseHeadersExtraParam(entry));
continue;
}
var value = entry.params[k];
@@ -324,31 +325,37 @@ function stripCookieOrLoginInfo(line) {
}
/**
- * Removes all cookie and unencrypted login text from a list of HTTP
- * header lines.
+ * If |entry| has headers, returns a copy of |entry| with all cookie and
+ * unencrypted login text removed. Otherwise, returns original |entry| object.
+ * This is needed so that JSON log dumps can be made without affecting the
+ * source data.
*/
-function stripCookiesAndLoginInfo(headers) {
- return headers.map(stripCookieOrLoginInfo);
+stripCookiesAndLoginInfo = function(entry) {
+ if (!entry.params || !entry.params.headers ||
+ !(entry.params.headers instanceof Array)) {
+ return entry;
+ }
+
+ // Duplicate the top level object, and |entry.params|. All other fields are
+ // just pointers to the original values, as they won't be modified, other than
+ // |entry.params.headers|.
+ entry = shallowCloneObject(entry);
+ entry.params = shallowCloneObject(entry.params);
+
+ entry.params.headers = entry.params.headers.map(stripCookieOrLoginInfo);
+ return entry;
}
-function getTextForRequestHeadersExtraParam(entry, enableSecurityStripping) {
+function getTextForRequestHeadersExtraParam(entry) {
var params = entry.params;
// Strip the trailing CRLF that params.line contains.
var lineWithoutCRLF = params.line.replace(/\r\n$/g, '');
-
- var headers = params.headers;
- if (enableSecurityStripping)
- headers = stripCookiesAndLoginInfo(headers);
-
- return indentLines(' --> ', [lineWithoutCRLF].concat(headers));
+ return indentLines(' --> ', [lineWithoutCRLF].concat(params.headers));
}
-function getTextForResponseHeadersExtraParam(entry, enableSecurityStripping) {
- var headers = entry.params.headers;
- if (enableSecurityStripping)
- headers = stripCookiesAndLoginInfo(headers);
- return indentLines(' --> ', headers);
+function getTextForResponseHeadersExtraParam(entry) {
+ return indentLines(' --> ', entry.params.headers);
}
function getTextForProxyConfigChangedExtraParam(entry) {
diff --git a/chrome/browser/resources/net_internals/main.js b/chrome/browser/resources/net_internals/main.js
index 8333761..af44622 100644
--- a/chrome/browser/resources/net_internals/main.js
+++ b/chrome/browser/resources/net_internals/main.js
@@ -3,7 +3,7 @@
// found in the LICENSE file.
/**
- * Dictionary of constants (initialized by browser).
+ * Dictionary of constants (initialized by browser, updated on load log).
*/
var LogEventType = null;
var LogEventPhase = null;
@@ -14,6 +14,9 @@ var NetError = null;
var LoadFlag = null;
var AddressFamily = null;
+// Dictionary of all constants, used for saving log files.
+var Constants = null;
+
/**
* Object to communicate between the renderer and the browser.
* @type {!BrowserBridge}
@@ -71,14 +74,17 @@ function onLoaded() {
'hostResolverCacheTTLSuccess',
'hostResolverCacheTTLFailure');
- // Create a view which will display import/export options to control the
+ // Create a view which will display save/load options to control the
// captured data.
var dataView = new DataView('dataTabContent', 'dataViewDownloadIframe',
- 'exportToFile', 'securityStrippingCheckbox',
+ 'dataViewSaveLogFile', 'dataViewSaveStatusText',
+ 'securityStrippingCheckbox',
'byteLoggingCheckbox', 'passivelyCapturedCount',
'activelyCapturedCount', 'dataViewDeleteAll',
- 'dataViewDumpDataDiv', 'dataViewLoadDataDiv',
- 'dataViewLoadLogFile',
+ 'dataViewDumpDataDiv',
+ 'dataViewLoadedDiv',
+ 'dataViewLoadedClientInfoText',
+ 'dataViewLoadLogFile', 'dataViewLoadStatusText',
'dataViewCapturingTextSpan',
'dataViewLoggingTextSpan');
@@ -115,25 +121,19 @@ function onLoaded() {
'spdySessionLinkSpan',
'spdySessionDiv');
- var serviceView;
- if (g_browser.isPlatformWindows()) {
- serviceView = new ServiceProvidersView('serviceProvidersTab',
- 'serviceProvidersTabContent',
- 'serviceProvidersTbody',
- 'namespaceProvidersTbody');
- }
+ var serviceView = new ServiceProvidersView('serviceProvidersTab',
+ 'serviceProvidersTabContent',
+ 'serviceProvidersTbody',
+ 'namespaceProvidersTbody');
var httpThrottlingView = new HttpThrottlingView(
'httpThrottlingTabContent', 'enableHttpThrottlingCheckbox');
- var logsView;
- if (g_browser.isChromeOS()) {
- logsView = new LogsView('logsTabContent',
- 'logTable',
- 'logsGlobalShowBtn',
- 'logsGlobalHideBtn',
- 'logsRefreshBtn');
- }
+ var logsView = new LogsView('logsTabContent',
+ 'logTable',
+ 'logsGlobalShowBtn',
+ 'logsGlobalHideBtn',
+ 'logsRefreshBtn');
var prerenderView = new PrerenderView('prerenderTabContent',
'prerenderEnabledSpan',
@@ -144,22 +144,25 @@ function onLoaded() {
var categoryTabSwitcher = new TabSwitcherView('categoryTabHandles');
g_browser.setTabSwitcher(categoryTabSwitcher);
- // Populate the main tabs.
- categoryTabSwitcher.addTab('eventsTab', eventsView, false);
- categoryTabSwitcher.addTab('proxyTab', proxyView, false);
- categoryTabSwitcher.addTab('dnsTab', dnsView, false);
- categoryTabSwitcher.addTab('socketsTab', socketsView, false);
- categoryTabSwitcher.addTab('spdyTab', spdyView, false);
- categoryTabSwitcher.addTab('httpCacheTab', httpCacheView, false);
- categoryTabSwitcher.addTab('dataTab', dataView, false);
- if (g_browser.isPlatformWindows())
- categoryTabSwitcher.addTab('serviceProvidersTab', serviceView, false);
- categoryTabSwitcher.addTab('testTab', testView, false);
- categoryTabSwitcher.addTab('hstsTab', hstsView, false);
- categoryTabSwitcher.addTab('httpThrottlingTab', httpThrottlingView, false);
- if (g_browser.isChromeOS())
- categoryTabSwitcher.addTab('logsTab', logsView, false);
- categoryTabSwitcher.addTab('prerenderTab', prerenderView, false);
+ // Populate the main tabs. Even tabs that don't contain information for the
+ // running OS should be created, so they can load log dumps from other
+ // OSes.
+ categoryTabSwitcher.addTab('eventsTab', eventsView, false, true);
+ categoryTabSwitcher.addTab('proxyTab', proxyView, false, true);
+ categoryTabSwitcher.addTab('dnsTab', dnsView, false, true);
+ categoryTabSwitcher.addTab('socketsTab', socketsView, false, true);
+ categoryTabSwitcher.addTab('spdyTab', spdyView, false, true);
+ categoryTabSwitcher.addTab('httpCacheTab', httpCacheView, false, true);
+ categoryTabSwitcher.addTab('dataTab', dataView, false, true);
+ categoryTabSwitcher.addTab('serviceProvidersTab', serviceView, false,
+ g_browser.isPlatformWindows());
+ categoryTabSwitcher.addTab('testTab', testView, false, true);
+ categoryTabSwitcher.addTab('hstsTab', hstsView, false, true);
+ categoryTabSwitcher.addTab('httpThrottlingTab', httpThrottlingView, false,
+ true);
+ categoryTabSwitcher.addTab('logsTab', logsView, false,
+ g_browser.isChromeOS());
+ categoryTabSwitcher.addTab('prerenderTab', prerenderView, false, true);
// 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.
@@ -184,9 +187,6 @@ 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();
}
@@ -204,6 +204,10 @@ function BrowserBridge() {
this.hstsObservers_ = [];
this.httpThrottlingObservers_ = [];
+ // This is set to true when a log file is being viewed to block all
+ // communication with the browser.
+ this.isViewingLogFile_ = false;
+
this.pollableDataHelpers_ = {};
this.pollableDataHelpers_.proxySettings =
new PollableDataHelper('onProxySettingsChanged',
@@ -247,11 +251,6 @@ function BrowserBridge() {
// 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;
-
// True when cookies and authentication information should be removed from
// displayed events. When true, such information should be hidden from
// all pages.
@@ -290,6 +289,47 @@ function onUrlHashChange(anchorMap, categoryTabSwitcher) {
}
/**
+ * Returns true if |constants| appears to be a valid constants object.
+ */
+BrowserBridge.prototype.areValidConstants = function(constants) {
+ return typeof(constants) == 'object' &&
+ typeof(constants.logEventTypes) == 'object' &&
+ typeof(constants.clientInfo) == 'object' &&
+ typeof(constants.logEventPhase) == 'object' &&
+ typeof(constants.logSourceType) == 'object' &&
+ typeof(constants.logLevelType) == 'object' &&
+ typeof(constants.loadFlag) == 'object' &&
+ typeof(constants.netError) == 'object' &&
+ typeof(constants.addressFamily) == 'object' &&
+ typeof(constants.timeTickOffset) == 'string' &&
+ typeof(constants.logFormatVersion) == 'number';
+};
+
+/**
+ * Attempts to load all constants from |constants|. Returns false if one or
+ * more entries are missing. On failure, global dictionaries are not modified.
+ */
+BrowserBridge.prototype.loadConstants = function(constants) {
+ if (!this.areValidConstants(constants))
+ return false;
+
+ LogEventType = constants.logEventTypes;
+ ClientInfo = constants.clientInfo;
+ LogEventPhase = constants.logEventPhase;
+ LogSourceType = constants.logSourceType;
+ LogLevelType = constants.logLevelType;
+ LoadFlag = constants.loadFlag;
+ NetError = constants.netError;
+ AddressFamily = constants.addressFamily;
+ this.timeTickOffset_ = constants.timeTickOffset;
+
+ // Used for saving dumps.
+ Constants = constants;
+
+ return true;
+};
+
+/**
* Delay in milliseconds between updates of certain browser information.
*/
BrowserBridge.POLL_INTERVAL_MS = 5000;
@@ -298,8 +338,16 @@ BrowserBridge.POLL_INTERVAL_MS = 5000;
// Messages sent to the browser
//------------------------------------------------------------------------------
+/**
+ * Wraps |chrome.send|. Doesn't send anything when viewing a log file.
+ */
+BrowserBridge.prototype.send = function(value) {
+ if (!this.isViewingLogFile_)
+ chrome.send(value);
+};
+
BrowserBridge.prototype.sendReady = function() {
- chrome.send('notifyReady');
+ this.send('notifyReady');
// Some of the data we are interested is not currently exposed as a stream,
// so we will poll the browser to find out when it changes and then notify
@@ -318,155 +366,124 @@ BrowserBridge.prototype.isChromeOS = function() {
BrowserBridge.prototype.sendGetProxySettings = function() {
// The browser will call receivedProxySettings on completion.
- chrome.send('getProxySettings');
+ this.send('getProxySettings');
};
BrowserBridge.prototype.sendReloadProxySettings = function() {
- chrome.send('reloadProxySettings');
+ this.send('reloadProxySettings');
};
BrowserBridge.prototype.sendGetBadProxies = function() {
// The browser will call receivedBadProxies on completion.
- chrome.send('getBadProxies');
+ this.send('getBadProxies');
};
BrowserBridge.prototype.sendGetHostResolverInfo = function() {
// The browser will call receivedHostResolverInfo on completion.
- chrome.send('getHostResolverInfo');
+ this.send('getHostResolverInfo');
};
BrowserBridge.prototype.sendClearBadProxies = function() {
- chrome.send('clearBadProxies');
+ this.send('clearBadProxies');
};
BrowserBridge.prototype.sendClearHostResolverCache = function() {
- chrome.send('clearHostResolverCache');
+ this.send('clearHostResolverCache');
};
BrowserBridge.prototype.sendStartConnectionTests = function(url) {
- chrome.send('startConnectionTests', [url]);
+ this.send('startConnectionTests', [url]);
};
BrowserBridge.prototype.sendHSTSQuery = function(domain) {
- chrome.send('hstsQuery', [domain]);
+ this.send('hstsQuery', [domain]);
};
BrowserBridge.prototype.sendHSTSAdd = function(domain,
include_subdomains,
pins) {
- chrome.send('hstsAdd', [domain, include_subdomains, pins]);
+ this.send('hstsAdd', [domain, include_subdomains, pins]);
};
BrowserBridge.prototype.sendHSTSDelete = function(domain) {
- chrome.send('hstsDelete', [domain]);
+ this.send('hstsDelete', [domain]);
};
BrowserBridge.prototype.sendGetHttpCacheInfo = function() {
- chrome.send('getHttpCacheInfo');
+ this.send('getHttpCacheInfo');
};
BrowserBridge.prototype.sendGetSocketPoolInfo = function() {
- chrome.send('getSocketPoolInfo');
+ this.send('getSocketPoolInfo');
};
BrowserBridge.prototype.sendCloseIdleSockets = function() {
- chrome.send('closeIdleSockets');
+ this.send('closeIdleSockets');
};
BrowserBridge.prototype.sendFlushSocketPools = function() {
- chrome.send('flushSocketPools');
+ this.send('flushSocketPools');
};
BrowserBridge.prototype.sendGetSpdySessionInfo = function() {
- chrome.send('getSpdySessionInfo');
+ this.send('getSpdySessionInfo');
};
BrowserBridge.prototype.sendGetSpdyStatus = function() {
- chrome.send('getSpdyStatus');
+ this.send('getSpdyStatus');
};
BrowserBridge.prototype.sendGetSpdyAlternateProtocolMappings = function() {
- chrome.send('getSpdyAlternateProtocolMappings');
+ this.send('getSpdyAlternateProtocolMappings');
};
BrowserBridge.prototype.sendGetServiceProviders = function() {
- chrome.send('getServiceProviders');
+ this.send('getServiceProviders');
};
BrowserBridge.prototype.sendGetPrerenderInfo = function() {
- chrome.send('getPrerenderInfo');
-}
+ this.send('getPrerenderInfo');
+};
BrowserBridge.prototype.enableIPv6 = function() {
- chrome.send('enableIPv6');
+ this.send('enableIPv6');
};
BrowserBridge.prototype.setLogLevel = function(logLevel) {
- chrome.send('setLogLevel', ['' + logLevel]);
+ this.send('setLogLevel', ['' + logLevel]);
};
BrowserBridge.prototype.enableHttpThrottling = function(enable) {
- chrome.send('enableHttpThrottling', [enable]);
+ this.send('enableHttpThrottling', [enable]);
};
BrowserBridge.prototype.refreshSystemLogs = function() {
- chrome.send('refreshSystemLogs');
+ this.send('refreshSystemLogs');
};
BrowserBridge.prototype.getSystemLog = function(log_key, cellId) {
- chrome.send('getSystemLog', [log_key, cellId]);
+ this.send('getSystemLog', [log_key, cellId]);
};
//------------------------------------------------------------------------------
-// Messages received from the browser
+// Messages received from the browser.
//------------------------------------------------------------------------------
-BrowserBridge.prototype.receivedLogEntries = function(logEntries) {
+BrowserBridge.prototype.receive = function(command, params) {
// Does nothing if viewing a log file.
if (this.isViewingLogFile_)
return;
- this.addLogEntries(logEntries);
-};
-
-BrowserBridge.prototype.receivedLogEventTypeConstants = function(constantsMap) {
- LogEventType = constantsMap;
-};
-
-BrowserBridge.prototype.receivedClientInfo =
-function(info) {
- ClientInfo = info;
-};
-
-BrowserBridge.prototype.receivedLogEventPhaseConstants =
-function(constantsMap) {
- LogEventPhase = constantsMap;
+ this[command](params);
};
-BrowserBridge.prototype.receivedLogSourceTypeConstants =
-function(constantsMap) {
- LogSourceType = constantsMap;
-};
-
-BrowserBridge.prototype.receivedLogLevelConstants =
-function(constantsMap) {
- LogLevelType = constantsMap;
-};
+BrowserBridge.prototype.receivedConstants = function(constants) {
+ this.logFormatVersion_ = constants.logFormatVersion;
-BrowserBridge.prototype.receivedLoadFlagConstants = function(constantsMap) {
- LoadFlag = constantsMap;
+ this.loadConstants(constants);
};
-BrowserBridge.prototype.receivedNetErrorConstants = function(constantsMap) {
- NetError = constantsMap;
-};
-
-BrowserBridge.prototype.receivedAddressFamilyConstants =
-function(constantsMap) {
- AddressFamily = constantsMap;
-};
-
-BrowserBridge.prototype.receivedTimeTickOffset = function(timeTickOffset) {
- this.timeTickOffset_ = timeTickOffset;
+BrowserBridge.prototype.receivedLogEntries = function(logEntries) {
+ this.addLogEntries(logEntries);
};
BrowserBridge.prototype.receivedProxySettings = function(proxySettings) {
@@ -573,84 +590,33 @@ BrowserBridge.prototype.receivedPrerenderInfo = function(prerenderInfo) {
this.pollableDataHelpers_.prerenderInfo.update(prerenderInfo);
};
-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();
+BrowserBridge.prototype.categoryTabSwitcher = function() {
+ return this.categoryTabSwitcher_;
+};
- this.setIsViewingLogFile_(true);
+BrowserBridge.prototype.logFormatVersion = function() {
+ return this.logFormatVersion_;
+};
- 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(entries[i]));
- }
- }
+BrowserBridge.prototype.isViewingLogFile = function() {
+ return this.isViewingLogFile_;
+};
- this.numPassivelyCapturedEvents_ = validEntries.length;
- this.addLogEntries(validEntries);
-
- var numInvalidEntries = entries.length - validEntries.length;
- if (numInvalidEntries > 0 || numInvalidLines > 0) {
- window.alert(
- numInvalidLines.toString() +
- ' entries 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.');
+/**
+ * Prevents receiving/sending events to/from the browser, so loaded data will
+ * not be mixed with current Chrome state. Also hides any interactive HTML
+ * elements that send messages to the browser. Cannot be undone without
+ * reloading the page.
+ */
+BrowserBridge.prototype.onLoadLogFile = function() {
+ if (!this.isViewingLogFile_) {
+ this.isViewingLogFile_ = true;
+ this.setSecurityStripping(false);
+ document.styleSheets[0].insertRule('.hideOnLoadLog { display: none; }');
}
-}
-
-BrowserBridge.prototype.getSystemLogCallback = function(result) {
- document.getElementById(result.cellId).textContent = result.log;
-}
-
-//------------------------------------------------------------------------------
+};
/**
* Sets the |categoryTabSwitcher_| of BrowserBridge. Since views depend on
@@ -766,9 +732,13 @@ BrowserBridge.prototype.addSpdyAlternateProtocolMappingsObserver =
* back when data is received, through:
*
* observer.onServiceProvidersChanged(serviceProviders)
+ *
+ * Will do nothing if on a platform other than Windows, as service providers are
+ * only present on Windows.
*/
BrowserBridge.prototype.addServiceProvidersObserver = function(observer) {
- this.pollableDataHelpers_.serviceProviders.addObserver(observer);
+ if (this.pollableDataHelpers_.serviceProviders)
+ this.pollableDataHelpers_.serviceProviders.addObserver(observer);
};
/**
@@ -822,7 +792,7 @@ BrowserBridge.prototype.addHttpThrottlingObserver = function(observer) {
*/
BrowserBridge.prototype.addPrerenderInfoObserver = function(observer) {
this.pollableDataHelpers_.prerenderInfo.addObserver(observer);
-}
+};
/**
* The browser gives us times in terms of "time ticks" in milliseconds.
@@ -938,32 +908,6 @@ BrowserBridge.prototype.getSecurityStripping = 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() {
@@ -1010,6 +954,14 @@ PollableDataHelper.prototype.getObserverMethodName = function() {
return this.observerMethodName_;
};
+PollableDataHelper.prototype.isObserver = function(object) {
+ for (var i = 0; i < this.observerInfos_.length; ++i) {
+ if (this.observerInfos_[i].observer == object)
+ return true;
+ }
+ return false;
+};
+
/**
* This is a helper class used by PollableDataHelper, to keep track of
* each observer and whether or not it has received any data. The
@@ -1028,7 +980,7 @@ PollableDataHelper.prototype.addObserver = function(observer) {
PollableDataHelper.prototype.removeObserver = function(observer) {
for (var i = 0; i < this.observerInfos_.length; ++i) {
- if (this.observerInfos_[i].observer == observer) {
+ if (this.observerInfos_[i].observer === observer) {
this.observerInfos_.splice(i, 1);
return;
}
diff --git a/chrome/browser/resources/net_internals/prerenderview.js b/chrome/browser/resources/net_internals/prerenderview.js
index 7edcb14..318addf 100644
--- a/chrome/browser/resources/net_internals/prerenderview.js
+++ b/chrome/browser/resources/net_internals/prerenderview.js
@@ -17,6 +17,10 @@ function PrerenderView(mainBoxId, prerenderEnabledSpanId, prerenderHistoryDivId,
inherits(PrerenderView, DivView);
+PrerenderView.prototype.onLoadLogFinish = function(data) {
+ return this.onPrerenderInfoChanged(data.prerenderInfo);
+};
+
function IsValidPrerenderInfo(prerenderInfo) {
if (prerenderInfo == null) {
return false;
@@ -33,9 +37,9 @@ PrerenderView.prototype.onPrerenderInfoChanged = function(prerenderInfo) {
this.prerenderEnabledSpan_.innerText = '';
this.prerenderHistoryDiv_.innerHTML = '';
this.prerenderActiveDiv_.innerHTML = '';
- if (!IsValidPrerenderInfo(prerenderInfo)) {
- return;
- }
+
+ if (!IsValidPrerenderInfo(prerenderInfo))
+ return false;
this.prerenderEnabledSpan_.innerText = prerenderInfo.enabled.toString();
@@ -46,6 +50,8 @@ PrerenderView.prototype.onPrerenderInfoChanged = function(prerenderInfo) {
var tabPrinter = PrerenderView.createActiveTablePrinter(
prerenderInfo.active);
tabPrinter.toHTML(this.prerenderActiveDiv_, 'styledTable');
+
+ return true;
};
PrerenderView.createHistoryTablePrinter = function(prerenderHistory) {
diff --git a/chrome/browser/resources/net_internals/proxyview.js b/chrome/browser/resources/net_internals/proxyview.js
index 48035e8..239229a 100644
--- a/chrome/browser/resources/net_internals/proxyview.js
+++ b/chrome/browser/resources/net_internals/proxyview.js
@@ -47,21 +47,57 @@ function ProxyView(mainBoxId,
inherits(ProxyView, DivView);
+ProxyView.prototype.onLoadLogStart = function(data) {
+ // Need to reset this so the latest proxy source from the dump can be
+ // identified when the log entries are loaded.
+ this.latestProxySourceId_ = 0;
+};
+
+ProxyView.prototype.onLoadLogFinish = function(data, tabData) {
+ // It's possible that the last INIT_PROXY_RESOLVER source was deleted from the
+ // log, but earlier sources remain. When that happens, clear the list of
+ // entries here, to avoid displaying misleading information.
+ if (tabData != this.latestProxySourceId_)
+ this.clearLog_();
+ return this.onProxySettingsChanged(data.proxySettings) &&
+ this.onBadProxiesChanged(data.badProxies);
+};
+
+/**
+ * Save view-specific state.
+ *
+ * Save the greatest seen proxy source id, so we will not incorrectly identify
+ * the log source associated with the current proxy configuration.
+ */
+ProxyView.prototype.saveState = function() {
+ return this.latestProxySourceId_;
+};
+
ProxyView.prototype.onProxySettingsChanged = function(proxySettings) {
+ // Both |original| and |effective| are dictionaries describing the settings.
+ this.originalSettingsDiv_.innerHTML = '';
+ this.effectiveSettingsDiv_.innerHTML = '';
+
+ if (!proxySettings)
+ return false;
+
var original = proxySettings.original;
var effective = proxySettings.effective;
- // Both |original| and |effective| are dictionaries describing the settings.
- this.originalSettingsDiv_.innerHTML = ''
- this.effectiveSettingsDiv_.innerHTML = ''
+ if (!original || !effective)
+ return false;
addTextNode(this.originalSettingsDiv_, proxySettingsToString(original));
addTextNode(this.effectiveSettingsDiv_, proxySettingsToString(effective));
+ return true;
};
ProxyView.prototype.onBadProxiesChanged = function(badProxies) {
this.badProxiesTbody_.innerHTML = '';
+ if (!badProxies)
+ return false;
+
// Add a table row for each bad proxy entry.
for (var i = 0; i < badProxies.length; ++i) {
var entry = badProxies[i];
@@ -75,6 +111,7 @@ ProxyView.prototype.onBadProxiesChanged = function(badProxies) {
addTextNode(nameCell, entry.proxy_uri);
addTextNode(badUntilCell, badUntilDate.toLocaleString());
}
+ return true;
};
ProxyView.prototype.onLogEntryAdded = function(logEntry) {
@@ -114,7 +151,3 @@ ProxyView.prototype.onLogEntriesDeleted = function(sourceIds) {
ProxyView.prototype.onAllLogEntriesDeleted = function() {
this.clearLog_();
};
-
-ProxyView.prototype.onSetIsViewingLogFile = function(isViewingLogFile) {
-};
-
diff --git a/chrome/browser/resources/net_internals/serviceprovidersview.js b/chrome/browser/resources/net_internals/serviceprovidersview.js
index ba5d8e4..48751a7 100644
--- a/chrome/browser/resources/net_internals/serviceprovidersview.js
+++ b/chrome/browser/resources/net_internals/serviceprovidersview.js
@@ -1,4 +1,4 @@
-// Copyright (c) 2010 The Chromium Authors. All rights reserved.
+// 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.
@@ -31,10 +31,15 @@ function ServiceProvidersView(tabId,
inherits(ServiceProvidersView, DivView);
+ServiceProvidersView.prototype.onLoadLogFinish = function(data) {
+ return this.onServiceProvidersChanged(data.serviceProviders);
+};
+
ServiceProvidersView.prototype.onServiceProvidersChanged =
function(serviceProviders) {
- this.updateServiceProviders_(serviceProviders['service_providers']);
- this.updateNamespaceProviders_(serviceProviders['namespace_providers']);
+ return serviceProviders &&
+ this.updateServiceProviders_(serviceProviders['service_providers']) &&
+ this.updateNamespaceProviders_(serviceProviders['namespace_providers']);
};
/**
@@ -105,6 +110,9 @@ ServiceProvidersView.prototype.updateServiceProviders_ =
function(serviceProviders) {
this.serviceProvidersTbody_.innerHTML = '';
+ if (!serviceProviders)
+ return false;
+
// Add a table row for each service provider.
for (var i = 0; i < serviceProviders.length; ++i) {
var tr = addNode(this.serviceProvidersTbody_, 'tr');
@@ -118,6 +126,7 @@ function(serviceProviders) {
addNodeWithText(tr, 'td', ServiceProvidersView.getProtocolType(entry));
addNodeWithText(tr, 'td', entry.path);
}
+ return true;
};
/**
@@ -127,6 +136,9 @@ ServiceProvidersView.prototype.updateNamespaceProviders_ =
function(namespaceProviders) {
this.namespaceProvidersTbody_.innerHTML = '';
+ if (!namespaceProviders)
+ return false;
+
// Add a table row for each namespace provider.
for (var i = 0; i < namespaceProviders.length; ++i) {
var tr = addNode(this.namespaceProvidersTbody_, 'tr');
@@ -137,5 +149,5 @@ function(namespaceProviders) {
ServiceProvidersView.getNamespaceProviderType(entry));
addNodeWithText(tr, 'td', entry.active);
}
+ return true;
};
-
diff --git a/chrome/browser/resources/net_internals/socketsview.js b/chrome/browser/resources/net_internals/socketsview.js
index 90e7a6b..b99ed2b 100644
--- a/chrome/browser/resources/net_internals/socketsview.js
+++ b/chrome/browser/resources/net_internals/socketsview.js
@@ -28,12 +28,16 @@ function SocketsView(mainBoxId, socketPoolDivId, socketPoolGroupsDivId,
inherits(SocketsView, DivView);
+SocketsView.prototype.onLoadLogFinish = function(data) {
+ return this.onSocketPoolInfoChanged(data.socketPoolInfo);
+};
+
SocketsView.prototype.onSocketPoolInfoChanged = function(socketPoolInfo) {
this.socketPoolDiv_.innerHTML = '';
this.socketPoolGroupsDiv_.innerHTML = '';
if (!socketPoolInfo)
- return;
+ return false;
var socketPools = SocketPoolWrapper.createArrayFrom(socketPoolInfo);
var tablePrinter = SocketPoolWrapper.createTablePrinter(socketPools);
@@ -48,6 +52,7 @@ SocketsView.prototype.onSocketPoolInfoChanged = function(socketPoolInfo) {
groupTablePrinter.toHTML(p, 'styledTable');
}
}
+ return true;
};
SocketsView.prototype.closeIdleSockets = function() {
diff --git a/chrome/browser/resources/net_internals/spdyview.js b/chrome/browser/resources/net_internals/spdyview.js
index d7abc5454..2fffaeb 100644
--- a/chrome/browser/resources/net_internals/spdyview.js
+++ b/chrome/browser/resources/net_internals/spdyview.js
@@ -1,4 +1,4 @@
-// Copyright (c) 2010 The Chromium Authors. All rights reserved.
+// 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.
@@ -36,9 +36,16 @@ function SpdyView(mainBoxId, spdyEnabledSpanId,
inherits(SpdyView, DivView);
+SpdyView.prototype.onLoadLogFinish = function(data) {
+ return this.onSpdySessionInfoChanged(data.spdySessionInfo) &&
+ this.onSpdyStatusChanged(data.spdyStatus) &&
+ this.onSpdyAlternateProtocolMappingsChanged(
+ data.spdyAlternateProtocolMappings);
+};
+
/**
- * If |spdySessionInfo| is not null, displays a single table with information
- * on each SPDY session. Otherwise, displays "None".
+ * If |spdySessionInfo| there are any sessions, display a single table with
+ * information on each SPDY session. Otherwise, displays "None".
*/
SpdyView.prototype.onSpdySessionInfoChanged = function(spdySessionInfo) {
this.spdySessionDiv_.innerHTML = '';
@@ -47,12 +54,17 @@ SpdyView.prototype.onSpdySessionInfoChanged = function(spdySessionInfo) {
setNodeDisplay(this.spdySessionNoneSpan_, hasNoSession);
setNodeDisplay(this.spdySessionLinkSpan_, !hasNoSession);
- if (hasNoSession)
- return;
+ // Only want to be hide the tab if there's no data. In the case of having
+ // data but no sessions, still show the tab.
+ if (!spdySessionInfo)
+ return false;
- var tablePrinter = SpdyView.createSessionTablePrinter(spdySessionInfo);
- tablePrinter.toHTML(this.spdySessionDiv_, 'styledTable');
+ if (!hasNoSession) {
+ var tablePrinter = SpdyView.createSessionTablePrinter(spdySessionInfo);
+ tablePrinter.toHTML(this.spdySessionDiv_, 'styledTable');
+ }
+ return true;
};
/**
@@ -65,7 +77,9 @@ SpdyView.prototype.onSpdyStatusChanged = function(spdyStatus) {
this.spdyForceAlwaysSpan_.innerText = spdyStatus.force_spdy_always;
this.spdyForceOverSslSpan_.innerText = spdyStatus.force_spdy_over_ssl;
this.spdyNextProtocolsSpan_.innerText = spdyStatus.next_protos;
-}
+
+ return true;
+};
/**
* If |spdyAlternateProtocolMappings| is not empty, displays a single table
@@ -85,6 +99,7 @@ SpdyView.prototype.onSpdyAlternateProtocolMappingsChanged =
} else {
this.spdyAlternateProtocolMappingsDiv_.innerHTML = 'None';
}
+ return true;
};
/**
diff --git a/chrome/browser/resources/net_internals/tabswitcherview.js b/chrome/browser/resources/net_internals/tabswitcherview.js
index 18cfa7a..97eca54 100644
--- a/chrome/browser/resources/net_internals/tabswitcherview.js
+++ b/chrome/browser/resources/net_internals/tabswitcherview.js
@@ -1,4 +1,4 @@
-// Copyright (c) 2010 The Chromium Authors. All rights reserved.
+// 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.
@@ -67,7 +67,8 @@ TabSwitcherView.prototype.show = function(isVisible) {
* "tab".
* @param {!View} view The tab's actual contents.
*/
-TabSwitcherView.prototype.addTab = function(id, contentView, switchOnClick) {
+TabSwitcherView.prototype.addTab = function(id, contentView, switchOnClick,
+ visible) {
var tab = new TabEntry(id, contentView);
this.tabs_.push(tab);
@@ -81,6 +82,8 @@ TabSwitcherView.prototype.addTab = function(id, contentView, switchOnClick) {
// Start tabs off as hidden.
tab.contentView.show(false);
+
+ this.showTabHandleNode(id, visible);
};
/**
diff --git a/chrome/browser/resources/net_internals/util.js b/chrome/browser/resources/net_internals/util.js
index f326a77..f847476 100644
--- a/chrome/browser/resources/net_internals/util.js
+++ b/chrome/browser/resources/net_internals/util.js
@@ -99,7 +99,7 @@ function changeClassName(node, classNameToAddOrRemove, isAdd) {
}
function getKeyWithValue(map, value) {
- for (key in map) {
+ for (var key in map) {
if (map[key] == value)
return key;
}
@@ -129,6 +129,20 @@ function makeRepeatedString(str, count) {
}
/**
+ * Clones a basic POD object. Only a new top level object will be cloned. It
+ * will continue to reference the same values as the original object.
+ */
+function shallowCloneObject(object) {
+ if (!(object instanceof Object))
+ return object;
+ var copy = {};
+ for (var key in object) {
+ copy[key] = object[key];
+ }
+ return copy;
+}
+
+/**
* TablePrinter is a helper to format a table as ascii art or an HTML table.
*
* Usage: call addRow() and addCell() repeatedly to specify the data.
diff --git a/chrome/browser/resources/net_internals/view.js b/chrome/browser/resources/net_internals/view.js
index 68f485e..b1d229a 100644
--- a/chrome/browser/resources/net_internals/view.js
+++ b/chrome/browser/resources/net_internals/view.js
@@ -1,4 +1,4 @@
-// Copyright (c) 2010 The Chromium Authors. All rights reserved.
+// 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.
@@ -69,6 +69,23 @@ View.prototype.getBottom = function() {
View.prototype.setParameters = function(params) {};
+/**
+ * Called when loading a log file, after clearing all events, but before
+ * loading the new ones. |polledData| contains the data from all
+ * PollableData helpers, and |tabData| contains the data for the particular tab.
+ */
+View.prototype.onLoadLogStart = function(polledData, tabData) {
+};
+
+/**
+ * Called as the final step of loading a log file. Arguments are the same as
+ * onLoadLogStart. Returns true to indicate the tab should be shown, false
+ * otherwise.
+ */
+View.prototype.onLoadLogFinish = function(polledData, tabData) {
+ return false;
+};
+
//-----------------------------------------------------------------------------
/**
diff --git a/chrome/browser/ui/webui/net_internals_ui.cc b/chrome/browser/ui/webui/net_internals_ui.cc
index e4d83ec..a4341b2 100644
--- a/chrome/browser/ui/webui/net_internals_ui.cc
+++ b/chrome/browser/ui/webui/net_internals_ui.cc
@@ -75,6 +75,11 @@ namespace {
// sent to the page at once, which reduces context switching and CPU usage.
const int kNetLogEventDelayMilliseconds = 100;
+// about:net-internals will not even attempt to load a log dump when it
+// encounters a new version. This should be incremented when significant
+// changes are made that will invalidate the old loading code.
+const int kLogFormatVersion = 1;
+
// Returns the HostCache for |context|'s primary HostResolver, or NULL if
// there is none.
net::HostCache* GetHostResolverCache(net::URLRequestContext* context) {
@@ -161,9 +166,10 @@ class NetInternalsMessageHandler
virtual WebUIMessageHandler* Attach(WebUI* web_ui);
virtual void RegisterMessages();
- // Executes the javascript function |function_name| in the renderer, passing
- // it the argument |arg|. Takes ownership of |arg|.
- void CallJavascriptFunction(const std::wstring& function_name, Value* arg);
+ // Calls g_browser.receive in the renderer, passing in |command| and |arg|.
+ // Takes ownership of |arg|. If the renderer is displaying a log file, the
+ // message will be ignored.
+ void SendJavascriptCommand(const std::wstring& command, Value* arg);
// NotificationObserver implementation.
virtual void Observe(int type,
@@ -265,8 +271,8 @@ class NetInternalsMessageHandler
// This class is the "real" message handler. It is allocated and destroyed on
// the UI thread. With the exception of OnAddEntry, OnWebUIDeleted, and
-// CallJavascriptFunction, its methods are all expected to be called from the IO
-// thread. OnAddEntry and CallJavascriptFunction can be called from any thread,
+// SendJavascriptCommand, its methods are all expected to be called from the IO
+// thread. OnAddEntry and SendJavascriptCommand can be called from any thread,
// and OnWebUIDeleted can only be called from the UI thread.
class NetInternalsMessageHandler::IOThreadImpl
: public base::RefCountedThreadSafe<
@@ -351,10 +357,11 @@ 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);
+ // Helper that calls g_browser.receive in the renderer, passing in |command|
+ // and |arg|. Takes ownership of |arg|. If the renderer is displaying a log
+ // file, the message will be ignored. Note that this can be called from any
+ // thread.
+ void SendJavascriptCommand(const std::wstring& command, Value* arg);
private:
friend struct BrowserThread::DeleteOnThread<BrowserThread::UI>;
@@ -626,15 +633,20 @@ void NetInternalsMessageHandler::RegisterMessages() {
NewCallback(this, &NetInternalsMessageHandler::OnGetPrerenderInfo));
}
-void NetInternalsMessageHandler::CallJavascriptFunction(
- const std::wstring& function_name,
+void NetInternalsMessageHandler::SendJavascriptCommand(
+ const std::wstring& command,
Value* arg) {
+ scoped_ptr<Value> command_value(
+ Value::CreateStringValue(WideToASCII(command)));
scoped_ptr<Value> value(arg);
DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
if (value.get()) {
- web_ui_->CallJavascriptFunction(WideToASCII(function_name), *value.get());
+ web_ui_->CallJavascriptFunction("g_browser.receive",
+ *command_value.get(),
+ *value.get());
} else {
- web_ui_->CallJavascriptFunction(WideToASCII(function_name));
+ web_ui_->CallJavascriptFunction("g_browser.receive",
+ *command_value.get());
}
}
@@ -646,8 +658,8 @@ void NetInternalsMessageHandler::Observe(int type,
std::string* pref_name = Details<std::string>(details).ptr();
if (*pref_name == prefs::kHttpThrottlingEnabled) {
- CallJavascriptFunction(
- L"g_browser.receivedHttpThrottlingEnabledPrefChanged",
+ SendJavascriptCommand(
+ L"receivedHttpThrottlingEnabledPrefChanged",
Value::CreateBooleanValue(*http_throttling_enabled_));
}
}
@@ -656,8 +668,8 @@ void NetInternalsMessageHandler::OnRendererReady(const ListValue* list) {
CHECK(renderer_ready_io_callback_.get());
renderer_ready_io_callback_->Run(list);
- CallJavascriptFunction(
- L"g_browser.receivedHttpThrottlingEnabledPrefChanged",
+ SendJavascriptCommand(
+ L"receivedHttpThrottlingEnabledPrefChanged",
Value::CreateBooleanValue(*http_throttling_enabled_));
}
@@ -697,7 +709,7 @@ void NetInternalsMessageHandler::OnGetPrerenderInfo(const ListValue* list) {
} else {
value = prerender_manager->GetAsValue();
}
- CallJavascriptFunction(L"g_browser.receivedPrerenderInfo", value);
+ SendJavascriptCommand(L"receivedPrerenderInfo", value);
}
@@ -793,7 +805,7 @@ void NetInternalsMessageHandler::SystemLogsGetter::SendLogs(
}
result->SetString("cellId", request.cell_id);
- handler_->CallJavascriptFunction(L"g_browser.getSystemLogCallback", result);
+ handler_->SendJavascriptCommand(L"getSystemLogCallback", result);
}
#endif
////////////////////////////////////////////////////////////////////////////////
@@ -850,7 +862,7 @@ void NetInternalsMessageHandler::IOThreadImpl::SendPassiveLogEntries(
false));
}
- CallJavascriptFunction(L"g_browser.receivedPassiveLogEntries", dict_list);
+ SendJavascriptCommand(L"receivedPassiveLogEntries", dict_list);
}
void NetInternalsMessageHandler::IOThreadImpl::OnWebUIDeleted() {
@@ -863,152 +875,8 @@ void NetInternalsMessageHandler::IOThreadImpl::OnRendererReady(
DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
DCHECK(!is_observing_log_) << "notifyReady called twice";
- // Tell the javascript about the relationship between event type enums and
- // their symbolic name.
- {
- std::vector<net::NetLog::EventType> event_types =
- net::NetLog::GetAllEventTypes();
-
- DictionaryValue* dict = new DictionaryValue();
-
- for (size_t i = 0; i < event_types.size(); ++i) {
- const char* name = net::NetLog::EventTypeToString(event_types[i]);
- dict->SetInteger(name, static_cast<int>(event_types[i]));
- }
-
- CallJavascriptFunction(L"g_browser.receivedLogEventTypeConstants", dict);
- }
-
- // Tell the javascript about the version of the client and its
- // command line arguments.
- {
- DictionaryValue* dict = new DictionaryValue();
-
- chrome::VersionInfo version_info;
-
- if (!version_info.is_valid()) {
- DLOG(ERROR) << "Unable to create chrome::VersionInfo";
- } else {
- // We have everything we need to send the right values.
- dict->SetString("version", version_info.Version());
- dict->SetString("cl", version_info.LastChange());
- dict->SetString("version_mod",
- chrome::VersionInfo::GetVersionStringModifier());
- dict->SetString("official",
- l10n_util::GetStringUTF16(
- version_info.IsOfficialBuild() ?
- IDS_ABOUT_VERSION_OFFICIAL
- : IDS_ABOUT_VERSION_UNOFFICIAL));
-
- dict->SetString("command_line",
- CommandLine::ForCurrentProcess()->command_line_string());
- }
-
- CallJavascriptFunction(L"g_browser.receivedClientInfo",
- dict);
- }
-
- // Tell the javascript about the relationship between load flag enums and
- // their symbolic name.
- {
- DictionaryValue* dict = new DictionaryValue();
-
-#define LOAD_FLAG(label, value) \
- dict->SetInteger(# label, static_cast<int>(value));
-#include "net/base/load_flags_list.h"
-#undef LOAD_FLAG
-
- CallJavascriptFunction(L"g_browser.receivedLoadFlagConstants", dict);
- }
-
- // Tell the javascript about the relationship between net error codes and
- // their symbolic name.
- {
- DictionaryValue* dict = new DictionaryValue();
-
-#define NET_ERROR(label, value) \
- dict->SetInteger(# label, static_cast<int>(value));
-#include "net/base/net_error_list.h"
-#undef NET_ERROR
-
- CallJavascriptFunction(L"g_browser.receivedNetErrorConstants", dict);
- }
-
- // Tell the javascript about the relationship between event phase enums and
- // their symbolic name.
- {
- DictionaryValue* dict = new DictionaryValue();
-
- dict->SetInteger("PHASE_BEGIN", net::NetLog::PHASE_BEGIN);
- dict->SetInteger("PHASE_END", net::NetLog::PHASE_END);
- dict->SetInteger("PHASE_NONE", net::NetLog::PHASE_NONE);
-
- CallJavascriptFunction(L"g_browser.receivedLogEventPhaseConstants", dict);
- }
-
- // Tell the javascript about the relationship between source type enums and
- // their symbolic names.
- {
- DictionaryValue* dict = new DictionaryValue();
-
-#define SOURCE_TYPE(label, value) dict->SetInteger(# label, value);
-#include "net/base/net_log_source_type_list.h"
-#undef SOURCE_TYPE
-
- CallJavascriptFunction(L"g_browser.receivedLogSourceTypeConstants", dict);
- }
-
- // Tell the javascript about the relationship between LogLevel enums and their
- // symbolic names.
- {
- DictionaryValue* dict = new DictionaryValue();
-
- dict->SetInteger("LOG_ALL", net::NetLog::LOG_ALL);
- dict->SetInteger("LOG_ALL_BUT_BYTES", net::NetLog::LOG_ALL_BUT_BYTES);
- dict->SetInteger("LOG_BASIC", net::NetLog::LOG_BASIC);
-
- CallJavascriptFunction(L"g_browser.receivedLogLevelConstants", dict);
- }
-
- // Tell the javascript about the relationship between address family enums and
- // their symbolic names.
- {
- DictionaryValue* dict = new DictionaryValue();
-
- dict->SetInteger("ADDRESS_FAMILY_UNSPECIFIED",
- net::ADDRESS_FAMILY_UNSPECIFIED);
- dict->SetInteger("ADDRESS_FAMILY_IPV4",
- net::ADDRESS_FAMILY_IPV4);
- dict->SetInteger("ADDRESS_FAMILY_IPV6",
- net::ADDRESS_FAMILY_IPV6);
-
- CallJavascriptFunction(L"g_browser.receivedAddressFamilyConstants", dict);
- }
-
- // Tell the javascript how the "time ticks" values we have given it relate to
- // actual system times. (We used time ticks throughout since they are stable
- // across system clock changes).
- {
- int64 cur_time_ms = (base::Time::Now() - base::Time()).InMilliseconds();
-
- int64 cur_time_ticks_ms =
- (base::TimeTicks::Now() - base::TimeTicks()).InMilliseconds();
-
- // If we add this number to a time tick value, it gives the timestamp.
- int64 tick_to_time_ms = cur_time_ms - cur_time_ticks_ms;
-
- // Chrome on all platforms stores times using the Windows epoch
- // (Jan 1 1601), but the javascript wants a unix epoch.
- // TODO(eroman): Getting the timestamp relative the to unix epoch should
- // be part of the time library.
- const int64 kUnixEpochMs = 11644473600000LL;
- int64 tick_to_unix_time_ms = tick_to_time_ms - kUnixEpochMs;
-
- // Pass it as a string, since it may be too large to fit in an integer.
- CallJavascriptFunction(L"g_browser.receivedTimeTickOffset",
- Value::CreateStringValue(
- base::Int64ToString(tick_to_unix_time_ms)));
- }
+ SendJavascriptCommand(L"receivedConstants",
+ NetInternalsUI::GetConstants());
// Register with network stack to observe events.
is_observing_log_ = true;
@@ -1029,7 +897,7 @@ void NetInternalsMessageHandler::IOThreadImpl::OnGetProxySettings(
if (proxy_service->config().is_valid())
dict->Set("effective", proxy_service->config().ToValue());
- CallJavascriptFunction(L"g_browser.receivedProxySettings", dict);
+ SendJavascriptCommand(L"receivedProxySettings", dict);
}
void NetInternalsMessageHandler::IOThreadImpl::OnReloadProxySettings(
@@ -1063,7 +931,7 @@ void NetInternalsMessageHandler::IOThreadImpl::OnGetBadProxies(
dict_list->Append(dict);
}
- CallJavascriptFunction(L"g_browser.receivedBadProxies", dict_list);
+ SendJavascriptCommand(L"receivedBadProxies", dict_list);
}
void NetInternalsMessageHandler::IOThreadImpl::OnClearBadProxies(
@@ -1083,7 +951,7 @@ void NetInternalsMessageHandler::IOThreadImpl::OnGetHostResolverInfo(
net::HostCache* cache = GetHostResolverCache(context);
if (!host_resolver_impl || !cache) {
- CallJavascriptFunction(L"g_browser.receivedHostResolverInfo", NULL);
+ SendJavascriptCommand(L"receivedHostResolverInfo", NULL);
return;
}
@@ -1142,7 +1010,7 @@ void NetInternalsMessageHandler::IOThreadImpl::OnGetHostResolverInfo(
cache_info_dict->Set("entries", entry_list);
dict->Set("cache", cache_info_dict);
- CallJavascriptFunction(L"g_browser.receivedHostResolverInfo", dict);
+ SendJavascriptCommand(L"receivedHostResolverInfo", dict);
}
void NetInternalsMessageHandler::IOThreadImpl::OnClearHostResolverCache(
@@ -1230,7 +1098,7 @@ void NetInternalsMessageHandler::IOThreadImpl::OnHSTSQuery(
}
}
- CallJavascriptFunction(L"g_browser.receivedHSTSResult", result);
+ SendJavascriptCommand(L"receivedHSTSResult", result);
}
void NetInternalsMessageHandler::IOThreadImpl::OnHSTSAdd(
@@ -1318,7 +1186,7 @@ void NetInternalsMessageHandler::IOThreadImpl::OnGetHttpCacheInfo(
info_dict->Set("stats", stats_dict);
- CallJavascriptFunction(L"g_browser.receivedHttpCacheInfo", info_dict);
+ SendJavascriptCommand(L"receivedHttpCacheInfo", info_dict);
}
void NetInternalsMessageHandler::IOThreadImpl::OnGetSocketPoolInfo(
@@ -1330,7 +1198,7 @@ void NetInternalsMessageHandler::IOThreadImpl::OnGetSocketPoolInfo(
if (http_network_session)
socket_pool_info = http_network_session->SocketPoolInfoToValue();
- CallJavascriptFunction(L"g_browser.receivedSocketPoolInfo", socket_pool_info);
+ SendJavascriptCommand(L"receivedSocketPoolInfo", socket_pool_info);
}
@@ -1362,7 +1230,7 @@ void NetInternalsMessageHandler::IOThreadImpl::OnGetSpdySessionInfo(
spdy_info = http_network_session->SpdySessionPoolInfoToValue();
}
- CallJavascriptFunction(L"g_browser.receivedSpdySessionInfo", spdy_info);
+ SendJavascriptCommand(L"receivedSpdySessionInfo", spdy_info);
}
void NetInternalsMessageHandler::IOThreadImpl::OnGetSpdyStatus(
@@ -1385,7 +1253,7 @@ void NetInternalsMessageHandler::IOThreadImpl::OnGetSpdyStatus(
Value::CreateStringValue(
*net::HttpStreamFactory::next_protos()));
- CallJavascriptFunction(L"g_browser.receivedSpdyStatus", status_dict);
+ SendJavascriptCommand(L"receivedSpdyStatus", status_dict);
}
void
@@ -1412,8 +1280,7 @@ NetInternalsMessageHandler::IOThreadImpl::OnGetSpdyAlternateProtocolMappings(
}
}
- CallJavascriptFunction(L"g_browser.receivedSpdyAlternateProtocolMappings",
- dict_list);
+ SendJavascriptCommand(L"receivedSpdyAlternateProtocolMappings", dict_list);
}
#ifdef OS_WIN
@@ -1453,8 +1320,7 @@ void NetInternalsMessageHandler::IOThreadImpl::OnGetServiceProviders(
}
service_providers->Set("namespace_providers", namespace_list);
- CallJavascriptFunction(L"g_browser.receivedServiceProviders",
- service_providers);
+ SendJavascriptCommand(L"receivedServiceProviders", service_providers);
}
#endif
@@ -1516,19 +1382,17 @@ void NetInternalsMessageHandler::IOThreadImpl::AddEntryToQueue(Value* entry) {
void NetInternalsMessageHandler::IOThreadImpl::PostPendingEntries() {
DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
- CallJavascriptFunction(
- L"g_browser.receivedLogEntries",
- pending_entries_.release());
+ SendJavascriptCommand(L"receivedLogEntries", pending_entries_.release());
}
void NetInternalsMessageHandler::IOThreadImpl::OnStartConnectionTestSuite() {
- CallJavascriptFunction(L"g_browser.receivedStartConnectionTestSuite", NULL);
+ SendJavascriptCommand(L"receivedStartConnectionTestSuite", NULL);
}
void NetInternalsMessageHandler::IOThreadImpl::OnStartConnectionTestExperiment(
const ConnectionTester::Experiment& experiment) {
- CallJavascriptFunction(
- L"g_browser.receivedStartConnectionTestExperiment",
+ SendJavascriptCommand(
+ L"receivedStartConnectionTestExperiment",
ExperimentToValue(experiment));
}
@@ -1541,15 +1405,15 @@ NetInternalsMessageHandler::IOThreadImpl::OnCompletedConnectionTestExperiment(
dict->Set("experiment", ExperimentToValue(experiment));
dict->SetInteger("result", result);
- CallJavascriptFunction(
- L"g_browser.receivedCompletedConnectionTestExperiment",
+ SendJavascriptCommand(
+ L"receivedCompletedConnectionTestExperiment",
dict);
}
void
NetInternalsMessageHandler::IOThreadImpl::OnCompletedConnectionTestSuite() {
- CallJavascriptFunction(
- L"g_browser.receivedCompletedConnectionTestSuite",
+ SendJavascriptCommand(
+ L"receivedCompletedConnectionTestSuite",
NULL);
}
@@ -1561,14 +1425,14 @@ void NetInternalsMessageHandler::IOThreadImpl::DispatchToMessageHandler(
}
// Note that this can be called from ANY THREAD.
-void NetInternalsMessageHandler::IOThreadImpl::CallJavascriptFunction(
- const std::wstring& function_name,
+void NetInternalsMessageHandler::IOThreadImpl::SendJavascriptCommand(
+ const std::wstring& command,
Value* arg) {
if (BrowserThread::CurrentlyOn(BrowserThread::UI)) {
if (handler_ && !was_webui_deleted_) {
// We check |handler_| in case it was deleted on the UI thread earlier
// while we were running on the IO thread.
- handler_->CallJavascriptFunction(function_name, arg);
+ handler_->SendJavascriptCommand(command, arg);
} else {
delete arg;
}
@@ -1579,8 +1443,9 @@ void NetInternalsMessageHandler::IOThreadImpl::CallJavascriptFunction(
BrowserThread::UI, FROM_HERE,
NewRunnableMethod(
this,
- &IOThreadImpl::CallJavascriptFunction,
- function_name, arg))) {
+ &IOThreadImpl::SendJavascriptCommand,
+ command,
+ arg))) {
// Failed posting the task, avoid leaking.
delete arg;
}
@@ -1595,6 +1460,158 @@ void NetInternalsMessageHandler::IOThreadImpl::CallJavascriptFunction(
//
////////////////////////////////////////////////////////////////////////////////
+// static
+Value* NetInternalsUI::GetConstants() {
+ DictionaryValue* constants_dict = new DictionaryValue();
+
+ // Version of the file format.
+ constants_dict->SetInteger("logFormatVersion", kLogFormatVersion);
+
+ // Add a dictionary with information on the relationship between event type
+ // enums and their symbolic names.
+ {
+ std::vector<net::NetLog::EventType> event_types =
+ net::NetLog::GetAllEventTypes();
+
+ DictionaryValue* dict = new DictionaryValue();
+
+ for (size_t i = 0; i < event_types.size(); ++i) {
+ const char* name = net::NetLog::EventTypeToString(event_types[i]);
+ dict->SetInteger(name, static_cast<int>(event_types[i]));
+ }
+ constants_dict->Set("logEventTypes", dict);
+ }
+
+ // Add a dictionary with the version of the client and its command line
+ // arguments.
+ {
+ DictionaryValue* dict = new DictionaryValue();
+
+ chrome::VersionInfo version_info;
+
+ if (!version_info.is_valid()) {
+ DLOG(ERROR) << "Unable to create chrome::VersionInfo";
+ } else {
+ // We have everything we need to send the right values.
+ dict->SetString("name", version_info.Name());
+ dict->SetString("version", version_info.Version());
+ dict->SetString("cl", version_info.LastChange());
+ dict->SetString("version_mod",
+ chrome::VersionInfo::GetVersionStringModifier());
+ dict->SetString("official",
+ version_info.IsOfficialBuild() ? "official" :
+ "unofficial");
+ dict->SetString("os_type", version_info.OSType());
+ dict->SetString("command_line",
+ CommandLine::ForCurrentProcess()->command_line_string());
+ }
+
+ constants_dict->Set("clientInfo", dict);
+ }
+
+ // Add a dictionary with information about the relationship between load flag
+ // enums and their symbolic names.
+ {
+ DictionaryValue* dict = new DictionaryValue();
+
+#define LOAD_FLAG(label, value) \
+ dict->SetInteger(# label, static_cast<int>(value));
+#include "net/base/load_flags_list.h"
+#undef LOAD_FLAG
+
+ constants_dict->Set("loadFlag", dict);
+ }
+
+ // Add information on the relationship between net error codes and their
+ // symbolic names.
+ {
+ DictionaryValue* dict = new DictionaryValue();
+
+#define NET_ERROR(label, value) \
+ dict->SetInteger(# label, static_cast<int>(value));
+#include "net/base/net_error_list.h"
+#undef NET_ERROR
+
+ constants_dict->Set("netError", dict);
+ }
+
+ // Information about the relationship between event phase enums and their
+ // symbolic names.
+ {
+ DictionaryValue* dict = new DictionaryValue();
+
+ dict->SetInteger("PHASE_BEGIN", net::NetLog::PHASE_BEGIN);
+ dict->SetInteger("PHASE_END", net::NetLog::PHASE_END);
+ dict->SetInteger("PHASE_NONE", net::NetLog::PHASE_NONE);
+
+ constants_dict->Set("logEventPhase", dict);
+ }
+
+ // Information about the relationship between source type enums and
+ // their symbolic names.
+ {
+ DictionaryValue* dict = new DictionaryValue();
+
+#define SOURCE_TYPE(label, value) dict->SetInteger(# label, value);
+#include "net/base/net_log_source_type_list.h"
+#undef SOURCE_TYPE
+
+ constants_dict->Set("logSourceType", dict);
+ }
+
+ // Information about the relationship between LogLevel enums and their
+ // symbolic names.
+ {
+ DictionaryValue* dict = new DictionaryValue();
+
+ dict->SetInteger("LOG_ALL", net::NetLog::LOG_ALL);
+ dict->SetInteger("LOG_ALL_BUT_BYTES", net::NetLog::LOG_ALL_BUT_BYTES);
+ dict->SetInteger("LOG_BASIC", net::NetLog::LOG_BASIC);
+
+ constants_dict->Set("logLevelType", dict);
+ }
+
+ // Information about the relationship between address family enums and
+ // their symbolic names.
+ {
+ DictionaryValue* dict = new DictionaryValue();
+
+ dict->SetInteger("ADDRESS_FAMILY_UNSPECIFIED",
+ net::ADDRESS_FAMILY_UNSPECIFIED);
+ dict->SetInteger("ADDRESS_FAMILY_IPV4",
+ net::ADDRESS_FAMILY_IPV4);
+ dict->SetInteger("ADDRESS_FAMILY_IPV6",
+ net::ADDRESS_FAMILY_IPV6);
+
+ constants_dict->Set("addressFamily", dict);
+ }
+
+ // Information about how the "time ticks" values we have given it relate to
+ // actual system times. (We used time ticks throughout since they are stable
+ // across system clock changes).
+ {
+ int64 cur_time_ms = (base::Time::Now() - base::Time()).InMilliseconds();
+
+ int64 cur_time_ticks_ms =
+ (base::TimeTicks::Now() - base::TimeTicks()).InMilliseconds();
+
+ // If we add this number to a time tick value, it gives the timestamp.
+ int64 tick_to_time_ms = cur_time_ms - cur_time_ticks_ms;
+
+ // Chrome on all platforms stores times using the Windows epoch
+ // (Jan 1 1601), but the javascript wants a unix epoch.
+ // TODO(eroman): Getting the timestamp relative to the unix epoch should
+ // be part of the time library.
+ const int64 kUnixEpochMs = 11644473600000LL;
+ int64 tick_to_unix_time_ms = tick_to_time_ms - kUnixEpochMs;
+
+ // Pass it as a string, since it may be too large to fit in an integer.
+ constants_dict->SetString("timeTickOffset",
+ base::Int64ToString(tick_to_unix_time_ms));
+ }
+ return constants_dict;
+}
+
NetInternalsUI::NetInternalsUI(TabContents* contents) : ChromeWebUI(contents) {
AddMessageHandler((new NetInternalsMessageHandler())->Attach(this));
diff --git a/chrome/browser/ui/webui/net_internals_ui.h b/chrome/browser/ui/webui/net_internals_ui.h
index 3acf4ce..6a70c4e 100644
--- a/chrome/browser/ui/webui/net_internals_ui.h
+++ b/chrome/browser/ui/webui/net_internals_ui.h
@@ -8,10 +8,16 @@
#include "chrome/browser/ui/webui/chrome_web_ui.h"
+class Value;
+
class NetInternalsUI : public ChromeWebUI {
public:
explicit NetInternalsUI(TabContents* contents);
+ // Returns a Value containing constants NetInternals needs to load a log file.
+ // Safe to call on any thread. Caller takes ownership of the returned Value.
+ static Value* GetConstants();
+
private:
DISALLOW_COPY_AND_ASSIGN(NetInternalsUI);
};