Bug 1134073 - Part 3: Show network request cause and stacktrace in netmonitor - mochitests r=ochameau

MozReview-Commit-ID: 1p5gHLp4Fdw
This commit is contained in:
Jarda Snajdr 2016-06-03 01:55:00 +02:00
parent add5047623
commit fb26b2e2cd
10 changed files with 289 additions and 25 deletions

View File

@ -4,6 +4,7 @@ subsuite = devtools
support-files =
dropmarker.svg
head.js
html_cause-test-page.html
html_content-type-test-page.html
html_content-type-without-cache-test-page.html
html_cors-test-page.html
@ -33,6 +34,7 @@ support-files =
sjs_content-type-test-server.sjs
sjs_cors-test-server.sjs
sjs_https-redirect-test-server.sjs
sjs_hsts-test-server.sjs
sjs_simple-test-server.sjs
sjs_sorting-test-server.sjs
sjs_status-codes-test-server.sjs
@ -47,6 +49,8 @@ skip-if = (toolkit == "cocoa" && e10s) # bug 1252254
[browser_net_api-calls.js]
[browser_net_autoscroll.js]
[browser_net_cached-status.js]
[browser_net_cause.js]
[browser_net_cause_redirect.js]
[browser_net_service-worker-status.js]
[browser_net_charts-01.js]
[browser_net_charts-02.js]

View File

@ -0,0 +1,102 @@
/* Any copyright is dedicated to the Public Domain.
http://creativecommons.org/publicdomain/zero/1.0/ */
"use strict";
/**
* Tests if request cause is reported correctly.
*/
const CAUSE_FILE_NAME = "html_cause-test-page.html";
const CAUSE_URL = EXAMPLE_URL + CAUSE_FILE_NAME;
const EXPECTED_REQUESTS = [
{
method: "GET",
url: CAUSE_URL,
causeType: "document",
causeUri: "",
// The document load is from JS function in e10s, native in non-e10s
hasStack: !gMultiProcessBrowser
},
{
method: "GET",
url: EXAMPLE_URL + "stylesheet_request",
causeType: "stylesheet",
causeUri: CAUSE_URL,
hasStack: false
},
{
method: "GET",
url: EXAMPLE_URL + "img_request",
causeType: "img",
causeUri: CAUSE_URL,
hasStack: false
},
{
method: "GET",
url: EXAMPLE_URL + "xhr_request",
causeType: "xhr",
causeUri: CAUSE_URL,
hasStack: { fn: "performXhrRequest", file: CAUSE_FILE_NAME, line: 22 }
},
{
method: "POST",
url: EXAMPLE_URL + "beacon_request",
causeType: "beacon",
causeUri: CAUSE_URL,
hasStack: { fn: "performBeaconRequest", file: CAUSE_FILE_NAME, line: 26 }
},
];
var test = Task.async(function* () {
// the initNetMonitor function clears the network request list after the
// page is loaded. That's why we first load a bogus page from SIMPLE_URL,
// and only then load the real thing from CAUSE_URL - we want to catch
// all the requests the page is making, not only the XHRs.
// We can't use about:blank here, because initNetMonitor checks that the
// page has actually made at least one request.
let [, debuggee, monitor] = yield initNetMonitor(SIMPLE_URL);
let { RequestsMenu } = monitor.panelWin.NetMonitorView;
RequestsMenu.lazyUpdate = false;
debuggee.location = CAUSE_URL;
yield waitForNetworkEvents(monitor, EXPECTED_REQUESTS.length);
is(RequestsMenu.itemCount, EXPECTED_REQUESTS.length,
"All the page events should be recorded.");
EXPECTED_REQUESTS.forEach((spec, i) => {
let { method, url, causeType, causeUri, hasStack } = spec;
let requestItem = RequestsMenu.getItemAtIndex(i);
verifyRequestItemTarget(requestItem,
method, url, { cause: { type: causeType, loadingDocumentUri: causeUri } }
);
let { stacktrace } = requestItem.attachment.cause;
let stackLen = stacktrace ? stacktrace.length : 0;
if (hasStack) {
ok(stacktrace, `Request #${i} has a stacktrace`);
ok(stackLen > 0,
`Request #${i} (${causeType}) has a stacktrace with ${stackLen} items`);
// if "hasStack" is object, check the details about the top stack frame
if (typeof hasStack === "object") {
is(stacktrace[0].functionName, hasStack.fn,
`Request #${i} has the correct function on top of the JS stack`);
is(stacktrace[0].filename.split("/").pop(), hasStack.file,
`Request #${i} has the correct file on top of the JS stack`);
is(stacktrace[0].lineNumber, hasStack.line,
`Request #${i} has the correct line number on top of the JS stack`);
}
} else {
is(stackLen, 0, `Request #${i} (${causeType}) has an empty stacktrace`);
}
});
yield teardown(monitor);
finish();
});

