Commit 853b209f authored by Douglas Creager's avatar Douglas Creager Committed by Commit Bot

Reporting: Show reporting cache in net-internals

This patch adds a new tab to the net-internals page, which shows the
current contents of the Reporting cache.  This includes a section on any
queued reports, and a section showing any origins that we've received
Reporting headers from.

Bug: 829353
Cq-Include-Trybots: master.tryserver.chromium.linux:closure_compilation
Change-Id: I496d360fb1cf0f5e6c626e72415d8a624a25151b
Reviewed-on: https://chromium-review.googlesource.com/998752
Commit-Queue: Douglas Creager <dcreager@google.com>
Reviewed-by: default avatarEric Roman <eroman@chromium.org>
Cr-Commit-Position: refs/heads/master@{#550771}
parent ff1eb686
...@@ -53,6 +53,8 @@ var BrowserBridge = (function() { ...@@ -53,6 +53,8 @@ var BrowserBridge = (function() {
this.addNetInfoPollableDataHelper( this.addNetInfoPollableDataHelper(
'altSvcMappings', 'onAltSvcMappingsChanged'); 'altSvcMappings', 'onAltSvcMappingsChanged');
this.addNetInfoPollableDataHelper('quicInfo', 'onQuicInfoChanged'); this.addNetInfoPollableDataHelper('quicInfo', 'onQuicInfoChanged');
this.addNetInfoPollableDataHelper(
'reportingInfo', 'onReportingInfoChanged');
this.addNetInfoPollableDataHelper( this.addNetInfoPollableDataHelper(
'httpCacheInfo', 'onHttpCacheInfoChanged'); 'httpCacheInfo', 'onHttpCacheInfoChanged');
...@@ -481,6 +483,17 @@ var BrowserBridge = (function() { ...@@ -481,6 +483,17 @@ var BrowserBridge = (function() {
observer, ignoreWhenUnchanged); observer, ignoreWhenUnchanged);
}, },
/**
* Adds a listener of the Reporting info. |observer| will be called back
* when data is received, through:
*
* observer.onReportingInfoChanged(reportingInfo)
*/
addReportingInfoObserver: function(observer, ignoreWhenUnchanged) {
this.pollableDataHelpers_.reportingInfo.addObserver(
observer, ignoreWhenUnchanged);
},
/** /**
* Adds a listener of the SPDY info. |observer| will be called back * Adds a listener of the SPDY info. |observer| will be called back
* when data is received, through: * when data is received, through:
......
...@@ -35,6 +35,7 @@ found in the LICENSE file. ...@@ -35,6 +35,7 @@ found in the LICENSE file.
<include src="alt_svc_view.html"> <include src="alt_svc_view.html">
<include src="spdy_view.html"> <include src="spdy_view.html">
<include src="quic_view.html"> <include src="quic_view.html">
<include src="reporting_view.html">
<include src="http_cache_view.html"> <include src="http_cache_view.html">
<include src="bandwidth_view.html"> <include src="bandwidth_view.html">
<include src="prerender_view.html"> <include src="prerender_view.html">
......
...@@ -37,6 +37,7 @@ ...@@ -37,6 +37,7 @@
// <include src="log_grouper.js"> // <include src="log_grouper.js">
// <include src="proxy_view.js"> // <include src="proxy_view.js">
// <include src="quic_view.js"> // <include src="quic_view.js">
// <include src="reporting_view.js">
// <include src="socket_pool_wrapper.js"> // <include src="socket_pool_wrapper.js">
// <include src="sockets_view.js"> // <include src="sockets_view.js">
// <include src="alt_svc_view.js"> // <include src="alt_svc_view.js">
......
...@@ -189,6 +189,7 @@ var MainView = (function() { ...@@ -189,6 +189,7 @@ var MainView = (function() {
addTab(AltSvcView); addTab(AltSvcView);
addTab(SpdyView); addTab(SpdyView);
addTab(QuicView); addTab(QuicView);
addTab(ReportingView);
addTab(HttpCacheView); addTab(HttpCacheView);
addTab(ModulesView); addTab(ModulesView);
addTab(DomainSecurityPolicyView); addTab(DomainSecurityPolicyView);
......
<style>
td.reporting-centered {
text-align: center;
}
td .reporting-content-summary {
}
td .reporting-content-expand-button {
color: rgb(0, 0, 255);
cursor: pointer;
float: right;
font-size: 80%;
margin-left: 1em;
text-decoration: underline;
}
td .reporting-content-detail {
}
</style>
<div id=reporting-view-tab-content class=content-box>
<!-- Only one of these two are shown -->
<div id=reporting-view-disabled-content><h4>Reporting is disabled</h4></div>
<div id=reporting-view-enabled-content>
<h4 style='margin-top:0'>Queued reports</h4>
<table id=reporting-view-reports-table class="styled-table">
<thead>
<tr>
<th>Queued</th>
<th>URL</th>
<th>Status</th>
<th>Type</th>
<th>Content</th>
</tr>
</thead>
<tbody id=reporting-view-reports-tbody>
</tbody>
</table>
<div id=reporting-view-reports-empty>There are no queued reports</div>
<h4>Per-origin config</h4>
<table id=reporting-view-clients-table class="styled-table">
<thead>
<tr>
<th>Origin</th>
<th>Group</th>
<th>Expires</th>
<th>Endpoint</th>
<th>Priority</th>
<th>Weight</th>
<th>Uploads</th>
<th>Failed uploads</th>
</tr>
</thead>
<tbody id=reporting-view-clients-tbody>
</tbody>
</table>
<div id=reporting-view-clients-empty>
No origins have provided Reporting headers
</div>
</div>
</div>
// Copyright (c) 2018 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 a summary of the current Reporting cache, including the
* configuration headers received for Reporting-enabled origins, and any queued
* reports that are waiting to be uploaded.
*/
var ReportingView = (function() {
'use strict';
// We inherit from DivView.
var superClass = DivView;
/**
* @constructor
*/
function ReportingView() {
assertFirstConstructorCall(ReportingView);
// Call superclass's constructor.
superClass.call(this, ReportingView.MAIN_BOX_ID);
g_browser.addReportingInfoObserver(this, true);
}
ReportingView.TAB_ID = 'tab-handle-reporting';
ReportingView.TAB_NAME = 'Reporting';
ReportingView.TAB_HASH = '#reporting';
// IDs for special HTML elements in reporting_view.html
ReportingView.MAIN_BOX_ID = 'reporting-view-tab-content';
ReportingView.DISABLED_BOX_ID = 'reporting-view-disabled-content';
ReportingView.ENABLED_BOX_ID = 'reporting-view-enabled-content';
ReportingView.CLIENTS_EMPTY_ID = 'reporting-view-clients-empty';
ReportingView.CLIENTS_TABLE_ID = 'reporting-view-clients-table';
ReportingView.CLIENTS_TBODY_ID = 'reporting-view-clients-tbody';
ReportingView.REPORTS_EMPTY_ID = 'reporting-view-reports-empty';
ReportingView.REPORTS_TABLE_ID = 'reporting-view-reports-table';
ReportingView.REPORTS_TBODY_ID = 'reporting-view-reports-tbody';
cr.addSingletonGetter(ReportingView);
ReportingView.prototype = {
// Inherit the superclass's methods.
__proto__: superClass.prototype,
onLoadLogFinish: function(data) {
return this.onReportingInfoChanged(data.reportingInfo);
},
onReportingInfoChanged: function(reportingInfo) {
if (!reportingInfo)
return false;
var enabled = reportingInfo.reportingEnabled;
setNodeDisplay($(ReportingView.DISABLED_BOX_ID), !enabled);
setNodeDisplay($(ReportingView.ENABLED_BOX_ID), enabled);
displayReportDetail_(reportingInfo.reports);
displayClientDetail_(reportingInfo.clients);
return true;
},
};
/**
* Displays information about each queued report in the Reporting cache.
*/
function displayReportDetail_(reportList) {
// Clear the existing content.
$(ReportingView.REPORTS_TBODY_ID).innerHTML = '';
var empty = reportList.length == 0;
setNodeDisplay($(ReportingView.REPORTS_EMPTY_ID), empty);
setNodeDisplay($(ReportingView.REPORTS_TABLE_ID), !empty);
if (empty)
return;
for (var i = 0; i < reportList.length; ++i) {
var report = reportList[i];
var tr = addNode($(ReportingView.REPORTS_TBODY_ID), 'tr');
var queuedNode = addNode(tr, 'td');
var queuedDate = timeutil.convertTimeTicksToDate(report.queued);
timeutil.addNodeWithDate(queuedNode, queuedDate);
addNodeWithText(tr, 'td', report.url);
var statusNode = addNode(tr, 'td');
addTextNode(statusNode, report.status + ' (' + report.group);
if (report.depth > 0)
addTextNode(statusNode, ', depth: ' + report.depth);
if (report.attempts > 0)
addTextNode(statusNode, ', attempts: ' + report.attempts);
addTextNode(statusNode, ')');
addNodeWithText(tr, 'td', report.type);
var contentNode = addNode(tr, 'td');
if (report.type == 'network-error')
displayNetworkErrorContent_(contentNode, report);
else
displayGenericReportContent_(contentNode, report);
}
}
/**
* Adds nodes to the "content" cell for a report that allow you to show a
* summary as well as collapsable detail. We will add a clickable button that
* toggles between showing and hiding the detail; its label will be `showText`
* when the detail is hidden, and `hideText` when it's visible.
*
* The result is an object containing `summary` and `detail` nodes. You can
* add whatever content you want to each of these nodes. The summary should
* be a one-liner, and will be a <span>. The detail can be as large as you
* want, and will be a <div>.
*/
function addContentSections_(contentNode, showText, hideText) {
var sections = {};
sections.summary = addNode(contentNode, 'span');
sections.summary.classList.add('reporting-content-summary');
var button = addNode(contentNode, 'span');
button.classList.add('reporting-content-expand-button');
addTextNode(button, showText);
button.onclick = function() {
toggleNodeDisplay(sections.detail);
button.textContent =
getNodeDisplay(sections.detail) ? hideText : showText;
};
sections.detail = addNode(contentNode, 'div');
sections.detail.classList.add('reporting-content-detail');
setNodeDisplay(sections.detail, false);
return sections;
}
/**
* Displays format-specific detail for Network Error Logging reports.
*/
function displayNetworkErrorContent_(contentNode, report) {
var contentSections =
addContentSections_(contentNode, 'Show raw report', 'Hide raw report');
addTextNode(contentSections.summary, report.body.type);
// Only show the status code if it's present and not 0.
if (report.body['status-code'])
addTextNode(
contentSections.summary, ' (' + report.body['status-code'] + ')');
addNodeWithText(
contentSections.detail, 'pre', JSON.stringify(report, null, ' '));
}
/**
* Displays a generic content cell for reports whose type we don't know how to
* render something specific for.
*/
function displayGenericReportContent_(contentNode, report) {
var contentSections =
addContentSections_(contentNode, 'Show raw report', 'Hide raw report');
addNodeWithText(
contentSections.detail, 'pre', JSON.stringify(report, null, ' '));
}
/**
* Displays information about each origin that has provided Reporting headers.
*/
function displayClientDetail_(clientList) {
// Clear the existing content.
$(ReportingView.CLIENTS_TBODY_ID).innerHTML = '';
var empty = clientList.length == 0;
setNodeDisplay($(ReportingView.CLIENTS_EMPTY_ID), empty);
setNodeDisplay($(ReportingView.CLIENTS_TABLE_ID), !empty);
if (empty)
return;
for (var i = 0; i < clientList.length; ++i) {
var now = new Date();
var client = clientList[i];
// Calculate the total number of endpoints for this origin, so that we can
// rowspan its origin cell.
var originHeight = 0;
for (var j = 0; j < client.groups.length; ++j) {
var group = client.groups[j];
originHeight += group.endpoints.length;
}
for (var j = 0; j < client.groups.length; ++j) {
var group = client.groups[j];
for (var k = 0; k < group.endpoints.length; ++k) {
var endpoint = group.endpoints[k];
var tr = addNode($(ReportingView.CLIENTS_TBODY_ID), 'tr');
if (j == 0 && k == 0) {
var originNode = addNode(tr, 'td');
originNode.setAttribute('rowspan', originHeight);
addTextNode(originNode, client.origin);
}
if (k == 0) {
var groupNode = addNode(tr, 'td');
groupNode.setAttribute('rowspan', group.endpoints.length);
addTextNode(groupNode, group.name);
var expiresNode = addNode(tr, 'td');
expiresNode.setAttribute('rowspan', group.endpoints.length);
var expiresDate = timeutil.convertTimeTicksToDate(group.expires);
timeutil.addNodeWithDate(expiresNode, expiresDate);
if (now > expiresDate) {
var expiredSpan = addNode(expiresNode, 'span');
expiredSpan.classList.add('warning-text');
addTextNode(expiredSpan, ' [expired]');
}
}
var endpointNode = addNode(tr, 'td');
addTextNode(endpointNode, endpoint.url);
var priorityNode = addNode(tr, 'td');
priorityNode.classList.add('reporting-centered');
addTextNode(priorityNode, endpoint.priority);
var weightNode = addNode(tr, 'td');
weightNode.classList.add('reporting-centered');
addTextNode(weightNode, endpoint.weight);
addUploadCount_(tr, endpoint.successful);
addUploadCount_(tr, endpoint.failed);
}
}
}
}
/**
* Adds an upload count cell to the client details table.
*/
function addUploadCount_(tr, counts) {
var node = addNode(tr, 'td');
node.classList.add('reporting-centered');
if (counts.uploads == 0 && counts.reports == 0) {
addTextNode(node, '-');
} else {
addTextNode(node, counts.uploads + ' (' + counts.reports + ')');
}
}
return ReportingView;
})();
...@@ -44,6 +44,22 @@ function setNodeDisplay(node, isVisible) { ...@@ -44,6 +44,22 @@ function setNodeDisplay(node, isVisible) {
node.style.display = isVisible ? '' : 'none'; node.style.display = isVisible ? '' : 'none';
} }
/**
* Toggles the visibility of a DOM node.
* @param {!HtmlNode} node The node to show or hide.
*/
function toggleNodeDisplay(node) {
setNodeDisplay(node, !getNodeDisplay(node));
}
/**
* Returns the visibility of a DOM node.
* @param {!HtmlNode} node The node to query.
*/
function getNodeDisplay(node) {
return node.style.display != 'none';
}
/** /**
* Adds a node to |parentNode|, of type |tagName|. * Adds a node to |parentNode|, of type |tagName|.
* @param {!HtmlNode} parentNode The node that will be the parent of the new * @param {!HtmlNode} parentNode The node that will be the parent of the new
......
...@@ -99,7 +99,7 @@ function GetNetLogFileContentsAndLoadLogTask(truncate) { ...@@ -99,7 +99,7 @@ function GetNetLogFileContentsAndLoadLogTask(truncate) {
NetInternalsTest.Task.call(this); NetInternalsTest.Task.call(this);
this.setCompleteAsync(true); this.setCompleteAsync(true);
this.truncate_ = truncate; this.truncate_ = truncate;
}; }
GetNetLogFileContentsAndLoadLogTask.prototype = { GetNetLogFileContentsAndLoadLogTask.prototype = {
__proto__: NetInternalsTest.Task.prototype, __proto__: NetInternalsTest.Task.prototype,
...@@ -156,6 +156,7 @@ function checkViewsAfterLogLoaded() { ...@@ -156,6 +156,7 @@ function checkViewsAfterLogLoaded() {
sockets: true, sockets: true,
http2: true, http2: true,
quic: true, quic: true,
reporting: true,
'alt-svc': true, 'alt-svc': true,
httpCache: true, httpCache: true,
modules: true, modules: true,
...@@ -183,6 +184,7 @@ function checkViewsAfterNetLogFileLoaded() { ...@@ -183,6 +184,7 @@ function checkViewsAfterNetLogFileLoaded() {
sockets: false, sockets: false,
http2: false, http2: false,
quic: false, quic: false,
reporting: false,
'alt-svc': false, 'alt-svc': false,
httpCache: false, httpCache: false,
modules: false, modules: false,
......
...@@ -30,6 +30,7 @@ TEST_F('NetInternalsTest', 'netInternalsTourTabs', function() { ...@@ -30,6 +30,7 @@ TEST_F('NetInternalsTest', 'netInternalsTourTabs', function() {
sockets: true, sockets: true,
http2: true, http2: true,
quic: true, quic: true,
reporting: true,
'alt-svc': true, 'alt-svc': true,
httpCache: true, httpCache: true,
modules: true, modules: true,
......
...@@ -309,6 +309,7 @@ var NetInternalsTest = (function() { ...@@ -309,6 +309,7 @@ var NetInternalsTest = (function() {
http2: SpdyView.TAB_ID, http2: SpdyView.TAB_ID,
'alt-svc': AltSvcView.TAB_ID, 'alt-svc': AltSvcView.TAB_ID,
quic: QuicView.TAB_ID, quic: QuicView.TAB_ID,
reporting: ReportingView.TAB_ID,
httpCache: HttpCacheView.TAB_ID, httpCache: HttpCacheView.TAB_ID,
modules: ModulesView.TAB_ID, modules: ModulesView.TAB_ID,
hsts: DomainSecurityPolicyView.TAB_ID, hsts: DomainSecurityPolicyView.TAB_ID,
......
...@@ -21,3 +21,4 @@ NET_INFO_SOURCE(SPDY_SESSIONS, "spdySessionInfo", 1 << 5) ...@@ -21,3 +21,4 @@ NET_INFO_SOURCE(SPDY_SESSIONS, "spdySessionInfo", 1 << 5)
NET_INFO_SOURCE(SPDY_STATUS, "spdyStatus", 1 << 6) NET_INFO_SOURCE(SPDY_STATUS, "spdyStatus", 1 << 6)
NET_INFO_SOURCE(ALT_SVC_MAPPINGS, "altSvcMappings", 1 << 7) NET_INFO_SOURCE(ALT_SVC_MAPPINGS, "altSvcMappings", 1 << 7)
NET_INFO_SOURCE(HTTP_CACHE, "httpCacheInfo", 1 << 8) NET_INFO_SOURCE(HTTP_CACHE, "httpCacheInfo", 1 << 8)
NET_INFO_SOURCE(REPORTING, "reportingInfo", 1 << 9)
...@@ -42,6 +42,10 @@ ...@@ -42,6 +42,10 @@
#include "net/url_request/url_request.h" #include "net/url_request/url_request.h"
#include "net/url_request/url_request_context.h" #include "net/url_request/url_request_context.h"
#if BUILDFLAG(ENABLE_REPORTING)
#include "net/reporting/reporting_service.h"
#endif // BUILDFLAG(ENABLE_REPORTING)
namespace net { namespace net {
namespace { namespace {
...@@ -450,6 +454,26 @@ NET_EXPORT std::unique_ptr<base::DictionaryValue> GetNetInfo( ...@@ -450,6 +454,26 @@ NET_EXPORT std::unique_ptr<base::DictionaryValue> GetNetInfo(
std::move(info_dict)); std::move(info_dict));
} }
if (info_sources & NET_INFO_REPORTING) {
#if BUILDFLAG(ENABLE_REPORTING)
ReportingService* reporting_service = context->reporting_service();
if (reporting_service) {
net_info_dict->SetKey(NetInfoSourceToString(NET_INFO_REPORTING),
reporting_service->StatusAsValue());
} else {
base::Value reporting_dict(base::Value::Type::DICTIONARY);
reporting_dict.SetKey("reportingEnabled", base::Value(false));
net_info_dict->SetKey(NetInfoSourceToString(NET_INFO_REPORTING),
std::move(reporting_dict));
}
#else // BUILDFLAG(ENABLE_REPORTING)
base::Value reporting_dict(base::Value::Type::DICTIONARY);
reporting_dict.SetKey("reportingEnabled", base::Value(false));
net_info_dict->SetKey(NetInfoSourceToString(NET_INFO_REPORTING),
std::move(reporting_dict));
#endif // BUILDFLAG(ENABLE_REPORTING)
}
return net_info_dict; return net_info_dict;
} }
......
...@@ -73,6 +73,14 @@ class ReportingServiceImpl : public ReportingService { ...@@ -73,6 +73,14 @@ class ReportingServiceImpl : public ReportingService {
return context_->policy(); return context_->policy();
} }
base::Value StatusAsValue() const override {
base::Value dict(base::Value::Type::DICTIONARY);
dict.SetKey("reportingEnabled", base::Value(true));
dict.SetKey("clients", context_->cache()->GetClientsAsValue());
dict.SetKey("reports", context_->cache()->GetReportsAsValue());
return dict;
}
private: private:
void ProcessHeaderValue(const GURL& url, std::unique_ptr<base::Value> value) { void ProcessHeaderValue(const GURL& url, std::unique_ptr<base::Value> value) {
ReportingHeaderParser::ParseHeader(context_.get(), url, std::move(value)); ReportingHeaderParser::ParseHeader(context_.get(), url, std::move(value));
...@@ -102,4 +110,9 @@ std::unique_ptr<ReportingService> ReportingService::CreateForTesting( ...@@ -102,4 +110,9 @@ std::unique_ptr<ReportingService> ReportingService::CreateForTesting(
return std::make_unique<ReportingServiceImpl>(std::move(reporting_context)); return std::make_unique<ReportingServiceImpl>(std::move(reporting_context));
} }
base::Value ReportingService::StatusAsValue() const {
NOTIMPLEMENTED();
return base::Value();
}
} // namespace net } // namespace net
...@@ -73,6 +73,8 @@ class NET_EXPORT ReportingService { ...@@ -73,6 +73,8 @@ class NET_EXPORT ReportingService {
virtual const ReportingPolicy& GetPolicy() const = 0; virtual const ReportingPolicy& GetPolicy() const = 0;
virtual base::Value StatusAsValue() const;
protected: protected:
ReportingService() {} ReportingService() {}
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment