summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--chrome/browser/resources/net_internals/dataview.js194
-rw-r--r--chrome/browser/resources/net_internals/index.html10
-rw-r--r--chrome/browser/resources/net_internals/main.js92
-rw-r--r--chrome/chrome_browser.gypi1
4 files changed, 271 insertions, 26 deletions
diff --git a/chrome/browser/resources/net_internals/dataview.js b/chrome/browser/resources/net_internals/dataview.js
new file mode 100644
index 0000000..778ea9f
--- /dev/null
+++ b/chrome/browser/resources/net_internals/dataview.js
@@ -0,0 +1,194 @@
+// Copyright (c) 2010 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+/**
+ * This view displays options for importing/exporting the captured data. Its
+ * primarily usefulness is to allow users to copy-paste their data in an easy
+ * to read format for bug reports.
+ *
+ * - Has a button to generate a text report.
+ * - Has a button to generate a raw JSON dump (most useful for testing).
+ *
+ * @constructor
+ */
+function DataView(mainBoxId, exportJsonButtonId, exportTextButtonId) {
+ DivView.call(this, mainBoxId);
+
+ var exportJsonButton = document.getElementById(exportJsonButtonId);
+ var exportTextButton = document.getElementById(exportTextButtonId);
+
+ exportJsonButton.onclick = this.onExportToJSON_.bind(this);
+ exportTextButton.onclick = this.onExportToText_.bind(this);
+}
+
+inherits(DataView, DivView);
+
+/**
+ * Very simple output format which directly displays the internally captured
+ * data in its javascript format.
+ */
+DataView.prototype.onExportToJSON_ = function() {
+ // Format some text describing the data we have captured.
+ var text = []; // Lines of text.
+
+ text.push('//----------------------------------------------');
+ text.push('// Proxy settings');
+ text.push('//----------------------------------------------');
+ text.push('');
+
+ text.push('proxySettings = ' +
+ JSON.stringify(g_browser.proxySettings_.currentData_));
+
+ text.push('');
+ text.push('//----------------------------------------------');
+ text.push('// Bad proxies');
+ text.push('//----------------------------------------------');
+ text.push('');
+
+ text.push('badProxies = ' +
+ JSON.stringify(g_browser.badProxies_.currentData_));
+
+ text.push('');
+ text.push('//----------------------------------------------');
+ text.push('// Host resolver cache');
+ text.push('//----------------------------------------------');
+ text.push('');
+
+ text.push('hostResolverCache = ' +
+ JSON.stringify(g_browser.hostResolverCache_.currentData_));
+
+ text.push('');
+ text.push('//----------------------------------------------');
+ text.push('// Passively captured events');
+ text.push('//----------------------------------------------');
+ text.push('');
+
+ this.appendPrettyPrintedJsonArray_('passive',
+ g_browser.getAllPassivelyCapturedEvents(),
+ text);
+
+ text.push('');
+ text.push('//----------------------------------------------');
+ text.push('// Actively captured events');
+ text.push('//----------------------------------------------');
+ text.push('');
+
+ this.appendPrettyPrintedJsonArray_('active',
+ g_browser.getAllActivelyCapturedEvents(),
+ text);
+
+ // Open a new window to display this text.
+ this.displayPopupWindow_(text.join('\n'));
+};
+
+/**
+ * Presents the captured data as formatted text.
+ */
+DataView.prototype.onExportToText_ = function() {
+ var text = [];
+
+ // Print some basic information about our environment.
+ text.push('Data exported on: ' + (new Date()).toLocaleString());
+ text.push('');
+ text.push('Number of passively captured events: ' +
+ g_browser.getAllPassivelyCapturedEvents().length);
+ text.push('Number of actively captured events: ' +
+ g_browser.getAllActivelyCapturedEvents().length);
+ text.push('Timetick to timestamp offset: ' + g_browser.timeTickOffset_);
+ text.push('');
+ // TODO(eroman): fill this with proper values.
+ text.push('Chrome version: ' + 'TODO');
+ text.push('Command line switches: ' + 'TODO');
+
+ text.push('');
+ text.push('----------------------------------------------');
+ text.push(' Proxy settings');
+ text.push('----------------------------------------------');
+ text.push('');
+
+ text.push(g_browser.proxySettings_.currentData_);
+
+ text.push('');
+ text.push('----------------------------------------------');
+ text.push(' Bad proxies cache');
+ text.push('----------------------------------------------');
+ text.push('');
+
+ var badProxiesList = g_browser.badProxies_.currentData_;
+ if (badProxiesList.length == 0) {
+ text.push('None');
+ } else {
+ this.appendPrettyPrintedTable_(badProxiesList, text);
+ }
+
+ text.push('');
+ text.push('----------------------------------------------');
+ text.push(' Host resolver cache');
+ text.push('----------------------------------------------');
+ text.push('');
+
+ var hostResolverCache = g_browser.hostResolverCache_.currentData_;
+
+ text.push('Capcity: ' + hostResolverCache.capacity);
+ text.push('Time to live for successful resolves (ms): ' +
+ hostResolverCache.ttl_success_ms);
+ text.push('Time to live for failed resolves (ms): ' +
+ hostResolverCache.ttl_failure_ms);
+
+ if (hostResolverCache.entries.length > 0) {
+ text.push('');
+ this.appendPrettyPrintedTable_(hostResolverCache.entries, text);
+ }
+
+ text.push('');
+ text.push('----------------------------------------------');
+ text.push(' URL requests');
+ text.push('----------------------------------------------');
+ text.push('');
+
+ // TODO(eroman): Output the per-request events.
+ text.push('TODO');
+
+ // Open a new window to display this text.
+ this.displayPopupWindow_(text.join('\n'));
+};
+
+/**
+ * Helper function to open a new window and display |text| in it.
+ */
+DataView.prototype.displayPopupWindow_ = function(text) {
+ // Note that we use a data:URL because the chrome:// URL scheme doesn't
+ // allow us to mutate any new windows we open.
+ dataUrl = 'data:text/plain,' + encodeURIComponent(text);
+ window.open(dataUrl, '_blank');
+};
+
+/**
+ * Stringifies |arrayData| as JSON-like output, and appends it to the
+ * line buffer |out|.
+ */
+DataView.prototype.appendPrettyPrintedJsonArray_ = function(name,
+ arrayData,
+ out) {
+ out.push(name + ' = [];');
+ for (var i = 0; i < arrayData.length; ++i) {
+ out.push(name + '[' + i + '] = ' + JSON.stringify(arrayData[i]) + ';');
+ }
+};
+
+/**
+ * Stringifies |arrayData| to formatted table output, and appends it to the
+ * line buffer |out|.
+ */
+DataView.prototype.appendPrettyPrintedTable_ = function(arrayData, out) {
+ for (var i = 0; i < arrayData.length; ++i) {
+ var e = arrayData[i];
+ var eString = '[' + i + ']: ';
+ for (var key in e) {
+ eString += key + "=" + e[key] + "; ";
+ }
+ out.push(eString);
+ }
+};
+
diff --git a/chrome/browser/resources/net_internals/index.html b/chrome/browser/resources/net_internals/index.html
index cd77f62..be62e42 100644
--- a/chrome/browser/resources/net_internals/index.html
+++ b/chrome/browser/resources/net_internals/index.html
@@ -9,6 +9,7 @@ found in the LICENSE file.
<script src="util.js"></script>
<script src="view.js"></script>
<script src="tabswitcherview.js"></script>
+ <script src="dataview.js"></script>
<script src="main.js"></script>
<script src="dnsview.js"></script>
<script src="requestsview.js"></script>
@@ -30,6 +31,7 @@ found in the LICENSE file.
<li><a href="#dns" id=dnsTab>DNS</a></li>
<li><a href="#sockets" id=socketsTab>Sockets</a></li>
<li><a href="#httpCache" id=httpCacheTab>HTTP Cache</a></li>
+ <li><a href="#data" id=dataTab>Data</a></li>
</ul>
<div style="clear: both;"></div>
</div>
@@ -85,6 +87,14 @@ found in the LICENSE file.
<!-- Sections TODO -->
<div id=socketsTabContent>TODO: display socket information (outstanding connect jobs)</div>
<div id=httpCacheTabContent>TODO: display http cache information (disk cache navigator)</div>
+ <!-- Import/Export data -->
+ <div id=dataTabContent>
+ <br/>
+ <button id=exportToText><b>Click here to generate bug report data</b><br/>(Copy paste the result and attach to bug)</button>
+ <br/>
+ <br/>
+ <button id=exportToJson>Export data to JSON</button>
+ </div>
<!-- ================= Requests view =================== -->
diff --git a/chrome/browser/resources/net_internals/main.js b/chrome/browser/resources/net_internals/main.js
index f695c3c..d06e5e9 100644
--- a/chrome/browser/resources/net_internals/main.js
+++ b/chrome/browser/resources/net_internals/main.js
@@ -57,6 +57,10 @@ function onLoaded() {
"hostResolverCacheTTLSuccess",
"hostResolverCacheTTLFailure");
+ // Create a view which will display import/export options to control the
+ // captured data.
+ var dataView = new DataView("dataTabContent", "exportToJson", "exportToText");
+
// Create a view which lets you tab between the different sub-views.
var categoryTabSwitcher =
new TabSwitcherView(new DivView('categoryTabHandles'));
@@ -69,6 +73,7 @@ function onLoaded() {
false);
categoryTabSwitcher.addTab('httpCacheTab',
new DivView('httpCacheTabContent'), false);
+ categoryTabSwitcher.addTab('dataTab', dataView, false);
// Build a map from the anchor name of each tab handle to its "tab ID".
// We will consider navigations to the #hash as a switch tab request.
@@ -109,14 +114,15 @@ function onLoaded() {
function BrowserBridge() {
// List of observers for various bits of browser state.
this.logObservers_ = [];
- this.proxySettingsObservers_ = [];
- this.badProxiesObservers_ = [];
- this.hostResolverCacheObservers_ = [];
-
- // Map from observer method name (i.e. 'onProxySettingsChanged',
- // 'onBadProxiesChanged') to the previously received data for that type. Used
- // to tell if the data has actually changed since we last polled it.
- this.prevPollData_ = {};
+ this.proxySettings_ = new PollableDataHelper('onProxySettingsChanged');
+ this.badProxies_ = new PollableDataHelper('onBadProxiesChanged');
+ this.hostResolverCache_ = new PollableDataHelper('onHostResolverCacheChanged');
+
+ // Cache of the data received.
+ // TODO(eroman): the controls to clear data in the "Requests" tab should be
+ // affecting this as well.
+ this.passivelyCapturedEvents_ = [];
+ this.activelyCapturedEvents_ = [];
}
/**
@@ -169,7 +175,11 @@ BrowserBridge.prototype.sendClearHostResolverCache = function() {
// Messages received from the browser
//------------------------------------------------------------------------------
-BrowserBridge.prototype.receivedLogEntry = function(logEntry) {
+BrowserBridge.prototype.receivedLogEntry = function(logEntry,
+ wasCapturedPassively) {
+ if (!wasCapturedPassively) {
+ this.activelyCapturedEvents_.push(logEntry);
+ }
for (var i = 0; i < this.logObservers_.length; ++i)
this.logObservers_[i].onLogEntryAdded(logEntry);
};
@@ -193,25 +203,23 @@ BrowserBridge.prototype.receivedTimeTickOffset = function(timeTickOffset) {
};
BrowserBridge.prototype.receivedProxySettings = function(proxySettings) {
- this.dispatchToObserversFromPoll_(
- this.proxySettingsObservers_, 'onProxySettingsChanged', proxySettings);
+ this.proxySettings_.update(proxySettings);
};
BrowserBridge.prototype.receivedBadProxies = function(badProxies) {
- this.dispatchToObserversFromPoll_(
- this.badProxiesObservers_, 'onBadProxiesChanged', badProxies);
+ this.badProxies_.update(badProxies);
};
BrowserBridge.prototype.receivedHostResolverCache =
function(hostResolverCache) {
- this.dispatchToObserversFromPoll_(
- this.hostResolverCacheObservers_, 'onHostResolverCacheChanged',
- hostResolverCache);
+ this.hostResolverCache_.update(hostResolverCache);
};
BrowserBridge.prototype.receivedPassiveLogEntries = function(entries) {
+ this.passivelyCapturedEvents_ =
+ this.passivelyCapturedEvents_.concat(entries);
for (var i = 0; i < entries.length; ++i)
- this.receivedLogEntry(entries[i]);
+ this.receivedLogEntry(entries[i], true);
};
//------------------------------------------------------------------------------
@@ -236,7 +244,7 @@ BrowserBridge.prototype.addLogObserver = function(observer) {
* TODO(eroman): send a dictionary instead.
*/
BrowserBridge.prototype.addProxySettingsObserver = function(observer) {
- this.proxySettingsObservers_.push(observer);
+ this.proxySettings_.addObserver(observer);
};
/**
@@ -251,7 +259,7 @@ BrowserBridge.prototype.addProxySettingsObserver = function(observer) {
* bad. Note the time is in time ticks.
*/
BrowserBridge.prototype.addBadProxiesObsever = function(observer) {
- this.badProxiesObservers_.push(observer);
+ this.badProxies_.addObserver(observer);
};
/**
@@ -261,7 +269,7 @@ BrowserBridge.prototype.addBadProxiesObsever = function(observer) {
* observer.onHostResolverCacheChanged(hostResolverCache)
*/
BrowserBridge.prototype.addHostResolverCacheObserver = function(observer) {
- this.hostResolverCacheObservers_.push(observer);
+ this.hostResolverCache_.addObserver(observer);
};
/**
@@ -280,6 +288,22 @@ BrowserBridge.prototype.convertTimeTicksToDate = function(timeTicks) {
return d;
};
+/**
+ * Returns a list of all the events that were captured while we were
+ * listening for events.
+ */
+BrowserBridge.prototype.getAllActivelyCapturedEvents = function() {
+ return this.activelyCapturedEvents_;
+};
+
+/**
+ * Returns a list of all the events that were captured passively by the
+ * browser prior to when the net-internals page was started.
+ */
+BrowserBridge.prototype.getAllPassivelyCapturedEvents = function() {
+ return this.passivelyCapturedEvents_;
+};
+
BrowserBridge.prototype.doPolling_ = function() {
// TODO(eroman): Optimize this by using a separate polling frequency for the
// data consumed by the currently active view. Everything else can be on a low
@@ -290,21 +314,37 @@ BrowserBridge.prototype.doPolling_ = function() {
};
/**
+ * 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.
+ * @constructor
+ */
+function PollableDataHelper(observerMethodName) {
+ this.observerMethodName_ = observerMethodName;
+ this.observers_ = [];
+}
+
+PollableDataHelper.prototype.addObserver = function(observer) {
+ this.observers_.push(observer);
+};
+
+/**
* Helper function to handle calling all the observers, but ONLY if the data has
* actually changed since last time. This is used for data we received from
* browser on a poll loop.
*/
-BrowserBridge.prototype.dispatchToObserversFromPoll_ = function(
- observerList, method, data) {
- var prevData = this.prevPollData_[method];
+PollableDataHelper.prototype.update = function(data) {
+ var prevData = this.currentData_;
// If the data hasn't changed since last time, no need to notify observers.
if (prevData && JSON.stringify(prevData) == JSON.stringify(data))
return;
- this.prevPollData_[method] = data;
+ this.currentData_ = data;
// Ok, notify the observers of the change.
- for (var i = 0; i < observerList.length; ++i)
- observerList[i][method](data);
+ for (var i = 0; i < this.observers_.length; ++i)
+ var observer = this.observers_[i];
+ observer[this.observerMethodName_](data);
};
diff --git a/chrome/chrome_browser.gypi b/chrome/chrome_browser.gypi
index a47c61b..3a59ea4 100644
--- a/chrome/chrome_browser.gypi
+++ b/chrome/chrome_browser.gypi
@@ -3333,6 +3333,7 @@
{
'destination': '<(PRODUCT_DIR)/resources/net_internals',
'files': [
+ 'browser/resources/net_internals/dataview.js',
'browser/resources/net_internals/detailsview.js',
'browser/resources/net_internals/dnsview.js',
'browser/resources/net_internals/index.html',