View File

@ -0,0 +1,50 @@
/* Any copyright is dedicated to the Public Domain.
http://creativecommons.org/publicdomain/zero/1.0/ */
"use strict";
/**
* Tests if request JS stack is property reported if the request is internally
* redirected without hitting the network (HSTS is one of such cases)
*/
var test = Task.async(function* () {
const EXPECTED_REQUESTS = [
// Request to HTTP URL, redirects to HTTPS, has callstack
{ status: 302, hasStack: true },
// Serves HTTPS, sets the Strict-Transport-Security header, no stack
{ status: 200, hasStack: false },
// Second request to HTTP redirects to HTTPS internally
{ status: 200, hasStack: true },
];
let [, debuggee, monitor] = yield initNetMonitor(CUSTOM_GET_URL);
let { RequestsMenu } = monitor.panelWin.NetMonitorView;
RequestsMenu.lazyUpdate = false;
debuggee.performRequests(2, HSTS_SJS);
yield waitForNetworkEvents(monitor, EXPECTED_REQUESTS.length);
EXPECTED_REQUESTS.forEach(({status, hasStack}, i) => {
let { attachment } = RequestsMenu.getItemAtIndex(i);
is(attachment.status, status, `Request #${i} has the expected status`);
let { stacktrace } = attachment.cause;
let stackLen = stacktrace ? stacktrace.length : 0;
if (hasStack) {
ok(stacktrace, `Request #${i} has a stacktrace`);
ok(stackLen > 0, `Request #${i} has a stacktrace with ${stackLen} items`);
} else {
is(stackLen, 0, `Request #${i} has an empty stacktrace`);
}
});
// Send a request to reset the HSTS policy to state before the test
debuggee.performRequests(1, HSTS_SJS + "?reset");
yield waitForNetworkEvents(monitor, 1);
yield teardown(monitor);
finish();
});

View File

