# HG changeset patch # User Jarda Snajdr Bug 1134073 - Part 3: Show network request cause and stacktrace in netmonitor - mochitests r=ochameau diff --git a/devtools/client/netmonitor/test/browser.ini b/devtools/client/netmonitor/test/browser.ini index 8137f47..4b36fc6 100644 --- a/devtools/client/netmonitor/test/browser.ini +++ b/devtools/client/netmonitor/test/browser.ini @@ -1,14 +1,15 @@ [DEFAULT] tags = devtools 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 html_custom-get-page.html html_single-get-page.html html_cyrillic-test-page.html html_filter-test-page.html html_infinite-get-page.html @@ -28,30 +29,33 @@ support-files = html_statistics-test-page.html html_status-codes-test-page.html html_api-calls-test-page.html html_copy-as-curl.html html_curl-utils.html 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 test-image.png service-workers/status-codes.html service-workers/status-codes-service-worker.js [browser_net_aaa_leaktest.js] [browser_net_accessibility-01.js] [browser_net_accessibility-02.js] 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] [browser_net_charts-03.js] [browser_net_charts-04.js] [browser_net_charts-05.js] [browser_net_charts-06.js] [browser_net_charts-07.js] diff --git a/devtools/client/netmonitor/test/browser_net_cause.js b/devtools/client/netmonitor/test/browser_net_cause.js new file mode 100644 index 0000000..c9b085e --- /dev/null +++ b/devtools/client/netmonitor/test/browser_net_cause.js @@ -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(); +}); diff --git a/devtools/client/netmonitor/test/browser_net_cause_redirect.js b/devtools/client/netmonitor/test/browser_net_cause_redirect.js new file mode 100644 index 0000000..ff9c0f2 --- /dev/null +++ b/devtools/client/netmonitor/test/browser_net_cause_redirect.js @@ -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(); +}); diff --git a/devtools/client/netmonitor/test/browser_net_image-tooltip.js b/devtools/client/netmonitor/test/browser_net_image-tooltip.js index f526cfb..c555710 100644 --- a/devtools/client/netmonitor/test/browser_net_image-tooltip.js +++ b/devtools/client/netmonitor/test/browser_net_image-tooltip.js @@ -18,42 +18,39 @@ add_task(function* test() { let onEvents = waitForNetworkEvents(monitor, 7); let onThumbnail = waitFor(monitor.panelWin, EVENTS.RESPONSE_IMAGE_THUMBNAIL_DISPLAYED); debuggee.performRequests(); yield onEvents; 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); onThumbnail = waitFor(monitor.panelWin, EVENTS.RESPONSE_IMAGE_THUMBNAIL_DISPLAYED); info("Reloading the debuggee and performing all requests again..."); yield NetMonitorController.triggerActivity(ACTIVITY_TYPE.RELOAD.WITH_CACHE_ENABLED); debuggee.performRequests(); yield onEvents; 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(); /** * 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); info("Tooltip was successfully opened for the image request."); is(tooltip.content.querySelector("image").src, TEST_IMAGE_DATA_URI, "The tooltip's image content is displayed correctly."); } diff --git a/devtools/client/netmonitor/test/browser_net_service-worker-status.js b/devtools/client/netmonitor/test/browser_net_service-worker-status.js index 85f16f7..a65e146 100644 --- a/devtools/client/netmonitor/test/browser_net_service-worker-status.js +++ b/devtools/client/netmonitor/test/browser_net_service-worker-status.js @@ -8,48 +8,64 @@ */ // Service workers only work on https 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 = [ { method: "GET", uri: URL + "service-workers/test/200", details: { status: 200, statusText: "OK (service worker)", displayedStatus: "service worker", type: "plain", fullMimeType: "text/plain; charset=UTF-8" - } + }, + stackFunctions: ["doXHR", "performRequests"] }, ]; info("Registering the service worker..."); yield debuggee.registerServiceWorker(); info("Performing requests..."); debuggee.performRequests(); yield waitForNetworkEvents(monitor, REQUEST_DATA.length); let index = 0; 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(); }); diff --git a/devtools/client/netmonitor/test/head.js b/devtools/client/netmonitor/test/head.js index 75a1630..850ab14 100644 --- a/devtools/client/netmonitor/test/head.js +++ b/devtools/client/netmonitor/test/head.js @@ -45,16 +45,20 @@ const SEND_BEACON_URL = EXAMPLE_URL + "html_send-beacon.html"; const CORS_URL = EXAMPLE_URL + "html_cors-test-page.html"; const SIMPLE_SJS = EXAMPLE_URL + "sjs_simple-test-server.sjs"; const CONTENT_TYPE_SJS = EXAMPLE_URL + "sjs_content-type-test-server.sjs"; 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=="; const FRAME_SCRIPT_UTILS_URL = "chrome://devtools/content/shared/frame-script-utils.js"; DevToolsUtils.testing = true; SimpleTest.registerCleanupFunction(() => { @@ -279,17 +283,17 @@ function verifyRequestItemTarget(aRequestItem, aMethod, aUrl, aData = {}) { let requestsMenu = aRequestItem.ownerView; let widgetIndex = requestsMenu.indexOfItem(aRequestItem); let visibleIndex = requestsMenu.visibleItems.indexOf(aRequestItem); 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; let uri = Services.io.newURI(aUrl, null, null).QueryInterface(Ci.nsIURL); let unicodeUrl = NetworkHelper.convertToUnicode(unescape(aUrl)); let name = NetworkHelper.convertToUnicode(unescape(uri.fileName || uri.filePath || "/")); let query = NetworkHelper.convertToUnicode(unescape(uri.query)); let hostPort = uri.hostPort; @@ -331,16 +335,25 @@ function verifyRequestItemTarget(aRequestItem, aMethod, aUrl, aData = {}) { let tooltip = target.querySelector(".requests-menu-status").getAttribute("tooltiptext"); info("Displayed status: " + value); info("Displayed code: " + codeValue); info("Tooltip status: " + tooltip); is(value, displayedStatus ? displayedStatus : status, "The displayed status is correct."); 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"); info("Displayed type: " + value); info("Tooltip type: " + tooltip); is(value, type, "The displayed type is correct."); is(tooltip, fullMimeType, "The tooltip type is correct."); } diff --git a/devtools/client/netmonitor/test/html_cause-test-page.html b/devtools/client/netmonitor/test/html_cause-test-page.html new file mode 100644 index 0000000..40a6f40 --- /dev/null +++ b/devtools/client/netmonitor/test/html_cause-test-page.html @@ -0,0 +1,33 @@ + + + + + + + + + + Network Monitor test page + + + + +

