Skip to content

Commit

Permalink
Merge inbound to mozilla-central. a=merge
Browse files Browse the repository at this point in the history
  • Loading branch information
Mihai Alexandru Michis committed May 17, 2019
2 parents 65e97bc + cba8f8f commit 0078b9e
Show file tree
Hide file tree
Showing 27 changed files with 361 additions and 265 deletions.
4 changes: 4 additions & 0 deletions devtools/client/netmonitor/test/browser.ini
Expand Up @@ -44,6 +44,9 @@ support-files =
html_copy-as-curl.html
html_curl-utils.html
html_open-request-in-tab.html
html_worker-test-page.html
js_worker-test.js
js_worker-test2.js
sjs_content-type-test-server.sjs
sjs_cors-test-server.sjs
sjs_https-redirect-test-server.sjs
Expand Down Expand Up @@ -221,3 +224,4 @@ skip-if = true # TODO: fix the test
[browser_net_truncate.js]
[browser_net_view-source-debugger.js]
[browser_net_waterfall-click.js]
[browser_net_worker_stacks.js]
40 changes: 1 addition & 39 deletions devtools/client/netmonitor/test/browser_net_cause.js
Expand Up @@ -93,7 +93,6 @@ add_task(async function() {
const { document, store, windowRequire, connector } = monitor.panelWin;
const Actions = windowRequire("devtools/client/netmonitor/src/actions/index");
const {
getDisplayedRequests,
getSortedRequests,
} = windowRequire("devtools/client/netmonitor/src/selectors/index");

Expand All @@ -110,44 +109,7 @@ add_task(async function() {
is(store.getState().requests.requests.size, EXPECTED_REQUESTS.length,
"All the page events should be recorded.");

EXPECTED_REQUESTS.forEach((spec, i) => {
const { method, url, causeType, causeUri, stack } = spec;

const requestItem = getSortedRequests(store.getState()).get(i);
verifyRequestItemTarget(
document,
getDisplayedRequests(store.getState()),
requestItem,
method,
url,
{ cause: { type: causeType, loadingDocumentUri: causeUri } }
);

const stacktrace = requestItem.stacktrace;
const stackLen = stacktrace ? stacktrace.length : 0;

if (stack) {
ok(stacktrace, `Request #${i} has a stacktrace`);
ok(stackLen > 0,
`Request #${i} (${causeType}) has a stacktrace with ${stackLen} items`);

// if "stack" is array, check the details about the top stack frames
if (Array.isArray(stack)) {
stack.forEach((frame, j) => {
is(stacktrace[j].functionName, frame.fn,
`Request #${i} has the correct function on JS stack frame #${j}`);
is(stacktrace[j].filename.split("/").pop(), frame.file,
`Request #${i} has the correct file on JS stack frame #${j}`);
is(stacktrace[j].lineNumber, frame.line,
`Request #${i} has the correct line number on JS stack frame #${j}`);
is(stacktrace[j].asyncCause, frame.asyncCause,
`Request #${i} has the correct async cause on JS stack frame #${j}`);
});
}
} else {
is(stackLen, 0, `Request #${i} (${causeType}) has an empty stacktrace`);
}
});
validateRequests(EXPECTED_REQUESTS, monitor);

// Sort the requests by cause and check the order
EventUtils.sendMouseEvent({ type: "click" },
Expand Down
100 changes: 100 additions & 0 deletions devtools/client/netmonitor/test/browser_net_worker_stacks.js
@@ -0,0 +1,100 @@
/* Any copyright is dedicated to the Public Domain.
http://creativecommons.org/publicdomain/zero/1.0/ */

"use strict";

// Test that we get stack traces for the network requests made when starting or
// running worker threads.

const TOP_FILE_NAME = "html_worker-test-page.html";
const TOP_URL = EXAMPLE_URL + TOP_FILE_NAME;
const WORKER_FILE_NAME = "js_worker-test.js";
const WORKER_URL = EXAMPLE_URL + WORKER_FILE_NAME;

const EXPECTED_REQUESTS = [
{
method: "GET",
url: TOP_URL,
causeType: "document",
causeUri: null,
stack: true,
},
{
method: "GET",
url: WORKER_URL,
causeType: "script",
causeUri: TOP_URL,
stack: [
{ fn: "startWorkerInner", file: TOP_FILE_NAME, line: 11 },
{ fn: "startWorker", file: TOP_FILE_NAME, line: 8 },
{ file: TOP_FILE_NAME, line: 4 },
],
},
{
method: "GET",
url: EXAMPLE_URL + "missing1.js",
causeType: "script",
causeUri: TOP_URL,
stack: [
{ fn: "importScriptsFromWorker", file: WORKER_FILE_NAME, line: 14 },
{ file: WORKER_FILE_NAME, line: 10 },
],
},
{
method: "GET",
url: EXAMPLE_URL + "missing2.js",
causeType: "script",
causeUri: TOP_URL,
stack: [
{ fn: "importScriptsFromWorker", file: WORKER_FILE_NAME, line: 14 },
{ file: WORKER_FILE_NAME, line: 10 },
],
},
{
method: "GET",
url: EXAMPLE_URL + "js_worker-test2.js",
causeType: "script",
causeUri: TOP_URL,
stack: [
{ fn: "startWorkerFromWorker", file: WORKER_FILE_NAME, line: 7 },
{ file: WORKER_FILE_NAME, line: 3 },
],
},
{
method: "GET",
url: EXAMPLE_URL + "missing.json",
causeType: "xhr",
causeUri: TOP_URL,
stack: [
{ fn: "createJSONRequest", file: WORKER_FILE_NAME, line: 22 },
{ file: WORKER_FILE_NAME, line: 18 },
],
},
];

add_task(async function() {
// Load a different URL first to instantiate the network monitor before we
// load the page we're really interested in.
const { tab, monitor } = await initNetMonitor(SIMPLE_URL);

const { store, windowRequire, connector } = monitor.panelWin;
const {
getSortedRequests,
} = windowRequire("devtools/client/netmonitor/src/selectors/index");

BrowserTestUtils.loadURI(tab.linkedBrowser, TOP_URL);

await waitForNetworkEvents(monitor, EXPECTED_REQUESTS.length);

is(store.getState().requests.requests.size, EXPECTED_REQUESTS.length,
"All the page events should be recorded.");

// Wait for stack traces from all requests.
const requests = getSortedRequests(store.getState());
await Promise.all(requests.map(requestItem =>
connector.requestData(requestItem.id, "stackTrace")));

validateRequests(EXPECTED_REQUESTS, monitor);

await teardown(monitor);
});
47 changes: 47 additions & 0 deletions devtools/client/netmonitor/test/head.js
Expand Up @@ -869,6 +869,53 @@ function queryTelemetryEvents(query) {
return filtersChangedEvents.map(event => event[5]);
}

function validateRequests(requests, monitor) {
const { document, store, windowRequire } = monitor.panelWin;

const {
getDisplayedRequests,
} = windowRequire("devtools/client/netmonitor/src/selectors/index");

requests.forEach((spec, i) => {
const { method, url, causeType, causeUri, stack } = spec;

const requestItem = getSortedRequests(store.getState()).get(i);
verifyRequestItemTarget(
document,
getDisplayedRequests(store.getState()),
requestItem,
method,
url,
{ cause: { type: causeType, loadingDocumentUri: causeUri } }
);

const stacktrace = requestItem.stacktrace;
const stackLen = stacktrace ? stacktrace.length : 0;

if (stack) {
ok(stacktrace, `Request #${i} has a stacktrace`);
ok(stackLen > 0,
`Request #${i} (${causeType}) has a stacktrace with ${stackLen} items`);

// if "stack" is array, check the details about the top stack frames
if (Array.isArray(stack)) {
stack.forEach((frame, j) => {
is(stacktrace[j].functionName, frame.fn,
`Request #${i} has the correct function on JS stack frame #${j}`);
is(stacktrace[j].filename.split("/").pop(), frame.file,
`Request #${i} has the correct file on JS stack frame #${j}`);
is(stacktrace[j].lineNumber, frame.line,
`Request #${i} has the correct line number on JS stack frame #${j}`);
is(stacktrace[j].asyncCause, frame.asyncCause,
`Request #${i} has the correct async cause on JS stack frame #${j}`);
});
}
} else {
is(stackLen, 0, `Request #${i} (${causeType}) has an empty stacktrace`);
}
});
}

/**
* Retrieve the context menu element corresponding to the provided id, for the provided
* netmonitor instance.
Expand Down
13 changes: 13 additions & 0 deletions devtools/client/netmonitor/test/html_worker-test-page.html
@@ -0,0 +1,13 @@
<script>
/* eslint-disable no-unused-vars */
"use strict";
startWorker();

var w;
function startWorker() {
startWorkerInner();
}
function startWorkerInner() {
w = new Worker("js_worker-test.js");
}
</script>
24 changes: 24 additions & 0 deletions devtools/client/netmonitor/test/js_worker-test.js
@@ -0,0 +1,24 @@
/* eslint-disable no-unused-vars, no-undef */
"use strict";
startWorkerFromWorker();

var w;
function startWorkerFromWorker() {
w = new Worker("js_worker-test2.js");
}

importScriptsFromWorker();

function importScriptsFromWorker() {
try {
importScripts("missing1.js", "missing2.js");
} catch (e) {}
}

createJSONRequest();

function createJSONRequest() {
const request = new XMLHttpRequest();
request.open("GET", "missing.json", true);
request.send(null);
}
3 changes: 3 additions & 0 deletions devtools/client/netmonitor/test/js_worker-test2.js
@@ -0,0 +1,3 @@
"use strict";

console.log("I AM A WORKER");
75 changes: 60 additions & 15 deletions devtools/server/actors/network-monitor/stack-trace-collector.js
Expand Up @@ -25,6 +25,7 @@ function StackTraceCollector(filters, netmonitors) {
StackTraceCollector.prototype = {
init() {
Services.obs.addObserver(this, "http-on-opening-request");
Services.obs.addObserver(this, "network-monitor-alternate-stack");
ChannelEventSinkFactory.getService().registerCollector(this);
this.onGetStack = this.onGetStack.bind(this);
for (const { messageManager } of this.netmonitors) {
Expand All @@ -34,6 +35,7 @@ StackTraceCollector.prototype = {

destroy() {
Services.obs.removeObserver(this, "http-on-opening-request");
Services.obs.removeObserver(this, "network-monitor-alternate-stack");
ChannelEventSinkFactory.getService().unregisterCollector(this);
for (const { messageManager } of this.netmonitors) {
messageManager.removeMessageListener("debug:request-stack:request",
Expand All @@ -42,6 +44,12 @@ StackTraceCollector.prototype = {
},

_saveStackTrace(channel, stacktrace) {
if (this.stacktracesById.has(channel.channelId)) {
// We can get up to two stack traces for the same channel: one each from
// the two observer topics we are listening to. Use the first stack trace
// which is specified, and ignore any later one.
return;
}
for (const { messageManager } of this.netmonitors) {
messageManager.sendAsyncMessage("debug:request-stack-available", {
channelId: channel.channelId,
Expand All @@ -51,29 +59,66 @@ StackTraceCollector.prototype = {
this.stacktracesById.set(channel.channelId, stacktrace);
},

observe(subject) {
observe(subject, topic, data) {
const channel = subject.QueryInterface(Ci.nsIHttpChannel);

if (!matchRequest(channel, this.filters)) {
return;
}

// Convert the nsIStackFrame XPCOM objects to a nice JSON that can be
// passed around through message managers etc.
let frame = components.stack;
const stacktrace = [];
if (frame && frame.caller) {
frame = frame.caller;
while (frame) {
stacktrace.push({
filename: frame.filename,
lineNumber: frame.lineNumber,
columnNumber: frame.columnNumber,
functionName: frame.name,
asyncCause: frame.asyncCause,
});
frame = frame.caller || frame.asyncCaller;
switch (topic) {
case "http-on-opening-request": {
// The channel is being opened on the main thread, associate the current
// stack with it.
//
// Convert the nsIStackFrame XPCOM objects to a nice JSON that can be
// passed around through message managers etc.
let frame = components.stack;
if (frame && frame.caller) {
frame = frame.caller;
while (frame) {
stacktrace.push({
filename: frame.filename,
lineNumber: frame.lineNumber,
columnNumber: frame.columnNumber,
functionName: frame.name,
asyncCause: frame.asyncCause,
});
frame = frame.caller || frame.asyncCaller;
}
}
break;
}
case "network-monitor-alternate-stack": {
// An alternate stack trace is being specified for this channel.
// The topic data is the JSON for the saved frame stack we should use,
// so convert this into the expected format.
//
// This topic is used in the following cases:
//
// - The HTTP channel is opened asynchronously or on a different thread
// from the code which triggered its creation, in which case the stack
// from components.stack will be empty. The alternate stack will be
// for the point we want to associate with the channel.
//
// - The channel is not a nsIHttpChannel, and we will receive no
// opening request notification for it.
let frame = JSON.parse(data);
while (frame) {
stacktrace.push({
filename: frame.source,
lineNumber: frame.line,
columnNumber: frame.column,
functionName: frame.functionDisplayName,
asyncCause: frame.asyncCause,
});
frame = frame.parent || frame.asyncParent;
}
break;
}
default:
throw new Error("Unexpected observe() topic");
}

this._saveStackTrace(channel, stacktrace);
Expand Down
2 changes: 2 additions & 0 deletions dom/base/nsDOMDataChannel.cpp
Expand Up @@ -142,6 +142,8 @@ mozilla::dom::Nullable<uint16_t> nsDOMDataChannel::GetMaxRetransmits() const {
return mDataChannel->GetMaxRetransmits();
}

bool nsDOMDataChannel::Negotiated() const { return mDataChannel->GetNegotiated(); }

bool nsDOMDataChannel::Ordered() const { return mDataChannel->GetOrdered(); }

RTCDataChannelState nsDOMDataChannel::ReadyState() const {
Expand Down
1 change: 1 addition & 0 deletions dom/base/nsDOMDataChannel.h
Expand Up @@ -74,6 +74,7 @@ class nsDOMDataChannel final : public mozilla::DOMEventTargetHelper,
void Send(const mozilla::dom::ArrayBufferView& aData,
mozilla::ErrorResult& aRv);

bool Negotiated() const;
bool Ordered() const;
mozilla::dom::Nullable<uint16_t> GetId() const;

Expand Down

0 comments on commit 0078b9e

Please sign in to comment.