@ -23,7 +23,7 @@ add_task(function* test() {
yield onThumbnail;
info("Checking the image thumbnail after a few requests were made...");
yield showTooltipAndVerify(RequestsMenu.items[5]);
yield showTooltipAndVerify(RequestsMenu.tooltip, RequestsMenu.items[5]);
// 7 XHRs as before + 1 extra document reload
onEvents = waitForNetworkEvents(monitor, 8);
@ -36,7 +36,7 @@ add_task(function* test() {
yield onThumbnail;
info("Checking the image thumbnail after a reload.");
yield showTooltipAndVerify(RequestsMenu.items[6]);
yield showTooltipAndVerify(RequestsMenu.tooltip, RequestsMenu.items[6]);
yield teardown(monitor);
finish();
@ -45,10 +45,7 @@ add_task(function* test() {
* Show a tooltip on the {requestItem} and verify that it was displayed
* with the expected content.
*/
function* showTooltipAndVerify(requestItem) {
let { tooltip } = requestItem.attachment;
ok(tooltip, "There should be a tooltip instance for the image request.");
function* showTooltipAndVerify(tooltip, requestItem) {
let anchor = $(".requests-menu-file", requestItem.target);
yield showTooltipOn(tooltip, anchor);

View File

@ -13,11 +13,11 @@ const URL = EXAMPLE_URL.replace("http:", "https:");
const TEST_URL = URL + "service-workers/status-codes.html";
var test = Task.async(function* () {
let [tab, debuggee, monitor] = yield initNetMonitor(TEST_URL, null, true);
let [, debuggee, monitor] = yield initNetMonitor(TEST_URL, null, true);
info("Starting test... ");
let { document, L10N, NetMonitorView } = monitor.panelWin;
let { RequestsMenu, NetworkDetails } = NetMonitorView;
let { NetMonitorView } = monitor.panelWin;
let { RequestsMenu } = NetMonitorView;
const REQUEST_DATA = [
{
@ -29,7 +29,8 @@ var test = Task.async(function* () {
displayedStatus: "service worker",
type: "plain",
fullMimeType: "text/plain; charset=UTF-8"
}
},
stackFunctions: ["doXHR", "performRequests"]
},
];
@ -44,12 +45,27 @@ var test = Task.async(function* () {
for (let request of REQUEST_DATA) {
let item = RequestsMenu.getItemAtIndex(index);
info("Verifying request #" + index);
info(`Verifying request #${index}`);
yield verifyRequestItemTarget(item, request.method, request.uri, request.details);
let { stacktrace } = item.attachment.cause;
let stackLen = stacktrace ? stacktrace.length : 0;
ok(stacktrace, `Request #${index} has a stacktrace`);
ok(stackLen >= request.stackFunctions.length,
`Request #${index} has a stacktrace with enough (${stackLen}) items`);
request.stackFunctions.forEach((functionName, j) => {
is(stacktrace[j].functionName, functionName,
`Request #${index} has the correct function at position #${j} on the stack`);
});
index++;
}
info("Unregistering the service worker...");
yield debuggee.unregisterServiceWorker();
yield teardown(monitor);
finish();
});

View File

@ -50,6 +50,10 @@ const STATUS_CODES_SJS = EXAMPLE_URL + "sjs_status-codes-test-server.sjs";
const SORTING_SJS = EXAMPLE_URL + "sjs_sorting-test-server.sjs";
const HTTPS_REDIRECT_SJS = EXAMPLE_URL + "sjs_https-redirect-test-server.sjs";
const CORS_SJS_PATH = "/browser/devtools/client/netmonitor/test/sjs_cors-test-server.sjs";
const HSTS_SJS = EXAMPLE_URL + "sjs_hsts-test-server.sjs";
const HSTS_BASE_URL = EXAMPLE_URL;
const HSTS_PAGE_URL = CUSTOM_GET_URL;
const TEST_IMAGE = EXAMPLE_URL + "test-image.png";
const TEST_IMAGE_DATA_URI = "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAABGdBTUEAAK/INwWK6QAAABl0RVh0U29mdHdhcmUAQWRvYmUgSW1hZ2VSZWFkeXHJZTwAAAHWSURBVHjaYvz//z8DJQAggJiQOe/fv2fv7Oz8rays/N+VkfG/iYnJfyD/1+rVq7ffu3dPFpsBAAHEAHIBCJ85c8bN2Nj4vwsDw/8zQLwKiO8CcRoQu0DxqlWrdsHUwzBAAIGJmTNnPgYa9j8UqhFElwPxf2MIDeIrKSn9FwSJoRkAEEAM0DD4DzMAyPi/G+QKY4hh5WAXGf8PDQ0FGwJ22d27CjADAAIIrLmjo+MXA9R2kAHvGBA2wwx6B8W7od6CeQcggKCmCEL8bgwxYCbUIGTDVkHDBia+CuotgACCueD3TDQN75D4xmAvCoK9ARMHBzAw0AECiBHkAlC0Mdy7x9ABNA3obAZXIAa6iKEcGlMVQHwWyjYuL2d4v2cPg8vZswx7gHyAAAK7AOif7SAbOqCmn4Ha3AHFsIDtgPq/vLz8P4MSkJ2W9h8ggBjevXvHDo4FQUQg/kdypqCg4H8lUIACnQ/SOBMYI8bAsAJFPcj1AAEEjwVQqLpAbXmH5BJjqI0gi9DTAAgDBBCcAVLkgmQ7yKCZxpCQxqUZhAECCJ4XgMl493ug21ZD+aDAXH0WLM4A9MZPXJkJIIAwTAR5pQMalaCABQUULttBGCCAGCnNzgABBgAMJ5THwGvJLAAAAABJRU5ErkJggg==";
@ -284,7 +288,7 @@ function verifyRequestItemTarget(aRequestItem, aMethod, aUrl, aData = {}) {
info("Widget index of item: " + widgetIndex);
info("Visible index of item: " + visibleIndex);
let { fuzzyUrl, status, statusText, type, fullMimeType,
let { fuzzyUrl, status, statusText, cause, type, fullMimeType,
transferred, size, time, displayedStatus } = aData;
let { attachment, target } = aRequestItem;
@ -336,6 +340,15 @@ function verifyRequestItemTarget(aRequestItem, aMethod, aUrl, aData = {}) {
is(codeValue, status, "The displayed status code is correct.");
is(tooltip, status + " " + statusText, "The tooltip status is correct.");
}
if (cause !== undefined) {
let causeLabel = target.querySelector(".requests-menu-cause-label");
let value = causeLabel.getAttribute("value");
let tooltip = causeLabel.getAttribute("tooltiptext");
info("Displayed cause: " + value);
info("Tooltip cause: " + tooltip);
is(value, cause.type, "The displayed cause is correct.");
is(tooltip, cause.loadingDocumentUri, "The tooltip cause is correct.")
}
if (type !== undefined) {
let value = target.querySelector(".requests-menu-type").getAttribute("value");
let tooltip = target.querySelector(".requests-menu-type").getAttribute("tooltiptext");

View File

@ -0,0 +1,33 @@
<!-- Any copyright is dedicated to the Public Domain.
http://creativecommons.org/publicdomain/zero/1.0/ -->
<!doctype html>
<html>
<head>
<meta charset="utf-8"/>
<meta http-equiv="Cache-Control" content="no-cache, no-store, must-revalidate" />
<meta http-equiv="Pragma" content="no-cache" />
<meta http-equiv="Expires" content="0" />
<title>Network Monitor test page</title>
<link rel="stylesheet" type="text/css" href="stylesheet_request" />
</head>
<body>
<p>Request cause test</p>
<img src="img_request" />
<script type="text/javascript">
function performXhrRequest() {
var xhr = new XMLHttpRequest();
xhr.open("GET", "xhr_request", true);
xhr.send();
}
function performBeaconRequest() {
navigator.sendBeacon("beacon_request");
}
performXhrRequest();
performBeaconRequest();
</script>
</body>
</html>

View File

@ -2,7 +2,14 @@
/* Any copyright is dedicated to the Public Domain.
http://creativecommons.org/publicdomain/zero/1.0/ */
addEventListener("fetch", function (event) {
"use strict";
self.addEventListener("activate", event => {
// start controlling the already loaded page
event.waitUntil(self.clients.claim());
});
self.addEventListener("fetch", event => {
let response = new Response("Service worker response");
event.respondWith(response);
});

View File

@ -15,24 +15,44 @@
<p>Status codes test</p>
<script type="text/javascript">
function get(url) {
return new Promise(done => {
let iframe = document.createElement("iframe");
iframe.setAttribute("src", url);
document.documentElement.appendChild(iframe);
iframe.contentWindow.onload = done;
});
}
let swRegistration;
function registerServiceWorker() {
return navigator.serviceWorker.register("status-codes-service-worker.js")
.then(() => navigator.serviceWorker.ready);
let sw = navigator.serviceWorker;
return sw.register("status-codes-service-worker.js")
.then(registration => {
swRegistration = registration;
console.log("Registered, scope is:", registration.scope);
return sw.ready;
}).then(() => {
// wait until the page is controlled
return new Promise(resolve => {
if (sw.controller) {
resolve();
} else {
sw.addEventListener('controllerchange', function onControllerChange() {
sw.removeEventListener('controllerchange', onControllerChange);
resolve();
});
}
});
}).catch(err => {
console.error("Registration failed");
});
}
function unregisterServiceWorker() {
return swRegistration.unregister();
}
function performRequests() {
return get("test/200");
return new Promise(function doXHR(done) {
let xhr = new XMLHttpRequest();
xhr.open("GET", "test/200", true);
xhr.onreadystatechange = done;
xhr.send(null);
});
}
</script>
</body>

View File

@ -0,0 +1,22 @@
/* Any copyright is dedicated to the Public Domain.
http://creativecommons.org/publicdomain/zero/1.0/ */
function handleRequest(request, response) {
response.setHeader("Cache-Control", "no-cache, no-store, must-revalidate");
response.setHeader("Pragma", "no-cache");
response.setHeader("Expires", "0");
if (request.queryString === "reset") {
// Reset the HSTS policy, prevent influencing other tests
response.setStatusLine(request.httpVersion, 200, "OK");
response.setHeader("Strict-Transport-Security", "max-age=0");
response.write("Resetting HSTS");
} else if (request.scheme === "http") {
response.setStatusLine(request.httpVersion, 302, "Found");
response.setHeader("Location", "https://" + request.host + request.path);
} else {
response.setStatusLine(request.httpVersion, 200, "OK");
response.setHeader("Strict-Transport-Security", "max-age=100");
response.write("Page was accessed over HTTPS!");
}
}