summaryrefslogtreecommitdiffstats
path: root/chrome/browser/resources/net_internals
diff options
context:
space:
mode:
Diffstat (limited to 'chrome/browser/resources/net_internals')
-rw-r--r--chrome/browser/resources/net_internals/browserbridge.js624
-rw-r--r--chrome/browser/resources/net_internals/dataview.js18
-rw-r--r--chrome/browser/resources/net_internals/dnsview.js2
-rw-r--r--chrome/browser/resources/net_internals/eventsview.js161
-rw-r--r--chrome/browser/resources/net_internals/index.html1
-rw-r--r--chrome/browser/resources/net_internals/index.js4
-rw-r--r--chrome/browser/resources/net_internals/logdumputil.js229
-rw-r--r--chrome/browser/resources/net_internals/logviewpainter.js8
-rw-r--r--chrome/browser/resources/net_internals/main.js1065
-rw-r--r--chrome/browser/resources/net_internals/proxyview.js25
-rw-r--r--chrome/browser/resources/net_internals/sourceentry.js365
-rw-r--r--chrome/browser/resources/net_internals/sourcerow.js284
-rw-r--r--chrome/browser/resources/net_internals/sourcetracker.js201
13 files changed, 1537 insertions, 1450 deletions
diff --git a/chrome/browser/resources/net_internals/browserbridge.js b/chrome/browser/resources/net_internals/browserbridge.js
new file mode 100644
index 0000000..eb362ca
--- /dev/null
+++ b/chrome/browser/resources/net_internals/browserbridge.js
@@ -0,0 +1,624 @@
+// Copyright (c) 2011 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+/**
+ * This class provides a "bridge" for communicating between the javascript and
+ * the browser.
+ *
+ * @constructor
+ */
+function BrowserBridge() {
+ // List of observers for various bits of browser state.
+ this.connectionTestsObservers_ = [];
+ this.hstsObservers_ = [];
+ this.httpThrottlingObservers_ = [];
+ this.constantsObservers_ = [];
+
+ this.pollableDataHelpers_ = {};
+ this.pollableDataHelpers_.proxySettings =
+ new PollableDataHelper('onProxySettingsChanged',
+ this.sendGetProxySettings.bind(this));
+ this.pollableDataHelpers_.badProxies =
+ new PollableDataHelper('onBadProxiesChanged',
+ this.sendGetBadProxies.bind(this));
+ this.pollableDataHelpers_.httpCacheInfo =
+ new PollableDataHelper('onHttpCacheInfoChanged',
+ this.sendGetHttpCacheInfo.bind(this));
+ this.pollableDataHelpers_.hostResolverInfo =
+ new PollableDataHelper('onHostResolverInfoChanged',
+ this.sendGetHostResolverInfo.bind(this));
+ this.pollableDataHelpers_.socketPoolInfo =
+ new PollableDataHelper('onSocketPoolInfoChanged',
+ this.sendGetSocketPoolInfo.bind(this));
+ this.pollableDataHelpers_.spdySessionInfo =
+ new PollableDataHelper('onSpdySessionInfoChanged',
+ this.sendGetSpdySessionInfo.bind(this));
+ this.pollableDataHelpers_.spdyStatus =
+ new PollableDataHelper('onSpdyStatusChanged',
+ this.sendGetSpdyStatus.bind(this));
+ this.pollableDataHelpers_.spdyAlternateProtocolMappings =
+ new PollableDataHelper('onSpdyAlternateProtocolMappingsChanged',
+ this.sendGetSpdyAlternateProtocolMappings.bind(
+ this));
+ if (cr.isWindows) {
+ this.pollableDataHelpers_.serviceProviders =
+ new PollableDataHelper('onServiceProvidersChanged',
+ this.sendGetServiceProviders.bind(this));
+ }
+ this.pollableDataHelpers_.prerenderInfo =
+ new PollableDataHelper('onPrerenderInfoChanged',
+ this.sendGetPrerenderInfo.bind(this));
+
+ // NetLog entries are all sent to the |SourceTracker|, which both tracks them
+ // and manages its own observer list.
+ this.sourceTracker = new SourceTracker();
+
+ // Setting this to true will cause messages from the browser to be ignored,
+ // and no messages will be sent to the browser, either. Intended for use when
+ // viewing log files.
+ this.disabled_ = false;
+}
+
+/**
+ * Delay in milliseconds between updates of certain browser information.
+ */
+BrowserBridge.POLL_INTERVAL_MS = 5000;
+
+//------------------------------------------------------------------------------
+// Messages sent to the browser
+//------------------------------------------------------------------------------
+
+/**
+ * Wraps |chrome.send|. Doesn't send anything when disabled.
+ */
+BrowserBridge.prototype.send = function(value1, value2) {
+ if (!this.disabled_) {
+ if (arguments.length == 1) {
+ chrome.send(value1);
+ } else if (arguments.length == 2) {
+ chrome.send(value1, value2);
+ } else {
+ throw 'Unsupported number of arguments.';
+ }
+ }
+};
+
+BrowserBridge.prototype.sendReady = function() {
+ 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
+ // the observers.
+ window.setInterval(this.checkForUpdatedInfo.bind(this, false),
+ BrowserBridge.POLL_INTERVAL_MS);
+};
+
+BrowserBridge.prototype.sendGetProxySettings = function() {
+ // The browser will call receivedProxySettings on completion.
+ this.send('getProxySettings');
+};
+
+BrowserBridge.prototype.sendReloadProxySettings = function() {
+ this.send('reloadProxySettings');
+};
+
+BrowserBridge.prototype.sendGetBadProxies = function() {
+ // The browser will call receivedBadProxies on completion.
+ this.send('getBadProxies');
+};
+
+BrowserBridge.prototype.sendGetHostResolverInfo = function() {
+ // The browser will call receivedHostResolverInfo on completion.
+ this.send('getHostResolverInfo');
+};
+
+BrowserBridge.prototype.sendClearBadProxies = function() {
+ this.send('clearBadProxies');
+};
+
+BrowserBridge.prototype.sendClearHostResolverCache = function() {
+ this.send('clearHostResolverCache');
+};
+
+BrowserBridge.prototype.sendStartConnectionTests = function(url) {
+ this.send('startConnectionTests', [url]);
+};
+
+BrowserBridge.prototype.sendHSTSQuery = function(domain) {
+ this.send('hstsQuery', [domain]);
+};
+
+BrowserBridge.prototype.sendHSTSAdd = function(domain,
+ include_subdomains,
+ pins) {
+ this.send('hstsAdd', [domain, include_subdomains, pins]);
+};
+
+BrowserBridge.prototype.sendHSTSDelete = function(domain) {
+ this.send('hstsDelete', [domain]);
+};
+
+BrowserBridge.prototype.sendGetHttpCacheInfo = function() {
+ this.send('getHttpCacheInfo');
+};
+
+BrowserBridge.prototype.sendGetSocketPoolInfo = function() {
+ this.send('getSocketPoolInfo');
+};
+
+BrowserBridge.prototype.sendCloseIdleSockets = function() {
+ this.send('closeIdleSockets');
+};
+
+BrowserBridge.prototype.sendFlushSocketPools = function() {
+ this.send('flushSocketPools');
+};
+
+BrowserBridge.prototype.sendGetSpdySessionInfo = function() {
+ this.send('getSpdySessionInfo');
+};
+
+BrowserBridge.prototype.sendGetSpdyStatus = function() {
+ this.send('getSpdyStatus');
+};
+
+BrowserBridge.prototype.sendGetSpdyAlternateProtocolMappings = function() {
+ this.send('getSpdyAlternateProtocolMappings');
+};
+
+BrowserBridge.prototype.sendGetServiceProviders = function() {
+ this.send('getServiceProviders');
+};
+
+BrowserBridge.prototype.sendGetPrerenderInfo = function() {
+ this.send('getPrerenderInfo');
+};
+
+BrowserBridge.prototype.enableIPv6 = function() {
+ this.send('enableIPv6');
+};
+
+BrowserBridge.prototype.setLogLevel = function(logLevel) {
+ this.send('setLogLevel', ['' + logLevel]);
+};
+
+BrowserBridge.prototype.enableHttpThrottling = function(enable) {
+ this.send('enableHttpThrottling', [enable]);
+};
+
+BrowserBridge.prototype.refreshSystemLogs = function() {
+ this.send('refreshSystemLogs');
+};
+
+BrowserBridge.prototype.getSystemLog = function(log_key, cellId) {
+ this.send('getSystemLog', [log_key, cellId]);
+};
+
+//------------------------------------------------------------------------------
+// Messages received from the browser.
+//------------------------------------------------------------------------------
+
+BrowserBridge.prototype.receive = function(command, params) {
+ // Does nothing if disabled.
+ if (this.disabled_)
+ return;
+ this[command](params);
+};
+
+BrowserBridge.prototype.receivedConstants = function(constants) {
+ for (var i = 0; i < this.constantsObservers_.length; ++i)
+ this.constantsObservers_[i].onReceivedConstants(constants);
+};
+
+BrowserBridge.prototype.receivedLogEntries = function(logEntries) {
+ this.sourceTracker.onReceivedLogEntries(logEntries);
+};
+
+BrowserBridge.prototype.receivedProxySettings = function(proxySettings) {
+ this.pollableDataHelpers_.proxySettings.update(proxySettings);
+};
+
+BrowserBridge.prototype.receivedBadProxies = function(badProxies) {
+ this.pollableDataHelpers_.badProxies.update(badProxies);
+};
+
+BrowserBridge.prototype.receivedHostResolverInfo =
+function(hostResolverInfo) {
+ this.pollableDataHelpers_.hostResolverInfo.update(hostResolverInfo);
+};
+
+BrowserBridge.prototype.receivedSocketPoolInfo = function(socketPoolInfo) {
+ this.pollableDataHelpers_.socketPoolInfo.update(socketPoolInfo);
+};
+
+BrowserBridge.prototype.receivedSpdySessionInfo = function(spdySessionInfo) {
+ this.pollableDataHelpers_.spdySessionInfo.update(spdySessionInfo);
+};
+
+BrowserBridge.prototype.receivedSpdyStatus = function(spdyStatus) {
+ this.pollableDataHelpers_.spdyStatus.update(spdyStatus);
+};
+
+BrowserBridge.prototype.receivedSpdyAlternateProtocolMappings =
+ function(spdyAlternateProtocolMappings) {
+ this.pollableDataHelpers_.spdyAlternateProtocolMappings.update(
+ spdyAlternateProtocolMappings);
+};
+
+BrowserBridge.prototype.receivedServiceProviders = function(serviceProviders) {
+ this.pollableDataHelpers_.serviceProviders.update(serviceProviders);
+};
+
+BrowserBridge.prototype.receivedPassiveLogEntries = function(entries) {
+ this.sourceTracker.onReceivedPassiveLogEntries(entries);
+};
+
+BrowserBridge.prototype.receivedStartConnectionTestSuite = function() {
+ for (var i = 0; i < this.connectionTestsObservers_.length; ++i)
+ this.connectionTestsObservers_[i].onStartedConnectionTestSuite();
+};
+
+BrowserBridge.prototype.receivedStartConnectionTestExperiment = function(
+ experiment) {
+ for (var i = 0; i < this.connectionTestsObservers_.length; ++i) {
+ this.connectionTestsObservers_[i].onStartedConnectionTestExperiment(
+ experiment);
+ }
+};
+
+BrowserBridge.prototype.receivedCompletedConnectionTestExperiment =
+function(info) {
+ for (var i = 0; i < this.connectionTestsObservers_.length; ++i) {
+ this.connectionTestsObservers_[i].onCompletedConnectionTestExperiment(
+ info.experiment, info.result);
+ }
+};
+
+BrowserBridge.prototype.receivedCompletedConnectionTestSuite = function() {
+ for (var i = 0; i < this.connectionTestsObservers_.length; ++i)
+ this.connectionTestsObservers_[i].onCompletedConnectionTestSuite();
+};
+
+BrowserBridge.prototype.receivedHSTSResult = function(info) {
+ for (var i = 0; i < this.hstsObservers_.length; ++i)
+ this.hstsObservers_[i].onHSTSQueryResult(info);
+};
+
+BrowserBridge.prototype.receivedHttpCacheInfo = function(info) {
+ this.pollableDataHelpers_.httpCacheInfo.update(info);
+};
+
+BrowserBridge.prototype.receivedHttpThrottlingEnabledPrefChanged = function(
+ enabled) {
+ for (var i = 0; i < this.httpThrottlingObservers_.length; ++i) {
+ this.httpThrottlingObservers_[i].onHttpThrottlingEnabledPrefChanged(
+ enabled);
+ }
+};
+
+BrowserBridge.prototype.receivedPrerenderInfo = function(prerenderInfo) {
+ this.pollableDataHelpers_.prerenderInfo.update(prerenderInfo);
+};
+
+//------------------------------------------------------------------------------
+
+/**
+ * Prevents receiving/sending events to/from the browser.
+ */
+BrowserBridge.prototype.disable = function() {
+ this.disabled_ = true;
+};
+
+/**
+ * Adds a listener of the proxy settings. |observer| will be called back when
+ * data is received, through:
+ *
+ * observer.onProxySettingsChanged(proxySettings)
+ *
+ * |proxySettings| is a dictionary with (up to) two properties:
+ *
+ * "original" -- The settings that chrome was configured to use
+ * (i.e. system settings.)
+ * "effective" -- The "effective" proxy settings that chrome is using.
+ * (decides between the manual/automatic modes of the
+ * fetched settings).
+ *
+ * Each of these two configurations is formatted as a string, and may be
+ * omitted if not yet initialized.
+ *
+ * TODO(eroman): send a dictionary instead.
+ */
+BrowserBridge.prototype.addProxySettingsObserver = function(observer) {
+ this.pollableDataHelpers_.proxySettings.addObserver(observer);
+};
+
+/**
+ * Adds a listener of the proxy settings. |observer| will be called back when
+ * data is received, through:
+ *
+ * observer.onBadProxiesChanged(badProxies)
+ *
+ * |badProxies| is an array, where each entry has the property:
+ * badProxies[i].proxy_uri: String identify the proxy.
+ * badProxies[i].bad_until: The time when the proxy stops being considered
+ * bad. Note the time is in time ticks.
+ */
+BrowserBridge.prototype.addBadProxiesObserver = function(observer) {
+ this.pollableDataHelpers_.badProxies.addObserver(observer);
+};
+
+/**
+ * Adds a listener of the host resolver info. |observer| will be called back
+ * when data is received, through:
+ *
+ * observer.onHostResolverInfoChanged(hostResolverInfo)
+ */
+BrowserBridge.prototype.addHostResolverInfoObserver = function(observer) {
+ this.pollableDataHelpers_.hostResolverInfo.addObserver(observer);
+};
+
+/**
+ * Adds a listener of the socket pool. |observer| will be called back
+ * when data is received, through:
+ *
+ * observer.onSocketPoolInfoChanged(socketPoolInfo)
+ */
+BrowserBridge.prototype.addSocketPoolInfoObserver = function(observer) {
+ this.pollableDataHelpers_.socketPoolInfo.addObserver(observer);
+};
+
+/**
+ * Adds a listener of the SPDY info. |observer| will be called back
+ * when data is received, through:
+ *
+ * observer.onSpdySessionInfoChanged(spdySessionInfo)
+ */
+BrowserBridge.prototype.addSpdySessionInfoObserver = function(observer) {
+ this.pollableDataHelpers_.spdySessionInfo.addObserver(observer);
+};
+
+/**
+ * Adds a listener of the SPDY status. |observer| will be called back
+ * when data is received, through:
+ *
+ * observer.onSpdyStatusChanged(spdyStatus)
+ */
+BrowserBridge.prototype.addSpdyStatusObserver = function(observer) {
+ this.pollableDataHelpers_.spdyStatus.addObserver(observer);
+};
+
+/**
+ * Adds a listener of the AlternateProtocolMappings. |observer| will be called
+ * back when data is received, through:
+ *
+ * observer.onSpdyAlternateProtocolMappingsChanged(
+ * spdyAlternateProtocolMappings)
+ */
+BrowserBridge.prototype.addSpdyAlternateProtocolMappingsObserver =
+ function(observer) {
+ this.pollableDataHelpers_.spdyAlternateProtocolMappings.addObserver(observer);
+};
+
+/**
+ * Adds a listener of the service providers info. |observer| will be called
+ * 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) {
+ if (this.pollableDataHelpers_.serviceProviders)
+ this.pollableDataHelpers_.serviceProviders.addObserver(observer);
+};
+
+/**
+ * Adds a listener for the progress of the connection tests.
+ * The observer will be called back with:
+ *
+ * observer.onStartedConnectionTestSuite();
+ * observer.onStartedConnectionTestExperiment(experiment);
+ * observer.onCompletedConnectionTestExperiment(experiment, result);
+ * observer.onCompletedConnectionTestSuite();
+ */
+BrowserBridge.prototype.addConnectionTestsObserver = function(observer) {
+ this.connectionTestsObservers_.push(observer);
+};
+
+/**
+ * Adds a listener for the http cache info results.
+ * The observer will be called back with:
+ *
+ * observer.onHttpCacheInfoChanged(info);
+ */
+BrowserBridge.prototype.addHttpCacheInfoObserver = function(observer) {
+ this.pollableDataHelpers_.httpCacheInfo.addObserver(observer);
+};
+
+/**
+ * Adds a listener for the results of HSTS (HTTPS Strict Transport Security)
+ * queries. The observer will be called back with:
+ *
+ * observer.onHSTSQueryResult(result);
+ */
+BrowserBridge.prototype.addHSTSObserver = function(observer) {
+ this.hstsObservers_.push(observer);
+};
+
+/**
+ * Adds a listener for HTTP throttling-related events. |observer| will be called
+ * back when HTTP throttling is enabled/disabled, through:
+ *
+ * observer.onHttpThrottlingEnabledPrefChanged(enabled);
+ */
+BrowserBridge.prototype.addHttpThrottlingObserver = function(observer) {
+ this.httpThrottlingObservers_.push(observer);
+};
+
+/**
+ * Adds a listener for the received constants event. |observer| will be called
+ * back when the constants are received, through:
+ *
+ * observer.onReceivedConstants(constants);
+ */
+BrowserBridge.prototype.addConstantsObserver = function(observer) {
+ this.constantsObservers_.push(observer);
+};
+
+/**
+ * Adds a listener for updated prerender info events
+ * |observer| will be called back with:
+ *
+ * observer.onPrerenderInfoChanged(prerenderInfo);
+ */
+BrowserBridge.prototype.addPrerenderInfoObserver = function(observer) {
+ this.pollableDataHelpers_.prerenderInfo.addObserver(observer);
+};
+
+/**
+ * If |force| is true, calls all startUpdate functions. Otherwise, just
+ * runs updates with active observers.
+ */
+BrowserBridge.prototype.checkForUpdatedInfo = function(force) {
+ for (name in this.pollableDataHelpers_) {
+ var helper = this.pollableDataHelpers_[name];
+ if (force || helper.hasActiveObserver())
+ helper.startUpdate();
+ }
+};
+
+/**
+ * Calls all startUpdate functions and, if |callback| is non-null,
+ * calls it with the results of all updates.
+ */
+BrowserBridge.prototype.updateAllInfo = function(callback) {
+ if (callback)
+ new UpdateAllObserver(callback, this.pollableDataHelpers_);
+ this.checkForUpdatedInfo(true);
+};
+
+/**
+ * This is a helper class used by BrowserBridge, to keep track of:
+ * - the list of observers interested in some piece of data.
+ * - the last known value of that piece of data.
+ * - the name of the callback method to invoke on observers.
+ * - the update function.
+ * @constructor
+ */
+function PollableDataHelper(observerMethodName, startUpdateFunction) {
+ this.observerMethodName_ = observerMethodName;
+ this.startUpdate = startUpdateFunction;
+ this.observerInfos_ = [];
+}
+
+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
+ * latter is used to make sure that new observers get sent data on the
+ * update following their creation.
+ * @constructor
+ */
+function ObserverInfo(observer) {
+ this.observer = observer;
+ this.hasReceivedData = false;
+}
+
+PollableDataHelper.prototype.addObserver = function(observer) {
+ this.observerInfos_.push(new ObserverInfo(observer));
+};
+
+PollableDataHelper.prototype.removeObserver = function(observer) {
+ for (var i = 0; i < this.observerInfos_.length; ++i) {
+ if (this.observerInfos_[i].observer === observer) {
+ this.observerInfos_.splice(i, 1);
+ return;
+ }
+ }
+};
+
+/**
+ * Helper function to handle calling all the observers, but ONLY if the data has
+ * actually changed since last time or the observer has yet to receive any data.
+ * This is used for data we received from browser on an update loop.
+ */
+PollableDataHelper.prototype.update = function(data) {
+ var prevData = this.currentData_;
+ var changed = false;
+
+ // If the data hasn't changed since last time, will only need to notify
+ // observers that have not yet received any data.
+ if (!prevData || JSON.stringify(prevData) != JSON.stringify(data)) {
+ changed = true;
+ this.currentData_ = data;
+ }
+
+ // Notify the observers of the change, as needed.
+ for (var i = 0; i < this.observerInfos_.length; ++i) {
+ var observerInfo = this.observerInfos_[i];
+ if (changed || !observerInfo.hasReceivedData) {
+ observerInfo.observer[this.observerMethodName_](this.currentData_);
+ observerInfo.hasReceivedData = true;
+ }
+ }
+};
+
+/**
+ * Returns true if one of the observers actively wants the data
+ * (i.e. is visible).
+ */
+PollableDataHelper.prototype.hasActiveObserver = function() {
+ for (var i = 0; i < this.observerInfos_.length; ++i) {
+ if (this.observerInfos_[i].observer.isActive())
+ return true;
+ }
+ return false;
+};
+
+/**
+ * This is a helper class used by BrowserBridge to send data to
+ * a callback once data from all polls has been received.
+ *
+ * It works by keeping track of how many polling functions have
+ * yet to receive data, and recording the data as it it received.
+ *
+ * @constructor
+ */
+function UpdateAllObserver(callback, pollableDataHelpers) {
+ this.callback_ = callback;
+ this.observingCount_ = 0;
+ this.updatedData_ = {};
+
+ for (name in pollableDataHelpers) {
+ ++this.observingCount_;
+ var helper = pollableDataHelpers[name];
+ helper.addObserver(this);
+ this[helper.getObserverMethodName()] =
+ this.onDataReceived_.bind(this, helper, name);
+ }
+}
+
+UpdateAllObserver.prototype.isActive = function() {
+ return true;
+};
+
+UpdateAllObserver.prototype.onDataReceived_ = function(helper, name, data) {
+ helper.removeObserver(this);
+ --this.observingCount_;
+ this.updatedData_[name] = data;
+ if (this.observingCount_ == 0)
+ this.callback_(this.updatedData_);
+};
diff --git a/chrome/browser/resources/net_internals/dataview.js b/chrome/browser/resources/net_internals/dataview.js
index cc607fe..aad6ae1 100644
--- a/chrome/browser/resources/net_internals/dataview.js
+++ b/chrome/browser/resources/net_internals/dataview.js
@@ -51,7 +51,8 @@ function DataView() {
this.activelyCapturedCountBox_ = $(activelyCapturedCountId);
this.passivelyCapturedCountBox_ = $(passivelyCapturedCountId);
- $(deleteAllId).onclick = g_browser.deleteAllEvents.bind(g_browser);
+ $(deleteAllId).onclick = g_browser.sourceTracker.deleteAllSourceEntries.bind(
+ g_browser.sourceTracker);
this.dumpDataDiv_ = $(dumpDataDivId);
this.capturingTextSpan_ = $(capturingTextSpanId);
@@ -75,7 +76,7 @@ function DataView() {
// saved.
this.lastBlobURL_ = null;
- g_browser.addLogObserver(this);
+ g_browser.sourceTracker.addObserver(this);
}
inherits(DataView, DivView);
@@ -83,7 +84,7 @@ inherits(DataView, DivView);
/**
* Called whenever a new event is received.
*/
-DataView.prototype.onLogEntryAdded = function(logEntry) {
+DataView.prototype.onSourceEntryUpdated = function(sourceEntry) {
this.updateEventCounts_();
};
@@ -91,14 +92,14 @@ DataView.prototype.onLogEntryAdded = function(logEntry) {
* Called whenever some log events are deleted. |sourceIds| lists
* the source IDs of all deleted log entries.
*/
-DataView.prototype.onLogEntriesDeleted = function(sourceIds) {
+DataView.prototype.onSourceEntriesDeleted = function(sourceIds) {
this.updateEventCounts_();
};
/**
* Called whenever all log events are deleted.
*/
-DataView.prototype.onAllLogEntriesDeleted = function() {
+DataView.prototype.onAllSourceEntriesDeleted = function() {
this.updateEventCounts_();
};
@@ -122,9 +123,9 @@ DataView.prototype.onLoadLogFinish = function(data) {
*/
DataView.prototype.updateEventCounts_ = function() {
this.activelyCapturedCountBox_.textContent =
- g_browser.getNumActivelyCapturedEvents()
+ g_browser.sourceTracker.getNumActivelyCapturedEvents();
this.passivelyCapturedCountBox_.textContent =
- g_browser.getNumPassivelyCapturedEvents();
+ g_browser.sourceTracker.getNumPassivelyCapturedEvents();
};
/**
@@ -145,7 +146,8 @@ DataView.prototype.onSetByteLogging_ = function(byteLoggingCheckbox) {
*/
DataView.prototype.onSetSecurityStripping_ =
function(securityStrippingCheckbox) {
- g_browser.setSecurityStripping(securityStrippingCheckbox.checked);
+ g_browser.sourceTracker.setSecurityStripping(
+ securityStrippingCheckbox.checked);
};
DataView.prototype.onSecurityStrippingChanged = function() {
diff --git a/chrome/browser/resources/net_internals/dnsview.js b/chrome/browser/resources/net_internals/dnsview.js
index 0479215..ebfcde5 100644
--- a/chrome/browser/resources/net_internals/dnsview.js
+++ b/chrome/browser/resources/net_internals/dnsview.js
@@ -99,7 +99,7 @@ DnsView.prototype.onHostResolverInfoChanged = function(hostResolverInfo) {
}
}
- var expiresDate = g_browser.convertTimeTicksToDate(e.expiration);
+ var expiresDate = convertTimeTicksToDate(e.expiration);
var expiresCell = addNode(tr, 'td');
addTextNode(expiresCell, expiresDate.toLocaleString());
}
diff --git a/chrome/browser/resources/net_internals/eventsview.js b/chrome/browser/resources/net_internals/eventsview.js
index 14e84c5..674a442 100644
--- a/chrome/browser/resources/net_internals/eventsview.js
+++ b/chrome/browser/resources/net_internals/eventsview.js
@@ -47,9 +47,6 @@ function EventsView() {
View.call(this);
- // Used for sorting entries with automatically assigned IDs.
- this.maxReceivedSourceId_ = 0;
-
// Initialize the sub-views.
var leftPane = new TopMidBottomView(new DivView(topbarId),
new DivView(middleboxId),
@@ -64,7 +61,7 @@ function EventsView() {
this.splitterView_ = new ResizableVerticalSplitView(
leftPane, this.detailsView_, new DivView(sizerId));
- g_browser.addLogObserver(this);
+ g_browser.sourceTracker.addObserver(this);
this.tableBody_ = $(tableBodyId);
@@ -76,7 +73,8 @@ function EventsView() {
$(deleteSelectedId).onclick = this.deleteSelected_.bind(this);
- $(deleteAllId).onclick = g_browser.deleteAllEvents.bind(g_browser);
+ $(deleteAllId).onclick = g_browser.sourceTracker.deleteAllSourceEntries.bind(
+ g_browser.sourceTracker);
$(selectAllId).addEventListener('click', this.selectAll_.bind(this), true);
@@ -101,8 +99,8 @@ inherits(EventsView, View);
* being displayed, removes them all in the process.
*/
EventsView.prototype.initializeSourceList_ = function() {
- this.currentSelectedSources_ = [];
- this.sourceIdToEntryMap_ = {};
+ this.currentSelectedRows_ = [];
+ this.sourceIdToRowMap_ = {};
this.tableBody_.innerHTML = '';
this.numPrefilter_ = 0;
this.numPostfilter_ = 0;
@@ -150,11 +148,11 @@ EventsView.prototype.onSecurityStrippingChanged = function() {
* duration or time of first event.
*/
EventsView.compareActive_ = function(source1, source2) {
- if (source1.isActive() && !source2.isActive())
+ if (!source1.isInactive() && source2.isInactive())
return -1;
- if (!source1.isActive() && source2.isActive())
+ if (source1.isInactive() && !source2.isInactive())
return 1;
- if (!source1.isActive()) {
+ if (source1.isInactive()) {
var deltaEndTime = source1.getEndTime() - source2.getEndTime();
if (deltaEndTime != 0) {
// The one that ended most recently (Highest end time) should be sorted
@@ -237,17 +235,19 @@ EventsView.comparisonFunctionTable_ = {
EventsView.prototype.Sort_ = function() {
var sourceEntries = [];
- for (var id in this.sourceIdToEntryMap_) {
- // Can only sort items with an actual row in the table.
- if (this.sourceIdToEntryMap_[id].hasRow())
- sourceEntries.push(this.sourceIdToEntryMap_[id]);
+ for (var id in this.sourceIdToRowMap_) {
+ sourceEntries.push(this.sourceIdToRowMap_[id].getSourceEntry());
}
sourceEntries.sort(this.comparisonFuncWithReversing_.bind(this));
+ // Reposition source rows from back to front.
for (var i = sourceEntries.length - 2; i >= 0; --i) {
- if (sourceEntries[i].getNextNodeSourceId() !=
- sourceEntries[i + 1].getSourceId())
- sourceEntries[i].moveBefore(sourceEntries[i + 1]);
+ var sourceRow = this.sourceIdToRowMap_[sourceEntries[i].getSourceId()];
+ var nextSourceId = sourceEntries[i + 1].getSourceId();
+ if (sourceRow.getNextNodeSourceId() != nextSourceId) {
+ var nextSourceRow = this.sourceIdToRowMap_[nextSourceId];
+ sourceRow.moveBefore(nextSourceRow);
+ }
}
};
@@ -404,94 +404,95 @@ EventsView.prototype.setFilter_ = function(filterText) {
this.currentFilter_ = this.createFilter_(filterText);
// Iterate through all of the rows and see if they match the filter.
- for (var id in this.sourceIdToEntryMap_) {
- var entry = this.sourceIdToEntryMap_[id];
+ for (var id in this.sourceIdToRowMap_) {
+ var entry = this.sourceIdToRowMap_[id];
entry.setIsMatchedByFilter(entry.matchesFilter(this.currentFilter_));
}
};
/**
- * Repositions |sourceEntry|'s row in the table using an insertion sort.
+ * Repositions |sourceRow|'s in the table using an insertion sort.
* Significantly faster than sorting the entire table again, when only
* one entry has changed.
*/
-EventsView.prototype.InsertionSort_ = function(sourceEntry) {
- // SourceEntry that should be after |sourceEntry|, if it needs
+EventsView.prototype.InsertionSort_ = function(sourceRow) {
+ // SourceRow that should be after |sourceRow|, if it needs
// to be moved earlier in the list.
- var sourceEntryAfter = sourceEntry;
+ var sourceRowAfter = sourceRow;
while (true) {
- var prevSourceId = sourceEntryAfter.getPreviousNodeSourceId();
+ var prevSourceId = sourceRowAfter.getPreviousNodeSourceId();
if (prevSourceId == null)
break;
- var prevSourceEntry = this.sourceIdToEntryMap_[prevSourceId];
- if (this.comparisonFuncWithReversing_(sourceEntry, prevSourceEntry) >= 0)
+ var prevSourceRow = this.sourceIdToRowMap_[prevSourceId];
+ if (this.comparisonFuncWithReversing_(
+ sourceRow.getSourceEntry(),
+ prevSourceRow.getSourceEntry()) >= 0) {
break;
- sourceEntryAfter = prevSourceEntry;
+ }
+ sourceRowAfter = prevSourceRow;
}
- if (sourceEntryAfter != sourceEntry) {
- sourceEntry.moveBefore(sourceEntryAfter);
+ if (sourceRowAfter != sourceRow) {
+ sourceRow.moveBefore(sourceRowAfter);
return;
}
- var sourceEntryBefore = sourceEntry;
+ var sourceRowBefore = sourceRow;
while (true) {
- var nextSourceId = sourceEntryBefore.getNextNodeSourceId();
+ var nextSourceId = sourceRowBefore.getNextNodeSourceId();
if (nextSourceId == null)
break;
- var nextSourceEntry = this.sourceIdToEntryMap_[nextSourceId];
- if (this.comparisonFuncWithReversing_(sourceEntry, nextSourceEntry) <= 0)
+ var nextSourceRow = this.sourceIdToRowMap_[nextSourceId];
+ if (this.comparisonFuncWithReversing_(
+ sourceRow.getSourceEntry(),
+ nextSourceRow.getSourceEntry()) <= 0) {
break;
- sourceEntryBefore = nextSourceEntry;
+ }
+ sourceRowBefore = nextSourceRow;
}
- if (sourceEntryBefore != sourceEntry)
- sourceEntry.moveAfter(sourceEntryBefore);
+ if (sourceRowBefore != sourceRow)
+ sourceRow.moveAfter(sourceRowBefore);
};
-EventsView.prototype.onLogEntryAdded = function(logEntry) {
- var id = logEntry.source.id;
+EventsView.prototype.onSourceEntryUpdated = function(sourceEntry) {
+ // Lookup the row.
+ var sourceRow = this.sourceIdToRowMap_[sourceEntry.getSourceId()];
- // Lookup the source.
- var sourceEntry = this.sourceIdToEntryMap_[id];
-
- if (!sourceEntry) {
- sourceEntry = new SourceEntry(this, this.maxReceivedSourceId_);
- this.sourceIdToEntryMap_[id] = sourceEntry;
- this.incrementPrefilterCount(1);
- if (id > this.maxReceivedSourceId_)
- this.maxReceivedSourceId_ = id;
+ if (!sourceRow) {
+ sourceRow = new SourceRow(this, sourceEntry);
+ this.sourceIdToRowMap_[sourceEntry.getSourceId()] = sourceRow;
}
- sourceEntry.update(logEntry);
+ sourceRow.onSourceUpdated();
- if (sourceEntry.isSelected())
+ if (sourceRow.isSelected())
this.invalidateDetailsView_();
// TODO(mmenke): Fix sorting when sorting by duration.
// Duration continuously increases for all entries that are
// still active. This can result in incorrect sorting, until
// Sort_ is called.
- this.InsertionSort_(sourceEntry);
+ this.InsertionSort_(sourceRow);
};
/**
- * Returns the SourceEntry with the specified ID, if there is one.
+ * Returns the SourceRow with the specified ID, if there is one.
* Otherwise, returns undefined.
*/
-EventsView.prototype.getSourceEntry = function(id) {
- return this.sourceIdToEntryMap_[id];
+EventsView.prototype.getSourceRow = function(id) {
+ return this.sourceIdToRowMap_[id];
};
/**
* Called whenever some log events are deleted. |sourceIds| lists
* the source IDs of all deleted log entries.
*/
-EventsView.prototype.onLogEntriesDeleted = function(sourceIds) {
+EventsView.prototype.onSourceEntriesDeleted = function(sourceIds) {
for (var i = 0; i < sourceIds.length; ++i) {
var id = sourceIds[i];
- var entry = this.sourceIdToEntryMap_[id];
- if (entry) {
- entry.remove();
- delete this.sourceIdToEntryMap_[id];
+ var sourceRow = this.sourceIdToRowMap_[id];
+ if (sourceRow) {
+ sourceRow.remove();
+ delete this.sourceIdToRowMap_[id];
this.incrementPrefilterCount(-1);
}
}
@@ -500,7 +501,7 @@ EventsView.prototype.onLogEntriesDeleted = function(sourceIds) {
/**
* Called whenever all log events are deleted.
*/
-EventsView.prototype.onAllLogEntriesDeleted = function() {
+EventsView.prototype.onAllSourceEntriesDeleted = function() {
this.initializeSourceList_();
};
@@ -532,8 +533,8 @@ EventsView.prototype.onSelectionChanged = function() {
};
EventsView.prototype.clearSelection = function() {
- var prevSelection = this.currentSelectedSources_;
- this.currentSelectedSources_ = [];
+ var prevSelection = this.currentSelectedRows_;
+ this.currentSelectedRows_ = [];
// Unselect everything that is currently selected.
for (var i = 0; i < prevSelection.length; ++i) {
@@ -545,25 +546,25 @@ EventsView.prototype.clearSelection = function() {
EventsView.prototype.deleteSelected_ = function() {
var sourceIds = [];
- for (var i = 0; i < this.currentSelectedSources_.length; ++i) {
- var entry = this.currentSelectedSources_[i];
- sourceIds.push(entry.getSourceId());
+ for (var i = 0; i < this.currentSelectedRows_.length; ++i) {
+ var sourceRow = this.currentSelectedRows_[i];
+ sourceIds.push(sourceRow.getSourceEntry().getSourceId());
}
- g_browser.deleteEventsBySourceId(sourceIds);
+ g_browser.sourceTracker.deleteSourceEntries(sourceIds);
};
EventsView.prototype.selectAll_ = function(event) {
- for (var id in this.sourceIdToEntryMap_) {
- var entry = this.sourceIdToEntryMap_[id];
- if (entry.isMatchedByFilter()) {
- entry.setSelected(true);
+ for (var id in this.sourceIdToRowMap_) {
+ var sourceRow = this.sourceIdToRowMap_[id];
+ if (sourceRow.isMatchedByFilter()) {
+ sourceRow.setSelected(true);
}
}
event.preventDefault();
};
EventsView.prototype.unselectAll_ = function() {
- var entries = this.currentSelectedSources_.slice(0);
+ var entries = this.currentSelectedRows_.slice(0);
for (var i = 0; i < entries.length; ++i) {
entries[i].setSelected(false);
}
@@ -611,11 +612,11 @@ EventsView.prototype.sortByDescription_ = function(event) {
};
EventsView.prototype.modifySelectionArray = function(
- sourceEntry, addToSelection) {
+ sourceRow, addToSelection) {
// Find the index for |sourceEntry| in the current selection list.
var index = -1;
- for (var i = 0; i < this.currentSelectedSources_.length; ++i) {
- if (this.currentSelectedSources_[i] == sourceEntry) {
+ for (var i = 0; i < this.currentSelectedRows_.length; ++i) {
+ if (this.currentSelectedRows_[i] == sourceRow) {
index = i;
break;
}
@@ -623,16 +624,24 @@ EventsView.prototype.modifySelectionArray = function(
if (index != -1 && !addToSelection) {
// Remove from the selection.
- this.currentSelectedSources_.splice(index, 1);
+ this.currentSelectedRows_.splice(index, 1);
}
if (index == -1 && addToSelection) {
- this.currentSelectedSources_.push(sourceEntry);
+ this.currentSelectedRows_.push(sourceRow);
+ }
+};
+
+EventsView.prototype.getSelectedSourceEntries_ = function() {
+ var sourceEntries = [];
+ for (var id in this.currentSelectedRows_) {
+ sourceEntries.push(this.currentSelectedRows_[id].getSourceEntry());
}
+ return sourceEntries;
};
EventsView.prototype.invalidateDetailsView_ = function() {
- this.detailsView_.setData(this.currentSelectedSources_);
+ this.detailsView_.setData(this.getSelectedSourceEntries_());
};
EventsView.prototype.invalidateFilterCounter_ = function() {
diff --git a/chrome/browser/resources/net_internals/index.html b/chrome/browser/resources/net_internals/index.html
index f48a57d..6dd6be23 100644
--- a/chrome/browser/resources/net_internals/index.html
+++ b/chrome/browser/resources/net_internals/index.html
@@ -10,6 +10,7 @@ found in the LICENSE file.
<link rel="stylesheet" href="main.css">
<link rel="stylesheet" href="tabswitcherview.css">
<script src="chrome://resources/js/util.js"></script>
+ <script src="chrome://resources/js/cr.js"></script>
<script src="chrome://net-internals/index.js"></script>
<script src="chrome://net-internals/strings.js"></script>
</head>
diff --git a/chrome/browser/resources/net_internals/index.js b/chrome/browser/resources/net_internals/index.js
index 8197867..bc42978 100644
--- a/chrome/browser/resources/net_internals/index.js
+++ b/chrome/browser/resources/net_internals/index.js
@@ -9,9 +9,11 @@
<include src="httpcacheview.js"/>
<include src="testview.js"/>
<include src="hstsview.js"/>
-<include src="logdumputil.js"/>
+<include src="browserbridge.js"/>
+<include src="sourcetracker.js"/>
<include src="main.js"/>
<include src="dnsview.js"/>
+<include src="sourcerow.js"/>
<include src="eventsview.js"/>
<include src="detailsview.js"/>
<include src="sourceentry.js"/>
diff --git a/chrome/browser/resources/net_internals/logdumputil.js b/chrome/browser/resources/net_internals/logdumputil.js
deleted file mode 100644
index cb48c59..0000000
--- a/chrome/browser/resources/net_internals/logdumputil.js
+++ /dev/null
@@ -1,229 +0,0 @@
-// 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, fileName) {
- // 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(fileName);
-
- // 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, fileName) {
- // 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, fileName);
-};
-
-// End of anonymous namespace.
-})();
-
diff --git a/chrome/browser/resources/net_internals/logviewpainter.js b/chrome/browser/resources/net_internals/logviewpainter.js
index b95cb5d..f37e593 100644
--- a/chrome/browser/resources/net_internals/logviewpainter.js
+++ b/chrome/browser/resources/net_internals/logviewpainter.js
@@ -38,7 +38,7 @@ function addSourceEntry_(node, sourceEntry) {
var nobr2 = addNode(p2, 'nobr');
var logEntries = sourceEntry.getLogEntries();
- var startDate = g_browser.convertTimeTicksToDate(logEntries[0].time);
+ var startDate = convertTimeTicksToDate(logEntries[0].time);
addTextNode(nobr2, 'Start Time: ' + startDate.toLocaleString());
var pre = addNode(div, 'pre');
@@ -60,7 +60,7 @@ PrintSourceEntriesAsText = function(sourceEntries) {
if (entries.length == 0)
return '';
- var startDate = g_browser.convertTimeTicksToDate(entries[0].orig.time);
+ var startDate = convertTimeTicksToDate(entries[0].orig.time);
var startTime = startDate.getTime();
var tablePrinter = new TablePrinter();
@@ -77,7 +77,7 @@ PrintSourceEntriesAsText = function(sourceEntries) {
tablePrinter.addCell(entry.orig.wasPassivelyCaptured ? '(P) ' : '');
tablePrinter.addCell('t=');
- var date = g_browser.convertTimeTicksToDate(entry.orig.time) ;
+ var date = convertTimeTicksToDate(entry.orig.time) ;
var tCell = tablePrinter.addCell(date.getTime());
tCell.alignRight = true;
tablePrinter.addCell(' [st=');
@@ -112,7 +112,7 @@ PrintSourceEntriesAsText = function(sourceEntries) {
// Add a continuation row for each line of text from the extra parameters.
var extraParamsText = getTextForExtraParams(
entry.orig,
- g_browser.getSecurityStripping());
+ g_browser.sourceTracker.getSecurityStripping());
var extraParamsTextLines = extraParamsText.split('\n');
for (var j = 0; j < extraParamsTextLines.length; ++j) {
diff --git a/chrome/browser/resources/net_internals/main.js b/chrome/browser/resources/net_internals/main.js
index c53bf2e..4d7c5d9 100644
--- a/chrome/browser/resources/net_internals/main.js
+++ b/chrome/browser/resources/net_internals/main.js
@@ -14,9 +14,6 @@ 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}
@@ -24,14 +21,42 @@ var Constants = null;
var g_browser = null;
/**
+ * The browser gives us times in terms of "time ticks" in milliseconds.
+ * This function converts the tick count to a Date() object.
+ *
+ * @param {String} timeTicks.
+ * @returns {Date} The time that |timeTicks| represents.
+ */
+var convertTimeTicksToDate;
+
+/**
+ * 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.
+ */
+var createLogDumpAsync;
+
+/**
+ * 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.
+ */
+var loadLogFile;
+
+// Start of annonymous namespace.
+(function() {
+
+var categoryTabSwitcher;
+
+/**
* Main entry point. called once the page has loaded.
*/
-function onLoaded() {
+onLoaded = function() {
g_browser = new BrowserBridge();
// Create a view which lets you tab between the different sub-views.
// This view is a left (resizable) navigation bar.
- var categoryTabSwitcher = new TabSwitcherView();
+ categoryTabSwitcher = new TabSwitcherView();
var tabSwitcherSplitView = new ResizableVerticalSplitView(
new DivView('categoryTabHandles'),
categoryTabSwitcher,
@@ -43,8 +68,6 @@ function onLoaded() {
// name rather than using a fixed width.
tabSwitcherSplitView.setLeftSplit(150);
- g_browser.setTabSwitcher(categoryTabSwitcher);
-
// 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.
@@ -56,13 +79,13 @@ function onLoaded() {
categoryTabSwitcher.addTab('httpCacheTab', new HttpCacheView(), false, true);
categoryTabSwitcher.addTab('dataTab', new DataView(), false, true);
categoryTabSwitcher.addTab('serviceProvidersTab', new ServiceProvidersView(),
- false, g_browser.isPlatformWindows());
+ false, cr.isWindows);
categoryTabSwitcher.addTab('testTab', new TestView(), false, true);
categoryTabSwitcher.addTab('hstsTab', new HSTSView(), false, true);
categoryTabSwitcher.addTab('httpThrottlingTab', new HttpThrottlingView(),
false, true);
categoryTabSwitcher.addTab('logsTab', new LogsView(), false,
- g_browser.isChromeOS());
+ cr.isChromeOS);
categoryTabSwitcher.addTab('prerenderTab', new PrerenderView(), false, true);
// Build a map from the anchor name of each tab handle to its "tab ID".
@@ -76,8 +99,7 @@ function onLoaded() {
// Default the empty hash to the data tab.
anchorMap['#'] = anchorMap[''] = 'dataTab';
- window.onhashchange = onUrlHashChange.bind(null, anchorMap,
- categoryTabSwitcher);
+ window.onhashchange = onUrlHashChange.bind(null, anchorMap);
// Cut out a small vertical strip at the top of the window, to display
// a high level status (i.e. if we are capturing events, or displaying a
@@ -94,75 +116,11 @@ function onLoaded() {
// Select the initial view based on the current URL.
window.onhashchange();
+ g_browser.addConstantsObserver(new ConstantsObserver());
+
// Tell the browser that we are ready to start receiving log events.
g_browser.sendReady();
-}
-
-/**
- * This class provides a "bridge" for communicating between the javascript and
- * the browser.
- *
- * @constructor
- */
-function BrowserBridge() {
- // List of observers for various bits of browser state.
- this.logObservers_ = [];
- this.connectionTestsObservers_ = [];
- 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',
- this.sendGetProxySettings.bind(this));
- this.pollableDataHelpers_.badProxies =
- new PollableDataHelper('onBadProxiesChanged',
- this.sendGetBadProxies.bind(this));
- this.pollableDataHelpers_.httpCacheInfo =
- new PollableDataHelper('onHttpCacheInfoChanged',
- this.sendGetHttpCacheInfo.bind(this));
- this.pollableDataHelpers_.hostResolverInfo =
- new PollableDataHelper('onHostResolverInfoChanged',
- this.sendGetHostResolverInfo.bind(this));
- this.pollableDataHelpers_.socketPoolInfo =
- new PollableDataHelper('onSocketPoolInfoChanged',
- this.sendGetSocketPoolInfo.bind(this));
- this.pollableDataHelpers_.spdySessionInfo =
- new PollableDataHelper('onSpdySessionInfoChanged',
- this.sendGetSpdySessionInfo.bind(this));
- this.pollableDataHelpers_.spdyStatus =
- new PollableDataHelper('onSpdyStatusChanged',
- this.sendGetSpdyStatus.bind(this));
- this.pollableDataHelpers_.spdyAlternateProtocolMappings =
- new PollableDataHelper('onSpdyAlternateProtocolMappingsChanged',
- this.sendGetSpdyAlternateProtocolMappings.bind(
- this));
- if (this.isPlatformWindows()) {
- this.pollableDataHelpers_.serviceProviders =
- new PollableDataHelper('onServiceProvidersChanged',
- this.sendGetServiceProviders.bind(this));
- }
- this.pollableDataHelpers_.prerenderInfo =
- new PollableDataHelper('onPrerenderInfoChanged',
- this.sendGetPrerenderInfo.bind(this));
-
- // Cache of the data received.
- this.numPassivelyCapturedEvents_ = 0;
- this.capturedEvents_ = [];
-
- // Next unique id to be assigned to a log entry without a source.
- // Needed to simplify deletion, identify associated GUI elements, etc.
- this.nextSourcelessEventId_ = -1;
-
- // True when cookies and authentication information should be removed from
- // displayed events. When true, such information should be hidden from
- // all pages.
- this.enableSecurityStripping_ = true;
-}
+};
/*
* Takes the current hash in form of "#tab&param1=value1&param2=value2&...".
@@ -172,7 +130,7 @@ function BrowserBridge() {
*
* Parameters and values are decoded with decodeURIComponent().
*/
-function onUrlHashChange(anchorMap, categoryTabSwitcher) {
+function onUrlHashChange(anchorMap) {
var parameters = window.location.hash.split('&');
var tabId = anchorMap[parameters[0]];
@@ -196,329 +154,6 @@ 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;
-
-//------------------------------------------------------------------------------
-// Messages sent to the browser
-//------------------------------------------------------------------------------
-
-/**
- * Wraps |chrome.send|. Doesn't send anything when viewing a log file.
- */
-BrowserBridge.prototype.send = function(value1, value2) {
- if (!this.isViewingLogFile_) {
- if (arguments.length == 1) {
- chrome.send(value1);
- } else if (arguments.length == 2) {
- chrome.send(value1, value2);
- } else {
- throw 'Unsupported number of arguments.';
- }
- }
-};
-
-BrowserBridge.prototype.sendReady = function() {
- 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
- // the observers.
- window.setInterval(this.checkForUpdatedInfo.bind(this, false),
- BrowserBridge.POLL_INTERVAL_MS);
-};
-
-BrowserBridge.prototype.isPlatformWindows = function() {
- return /Win/.test(navigator.platform);
-};
-
-BrowserBridge.prototype.isChromeOS = function() {
- return /CrOS/.test(navigator.userAgent);
-};
-
-BrowserBridge.prototype.sendGetProxySettings = function() {
- // The browser will call receivedProxySettings on completion.
- this.send('getProxySettings');
-};
-
-BrowserBridge.prototype.sendReloadProxySettings = function() {
- this.send('reloadProxySettings');
-};
-
-BrowserBridge.prototype.sendGetBadProxies = function() {
- // The browser will call receivedBadProxies on completion.
- this.send('getBadProxies');
-};
-
-BrowserBridge.prototype.sendGetHostResolverInfo = function() {
- // The browser will call receivedHostResolverInfo on completion.
- this.send('getHostResolverInfo');
-};
-
-BrowserBridge.prototype.sendClearBadProxies = function() {
- this.send('clearBadProxies');
-};
-
-BrowserBridge.prototype.sendClearHostResolverCache = function() {
- this.send('clearHostResolverCache');
-};
-
-BrowserBridge.prototype.sendStartConnectionTests = function(url) {
- this.send('startConnectionTests', [url]);
-};
-
-BrowserBridge.prototype.sendHSTSQuery = function(domain) {
- this.send('hstsQuery', [domain]);
-};
-
-BrowserBridge.prototype.sendHSTSAdd = function(domain,
- include_subdomains,
- pins) {
- this.send('hstsAdd', [domain, include_subdomains, pins]);
-};
-
-BrowserBridge.prototype.sendHSTSDelete = function(domain) {
- this.send('hstsDelete', [domain]);
-};
-
-BrowserBridge.prototype.sendGetHttpCacheInfo = function() {
- this.send('getHttpCacheInfo');
-};
-
-BrowserBridge.prototype.sendGetSocketPoolInfo = function() {
- this.send('getSocketPoolInfo');
-};
-
-BrowserBridge.prototype.sendCloseIdleSockets = function() {
- this.send('closeIdleSockets');
-};
-
-BrowserBridge.prototype.sendFlushSocketPools = function() {
- this.send('flushSocketPools');
-};
-
-BrowserBridge.prototype.sendGetSpdySessionInfo = function() {
- this.send('getSpdySessionInfo');
-};
-
-BrowserBridge.prototype.sendGetSpdyStatus = function() {
- this.send('getSpdyStatus');
-};
-
-BrowserBridge.prototype.sendGetSpdyAlternateProtocolMappings = function() {
- this.send('getSpdyAlternateProtocolMappings');
-};
-
-BrowserBridge.prototype.sendGetServiceProviders = function() {
- this.send('getServiceProviders');
-};
-
-BrowserBridge.prototype.sendGetPrerenderInfo = function() {
- this.send('getPrerenderInfo');
-};
-
-BrowserBridge.prototype.enableIPv6 = function() {
- this.send('enableIPv6');
-};
-
-BrowserBridge.prototype.setLogLevel = function(logLevel) {
- this.send('setLogLevel', ['' + logLevel]);
-};
-
-BrowserBridge.prototype.enableHttpThrottling = function(enable) {
- this.send('enableHttpThrottling', [enable]);
-};
-
-BrowserBridge.prototype.refreshSystemLogs = function() {
- this.send('refreshSystemLogs');
-};
-
-BrowserBridge.prototype.getSystemLog = function(log_key, cellId) {
- this.send('getSystemLog', [log_key, cellId]);
-};
-
-//------------------------------------------------------------------------------
-// Messages received from the browser.
-//------------------------------------------------------------------------------
-
-BrowserBridge.prototype.receive = function(command, params) {
- // Does nothing if viewing a log file.
- if (this.isViewingLogFile_)
- return;
- this[command](params);
-};
-
-BrowserBridge.prototype.receivedConstants = function(constants) {
- this.logFormatVersion_ = constants.logFormatVersion;
-
- this.loadConstants(constants);
-};
-
-BrowserBridge.prototype.receivedLogEntries = function(logEntries) {
- this.addLogEntries(logEntries);
-};
-
-BrowserBridge.prototype.receivedProxySettings = function(proxySettings) {
- this.pollableDataHelpers_.proxySettings.update(proxySettings);
-};
-
-BrowserBridge.prototype.receivedBadProxies = function(badProxies) {
- this.pollableDataHelpers_.badProxies.update(badProxies);
-};
-
-BrowserBridge.prototype.receivedHostResolverInfo =
-function(hostResolverInfo) {
- this.pollableDataHelpers_.hostResolverInfo.update(hostResolverInfo);
-};
-
-BrowserBridge.prototype.receivedSocketPoolInfo = function(socketPoolInfo) {
- this.pollableDataHelpers_.socketPoolInfo.update(socketPoolInfo);
-};
-
-BrowserBridge.prototype.receivedSpdySessionInfo = function(spdySessionInfo) {
- this.pollableDataHelpers_.spdySessionInfo.update(spdySessionInfo);
-};
-
-BrowserBridge.prototype.receivedSpdyStatus = function(spdyStatus) {
- this.pollableDataHelpers_.spdyStatus.update(spdyStatus);
-};
-
-BrowserBridge.prototype.receivedSpdyAlternateProtocolMappings =
- function(spdyAlternateProtocolMappings) {
- this.pollableDataHelpers_.spdyAlternateProtocolMappings.update(
- spdyAlternateProtocolMappings);
-};
-
-BrowserBridge.prototype.receivedServiceProviders = function(serviceProviders) {
- this.pollableDataHelpers_.serviceProviders.update(serviceProviders);
-};
-
-BrowserBridge.prototype.receivedPassiveLogEntries = function(entries) {
- // Due to an expected race condition, it is possible to receive actively
- // captured log entries before the passively logged entries are received.
- //
- // When that happens, we create a copy of the actively logged entries, delete
- // all entries, and, after handling all the passively logged entries, add back
- // the deleted actively logged entries.
- var earlyActivelyCapturedEvents = this.capturedEvents_.slice(0);
- if (earlyActivelyCapturedEvents.length > 0)
- this.deleteAllEvents();
-
- this.numPassivelyCapturedEvents_ = entries.length;
- for (var i = 0; i < entries.length; ++i)
- entries[i].wasPassivelyCaptured = true;
- this.receivedLogEntries(entries);
-
- // Add back early actively captured events, if any.
- if (earlyActivelyCapturedEvents.length)
- this.receivedLogEntries(earlyActivelyCapturedEvents);
-};
-
-
-BrowserBridge.prototype.receivedStartConnectionTestSuite = function() {
- for (var i = 0; i < this.connectionTestsObservers_.length; ++i)
- this.connectionTestsObservers_[i].onStartedConnectionTestSuite();
-};
-
-BrowserBridge.prototype.receivedStartConnectionTestExperiment = function(
- experiment) {
- for (var i = 0; i < this.connectionTestsObservers_.length; ++i) {
- this.connectionTestsObservers_[i].onStartedConnectionTestExperiment(
- experiment);
- }
-};
-
-BrowserBridge.prototype.receivedCompletedConnectionTestExperiment =
-function(info) {
- for (var i = 0; i < this.connectionTestsObservers_.length; ++i) {
- this.connectionTestsObservers_[i].onCompletedConnectionTestExperiment(
- info.experiment, info.result);
- }
-};
-
-BrowserBridge.prototype.receivedCompletedConnectionTestSuite = function() {
- for (var i = 0; i < this.connectionTestsObservers_.length; ++i)
- this.connectionTestsObservers_[i].onCompletedConnectionTestSuite();
-};
-
-BrowserBridge.prototype.receivedHSTSResult = function(info) {
- for (var i = 0; i < this.hstsObservers_.length; ++i)
- this.hstsObservers_[i].onHSTSQueryResult(info);
-};
-
-BrowserBridge.prototype.receivedHttpCacheInfo = function(info) {
- this.pollableDataHelpers_.httpCacheInfo.update(info);
-};
-
-BrowserBridge.prototype.receivedHttpThrottlingEnabledPrefChanged = function(
- enabled) {
- for (var i = 0; i < this.httpThrottlingObservers_.length; ++i) {
- this.httpThrottlingObservers_[i].onHttpThrottlingEnabledPrefChanged(
- enabled);
- }
-};
-
-BrowserBridge.prototype.receivedPrerenderInfo = function(prerenderInfo) {
- this.pollableDataHelpers_.prerenderInfo.update(prerenderInfo);
-};
-
-//------------------------------------------------------------------------------
-
-BrowserBridge.prototype.categoryTabSwitcher = function() {
- return this.categoryTabSwitcher_;
-};
-
-BrowserBridge.prototype.logFormatVersion = function() {
- return this.logFormatVersion_;
-};
-
-BrowserBridge.prototype.isViewingLogFile = function() {
- return this.isViewingLogFile_;
-};
-
-/**
* 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
@@ -526,460 +161,284 @@ BrowserBridge.prototype.isViewingLogFile = function() {
*
* @param {String} fileName The name of the log file that has been loaded.
*/
-BrowserBridge.prototype.onLoadLogFile = function(fileName) {
- if (!this.isViewingLogFile_) {
- this.isViewingLogFile_ = true;
- this.setSecurityStripping(false);
-
- // Swap out the status bar to indicate we have loaded from a file.
- setNodeDisplay($('statusViewForCapture'), false);
- setNodeDisplay($('statusViewForFile'), true);
-
- // Indicate which file is being displayed.
- $('statusViewDumpFileName').innerText = fileName;
-
- document.styleSheets[0].insertRule('.hideOnLoadLog { display: none; }');
- }
-};
-
-/**
- * Sets the |categoryTabSwitcher_| of BrowserBridge. Since views depend on
- * g_browser being initialized, have to have a BrowserBridge prior to tab
- * construction.
- */
-BrowserBridge.prototype.setTabSwitcher = function(categoryTabSwitcher) {
- this.categoryTabSwitcher_ = categoryTabSwitcher;
-};
-
-/**
- * Adds a listener of log entries. |observer| will be called back when new log
- * data arrives, through:
- *
- * observer.onLogEntryAdded(logEntry)
- */
-BrowserBridge.prototype.addLogObserver = function(observer) {
- this.logObservers_.push(observer);
-};
-
-/**
- * Adds a listener of the proxy settings. |observer| will be called back when
- * data is received, through:
- *
- * observer.onProxySettingsChanged(proxySettings)
- *
- * |proxySettings| is a dictionary with (up to) two properties:
- *
- * "original" -- The settings that chrome was configured to use
- * (i.e. system settings.)
- * "effective" -- The "effective" proxy settings that chrome is using.
- * (decides between the manual/automatic modes of the
- * fetched settings).
- *
- * Each of these two configurations is formatted as a string, and may be
- * omitted if not yet initialized.
- *
- * TODO(eroman): send a dictionary instead.
- */
-BrowserBridge.prototype.addProxySettingsObserver = function(observer) {
- this.pollableDataHelpers_.proxySettings.addObserver(observer);
-};
+function onLoadLogFile(fileName) {
+ // Swap out the status bar to indicate we have loaded from a file.
+ setNodeDisplay($('statusViewForCapture'), false);
+ setNodeDisplay($('statusViewForFile'), true);
-/**
- * Adds a listener of the proxy settings. |observer| will be called back when
- * data is received, through:
- *
- * observer.onBadProxiesChanged(badProxies)
- *
- * |badProxies| is an array, where each entry has the property:
- * badProxies[i].proxy_uri: String identify the proxy.
- * badProxies[i].bad_until: The time when the proxy stops being considered
- * bad. Note the time is in time ticks.
- */
-BrowserBridge.prototype.addBadProxiesObserver = function(observer) {
- this.pollableDataHelpers_.badProxies.addObserver(observer);
-};
+ // Indicate which file is being displayed.
+ $('statusViewDumpFileName').innerText = fileName;
-/**
- * Adds a listener of the host resolver info. |observer| will be called back
- * when data is received, through:
- *
- * observer.onHostResolverInfoChanged(hostResolverInfo)
- */
-BrowserBridge.prototype.addHostResolverInfoObserver = function(observer) {
- this.pollableDataHelpers_.hostResolverInfo.addObserver(observer);
-};
-
-/**
- * Adds a listener of the socket pool. |observer| will be called back
- * when data is received, through:
- *
- * observer.onSocketPoolInfoChanged(socketPoolInfo)
- */
-BrowserBridge.prototype.addSocketPoolInfoObserver = function(observer) {
- this.pollableDataHelpers_.socketPoolInfo.addObserver(observer);
-};
+ document.styleSheets[0].insertRule('.hideOnLoadLog { display: none; }');
-/**
- * Adds a listener of the SPDY info. |observer| will be called back
- * when data is received, through:
- *
- * observer.onSpdySessionInfoChanged(spdySessionInfo)
- */
-BrowserBridge.prototype.addSpdySessionInfoObserver = function(observer) {
- this.pollableDataHelpers_.spdySessionInfo.addObserver(observer);
-};
-
-/**
- * Adds a listener of the SPDY status. |observer| will be called back
- * when data is received, through:
- *
- * observer.onSpdyStatusChanged(spdyStatus)
- */
-BrowserBridge.prototype.addSpdyStatusObserver = function(observer) {
- this.pollableDataHelpers_.spdyStatus.addObserver(observer);
-};
+ g_browser.sourceTracker.setSecurityStripping(false);
+ g_browser.disable();
+}
/**
- * Adds a listener of the AlternateProtocolMappings. |observer| will be called
- * back when data is received, through:
- *
- * observer.onSpdyAlternateProtocolMappingsChanged(
- * spdyAlternateProtocolMappings)
+ * Returns true if |constants| appears to be a valid constants object.
*/
-BrowserBridge.prototype.addSpdyAlternateProtocolMappingsObserver =
- function(observer) {
- this.pollableDataHelpers_.spdyAlternateProtocolMappings.addObserver(observer);
-};
+function areValidConstants(receivedConstants) {
+ return typeof(receivedConstants) == 'object' &&
+ typeof(receivedConstants.logEventTypes) == 'object' &&
+ typeof(receivedConstants.clientInfo) == 'object' &&
+ typeof(receivedConstants.logEventPhase) == 'object' &&
+ typeof(receivedConstants.logSourceType) == 'object' &&
+ typeof(receivedConstants.logLevelType) == 'object' &&
+ typeof(receivedConstants.loadFlag) == 'object' &&
+ typeof(receivedConstants.netError) == 'object' &&
+ typeof(receivedConstants.addressFamily) == 'object' &&
+ typeof(receivedConstants.timeTickOffset) == 'string' &&
+ typeof(receivedConstants.logFormatVersion) == 'number';
+}
/**
- * Adds a listener of the service providers info. |observer| will be called
- * 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.
+ * Dictionary of all constants, used for saving log files.
*/
-BrowserBridge.prototype.addServiceProvidersObserver = function(observer) {
- if (this.pollableDataHelpers_.serviceProviders)
- this.pollableDataHelpers_.serviceProviders.addObserver(observer);
-};
+var constants = null;
/**
- * Adds a listener for the progress of the connection tests.
- * The observer will be called back with:
- *
- * observer.onStartedConnectionTestSuite();
- * observer.onStartedConnectionTestExperiment(experiment);
- * observer.onCompletedConnectionTestExperiment(experiment, result);
- * observer.onCompletedConnectionTestSuite();
+ * Offset needed to convert event times to Date objects.
*/
-BrowserBridge.prototype.addConnectionTestsObserver = function(observer) {
- this.connectionTestsObservers_.push(observer);
-};
+var timeTickOffset = 0;
-/**
- * Adds a listener for the http cache info results.
- * The observer will be called back with:
- *
- * observer.onHttpCacheInfoChanged(info);
- */
-BrowserBridge.prototype.addHttpCacheInfoObserver = function(observer) {
- this.pollableDataHelpers_.httpCacheInfo.addObserver(observer);
-};
+function ConstantsObserver() {}
/**
- * Adds a listener for the results of HSTS (HTTPS Strict Transport Security)
- * queries. The observer will be called back with:
- *
- * observer.onHSTSQueryResult(result);
+ * 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.addHSTSObserver = function(observer) {
- this.hstsObservers_.push(observer);
-};
+ConstantsObserver.prototype.onReceivedConstants = function(receivedConstants) {
+ if (!areValidConstants(receivedConstants))
+ return false;
-/**
- * Adds a listener for HTTP throttling-related events. |observer| will be called
- * back when HTTP throttling is enabled/disabled, through:
- *
- * observer.onHttpThrottlingEnabledPrefChanged(enabled);
- */
-BrowserBridge.prototype.addHttpThrottlingObserver = function(observer) {
- this.httpThrottlingObservers_.push(observer);
-};
+ constants = receivedConstants;
-/**
- * Adds a listener for updated prerender info events
- * |observer| will be called back with:
- *
- * observer.onPrerenderInfoChanged(prerenderInfo);
- */
-BrowserBridge.prototype.addPrerenderInfoObserver = function(observer) {
- this.pollableDataHelpers_.prerenderInfo.addObserver(observer);
-};
+ LogEventType = constants.logEventTypes;
+ ClientInfo = constants.clientInfo;
+ LogEventPhase = constants.logEventPhase;
+ LogSourceType = constants.logSourceType;
+ LogLevelType = constants.logLevelType;
+ LoadFlag = constants.loadFlag;
+ NetError = constants.netError;
+ AddressFamily = constants.addressFamily;
-/**
- * The browser gives us times in terms of "time ticks" in milliseconds.
- * This function converts the tick count to a Date() object.
- *
- * @param {String} timeTicks.
- * @returns {Date} The time that |timeTicks| represents.
- */
-BrowserBridge.prototype.convertTimeTicksToDate = function(timeTicks) {
// Note that the subtraction by 0 is to cast to a number (probably a float
// since the numbers are big).
- var timeStampMs = (this.timeTickOffset_ - 0) + (timeTicks - 0);
- var d = new Date();
- d.setTime(timeStampMs);
- return d;
-};
+ timeTickOffset = constants.timeTickOffset - 0;
-/**
- * Returns a list of all captured events.
- */
-BrowserBridge.prototype.getAllCapturedEvents = function() {
- return this.capturedEvents_;
-};
-
-/**
- * Returns the number of events that were captured while we were
- * listening for events.
- */
-BrowserBridge.prototype.getNumActivelyCapturedEvents = function() {
- return this.capturedEvents_.length - this.numPassivelyCapturedEvents_;
+ return true;
};
-/**
- * Returns the number of events that were captured passively by the
- * browser prior to when the net-internals page was started.
- */
-BrowserBridge.prototype.getNumPassivelyCapturedEvents = function() {
- return this.numPassivelyCapturedEvents_;
+convertTimeTicksToDate = function(timeTicks) {
+ var timeStampMs = timeTickOffset + (timeTicks - 0);
+ return new Date(timeStampMs);
};
/**
- * Sends each entry to all log observers, and updates |capturedEvents_|.
- * Also assigns unique ids to log entries without a source.
+ * 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.
*/
-BrowserBridge.prototype.addLogEntries = function(logEntries) {
- for (var e = 0; e < logEntries.length; ++e) {
- var logEntry = logEntries[e];
-
- // Assign unique ID, if needed.
- if (logEntry.source.id == 0) {
- logEntry.source.id = this.nextSourcelessEventId_;
- --this.nextSourcelessEventId_;
- }
- this.capturedEvents_.push(logEntry);
- for (var i = 0; i < this.logObservers_.length; ++i)
- this.logObservers_[i].onLogEntryAdded(logEntry);
- }
-};
+function createLogDump(constants, events, polledData, tabData, date) {
+ if (g_browser.sourceTracker.getSecurityStripping())
+ events = events.map(stripCookiesAndLoginInfo);
-/**
- * Deletes captured events with source IDs in |sourceIds|.
- */
-BrowserBridge.prototype.deleteEventsBySourceId = function(sourceIds) {
- var sourceIdDict = {};
- for (var i = 0; i < sourceIds.length; i++)
- sourceIdDict[sourceIds[i]] = true;
-
- var newEventList = [];
- for (var i = 0; i < this.capturedEvents_.length; ++i) {
- var id = this.capturedEvents_[i].source.id;
- if (id in sourceIdDict) {
- if (this.capturedEvents_[i].wasPassivelyCaptured)
- --this.numPassivelyCapturedEvents_;
- continue;
- }
- newEventList.push(this.capturedEvents_[i]);
- }
- this.capturedEvents_ = newEventList;
+ var logDump = {
+ 'constants': constants,
+ 'events': events,
+ 'polledData': polledData,
+ 'tabData': tabData
+ };
- for (var i = 0; i < this.logObservers_.length; ++i)
- this.logObservers_[i].onLogEntriesDeleted(sourceIds);
-};
+ // Not technically client info, but it's used at the same point in the code.
+ if (date && constants.clientInfo)
+ constants.clientInfo.date = date;
-/**
- * Deletes all captured events.
- */
-BrowserBridge.prototype.deleteAllEvents = function() {
- this.capturedEvents_ = [];
- this.numPassivelyCapturedEvents_ = 0;
- for (var i = 0; i < this.logObservers_.length; ++i)
- this.logObservers_[i].onAllLogEntriesDeleted();
-};
+ return logDump;
+}
/**
- * Sets the value of |enableSecurityStripping_| and informs log observers
- * of the change.
+ * Creates a full log dump using |polledData| and the return value of each tab's
+ * saveState function and passes it to |callback|.
*/
-BrowserBridge.prototype.setSecurityStripping =
- function(enableSecurityStripping) {
- this.enableSecurityStripping_ = enableSecurityStripping;
- for (var i = 0; i < this.logObservers_.length; ++i) {
- if (this.logObservers_[i].onSecurityStrippingChanged)
- this.logObservers_[i].onSecurityStrippingChanged();
+function onUpdateAllCompleted(callback, polledData) {
+ // Gather any tab-specific state information.
+ var tabData = {};
+ 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();
}
-};
-
-/**
- * Returns whether or not cookies and authentication information should be
- * displayed for events that contain them.
- */
-BrowserBridge.prototype.getSecurityStripping = function() {
- return this.enableSecurityStripping_;
-};
-/**
- * Returns true if a log file is currently being viewed.
- */
-BrowserBridge.prototype.isViewingLogFile = function() {
- return this.isViewingLogFile_;
-};
+ var logDump = createLogDump(constants,
+ g_browser.sourceTracker.getAllCapturedEvents(),
+ polledData,
+ tabData,
+ (new Date()).toLocaleString());
+ callback(JSON.stringify(logDump, null, ' '));
+}
-/**
- * If |force| is true, calls all startUpdate functions. Otherwise, just
- * runs updates with active observers.
- */
-BrowserBridge.prototype.checkForUpdatedInfo = function(force) {
- for (name in this.pollableDataHelpers_) {
- var helper = this.pollableDataHelpers_[name];
- if (force || helper.hasActiveObserver())
- helper.startUpdate();
+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, fileName) {
+ // 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 (!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 != constants.logFormatVersion) {
+ return 'Unable to load different log version.' +
+ ' Found ' + logDump.constants.logFormatVersion +
+ ', Expected ' + constants.logFormatVersion;
}
-};
+ if (!areValidConstants(logDump.constants))
+ return 'Expected constants not found in log file.';
-/**
- * Calls all startUpdate functions and, if |callback| is non-null,
- * calls it with the results of all updates.
- */
-BrowserBridge.prototype.updateAllInfo = function(callback) {
- if (callback)
- new UpdateAllObserver(callback, this.pollableDataHelpers_);
- this.checkForUpdatedInfo(true);
-};
+ g_browser.receivedConstants(logDump.constants);
-/**
- * This is a helper class used by BrowserBridge, to keep track of:
- * - the list of observers interested in some piece of data.
- * - the last known value of that piece of data.
- * - the name of the callback method to invoke on observers.
- * - the update function.
- * @constructor
- */
-function PollableDataHelper(observerMethodName, startUpdateFunction) {
- this.observerMethodName_ = observerMethodName;
- this.startUpdate = startUpdateFunction;
- this.observerInfos_ = [];
-}
+ // 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.
+ onLoadLogFile(fileName);
-PollableDataHelper.prototype.getObserverMethodName = function() {
- return this.observerMethodName_;
-};
+ // Delete all events. This will also update all logObservers.
+ g_browser.sourceTracker.deleteAllSourceEntries();
-PollableDataHelper.prototype.isObserver = function(object) {
- for (var i = 0; i < this.observerInfos_.length; ++i) {
- if (this.observerInfos_[i].observer == object)
- return true;
+ // Inform all the views that a log file is being loaded, and pass in
+ // view-specific saved state, if any.
+ 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]]);
}
- 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
- * latter is used to make sure that new observers get sent data on the
- * update following their creation.
- * @constructor
- */
-function ObserverInfo(observer) {
- this.observer = observer;
- this.hasReceivedData = false;
-}
-
-PollableDataHelper.prototype.addObserver = function(observer) {
- this.observerInfos_.push(new ObserverInfo(observer));
-};
-PollableDataHelper.prototype.removeObserver = function(observer) {
- for (var i = 0; i < this.observerInfos_.length; ++i) {
- if (this.observerInfos_[i].observer === observer) {
- this.observerInfos_.splice(i, 1);
- return;
+ // 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);
+ }
}
}
-};
-
-/**
- * Helper function to handle calling all the observers, but ONLY if the data has
- * actually changed since last time or the observer has yet to receive any data.
- * This is used for data we received from browser on an update loop.
- */
-PollableDataHelper.prototype.update = function(data) {
- var prevData = this.currentData_;
- var changed = false;
-
- // If the data hasn't changed since last time, will only need to notify
- // observers that have not yet received any data.
- if (!prevData || JSON.stringify(prevData) != JSON.stringify(data)) {
- changed = true;
- this.currentData_ = data;
+ g_browser.sourceTracker.onReceivedPassiveLogEntries(validPassiveEvents);
+ g_browser.sourceTracker.onReceivedLogEntries(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';
}
- // Notify the observers of the change, as needed.
- for (var i = 0; i < this.observerInfos_.length; ++i) {
- var observerInfo = this.observerInfos_[i];
- if (changed || !observerInfo.hasReceivedData) {
- observerInfo.observer[this.observerMethodName_](this.currentData_);
- observerInfo.hasReceivedData = true;
+ // 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);
}
-};
-/**
- * Returns true if one of the observers actively wants the data
- * (i.e. is visible).
- */
-PollableDataHelper.prototype.hasActiveObserver = function() {
- for (var i = 0; i < this.observerInfos_.length; ++i) {
- if (this.observerInfos_[i].observer.isActive())
- return true;
- }
- return false;
-};
+ return errorString + 'Log loaded.';
+}
-/**
- * This is a helper class used by BrowserBridge to send data to
- * a callback once data from all polls has been received.
- *
- * It works by keeping track of how many polling functions have
- * yet to receive data, and recording the data as it it received.
- *
- * @constructor
- */
-function UpdateAllObserver(callback, pollableDataHelpers) {
- this.callback_ = callback;
- this.observingCount_ = 0;
- this.updatedData_ = {};
-
- for (name in pollableDataHelpers) {
- ++this.observingCount_;
- var helper = pollableDataHelpers[name];
- helper.addObserver(this);
- this[helper.getObserverMethodName()] =
- this.onDataReceived_.bind(this, helper, name);
+loadLogFile = function(logFileContents, fileName) {
+ // 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) {
+ }
}
-}
-UpdateAllObserver.prototype.isActive = function() {
- return true;
+ if (!parsedDump)
+ return 'Unable to parse log dump as JSON file.';
+ return loadLogDump(parsedDump, fileName);
};
-UpdateAllObserver.prototype.onDataReceived_ = function(helper, name, data) {
- helper.removeObserver(this);
- --this.observingCount_;
- this.updatedData_[name] = data;
- if (this.observingCount_ == 0)
- this.callback_(this.updatedData_);
-};
+// End of anonymous namespace.
+})();
diff --git a/chrome/browser/resources/net_internals/proxyview.js b/chrome/browser/resources/net_internals/proxyview.js
index e50ab54..bece0c4 100644
--- a/chrome/browser/resources/net_internals/proxyview.js
+++ b/chrome/browser/resources/net_internals/proxyview.js
@@ -24,7 +24,6 @@ function ProxyView() {
DivView.call(this, mainBoxId);
- this.latestProxySourceEntries_ = null;
this.latestProxySourceId_ = 0;
// Hook up the UI components.
@@ -43,7 +42,7 @@ function ProxyView() {
// Register to receive proxy information as it changes.
g_browser.addProxySettingsObserver(this);
g_browser.addBadProxiesObserver(this);
- g_browser.addLogObserver(this);
+ g_browser.sourceTracker.addObserver(this);
}
inherits(ProxyView, DivView);
@@ -102,7 +101,7 @@ ProxyView.prototype.onBadProxiesChanged = function(badProxies) {
// Add a table row for each bad proxy entry.
for (var i = 0; i < badProxies.length; ++i) {
var entry = badProxies[i];
- var badUntilDate = g_browser.convertTimeTicksToDate(entry.bad_until);
+ var badUntilDate = convertTimeTicksToDate(entry.bad_until);
var tr = addNode(this.badProxiesTbody_, 'tr');
@@ -115,28 +114,22 @@ ProxyView.prototype.onBadProxiesChanged = function(badProxies) {
return true;
};
-ProxyView.prototype.onLogEntryAdded = function(logEntry) {
- if (logEntry.source.type != LogSourceType.INIT_PROXY_RESOLVER ||
- this.latestProxySourceId_ > logEntry.source.id) {
+ProxyView.prototype.onSourceEntryUpdated = function(sourceEntry) {
+ if (sourceEntry.getSourceType() != LogSourceType.INIT_PROXY_RESOLVER ||
+ this.latestProxySourceId_ > sourceEntry.getSourceId()) {
return;
}
- if (logEntry.source.id > this.latestProxySourceId_) {
- this.latestProxySourceId_ = logEntry.source.id;
- this.latestProxySourceEntries_ = [];
- }
+ this.latestProxySourceId_ = sourceEntry.getSourceId();
- this.latestProxySourceEntries_.push(logEntry);
this.proxyResolverLogPre_.innerHTML = '';
- addTextNode(this.proxyResolverLogPre_,
- PrintSourceEntriesAsText(this.latestProxySourceEntries_));
+ addTextNode(this.proxyResolverLogPre_, sourceEntry.printAsText());
};
/**
* Clears the display of and log entries for the last proxy lookup.
*/
ProxyView.prototype.clearLog_ = function() {
- this.latestProxySourceEntries_ = [];
// Prevents display of partial logs.
++this.latestProxySourceId_;
@@ -144,11 +137,11 @@ ProxyView.prototype.clearLog_ = function() {
addTextNode(this.proxyResolverLogPre_, 'Deleted.');
};
-ProxyView.prototype.onLogEntriesDeleted = function(sourceIds) {
+ProxyView.prototype.onSourceEntriesDeleted = function(sourceIds) {
if (sourceIds.indexOf(this.latestProxySourceId_) != -1)
this.clearLog_();
};
-ProxyView.prototype.onAllLogEntriesDeleted = function() {
+ProxyView.prototype.onAllSourceEntriesDeleted = function() {
this.clearLog_();
};
diff --git a/chrome/browser/resources/net_internals/sourceentry.js b/chrome/browser/resources/net_internals/sourceentry.js
index 1a901ca..50f5321 100644
--- a/chrome/browser/resources/net_internals/sourceentry.js
+++ b/chrome/browser/resources/net_internals/sourceentry.js
@@ -3,128 +3,34 @@
// found in the LICENSE file.
/**
- * Each row in the filtered items list is backed by a SourceEntry. This
- * instance contains all of the data pertaining to that row, and notifies
- * its parent view (the EventsView) whenever its data changes.
+ * A SourceEntry gathers all log entries with the same source.
*
* @constructor
*/
-function SourceEntry(parentView, maxPreviousSourceId) {
+function SourceEntry(logEntry, maxPreviousSourceId) {
this.maxPreviousSourceId_ = maxPreviousSourceId;
this.entries_ = [];
- this.parentView_ = parentView;
- this.isSelected_ = false;
- this.isMatchedByFilter_ = false;
+ this.description_ = '';
- // Used to set CSS class for display. Must only be modified by calling
- // corresponding set functions.
- this.isSelected_ = false;
- this.isMouseOver_ = false;
// Set to true on most net errors.
this.isError_ = false;
+
// If the first entry is a BEGIN_PHASE, set to false.
// Set to true when an END_PHASE matching the first entry is encountered.
this.isInactive_ = true;
-}
-
-SourceEntry.prototype.isSelected = function() {
- return this.isSelected_;
-};
-
-/**
- * Changes |row_|'s class based on currently set flags. Clears any previous
- * class set by this method. This method is needed so that some styles
- * override others.
- */
-SourceEntry.prototype.updateClass_ = function() {
- if (!this.row_)
- return;
- // Each element of this list contains a property of |this| and the
- // corresponding class name to set if that property is true. Entries earlier
- // in the list take precedence.
- var propertyNames = [
- ['isSelected_', 'selected'],
- ['isMouseOver_', 'mouseover'],
- ['isError_', 'error'],
- ['isInactive_', 'inactive']];
-
- // Loop through |propertyNames| in order, checking if each property
- // is true. For the first such property found, if any, add the
- // corresponding class to the SourceEntry's row. Remove classes
- // that correspond to any other property.
- var noStyleSet = true;
- for (var i = 0; i < propertyNames.length; ++i) {
- var setStyle = noStyleSet && this[propertyNames[i][0]];
- changeClassName(this.row_, propertyNames[i][1], setStyle);
- if (setStyle)
- noStyleSet = false;
- }
-};
-
-SourceEntry.prototype.setInactive_ = function(isInactive) {
- this.isInactive_ = isInactive;
- this.updateClass_();
-};
-
-SourceEntry.prototype.setError_ = function(isError) {
- this.isError_ = isError;
- this.updateClass_();
-};
+ if (logEntry.phase == LogEventPhase.PHASE_BEGIN)
+ this.isInactive_ = false;
-SourceEntry.prototype.setSelectedStyles = function(isSelected) {
- this.isSelected_ = isSelected;
- this.getSelectionCheckbox().checked = isSelected;
- this.updateClass_();
-};
-
-SourceEntry.prototype.setMouseoverStyle = function(isMouseOver) {
- this.isMouseOver_ = isMouseOver;
- this.updateClass_();
-};
-
-SourceEntry.prototype.setIsMatchedByFilter = function(isMatchedByFilter) {
- if (this.isMatchedByFilter() == isMatchedByFilter)
- return; // No change.
-
- this.isMatchedByFilter_ = isMatchedByFilter;
-
- this.setFilterStyles(isMatchedByFilter);
-
- if (isMatchedByFilter) {
- this.parentView_.incrementPostfilterCount(1);
- } else {
- this.parentView_.incrementPostfilterCount(-1);
- // If we are filtering an entry away, make sure it is no longer
- // part of the selection.
- this.setSelected(false);
- }
-};
-
-SourceEntry.prototype.isMatchedByFilter = function() {
- return this.isMatchedByFilter_;
-};
-
-SourceEntry.prototype.setFilterStyles = function(isMatchedByFilter) {
- // Hide rows which have been filtered away.
- if (isMatchedByFilter) {
- this.row_.style.display = '';
- } else {
- this.row_.style.display = 'none';
- }
-};
+ this.update(logEntry);
+}
SourceEntry.prototype.update = function(logEntry) {
- if (logEntry.phase == LogEventPhase.PHASE_BEGIN &&
- this.entries_.length == 0) {
- this.setInactive_(false);
- }
-
// Only the last event should have the same type first event,
if (!this.isInactive_ &&
logEntry.phase == LogEventPhase.PHASE_END &&
logEntry.type == this.entries_[0].type) {
- this.setInactive_(true);
+ this.isInactive_ = true;
}
// If we have a net error code, update |this.isError_| if apporpriate.
@@ -136,7 +42,7 @@ SourceEntry.prototype.update = function(logEntry) {
// Ignore error code caused by not finding an entry in the cache.
if (logEntry.type != LogEventType.HTTP_CACHE_OPEN_ENTRY ||
netErrorCode != NetError.FAILED) {
- this.setError_(true);
+ this.isError_ = true;
}
}
}
@@ -146,190 +52,73 @@ SourceEntry.prototype.update = function(logEntry) {
var curStartEntry = this.getStartEntry_();
// If we just got the first entry for this source.
- if (prevStartEntry != curStartEntry) {
- if (!prevStartEntry)
- this.createRow_();
- else
- this.updateDescription_();
- }
-
- // Update filters.
- var matchesFilter = this.matchesFilter(this.parentView_.currentFilter_);
- this.setIsMatchedByFilter(matchesFilter);
-};
-
-SourceEntry.prototype.onCheckboxToggled_ = function() {
- this.setSelected(this.getSelectionCheckbox().checked);
-};
-
-SourceEntry.prototype.matchesFilter = function(filter) {
- // Safety check.
- if (this.row_ == null)
- return false;
-
- if (filter.isActive && this.isInactive_)
- return false;
- if (filter.isInactive && !this.isInactive_)
- return false;
- if (filter.isError && !this.isError_)
- return false;
- if (filter.isNotError && this.isError_)
- return false;
-
- // Check source type, if needed.
- if (filter.type) {
- var sourceType = this.getSourceTypeString().toLowerCase();
- if (filter.type.indexOf(sourceType) == -1)
- return false;
- }
-
- // Check source ID, if needed.
- if (filter.id) {
- if (filter.id.indexOf(this.getSourceId() + '') == -1)
- return false;
- }
-
- if (filter.text == '')
- return true;
-
- var filterText = filter.text;
- var entryText = PrintSourceEntriesAsText(this.entries_).toLowerCase();
-
- return entryText.indexOf(filterText) != -1;
-};
-
-SourceEntry.prototype.setSelected = function(isSelected) {
- if (isSelected == this.isSelected())
- return;
-
- this.isSelected_ = isSelected;
-
- this.setSelectedStyles(isSelected);
- this.parentView_.modifySelectionArray(this, isSelected);
- this.parentView_.onSelectionChanged();
-};
-
-SourceEntry.prototype.onClicked_ = function() {
- this.parentView_.clearSelection();
- this.setSelected(true);
-};
-
-SourceEntry.prototype.onMouseover_ = function() {
- this.setMouseoverStyle(true);
-};
-
-SourceEntry.prototype.onMouseout_ = function() {
- this.setMouseoverStyle(false);
+ if (prevStartEntry != curStartEntry)
+ this.updateDescription_();
};
SourceEntry.prototype.updateDescription_ = function() {
- this.descriptionCell_.innerHTML = '';
- addTextNode(this.descriptionCell_, this.getDescription());
-};
-
-SourceEntry.prototype.createRow_ = function() {
- // Create a row.
- var tr = addNode(this.parentView_.tableBody_, 'tr');
- tr._id = this.getSourceId();
- tr.style.display = 'none';
- this.row_ = tr;
-
- var selectionCol = addNode(tr, 'td');
- var checkbox = addNode(selectionCol, 'input');
- checkbox.type = 'checkbox';
-
- var idCell = addNode(tr, 'td');
- idCell.style.textAlign = 'right';
-
- var typeCell = addNode(tr, 'td');
- var descriptionCell = addNode(tr, 'td');
- this.descriptionCell_ = descriptionCell;
-
- // Connect listeners.
- checkbox.onchange = this.onCheckboxToggled_.bind(this);
-
- var onclick = this.onClicked_.bind(this);
- idCell.onclick = onclick;
- typeCell.onclick = onclick;
- descriptionCell.onclick = onclick;
-
- tr.onmouseover = this.onMouseover_.bind(this);
- tr.onmouseout = this.onMouseout_.bind(this);
-
- // Set the cell values to match this source's data.
- if (this.getSourceId() >= 0)
- addTextNode(idCell, this.getSourceId());
- else
- addTextNode(idCell, '-');
- var sourceTypeString = this.getSourceTypeString();
- addTextNode(typeCell, sourceTypeString);
- this.updateDescription_();
-
- // Add a CSS classname specific to this source type (so CSS can specify
- // different stylings for different types).
- changeClassName(this.row_, 'source_' + sourceTypeString, true);
-
- this.updateClass_();
-};
-
-/**
- * Returns a description for this source log stream, which will be displayed
- * in the list view. Most often this is a URL that identifies the request,
- * or a hostname for a connect job, etc...
- */
-SourceEntry.prototype.getDescription = function() {
var e = this.getStartEntry_();
+ this.description_ = '';
if (!e)
- return '';
+ return;
if (e.source.type == LogSourceType.NONE) {
// NONE is what we use for global events that aren't actually grouped
// by a "source ID", so we will just stringize the event's type.
- return getKeyWithValue(LogEventType, e.type);
+ this.description_ = getKeyWithValue(LogEventType, e.type);
+ return;
}
- if (e.params == undefined)
- return '';
+ if (e.params == undefined) {
+ return;
+ }
- var description = '';
switch (e.source.type) {
case LogSourceType.URL_REQUEST:
case LogSourceType.SOCKET_STREAM:
case LogSourceType.HTTP_STREAM_JOB:
- description = e.params.url;
+ this.description_ = e.params.url;
break;
case LogSourceType.CONNECT_JOB:
- description = e.params.group_name;
+ this.description_ = e.params.group_name;
break;
case LogSourceType.HOST_RESOLVER_IMPL_REQUEST:
case LogSourceType.HOST_RESOLVER_IMPL_JOB:
- description = e.params.host;
+ this.description_ = e.params.host;
break;
case LogSourceType.DISK_CACHE_ENTRY:
case LogSourceType.MEMORY_CACHE_ENTRY:
- description = e.params.key;
+ this.description_ = e.params.key;
break;
case LogSourceType.SPDY_SESSION:
if (e.params.host)
- description = e.params.host + ' (' + e.params.proxy + ')';
+ this.description_ = e.params.host + ' (' + e.params.proxy + ')';
break;
case LogSourceType.SOCKET:
if (e.params.source_dependency != undefined) {
- var connectJobSourceEntry =
- this.parentView_.getSourceEntry(e.params.source_dependency.id);
- if (connectJobSourceEntry)
- description = connectJobSourceEntry.getDescription();
+ var connectJobId = e.params.source_dependency.id;
+ var connectJob = g_browser.sourceTracker.getSourceEntry(connectJobId);
+ if (connectJob)
+ this.description_ = connectJob.getDescription();
}
break;
case LogSourceType.ASYNC_HOST_RESOLVER_REQUEST:
case LogSourceType.DNS_TRANSACTION:
- description = e.params.hostname;
+ this.description_ = e.params.hostname;
break;
}
- if (description == undefined)
- return '';
- return description;
+ if (this.description_ == undefined)
+ this.description_ = '';
+};
+
+/**
+ * Returns a description for this source log stream, which will be displayed
+ * in the list view. Most often this is a URL that identifies the request,
+ * or a hostname for a connect job, etc...
+ */
+SourceEntry.prototype.getDescription = function() {
+ return this.description_;
};
/**
@@ -357,8 +146,8 @@ SourceEntry.prototype.getSourceTypeString = function() {
return getKeyWithValue(LogSourceType, this.entries_[0].source.type);
};
-SourceEntry.prototype.getSelectionCheckbox = function() {
- return this.row_.childNodes[0].firstChild;
+SourceEntry.prototype.getSourceType = function() {
+ return this.entries_[0].source.type;
};
SourceEntry.prototype.getSourceId = function() {
@@ -373,8 +162,12 @@ SourceEntry.prototype.getMaxPreviousEntrySourceId = function() {
return this.maxPreviousSourceId_;
};
-SourceEntry.prototype.isActive = function() {
- return !this.isInactive_;
+SourceEntry.prototype.isInactive = function() {
+ return this.isInactive_;
+};
+
+SourceEntry.prototype.isError = function() {
+ return this.isError_;
};
/**
@@ -383,10 +176,9 @@ SourceEntry.prototype.isActive = function() {
SourceEntry.prototype.getEndTime = function() {
if (!this.isInactive_) {
return (new Date()).getTime();
- }
- else {
+ } else {
var endTicks = this.entries_[this.entries_.length - 1].time;
- return g_browser.convertTimeTicksToDate(endTicks).getTime();
+ return convertTimeTicksToDate(endTicks).getTime();
}
};
@@ -397,62 +189,11 @@ SourceEntry.prototype.getEndTime = function() {
*/
SourceEntry.prototype.getDuration = function() {
var startTicks = this.entries_[0].time;
- var startTime = g_browser.convertTimeTicksToDate(startTicks).getTime();
+ var startTime = convertTimeTicksToDate(startTicks).getTime();
var endTime = this.getEndTime();
return endTime - startTime;
};
-/**
- * Returns source ID of the entry whose row is currently above this one's.
- * Returns null if no such node exists.
- */
-SourceEntry.prototype.getPreviousNodeSourceId = function() {
- if (!this.hasRow())
- return null;
- var prevNode = this.row_.previousSibling;
- if (prevNode == null)
- return null;
- return prevNode._id;
-};
-
-/**
- * Returns source ID of the entry whose row is currently below this one's.
- * Returns null if no such node exists.
- */
-SourceEntry.prototype.getNextNodeSourceId = function() {
- if (!this.hasRow())
- return null;
- var nextNode = this.row_.nextSibling;
- if (nextNode == null)
- return null;
- return nextNode._id;
-};
-
-SourceEntry.prototype.hasRow = function() {
- return this.row_ != null;
-};
-
-/**
- * Moves current object's row before |entry|'s row.
- */
-SourceEntry.prototype.moveBefore = function(entry) {
- if (this.hasRow() && entry.hasRow()) {
- this.row_.parentNode.insertBefore(this.row_, entry.row_);
- }
-};
-
-/**
- * Moves current object's row after |entry|'s row.
- */
-SourceEntry.prototype.moveAfter = function(entry) {
- if (this.hasRow() && entry.hasRow()) {
- this.row_.parentNode.insertBefore(this.row_, entry.row_.nextSibling);
- }
+SourceEntry.prototype.printAsText = function() {
+ return PrintSourceEntriesAsText(this.entries_);
};
-
-SourceEntry.prototype.remove = function() {
- this.setSelected(false);
- this.setIsMatchedByFilter(false);
- this.row_.parentNode.removeChild(this.row_);
-};
-
diff --git a/chrome/browser/resources/net_internals/sourcerow.js b/chrome/browser/resources/net_internals/sourcerow.js
new file mode 100644
index 0000000..96cdcf7
--- /dev/null
+++ b/chrome/browser/resources/net_internals/sourcerow.js
@@ -0,0 +1,284 @@
+// 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.
+
+/**
+ * A SourceRow represents the row corresponding to a single SourceEntry
+ * displayed by the EventsView.
+ *
+ * @constructor
+ */
+function SourceRow(parentView, sourceEntry) {
+ this.parentView_ = parentView;
+
+ this.sourceEntry_ = sourceEntry;
+ this.isSelected_ = false;
+ this.isMatchedByFilter_ = false;
+
+ // Used to set CSS class for display. Must only be modified by calling
+ // corresponding set functions.
+ this.isSelected_ = false;
+ this.isMouseOver_ = false;
+
+ // Mirror sourceEntry's values, so we only update the DOM when necessary.
+ this.isError_ = sourceEntry.isError();
+ this.isInactive_ = sourceEntry.isInactive();
+ this.description_ = sourceEntry.getDescription();
+
+ this.createRow_();
+ this.onSourceUpdated();
+}
+
+SourceRow.prototype.createRow_ = function() {
+ // Create a row.
+ var tr = addNode(this.parentView_.tableBody_, 'tr');
+ tr._id = this.sourceEntry_.getSourceId();
+ tr.style.display = 'none';
+ this.row_ = tr;
+
+ var selectionCol = addNode(tr, 'td');
+ var checkbox = addNode(selectionCol, 'input');
+ checkbox.type = 'checkbox';
+
+ var idCell = addNode(tr, 'td');
+ idCell.style.textAlign = 'right';
+
+ var typeCell = addNode(tr, 'td');
+ var descriptionCell = addNode(tr, 'td');
+ this.descriptionCell_ = descriptionCell;
+
+ // Connect listeners.
+ checkbox.onchange = this.onCheckboxToggled_.bind(this);
+
+ var onclick = this.onClicked_.bind(this);
+ idCell.onclick = onclick;
+ typeCell.onclick = onclick;
+ descriptionCell.onclick = onclick;
+
+ tr.onmouseover = this.onMouseover_.bind(this);
+ tr.onmouseout = this.onMouseout_.bind(this);
+
+ // Set the cell values to match this source's data.
+ if (this.sourceEntry_.getSourceId() >= 0) {
+ addTextNode(idCell, this.sourceEntry_.getSourceId());
+ } else {
+ addTextNode(idCell, '-');
+ }
+ var sourceTypeString = this.sourceEntry_.getSourceTypeString();
+ addTextNode(typeCell, sourceTypeString);
+ this.updateDescription_();
+
+ // Add a CSS classname specific to this source type (so CSS can specify
+ // different stylings for different types).
+ changeClassName(this.row_, 'source_' + sourceTypeString, true);
+
+ this.updateClass_();
+};
+
+SourceRow.prototype.onSourceUpdated = function() {
+ if (this.sourceEntry_.isInactive() != this.isInactive_ ||
+ this.sourceEntry_.isError() != this.isError_) {
+ this.updateClass_();
+ }
+
+ if (this.description_ != this.sourceEntry_.getDescription())
+ this.updateDescription_();
+
+ // Update filters.
+ var matchesFilter = this.matchesFilter(this.parentView_.currentFilter_);
+ this.setIsMatchedByFilter(matchesFilter);
+};
+
+/**
+ * Changes |row_|'s class based on currently set flags. Clears any previous
+ * class set by this method. This method is needed so that some styles
+ * override others.
+ */
+SourceRow.prototype.updateClass_ = function() {
+ this.isInactive_ = this.sourceEntry_.isInactive();
+ this.isError_ = this.sourceEntry_.isError();
+
+ // Each element of this list contains a property of |this| and the
+ // corresponding class name to set if that property is true. Entries earlier
+ // in the list take precedence.
+ var propertyNames = [
+ ['isSelected_', 'selected'],
+ ['isMouseOver_', 'mouseover'],
+ ['isError_', 'error'],
+ ['isInactive_', 'inactive'],
+ ];
+
+ // Loop through |propertyNames| in order, checking if each property
+ // is true. For the first such property found, if any, add the
+ // corresponding class to the SourceEntry's row. Remove classes
+ // that correspond to any other property.
+ var noStyleSet = true;
+ for (var i = 0; i < propertyNames.length; ++i) {
+ var setStyle = noStyleSet && this[propertyNames[i][0]];
+ changeClassName(this.row_, propertyNames[i][1], setStyle);
+ if (setStyle)
+ noStyleSet = false;
+ }
+};
+
+SourceRow.prototype.getSourceEntry = function() {
+ return this.sourceEntry_;
+};
+
+SourceRow.prototype.setIsMatchedByFilter = function(isMatchedByFilter) {
+ if (this.isMatchedByFilter() == isMatchedByFilter)
+ return; // No change.
+
+ this.isMatchedByFilter_ = isMatchedByFilter;
+
+ this.setFilterStyles(isMatchedByFilter);
+
+ if (isMatchedByFilter) {
+ this.parentView_.incrementPostfilterCount(1);
+ } else {
+ this.parentView_.incrementPostfilterCount(-1);
+ // If we are filtering an entry away, make sure it is no longer
+ // part of the selection.
+ this.setSelected(false);
+ }
+};
+
+SourceRow.prototype.isMatchedByFilter = function() {
+ return this.isMatchedByFilter_;
+};
+
+SourceRow.prototype.setFilterStyles = function(isMatchedByFilter) {
+ // Hide rows which have been filtered away.
+ if (isMatchedByFilter) {
+ this.row_.style.display = '';
+ } else {
+ this.row_.style.display = 'none';
+ }
+};
+
+SourceRow.prototype.matchesFilter = function(filter) {
+ if (filter.isActive && this.isInactive_)
+ return false;
+ if (filter.isInactive && !this.isInactive_)
+ return false;
+ if (filter.isError && !this.isError_)
+ return false;
+ if (filter.isNotError && this.isError_)
+ return false;
+
+ // Check source type, if needed.
+ if (filter.type) {
+ var sourceType = this.sourceEntry_.getSourceTypeString().toLowerCase();
+ if (filter.type.indexOf(sourceType) == -1)
+ return false;
+ }
+
+ // Check source ID, if needed.
+ if (filter.id) {
+ if (filter.id.indexOf(this.sourceEntry_.getSourceId() + '') == -1)
+ return false;
+ }
+
+ if (filter.text == '')
+ return true;
+
+ var filterText = filter.text;
+ var entryText = this.sourceEntry_.PrintEntriesAsText().toLowerCase();
+
+ return entryText.indexOf(filterText) != -1;
+};
+
+SourceRow.prototype.isSelected = function() {
+ return this.isSelected_;
+};
+
+SourceRow.prototype.setSelected = function(isSelected) {
+ if (isSelected == this.isSelected())
+ return;
+
+ this.isSelected_ = isSelected;
+
+ this.setSelectedStyles(isSelected);
+ this.parentView_.modifySelectionArray(this, isSelected);
+ this.parentView_.onSelectionChanged();
+};
+
+SourceRow.prototype.setSelectedStyles = function(isSelected) {
+ this.isSelected_ = isSelected;
+ this.getSelectionCheckbox().checked = isSelected;
+ this.updateClass_();
+};
+
+SourceRow.prototype.setMouseoverStyle = function(isMouseOver) {
+ this.isMouseOver_ = isMouseOver;
+ this.updateClass_();
+};
+
+SourceRow.prototype.onClicked_ = function() {
+ this.parentView_.clearSelection();
+ this.setSelected(true);
+};
+
+SourceRow.prototype.onMouseover_ = function() {
+ this.setMouseoverStyle(true);
+};
+
+SourceRow.prototype.onMouseout_ = function() {
+ this.setMouseoverStyle(false);
+};
+
+SourceRow.prototype.updateDescription_ = function() {
+ this.description_ = this.sourceEntry_.getDescription();
+ this.descriptionCell_.innerHTML = '';
+ addTextNode(this.descriptionCell_, this.description_);
+};
+
+SourceRow.prototype.onCheckboxToggled_ = function() {
+ this.setSelected(this.getSelectionCheckbox().checked);
+};
+
+SourceRow.prototype.getSelectionCheckbox = function() {
+ return this.row_.childNodes[0].firstChild;
+};
+
+/**
+ * Returns source ID of the entry whose row is currently above this one's.
+ * Returns null if no such node exists.
+ */
+SourceRow.prototype.getPreviousNodeSourceId = function() {
+ var prevNode = this.row_.previousSibling;
+ if (prevNode == null)
+ return null;
+ return prevNode._id;
+};
+
+/**
+ * Returns source ID of the entry whose row is currently below this one's.
+ * Returns null if no such node exists.
+ */
+SourceRow.prototype.getNextNodeSourceId = function() {
+ var nextNode = this.row_.nextSibling;
+ if (nextNode == null)
+ return null;
+ return nextNode._id;
+};
+
+/**
+ * Moves current object's row before |entry|'s row.
+ */
+SourceRow.prototype.moveBefore = function(entry) {
+ this.row_.parentNode.insertBefore(this.row_, entry.row_);
+};
+
+/**
+ * Moves current object's row after |entry|'s row.
+ */
+SourceRow.prototype.moveAfter = function(entry) {
+ this.row_.parentNode.insertBefore(this.row_, entry.row_.nextSibling);
+};
+
+SourceRow.prototype.remove = function() {
+ this.setSelected(false);
+ this.setIsMatchedByFilter(false);
+ this.row_.parentNode.removeChild(this.row_);
+};
diff --git a/chrome/browser/resources/net_internals/sourcetracker.js b/chrome/browser/resources/net_internals/sourcetracker.js
new file mode 100644
index 0000000..239ffd5
--- /dev/null
+++ b/chrome/browser/resources/net_internals/sourcetracker.js
@@ -0,0 +1,201 @@
+// Copyright (c) 2011 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+/**
+ * This class keeps track of all NetLog events.
+ * It receives events from the browser and when loading a log file, and passes
+ * them on to all its observers.
+ *
+ * @constructor
+ */
+function SourceTracker() {
+ this.observers_ = [];
+
+ // True when cookies and authentication information should be removed from
+ // displayed events. When true, such information should be hidden from
+ // all pages.
+ this.enableSecurityStripping_ = true;
+
+ this.clearEntries_();
+}
+
+/**
+ * Clears all log entries and SourceEntries and related state.
+ */
+SourceTracker.prototype.clearEntries_ = function() {
+ // Used for sorting entries with automatically assigned IDs.
+ this.maxReceivedSourceId_ = 0;
+
+ // Next unique id to be assigned to a log entry without a source.
+ // Needed to simplify deletion, identify associated GUI elements, etc.
+ this.nextSourcelessEventId_ = -1;
+
+ this.numPassivelyCapturedEvents_ = 0;
+
+ // Ordered list of log entries. Needed to maintain original order when
+ // generating log dumps
+ this.capturedEvents_ = [];
+
+ this.sourceEntries_ = {};
+};
+
+/**
+ * Returns a list of all captured events.
+ */
+SourceTracker.prototype.getAllCapturedEvents = function() {
+ return this.capturedEvents_;
+};
+
+/**
+ * Returns a list of all SourceEntries.
+ */
+SourceTracker.prototype.getAllSourceEntries = function() {
+ return this.sourceEntries_;
+};
+
+/**
+ * Returns the number of events that were captured while we were
+ * listening for events.
+ */
+SourceTracker.prototype.getNumActivelyCapturedEvents = function() {
+ return this.capturedEvents_.length - this.numPassivelyCapturedEvents_;
+};
+
+/**
+ * Returns the number of events that were captured passively by the
+ * browser prior to when the net-internals page was started.
+ */
+SourceTracker.prototype.getNumPassivelyCapturedEvents = function() {
+ return this.numPassivelyCapturedEvents_;
+};
+
+/**
+ * Returns the specified SourceEntry.
+ */
+SourceTracker.prototype.getSourceEntry = function(id) {
+ return this.sourceEntries_[id];
+};
+
+SourceTracker.prototype.onReceivedPassiveLogEntries = function(entries) {
+ // Due to an expected race condition, it is possible to receive actively
+ // captured log entries before the passively logged entries are received.
+ //
+ // When that happens, we create a copy of the actively logged entries, delete
+ // all entries, and, after handling all the passively logged entries, add back
+ // the deleted actively logged entries.
+ var earlyActivelyCapturedEvents = this.capturedEvents_.slice(0);
+ if (earlyActivelyCapturedEvents.length > 0)
+ this.deleteAllSourceEntries();
+
+ this.numPassivelyCapturedEvents_ = entries.length;
+ for (var i = 0; i < entries.length; ++i)
+ entries[i].wasPassivelyCaptured = true;
+ this.onReceivedLogEntries(entries);
+
+ // Add back early actively captured events, if any.
+ if (earlyActivelyCapturedEvents.length)
+ this.onReceivedLogEntries(earlyActivelyCapturedEvents);
+};
+
+/**
+ * Sends each entry to all log observers, and updates |capturedEvents_|.
+ * Also assigns unique ids to log entries without a source.
+ */
+SourceTracker.prototype.onReceivedLogEntries = function(logEntries) {
+ for (var e = 0; e < logEntries.length; ++e) {
+ var logEntry = logEntries[e];
+
+ // Assign unique ID, if needed.
+ if (logEntry.source.id == 0) {
+ logEntry.source.id = this.nextSourcelessEventId_;
+ --this.nextSourcelessEventId_;
+ } else if (this.maxReceivedSourceId_ < logEntry.source.id) {
+ this.maxReceivedSourceId_ = logEntry.source.id;
+ }
+
+ var sourceEntry = this.sourceEntries_[logEntry.source.id];
+ if (!sourceEntry) {
+ sourceEntry = new SourceEntry(logEntry, this.maxReceivedSourceId_);
+ this.sourceEntries_[logEntry.source.id] = sourceEntry;
+ } else {
+ sourceEntry.update(logEntry);
+ }
+ this.capturedEvents_.push(logEntry);
+
+ // TODO(mmenke): Send a list of all updated source entries instead,
+ // eliminating duplicates, to reduce CPU usage.
+ for (var i = 0; i < this.observers_.length; ++i)
+ this.observers_[i].onSourceEntryUpdated(sourceEntry);
+ }
+};
+
+/**
+ * Deletes captured events with source IDs in |sourceEntryIds|.
+ */
+SourceTracker.prototype.deleteSourceEntries = function(sourceEntryIds) {
+ var sourceIdDict = {};
+ for (var i = 0; i < sourceEntryIds.length; i++) {
+ sourceIdDict[sourceEntryIds[i]] = true;
+ delete this.sourceEntries_[sourceEntryIds[i]];
+ }
+
+ var newEventList = [];
+ for (var i = 0; i < this.capturedEvents_.length; ++i) {
+ var id = this.capturedEvents_[i].source.id;
+ if (id in sourceIdDict) {
+ if (this.capturedEvents_[i].wasPassivelyCaptured)
+ --this.numPassivelyCapturedEvents_;
+ continue;
+ }
+ newEventList.push(this.capturedEvents_[i]);
+ }
+ this.capturedEvents_ = newEventList;
+
+ for (var i = 0; i < this.observers_.length; ++i)
+ this.observers_[i].onSourceEntriesDeleted(sourceEntryIds);
+};
+
+/**
+ * Deletes all captured events.
+ */
+SourceTracker.prototype.deleteAllSourceEntries = function() {
+ this.clearEntries_();
+ for (var i = 0; i < this.observers_.length; ++i)
+ this.observers_[i].onAllSourceEntriesDeleted();
+};
+
+/**
+ * Sets the value of |enableSecurityStripping_| and informs log observers
+ * of the change.
+ */
+SourceTracker.prototype.setSecurityStripping =
+ function(enableSecurityStripping) {
+ this.enableSecurityStripping_ = enableSecurityStripping;
+ for (var i = 0; i < this.observers_.length; ++i) {
+ if (this.observers_[i].onSecurityStrippingChanged)
+ this.observers_[i].onSecurityStrippingChanged();
+ }
+};
+
+/**
+ * Returns whether or not cookies and authentication information should be
+ * displayed for events that contain them.
+ */
+SourceTracker.prototype.getSecurityStripping = function() {
+ return this.enableSecurityStripping_;
+};
+
+/**
+ * Adds a listener of log entries. |observer| will be called back when new log
+ * data arrives, source entries are deleted, or security stripping changes
+ * through:
+ *
+ * observer.onSourceEntryUpdated(sourceEntry)
+ * observer.deleteSourceEntries(sourceEntryIds)
+ * ovserver.deleteAllSourceEntries()
+ * observer.onSecurityStrippingChanged()
+ */
+SourceTracker.prototype.addObserver = function(observer) {
+ this.observers_.push(observer);
+};