--- a/devtools/client/netmonitor/test/browser.ini
+++ b/devtools/client/netmonitor/test/browser.ini
@@ -4,31 +4,33 @@ 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_frame-test-page.html
+ html_frame-subdocument.html
html_filter-test-page.html
html_infinite-get-page.html
html_json-custom-mime-test-page.html
html_json-long-test-page.html
html_json-malformed-test-page.html
html_json-text-mime-test-page.html
html_jsonp-test-page.html
html_navigate-test-page.html
html_params-test-page.html
html_post-data-test-page.html
html_post-raw-test-page.html
html_post-raw-with-headers-test-page.html
html_simple-test-page.html
+ html_single-get-page.html
html_send-beacon.html
html_sorting-test-page.html
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
@@ -78,16 +80,17 @@ subsuite = clipboard
[browser_net_copy_as_curl.js]
subsuite = clipboard
skip-if = e10s # Bug 1091596
[browser_net_cors_requests.js]
[browser_net_cyrillic-01.js]
[browser_net_cyrillic-02.js]
[browser_net_details-no-duplicated-content.js]
skip-if = (os == 'linux' && e10s && debug) # Bug 1242204
+[browser_net_frame.js]
[browser_net_filter-01.js]
[browser_net_filter-02.js]
[browser_net_filter-03.js]
[browser_net_filter-04.js]
[browser_net_footer-summary.js]
[browser_net_html-preview.js]
[browser_net_icon-preview.js]
[browser_net_image-tooltip.js]
new file mode 100644
--- /dev/null
+++ b/devtools/client/netmonitor/test/browser_net_frame.js
@@ -0,0 +1,218 @@
+/* Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/ */
+
+"use strict";
+
+/**
+ * Tests for all expected requests when an iframe is loading a subdocument.
+ */
+
+const TOP_FILE_NAME = "html_frame-test-page.html";
+const SUB_FILE_NAME = "html_frame-subdocument.html";
+const TOP_URL = EXAMPLE_URL + TOP_FILE_NAME;
+const SUB_URL = EXAMPLE_URL + SUB_FILE_NAME;
+
+const EXPECTED_REQUESTS_TOP = [
+ {
+ method: "GET",
+ url: TOP_URL,
+ causeType: "document",
+ causeUri: "",
+ stack: true
+ },
+ {
+ method: "GET",
+ url: EXAMPLE_URL + "stylesheet_request",
+ causeType: "stylesheet",
+ causeUri: TOP_URL,
+ stack: false
+ },
+ {
+ method: "GET",
+ url: EXAMPLE_URL + "img_request",
+ causeType: "img",
+ causeUri: TOP_URL,
+ stack: false
+ },
+ {
+ method: "GET",
+ url: EXAMPLE_URL + "xhr_request",
+ causeType: "xhr",
+ causeUri: TOP_URL,
+ stack: [{ fn: "performXhrRequest", file: TOP_FILE_NAME, line: 23 }]
+ },
+ {
+ method: "GET",
+ url: EXAMPLE_URL + "fetch_request",
+ causeType: "fetch",
+ causeUri: TOP_URL,
+ stack: [{ fn: "performFetchRequest", file: TOP_FILE_NAME, line: 27 }]
+ },
+ {
+ method: "GET",
+ url: EXAMPLE_URL + "promise_fetch_request",
+ causeType: "fetch",
+ causeUri: TOP_URL,
+ stack: [
+ { fn: "performPromiseFetchRequest", file: TOP_FILE_NAME, line: 39 },
+ { fn: null, file: TOP_FILE_NAME, line: 38, asyncCause: "promise callback" },
+ ]
+ },
+ {
+ method: "GET",
+ url: EXAMPLE_URL + "timeout_fetch_request",
+ causeType: "fetch",
+ causeUri: TOP_URL,
+ stack: [
+ { fn: "performTimeoutFetchRequest", file: TOP_FILE_NAME, line: 41 },
+ { fn: "performPromiseFetchRequest", file: TOP_FILE_NAME, line: 40,
+ asyncCause: "setTimeout handler" },
+ ]
+ },
+ {
+ method: "POST",
+ url: EXAMPLE_URL + "beacon_request",
+ causeType: "beacon",
+ causeUri: TOP_URL,
+ stack: [{ fn: "performBeaconRequest", file: TOP_FILE_NAME, line: 31 }]
+ },
+];
+
+const EXPECTED_REQUESTS_SUB = [
+ {
+ method: "GET",
+ url: SUB_URL,
+ causeType: "subdocument",
+ causeUri: TOP_URL,
+ stack: false
+ },
+ {
+ method: "GET",
+ url: EXAMPLE_URL + "stylesheet_request",
+ causeType: "stylesheet",
+ causeUri: SUB_URL,
+ stack: false
+ },
+ {
+ method: "GET",
+ url: EXAMPLE_URL + "img_request",
+ causeType: "img",
+ causeUri: SUB_URL,
+ stack: false
+ },
+ {
+ method: "GET",
+ url: EXAMPLE_URL + "xhr_request",
+ causeType: "xhr",
+ causeUri: SUB_URL,
+ stack: [{ fn: "performXhrRequest", file: SUB_FILE_NAME, line: 22 }]
+ },
+ {
+ method: "GET",
+ url: EXAMPLE_URL + "fetch_request",
+ causeType: "fetch",
+ causeUri: SUB_URL,
+ stack: [{ fn: "performFetchRequest", file: SUB_FILE_NAME, line: 26 }]
+ },
+ {
+ method: "GET",
+ url: EXAMPLE_URL + "promise_fetch_request",
+ causeType: "fetch",
+ causeUri: SUB_URL,
+ stack: [
+ { fn: "performPromiseFetchRequest", file: SUB_FILE_NAME, line: 38 },
+ { fn: null, file: SUB_FILE_NAME, line: 37, asyncCause: "promise callback" },
+ ]
+ },
+ {
+ method: "GET",
+ url: EXAMPLE_URL + "timeout_fetch_request",
+ causeType: "fetch",
+ causeUri: SUB_URL,
+ stack: [
+ { fn: "performTimeoutFetchRequest", file: SUB_FILE_NAME, line: 40 },
+ { fn: "performPromiseFetchRequest", file: SUB_FILE_NAME, line: 39,
+ asyncCause: "setTimeout handler" },
+ ]
+ },
+ {
+ method: "POST",
+ url: EXAMPLE_URL + "beacon_request",
+ causeType: "beacon",
+ causeUri: SUB_URL,
+ stack: [{ fn: "performBeaconRequest", file: SUB_FILE_NAME, line: 30 }]
+ },
+];
+
+const REQUEST_COUNT = EXPECTED_REQUESTS_TOP.length + EXPECTED_REQUESTS_SUB.length;
+
+add_task(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 TOP_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 [ tab, , monitor ] = yield initNetMonitor(SIMPLE_URL);
+ let { NetMonitorView } = monitor.panelWin;
+ let { RequestsMenu } = NetMonitorView;
+ RequestsMenu.lazyUpdate = false;
+
+ tab.linkedBrowser.loadURI(TOP_URL, null, null);
+
+ yield waitForNetworkEvents(monitor, REQUEST_COUNT);
+
+ is(RequestsMenu.itemCount, REQUEST_COUNT,
+ "All the page events should be recorded.");
+
+ // While there is a defined order for requests in each document separately, the requests
+ // from different documents may interleave in various ways that change per test run, so
+ // there is not a single order when considering all the requests together.
+ let currentTop = 0;
+ let currentSub = 0;
+ for (let i = 0; i < REQUEST_COUNT; i++) {
+ let requestItem = RequestsMenu.getItemAtIndex(i);
+
+ let itemUrl = requestItem.attachment.url;
+ let itemCauseUri = requestItem.target.querySelector(".requests-menu-cause-label")
+ .getAttribute("tooltiptext");
+ let spec;
+ if (itemUrl == SUB_URL || itemCauseUri == SUB_URL) {
+ spec = EXPECTED_REQUESTS_SUB[currentSub++];
+ } else {
+ spec = EXPECTED_REQUESTS_TOP[currentTop++];
+ }
+ let { method, url, causeType, causeUri, stack } = spec;
+
+ verifyRequestItemTarget(requestItem,
+ method, url, { cause: { type: causeType, loadingDocumentUri: causeUri } }
+ );
+
+ let { stacktrace } = requestItem.attachment.cause;
+ let 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`);
+ }
+ }
+
+ yield teardown(monitor);
+});
--- a/devtools/client/netmonitor/test/head.js
+++ b/devtools/client/netmonitor/test/head.js
@@ -151,27 +151,28 @@ function initNetMonitor(aUrl, aWindow, a
info("Net tab added successfully: " + aUrl);
let target = TargetFactory.forTab(tab);
yield target.makeRemote();
info("Target remoted.");
if (!aEnableCache) {
+ info("Disabling cache and reloading page.");
yield toggleCache(target, true);
info("Cache disabled when the current and all future toolboxes are open.");
// Remove any requests generated by the reload while toggling the cache to
// avoid interfering with the test.
isnot([...target.activeConsole.getNetworkEvents()].length, 0,
"Request to reconfigure the tab was recorded.");
target.activeConsole.clearNetworkRequests();
}
let toolbox = yield gDevTools.showToolbox(target, "netmonitor");
- info("Netork monitor pane shown successfully.");
+ info("Network monitor pane shown successfully.");
let debuggee = tab.linkedBrowser.contentWindow.wrappedJSObject;
let monitor = toolbox.getCurrentPanel();
return [tab, debuggee, monitor];
});
}
function restartNetMonitor(aMonitor, aNewUrl) {
new file mode 100644
--- /dev/null
+++ b/devtools/client/netmonitor/test/html_frame-subdocument.html
@@ -0,0 +1,48 @@
+<!-- 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 frame 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 performFetchRequest() {
+ fetch("fetch_request");
+ }
+
+ function performBeaconRequest() {
+ navigator.sendBeacon("beacon_request");
+ }
+
+ performXhrRequest();
+ performFetchRequest();
+
+ // Perform some requests with async stacks
+ Promise.resolve().then(function performPromiseFetchRequest() {
+ fetch("promise_fetch_request");
+ setTimeout(function performTimeoutFetchRequest() {
+ fetch("timeout_fetch_request");
+
+ // Finally, send a beacon request
+ performBeaconRequest();
+ }, 0);
+ });
+ </script>
+ </body>
+</html>
new file mode 100644
--- /dev/null
+++ b/devtools/client/netmonitor/test/html_frame-test-page.html
@@ -0,0 +1,49 @@
+<!-- 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 frame test</p>
+ <img src="img_request" />
+ <iframe src="html_frame-subdocument.html"></iframe>
+ <script type="text/javascript">
+ function performXhrRequest() {
+ var xhr = new XMLHttpRequest();
+ xhr.open("GET", "xhr_request", true);
+ xhr.send();
+ }
+
+ function performFetchRequest() {
+ fetch("fetch_request");
+ }
+
+ function performBeaconRequest() {
+ navigator.sendBeacon("beacon_request");
+ }
+
+ performXhrRequest();
+ performFetchRequest();
+
+ // Perform some requests with async stacks
+ Promise.resolve().then(function performPromiseFetchRequest() {
+ fetch("promise_fetch_request");
+ setTimeout(function performTimeoutFetchRequest() {
+ fetch("timeout_fetch_request");
+
+ // Finally, send a beacon request
+ performBeaconRequest();
+ }, 0);
+ });
+ </script>
+ </body>
+</html>
--- a/devtools/server/actors/webbrowser.js
+++ b/devtools/server/actors/webbrowser.js
@@ -864,19 +864,18 @@ function TabActor(connection) {
this.makeDebugger = makeDebugger.bind(null, {
findDebuggees: () => {
return this.windows.concat(this.webextensionsContentScriptGlobals);
},
shouldAddNewGlobalAsDebuggee: this._shouldAddNewGlobalAsDebuggee
});
// Flag eventually overloaded by sub classes in order to watch new docshells
- // Used on b2g to catch activity frames and in chrome to list all frames
- this.listenForNewDocShells =
- Services.appinfo.processType == Services.appinfo.PROCESS_TYPE_CONTENT;
+ // Used by the ChromeActor to list all frames in the Browser Toolbox
+ this.listenForNewDocShells = false;
this.traits = {
reconfigure: true,
// Supports frame listing via `listFrames` request and `frameUpdate` events
// as well as frame switching via `switchToFrame` request
frames: true,
// Do not require to send reconfigure request to reset the document state
// to what it was before using the TabActor
@@ -933,19 +932,23 @@ TabActor.prototype = {
get chromeEventHandler() {
return getDocShellChromeEventHandler(this.docShell);
},
/**
* Getter for the nsIMessageManager associated to the tab.
*/
get messageManager() {
- return this.docShell
- .QueryInterface(Ci.nsIInterfaceRequestor)
- .getInterface(Ci.nsIContentFrameMessageManager);
+ try {
+ return this.docShell
+ .QueryInterface(Ci.nsIInterfaceRequestor)
+ .getInterface(Ci.nsIContentFrameMessageManager);
+ } catch (e) {
+ return null;
+ }
},
/**
* Getter for the tab's doc shell.
*/
get docShell() {
throw new Error(
"The docShell getter should be implemented by a subclass of TabActor");
@@ -967,16 +970,25 @@ TabActor.prototype = {
if (this.docShell) {
return this.docShell
.QueryInterface(Ci.nsIInterfaceRequestor)
.getInterface(Ci.nsIDOMWindow);
}
return null;
},
+ get outerWindowID() {
+ if (this.window) {
+ return this.window.QueryInterface(Ci.nsIInterfaceRequestor)
+ .getInterface(Ci.nsIDOMWindowUtils)
+ .outerWindowID;
+ }
+ return null;
+ },
+
/**
* Getter for the WebExtensions ContentScript globals related to the
* current tab content's DOM window.
*/
get webextensionsContentScriptGlobals() {
// Ignore xpcshell runtime which spawn TabActors without a window.
if (this.window) {
return ExtensionContent.getContentScriptGlobalsForWindow(this.window);
@@ -1099,20 +1111,17 @@ TabActor.prototype = {
};
// We may try to access window while the document is closing, then
// accessing window throws. Also on xpcshell we are using tabactor even if
// there is no valid document.
if (this.docShell && !this.docShell.isBeingDestroyed()) {
response.title = this.title;
response.url = this.url;
- let windowUtils = this.window
- .QueryInterface(Ci.nsIInterfaceRequestor)
- .getInterface(Ci.nsIDOMWindowUtils);
- response.outerWindowID = windowUtils.outerWindowID;
+ response.outerWindowID = this.outerWindowID;
}
// Always use the same ActorPool, so existing actor instances
// (created in createExtraActors) are not lost.
if (!this._tabActorPool) {
this._tabActorPool = new ActorPool(this.conn);
this.conn.addActorPool(this._tabActorPool);
}
@@ -1396,39 +1405,41 @@ TabActor.prototype = {
_notifyDocShellsUpdate(docshells) {
let windows = this._docShellsToWindows(docshells);
// Do not send the `frameUpdate` event if the windows array is empty.
if (windows.length == 0) {
return;
}
- this.conn.send({ from: this.actorID,
- type: "frameUpdate",
- frames: windows
- });
+ this.conn.send({
+ from: this.actorID,
+ type: "frameUpdate",
+ frames: windows
+ });
},
_updateChildDocShells() {
this._notifyDocShellsUpdate(this.docShells);
},
_notifyDocShellDestroy(webProgress) {
webProgress = webProgress.QueryInterface(Ci.nsIWebProgress);
let id = webProgress.DOMWindow
.QueryInterface(Ci.nsIInterfaceRequestor)
.getInterface(Ci.nsIDOMWindowUtils)
.outerWindowID;
- this.conn.send({ from: this.actorID,
- type: "frameUpdate",
- frames: [{
- id: id,
- destroy: true
- }]
- });
+ this.conn.send({
+ from: this.actorID,
+ type: "frameUpdate",
+ frames: [{
+ id,
+ destroy: true
+ }]
+ });
// Stop watching this docshell (the unwatch() method will check if we
// started watching it before).
webProgress.QueryInterface(Ci.nsIDocShell);
this._progressListener.unwatch(webProgress);
if (webProgress.DOMWindow == this._originalWindow) {
// If the original top level document we connected to is removed,
@@ -1457,20 +1468,21 @@ TabActor.prototype = {
// we have to switch to the top-level one.
if (webProgress.DOMWindow == this.window &&
this.window != this._originalWindow) {
this._changeTopLevelDocument(this._originalWindow);
}
},
_notifyDocShellDestroyAll() {
- this.conn.send({ from: this.actorID,
- type: "frameUpdate",
- destroyAll: true
- });
+ this.conn.send({
+ from: this.actorID,
+ type: "frameUpdate",
+ destroyAll: true
+ });
},
/**
* Creates a thread actor and a pool for context-lifetime actors. It then sets
* up the content window for debugging.
*/
_pushContext() {
assert(!this._contextPool, "Can't push multiple contexts");
@@ -1867,23 +1879,21 @@ TabActor.prototype = {
// targeted context (it will indirectly update this.window and
// many other attributes defined from docShell).
Object.defineProperty(this, "docShell", {
value: docShell,
enumerable: true,
configurable: true
});
events.emit(this, "changed-toplevel-document");
- let id = window.QueryInterface(Ci.nsIInterfaceRequestor)
- .getInterface(Ci.nsIDOMWindowUtils)
- .outerWindowID;
- this.conn.send({ from: this.actorID,
- type: "frameUpdate",
- selected: id
- });
+ this.conn.send({
+ from: this.actorID,
+ type: "frameUpdate",
+ selected: this.outerWindowID
+ });
},
/**
* Handle location changes, by clearing the previous debuggees and enabling
* debugging, which may have been disabled temporarily by the
* DebuggerProgressListener.
*/
_windowReady(window, isFrameSwitching = false) {
--- a/devtools/server/actors/webconsole.js
+++ b/devtools/server/actors/webconsole.js
@@ -610,18 +610,18 @@ WebConsoleActor.prototype =
this.stackTraceCollector.init();
let processBoundary = Services.appinfo.processType !=
Ci.nsIXULRuntime.PROCESS_TYPE_DEFAULT;
if ((appId || messageManager) && processBoundary) {
// Start a network monitor in the parent process to listen to
// most requests than happen in parent
this.networkMonitor =
- new NetworkMonitorChild(appId, messageManager,
- this.parentActor.actorID, this);
+ new NetworkMonitorChild(appId, this.parentActor.outerWindowID,
+ messageManager, this.parentActor.actorID, this);
this.networkMonitor.init();
// Spawn also one in the child to listen to service workers
this.networkMonitorChild = new NetworkMonitor({ window }, this);
this.networkMonitorChild.init();
} else {
this.networkMonitor = new NetworkMonitor({ window }, this);
this.networkMonitor.init();
}
--- a/devtools/shared/webconsole/network-monitor.js
+++ b/devtools/shared/webconsole/network-monitor.js
@@ -45,17 +45,17 @@ const RESPONSE_BODY_LIMIT = 1048576;
* Request to check.
* @param filters
* NetworkMonitor filters to match against.
* @return boolean
* True if the network request should be logged, false otherwise.
*/
function matchRequest(channel, filters) {
// Log everything if no filter is specified
- if (!filters.topFrame && !filters.window && !filters.appId) {
+ if (!filters.outerWindowID && !filters.window && !filters.appId) {
return true;
}
// Ignore requests from chrome or add-on code when we are monitoring
// content.
// TODO: one particular test (browser_styleeditor_fetch-from-cache.js) needs
// the flags.testing check. We will move to a better way to serve
// its needs in bug 1167188, where this check should be removed.
@@ -76,37 +76,21 @@ function matchRequest(channel, filters)
}
if (win.parent == win) {
break;
}
win = win.parent;
}
}
- if (filters.topFrame) {
+ if (filters.outerWindowID) {
let topFrame = NetworkHelper.getTopFrameForRequest(channel);
- while (topFrame) {
- // In the normal case, a topFrame filter should match the request's topFrame if it
- // will match at all.
- if (topFrame === filters.topFrame) {
- return true;
- }
- // As a stop gap approach for RDM, where `filters.topFrame` will be the
- // <xul:browser> frame for an entire tab and the request's `topFrame` is the
- // <iframe mozbrower> that triggered the request, we try to climb up parent frames
- // above the request's `topFrame` to see if they might also match the filter.
- // In bug 1240912, we want to rework this, since we don't really want to be passing
- // a frame down to the network monitor.
- if (!topFrame.ownerGlobal) {
- break;
- }
- topFrame = topFrame.ownerGlobal
- .QueryInterface(Ci.nsIInterfaceRequestor)
- .getInterface(Ci.nsIDOMWindowUtils)
- .containerElement;
+ if (topFrame && topFrame.outerWindowID &&
+ topFrame.outerWindowID == filters.outerWindowID) {
+ return true;
}
}
if (filters.appId) {
let appId = NetworkHelper.getAppIdForRequest(channel);
if (appId && appId == filters.appId) {
return true;
}
@@ -684,17 +668,17 @@ NetworkResponseListener.prototype = {
* routed to the remote Web Console.
*
* @constructor
* @param object filters
* Object with the filters to use for network requests:
* - window (nsIDOMWindow): filter network requests by the associated
* window object.
* - appId (number): filter requests by the appId.
- * - topFrame (nsIDOMElement): filter requests by their topFrameElement.
+ * - outerWindowID (number): filter requests by their top frame's outerWindowID.
* Filters are optional. If any of these filters match the request is
* logged (OR is applied). If no filter is provided then all requests are
* logged.
* @param object owner
* The network monitor owner. This object needs to hold:
* - onNetworkEvent(requestInfo, channel, networkMonitor).
* This method is invoked once for every new network request and it is
* given the following arguments: the initial network request
@@ -1395,25 +1379,28 @@ NetworkMonitor.prototype = {
*
* The main process creates NetworkEventActorProxy instances per request. These
* send the data to this object using the nsIMessageManager. Here we proxy the
* data to the WebConsoleActor or to a NetworkEventActor.
*
* @constructor
* @param number appId
* The web appId of the child process.
+ * @param number outerWindowID
+ * The outerWindowID of the TabActor's main window.
* @param nsIMessageManager messageManager
* The nsIMessageManager to use to communicate with the parent process.
* @param string connID
* The connection ID to use for send messages to the parent process.
* @param object owner
* The WebConsoleActor that is listening for the network requests.
*/
-function NetworkMonitorChild(appId, messageManager, connID, owner) {
+function NetworkMonitorChild(appId, outerWindowID, messageManager, connID, owner) {
this.appId = appId;
+ this.outerWindowID = outerWindowID;
this.connID = connID;
this.owner = owner;
this._messageManager = messageManager;
this._onNewEvent = this._onNewEvent.bind(this);
this._onUpdateEvent = this._onUpdateEvent.bind(this);
this._netEvents = new Map();
}
@@ -1443,16 +1430,17 @@ NetworkMonitorChild.prototype = {
init: function () {
let mm = this._messageManager;
mm.addMessageListener("debug:netmonitor:" + this.connID + ":newEvent",
this._onNewEvent);
mm.addMessageListener("debug:netmonitor:" + this.connID + ":updateEvent",
this._onUpdateEvent);
mm.sendAsyncMessage("debug:netmonitor:" + this.connID, {
appId: this.appId,
+ outerWindowID: this.outerWindowID,
action: "start",
});
},
_onNewEvent: DevToolsUtils.makeInfallible(function _onNewEvent(msg) {
let {id, event} = msg.data;
// Try to add stack trace to the event data received from parent
@@ -1582,46 +1570,44 @@ NetworkEventActorProxy.prototype = {
* Instance identifier to use for messages.
*/
function NetworkMonitorManager(frame, id) {
this.id = id;
// Get messageManager from XUL browser (which might be a specialized tunnel for RDM)
// or else fallback to asking the frameLoader itself.
let mm = frame.messageManager || frame.frameLoader.messageManager;
this.messageManager = mm;
- this.frame = frame;
this.onNetMonitorMessage = this.onNetMonitorMessage.bind(this);
this.onNetworkEvent = this.onNetworkEvent.bind(this);
mm.addMessageListener("debug:netmonitor:" + id, this.onNetMonitorMessage);
}
exports.NetworkMonitorManager = NetworkMonitorManager;
NetworkMonitorManager.prototype = {
netMonitor: null,
- frame: null,
messageManager: null,
/**
* Handler for "debug:monitor" messages received through the message manager
* from the content process.
*
* @param object msg
* Message from the content.
*/
onNetMonitorMessage: DevToolsUtils.makeInfallible(function (msg) {
let {action} = msg.json;
// Pipe network monitor data from parent to child via the message manager.
switch (action) {
case "start":
if (!this.netMonitor) {
- let {appId} = msg.json;
+ let {appId, outerWindowID} = msg.json;
this.netMonitor = new NetworkMonitor({
- topFrame: this.frame,
- appId: appId,
+ outerWindowID,
+ appId,
}, this);
this.netMonitor.init();
}
break;
case "setPreferences": {
let {preferences} = msg.json;
for (let key of Object.keys(preferences)) {
if (key == "saveRequestAndResponseBodies" && this.netMonitor) {
@@ -1655,21 +1641,21 @@ NetworkMonitorManager.prototype = {
* data about the request is available.
*/
onNetworkEvent: DevToolsUtils.makeInfallible(function _onNetworkEvent(event) {
return new NetworkEventActorProxy(this.messageManager, this.id).init(event);
}),
destroy: function () {
if (this.messageManager) {
- this.messageManager.removeMessageListener("debug:netmonitor:" + this.id,
- this.onNetMonitorMessage);
+ let mm = this.messageManager;
+ this.messageManager = null;
+ mm.removeMessageListener("debug:netmonitor:" + this.id, this.onNetMonitorMessage);
}
- this.messageManager = null;
- this.filters = null;
+ this.frames = null;
if (this.netMonitor) {
this.netMonitor.destroy();
this.netMonitor = null;
}
},
};