Request cause test

+ + + + diff --git a/devtools/client/netmonitor/test/service-workers/status-codes-service-worker.js b/devtools/client/netmonitor/test/service-workers/status-codes-service-worker.js index 6d43875..3c70c7d 100644 --- a/devtools/client/netmonitor/test/service-workers/status-codes-service-worker.js +++ b/devtools/client/netmonitor/test/service-workers/status-codes-service-worker.js @@ -1,8 +1,15 @@ /* vim: set ft=javascript ts=2 et sw=2 tw=80: */ /* 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); }); diff --git a/devtools/client/netmonitor/test/service-workers/status-codes.html b/devtools/client/netmonitor/test/service-workers/status-codes.html index 1cef820..65c79ee 100644 --- a/devtools/client/netmonitor/test/service-workers/status-codes.html +++ b/devtools/client/netmonitor/test/service-workers/status-codes.html @@ -10,30 +10,50 @@ Network Monitor test page

Status codes test

diff --git a/devtools/client/netmonitor/test/sjs_hsts-test-server.sjs b/devtools/client/netmonitor/test/sjs_hsts-test-server.sjs new file mode 100644 index 0000000..c571588 --- /dev/null +++ b/devtools/client/netmonitor/test/sjs_hsts-test-server.sjs @@ -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!"); + } +}