mirror of
https://github.com/mozilla/gecko-dev.git
synced 2024-11-25 05:41:12 +00:00
Bug 1663523 - [devtools] Add the network event stacktrace watcher r=ochameau,devtools-backward-compat-reviewers
Differential Revision: https://phabricator.services.mozilla.com/D86809
This commit is contained in:
parent
61728de827
commit
3d1f26e2cd
@ -41,6 +41,7 @@ DevToolsModules(
|
|||||||
'root.js',
|
'root.js',
|
||||||
'screenshot.js',
|
'screenshot.js',
|
||||||
'source.js',
|
'source.js',
|
||||||
|
'stacktraces.js',
|
||||||
'storage.js',
|
'storage.js',
|
||||||
'string.js',
|
'string.js',
|
||||||
'styles.js',
|
'styles.js',
|
||||||
|
22
devtools/client/fronts/stacktraces.js
Normal file
22
devtools/client/fronts/stacktraces.js
Normal file
@ -0,0 +1,22 @@
|
|||||||
|
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||||
|
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||||
|
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||||
|
|
||||||
|
"use strict";
|
||||||
|
|
||||||
|
const {
|
||||||
|
FrontClassWithSpec,
|
||||||
|
registerFront,
|
||||||
|
} = require("devtools/shared/protocol");
|
||||||
|
const { stackTracesSpec } = require("devtools/shared/specs/stacktraces");
|
||||||
|
|
||||||
|
class StackTracesFront extends FrontClassWithSpec(stackTracesSpec) {
|
||||||
|
constructor(client, targetFront, parentFront) {
|
||||||
|
super(client, targetFront, parentFront);
|
||||||
|
// Attribute name from which to retrieve the actorID out of the target actor's form
|
||||||
|
this.formAttributeName = "stacktracesActor";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
exports.StackTracesFront = StackTracesFront;
|
||||||
|
registerFront(StackTracesFront);
|
@ -140,6 +140,7 @@ class FirefoxConnector {
|
|||||||
webConsoleFront: this.webConsoleFront,
|
webConsoleFront: this.webConsoleFront,
|
||||||
actions: this.actions,
|
actions: this.actions,
|
||||||
owner: this.owner,
|
owner: this.owner,
|
||||||
|
resourceWatcher: this.toolbox.resourceWatcher,
|
||||||
});
|
});
|
||||||
|
|
||||||
// Register target listeners if we switched to a new top level one
|
// Register target listeners if we switched to a new top level one
|
||||||
|
@ -27,17 +27,21 @@ class FirefoxDataProvider {
|
|||||||
* Constructor for data provider
|
* Constructor for data provider
|
||||||
*
|
*
|
||||||
* @param {Object} webConsoleFront represents the client object for Console actor.
|
* @param {Object} webConsoleFront represents the client object for Console actor.
|
||||||
* @param {Object} actions set of actions fired during data fetching process
|
* @param {Object} actions set of actions fired during data fetching process.
|
||||||
* @params {Object} owner all events are fired on this object
|
* @param {Object} owner all events are fired on this object.
|
||||||
|
* @param {Object} resourceWatcher enables checking for watcher support
|
||||||
*/
|
*/
|
||||||
constructor({ webConsoleFront, actions, owner }) {
|
constructor({ webConsoleFront, actions, owner, resourceWatcher }) {
|
||||||
// Options
|
// Options
|
||||||
this.webConsoleFront = webConsoleFront;
|
this.webConsoleFront = webConsoleFront;
|
||||||
this.actions = actions || {};
|
this.actions = actions || {};
|
||||||
this.actionsEnabled = true;
|
this.actionsEnabled = true;
|
||||||
this.owner = owner;
|
this.owner = owner;
|
||||||
// Map of all stacktrace resources keyed by network event's channelId
|
this.resourceWatcher = resourceWatcher;
|
||||||
|
// Map of all stacktrace resources keyed by network event's resourceId
|
||||||
this.stackTraces = new Map();
|
this.stackTraces = new Map();
|
||||||
|
// Map of the stacktrace information keyed by the actor id's
|
||||||
|
this.stackTraceRequestInfoByActorID = new Map();
|
||||||
|
|
||||||
// Internal properties
|
// Internal properties
|
||||||
this.payloadQueue = new Map();
|
this.payloadQueue = new Map();
|
||||||
@ -322,8 +326,31 @@ class FirefoxDataProvider {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retrieve the stack-trace information for the given StackTracesActor.
|
||||||
|
*
|
||||||
|
* @param object actor
|
||||||
|
* - {Object} targetFront: the target front.
|
||||||
|
*
|
||||||
|
* - {String} resourceId: the resource id for the network request".
|
||||||
|
* @return {object}
|
||||||
|
*/
|
||||||
|
async _getStackTraceFromWatcher(actor) {
|
||||||
|
const stacktracesFront = await actor.targetFront.getFront("stacktraces");
|
||||||
|
const stacktrace = await stacktracesFront.getStackTrace(
|
||||||
|
actor.stacktraceResourceId
|
||||||
|
);
|
||||||
|
return { stacktrace };
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The handler for when the network event stacktrace resource is available.
|
||||||
|
* The resource contains basic info, the actual stacktrace is fetched lazily
|
||||||
|
* using requestData.
|
||||||
|
* @param {object} resource The network event stacktrace resource
|
||||||
|
*/
|
||||||
onStackTraceAvailable(resource) {
|
onStackTraceAvailable(resource) {
|
||||||
this.stackTraces.set(resource.channelId, resource);
|
this.stackTraces.set(resource.resourceId, resource);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -345,15 +372,31 @@ class FirefoxDataProvider {
|
|||||||
referrerPolicy,
|
referrerPolicy,
|
||||||
blockedReason,
|
blockedReason,
|
||||||
blockingExtension,
|
blockingExtension,
|
||||||
channelId,
|
resourceId,
|
||||||
|
stacktraceResourceId,
|
||||||
} = resource;
|
} = resource;
|
||||||
|
|
||||||
// Check if a stacktrace resource exists for this network event
|
// Check if a stacktrace resource exists for this network resource.
|
||||||
if (this.stackTraces.has(channelId)) {
|
// The stacktrace event is expected to happen before the network
|
||||||
const { stacktrace, lastFrame } = this.stackTraces.get(channelId);
|
// event so any neccesary stacktrace info should be available.
|
||||||
cause.stacktraceAvailable = stacktrace;
|
if (this.stackTraces.has(stacktraceResourceId)) {
|
||||||
|
const {
|
||||||
|
stacktraceAvailable,
|
||||||
|
lastFrame,
|
||||||
|
targetFront,
|
||||||
|
} = this.stackTraces.get(stacktraceResourceId);
|
||||||
|
cause.stacktraceAvailable = stacktraceAvailable;
|
||||||
cause.lastFrame = lastFrame;
|
cause.lastFrame = lastFrame;
|
||||||
this.stackTraces.delete(channelId);
|
this.stackTraces.delete(stacktraceResourceId);
|
||||||
|
// We retrieve preliminary information about the stacktrace from the
|
||||||
|
// NETWORK_EVENT_STACKTRACE resource via `this.stackTraces` Map,
|
||||||
|
// The actual stacktrace is fetched lazily based on the actor id, using
|
||||||
|
// the targetFront and the stacktrace resource id therefore we
|
||||||
|
// map these for easy access.
|
||||||
|
this.stackTraceRequestInfoByActorID.set(actor, {
|
||||||
|
targetFront,
|
||||||
|
stacktraceResourceId,
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
// For resources from the resource watcher cache no updates are going to be fired
|
// For resources from the resource watcher cache no updates are going to be fired
|
||||||
@ -388,7 +431,7 @@ class FirefoxDataProvider {
|
|||||||
referrerPolicy,
|
referrerPolicy,
|
||||||
blockedReason,
|
blockedReason,
|
||||||
blockingExtension,
|
blockingExtension,
|
||||||
channelId,
|
resourceId,
|
||||||
mimeType: resource?.content?.mimeType,
|
mimeType: resource?.content?.mimeType,
|
||||||
contentSize: bodySize,
|
contentSize: bodySize,
|
||||||
...responseProps,
|
...responseProps,
|
||||||
@ -638,7 +681,15 @@ class FirefoxDataProvider {
|
|||||||
|
|
||||||
let response = await new Promise((resolve, reject) => {
|
let response = await new Promise((resolve, reject) => {
|
||||||
// Do a RDP request to fetch data from the actor.
|
// Do a RDP request to fetch data from the actor.
|
||||||
if (typeof this.webConsoleFront[clientMethodName] === "function") {
|
if (
|
||||||
|
clientMethodName == "getStackTrace" &&
|
||||||
|
this.resourceWatcher.hasWatcherSupport(
|
||||||
|
this.resourceWatcher.TYPES.NETWORK_EVENT_STACKTRACE
|
||||||
|
)
|
||||||
|
) {
|
||||||
|
const requestInfo = this.stackTraceRequestInfoByActorID.get(actor);
|
||||||
|
resolve(this._getStackTraceFromWatcher(requestInfo));
|
||||||
|
} else if (typeof this.webConsoleFront[clientMethodName] === "function") {
|
||||||
// Make sure we fetch the real actor data instead of cloned actor
|
// Make sure we fetch the real actor data instead of cloned actor
|
||||||
// e.g. CustomRequestPanel will clone a request with additional '-clone' actor id
|
// e.g. CustomRequestPanel will clone a request with additional '-clone' actor id
|
||||||
this.webConsoleFront[clientMethodName](
|
this.webConsoleFront[clientMethodName](
|
||||||
|
@ -67,7 +67,7 @@ function Messages(initialState = {}) {
|
|||||||
const { EVENT_STREAM, WEB_SOCKET } = CHANNEL_TYPE;
|
const { EVENT_STREAM, WEB_SOCKET } = CHANNEL_TYPE;
|
||||||
|
|
||||||
return {
|
return {
|
||||||
// Map with all requests (key = channelId, value = array of message objects)
|
// Map with all requests (key = resourceId, value = array of message objects)
|
||||||
messages: new Map(),
|
messages: new Map(),
|
||||||
messageFilterText: "",
|
messageFilterText: "",
|
||||||
// Default filter type is "all",
|
// Default filter type is "all",
|
||||||
@ -88,14 +88,14 @@ function Messages(initialState = {}) {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* When a network request is selected,
|
* When a network request is selected,
|
||||||
* set the current channelId affiliated with the connection.
|
* set the current resourceId affiliated with the connection.
|
||||||
*/
|
*/
|
||||||
function setCurrentChannel(state, action) {
|
function setCurrentChannel(state, action) {
|
||||||
if (!action.request) {
|
if (!action.request) {
|
||||||
return state;
|
return state;
|
||||||
}
|
}
|
||||||
|
|
||||||
const { id, cause, channelId, isEventStream } = action.request;
|
const { id, cause, resourceId, isEventStream } = action.request;
|
||||||
const { EVENT_STREAM, WEB_SOCKET } = CHANNEL_TYPE;
|
const { EVENT_STREAM, WEB_SOCKET } = CHANNEL_TYPE;
|
||||||
let currentChannelType = null;
|
let currentChannelType = null;
|
||||||
let columnsKey = "columns";
|
let columnsKey = "columns";
|
||||||
@ -113,7 +113,7 @@ function setCurrentChannel(state, action) {
|
|||||||
currentChannelType === state.currentChannelType
|
currentChannelType === state.currentChannelType
|
||||||
? { ...state.columns }
|
? { ...state.columns }
|
||||||
: { ...state[columnsKey] },
|
: { ...state[columnsKey] },
|
||||||
currentChannelId: channelId,
|
currentChannelId: resourceId,
|
||||||
currentChannelType,
|
currentChannelType,
|
||||||
currentRequestId: id,
|
currentRequestId: id,
|
||||||
// Default filter text is empty string for a new connection
|
// Default filter text is empty string for a new connection
|
||||||
|
@ -159,7 +159,7 @@ function getRequestById(state, id) {
|
|||||||
|
|
||||||
function getRequestByChannelId(state, channelId) {
|
function getRequestByChannelId(state, channelId) {
|
||||||
return [...state.requests.requests.values()].find(
|
return [...state.requests.requests.values()].find(
|
||||||
r => r.channelId == channelId
|
r => r.resourceId == channelId
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -82,11 +82,9 @@ async function performRequestAndWait(tab, monitor) {
|
|||||||
* Execute simple GET request
|
* Execute simple GET request
|
||||||
*/
|
*/
|
||||||
async function performPausedRequest(connector, tab, monitor) {
|
async function performPausedRequest(connector, tab, monitor) {
|
||||||
const wait = connector.connector.webConsoleFront.once("serverNetworkEvent");
|
|
||||||
await SpecialPowers.spawn(tab.linkedBrowser, [SIMPLE_SJS], async function(
|
await SpecialPowers.spawn(tab.linkedBrowser, [SIMPLE_SJS], async function(
|
||||||
url
|
url
|
||||||
) {
|
) {
|
||||||
await content.wrappedJSObject.performRequests(url);
|
await content.wrappedJSObject.performRequests(url);
|
||||||
});
|
});
|
||||||
await wait;
|
|
||||||
}
|
}
|
||||||
|
@ -75,8 +75,10 @@ async function generateNetworkEventStubs() {
|
|||||||
for (const resource of resources) {
|
for (const resource of resources) {
|
||||||
if (resource.resourceType == resourceWatcher.TYPES.NETWORK_EVENT) {
|
if (resource.resourceType == resourceWatcher.TYPES.NETWORK_EVENT) {
|
||||||
if (stacktraces.has(resource.channelId)) {
|
if (stacktraces.has(resource.channelId)) {
|
||||||
const { stacktrace, lastFrame } = stacktraces.get(resource.channelId);
|
const { stacktraceAvailable, lastFrame } = stacktraces.get(
|
||||||
resource.cause.stacktraceAvailable = stacktrace;
|
resource.channelId
|
||||||
|
);
|
||||||
|
resource.cause.stacktraceAvailable = stacktraceAvailable;
|
||||||
resource.cause.lastFrame = lastFrame;
|
resource.cause.lastFrame = lastFrame;
|
||||||
stacktraces.delete(resource.channelId);
|
stacktraces.delete(resource.channelId);
|
||||||
}
|
}
|
||||||
@ -121,6 +123,8 @@ async function generateNetworkEventStubs() {
|
|||||||
const updateKey = `${key} update`;
|
const updateKey = `${key} update`;
|
||||||
// make sure all the updates have been happened
|
// make sure all the updates have been happened
|
||||||
if (updateCount >= noExpectedUpdates) {
|
if (updateCount >= noExpectedUpdates) {
|
||||||
|
// make sure the network event stub contains all the updates
|
||||||
|
stubs.set(key, getCleanedPacket(key, getOrderedResource(resource)));
|
||||||
stubs.set(
|
stubs.set(
|
||||||
updateKey,
|
updateKey,
|
||||||
// We cannot ensure the form of the resource, some properties
|
// We cannot ensure the form of the resource, some properties
|
||||||
|
@ -288,6 +288,10 @@ function getCleanedPacket(key, packet) {
|
|||||||
res.totalTime = existingPacket.totalTime;
|
res.totalTime = existingPacket.totalTime;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (res.securityState && existingPacket.securityState) {
|
||||||
|
res.securityState = existingPacket.securityState;
|
||||||
|
}
|
||||||
|
|
||||||
if (res.actor && existingPacket.actor) {
|
if (res.actor && existingPacket.actor) {
|
||||||
res.actor = existingPacket.actor;
|
res.actor = existingPacket.actor;
|
||||||
}
|
}
|
||||||
|
@ -20,9 +20,7 @@ const {
|
|||||||
const rawPackets = new Map();
|
const rawPackets = new Map();
|
||||||
rawPackets.set(`GET request`, {
|
rawPackets.set(`GET request`, {
|
||||||
"resourceType": "network-event",
|
"resourceType": "network-event",
|
||||||
"_type": "NetworkEvent",
|
|
||||||
"timeStamp": 1572867483805,
|
"timeStamp": 1572867483805,
|
||||||
"node": null,
|
|
||||||
"actor": "server0.conn0.netEvent4",
|
"actor": "server0.conn0.netEvent4",
|
||||||
"discardRequestBody": true,
|
"discardRequestBody": true,
|
||||||
"discardResponseBody": false,
|
"discardResponseBody": false,
|
||||||
@ -63,7 +61,6 @@ rawPackets.set(`GET request`, {
|
|||||||
"private": false,
|
"private": false,
|
||||||
"isThirdPartyTrackingResource": false,
|
"isThirdPartyTrackingResource": false,
|
||||||
"referrerPolicy": "no-referrer-when-downgrade",
|
"referrerPolicy": "no-referrer-when-downgrade",
|
||||||
"channelId": 265845590720515,
|
|
||||||
"updates": [
|
"updates": [
|
||||||
"eventTimings",
|
"eventTimings",
|
||||||
"requestCookies",
|
"requestCookies",
|
||||||
@ -80,9 +77,7 @@ rawPackets.set(`GET request`, {
|
|||||||
|
|
||||||
rawPackets.set(`GET request update`, {
|
rawPackets.set(`GET request update`, {
|
||||||
"resourceType": "network-event",
|
"resourceType": "network-event",
|
||||||
"_type": "NetworkEvent",
|
|
||||||
"timeStamp": 1572867483805,
|
"timeStamp": 1572867483805,
|
||||||
"node": null,
|
|
||||||
"actor": "server0.conn0.netEvent5",
|
"actor": "server0.conn0.netEvent5",
|
||||||
"discardRequestBody": true,
|
"discardRequestBody": true,
|
||||||
"discardResponseBody": false,
|
"discardResponseBody": false,
|
||||||
@ -123,7 +118,6 @@ rawPackets.set(`GET request update`, {
|
|||||||
"private": false,
|
"private": false,
|
||||||
"isThirdPartyTrackingResource": false,
|
"isThirdPartyTrackingResource": false,
|
||||||
"referrerPolicy": "no-referrer-when-downgrade",
|
"referrerPolicy": "no-referrer-when-downgrade",
|
||||||
"channelId": 202499118071811,
|
|
||||||
"updates": [
|
"updates": [
|
||||||
"eventTimings",
|
"eventTimings",
|
||||||
"requestCookies",
|
"requestCookies",
|
||||||
@ -140,9 +134,7 @@ rawPackets.set(`GET request update`, {
|
|||||||
|
|
||||||
rawPackets.set(`XHR GET request`, {
|
rawPackets.set(`XHR GET request`, {
|
||||||
"resourceType": "network-event",
|
"resourceType": "network-event",
|
||||||
"_type": "NetworkEvent",
|
|
||||||
"timeStamp": 1572867483805,
|
"timeStamp": 1572867483805,
|
||||||
"node": null,
|
|
||||||
"actor": "server0.conn0.netEvent21",
|
"actor": "server0.conn0.netEvent21",
|
||||||
"discardRequestBody": true,
|
"discardRequestBody": true,
|
||||||
"discardResponseBody": false,
|
"discardResponseBody": false,
|
||||||
@ -183,7 +175,6 @@ rawPackets.set(`XHR GET request`, {
|
|||||||
"private": false,
|
"private": false,
|
||||||
"isThirdPartyTrackingResource": false,
|
"isThirdPartyTrackingResource": false,
|
||||||
"referrerPolicy": "no-referrer-when-downgrade",
|
"referrerPolicy": "no-referrer-when-downgrade",
|
||||||
"channelId": 202499118071812,
|
|
||||||
"updates": [
|
"updates": [
|
||||||
"eventTimings",
|
"eventTimings",
|
||||||
"requestCookies",
|
"requestCookies",
|
||||||
@ -200,9 +191,7 @@ rawPackets.set(`XHR GET request`, {
|
|||||||
|
|
||||||
rawPackets.set(`XHR GET request update`, {
|
rawPackets.set(`XHR GET request update`, {
|
||||||
"resourceType": "network-event",
|
"resourceType": "network-event",
|
||||||
"_type": "NetworkEvent",
|
|
||||||
"timeStamp": 1572867483805,
|
"timeStamp": 1572867483805,
|
||||||
"node": null,
|
|
||||||
"actor": "server0.conn0.netEvent20",
|
"actor": "server0.conn0.netEvent20",
|
||||||
"discardRequestBody": true,
|
"discardRequestBody": true,
|
||||||
"discardResponseBody": false,
|
"discardResponseBody": false,
|
||||||
@ -258,9 +247,7 @@ rawPackets.set(`XHR GET request update`, {
|
|||||||
|
|
||||||
rawPackets.set(`XHR POST request`, {
|
rawPackets.set(`XHR POST request`, {
|
||||||
"resourceType": "network-event",
|
"resourceType": "network-event",
|
||||||
"_type": "NetworkEvent",
|
|
||||||
"timeStamp": 1572867483805,
|
"timeStamp": 1572867483805,
|
||||||
"node": null,
|
|
||||||
"actor": "server0.conn0.netEvent36",
|
"actor": "server0.conn0.netEvent36",
|
||||||
"discardRequestBody": true,
|
"discardRequestBody": true,
|
||||||
"discardResponseBody": false,
|
"discardResponseBody": false,
|
||||||
@ -301,7 +288,6 @@ rawPackets.set(`XHR POST request`, {
|
|||||||
"private": false,
|
"private": false,
|
||||||
"isThirdPartyTrackingResource": false,
|
"isThirdPartyTrackingResource": false,
|
||||||
"referrerPolicy": "no-referrer-when-downgrade",
|
"referrerPolicy": "no-referrer-when-downgrade",
|
||||||
"channelId": 265845590720517,
|
|
||||||
"updates": [
|
"updates": [
|
||||||
"eventTimings",
|
"eventTimings",
|
||||||
"requestCookies",
|
"requestCookies",
|
||||||
@ -318,9 +304,7 @@ rawPackets.set(`XHR POST request`, {
|
|||||||
|
|
||||||
rawPackets.set(`XHR POST request update`, {
|
rawPackets.set(`XHR POST request update`, {
|
||||||
"resourceType": "network-event",
|
"resourceType": "network-event",
|
||||||
"_type": "NetworkEvent",
|
|
||||||
"timeStamp": 1572867483805,
|
"timeStamp": 1572867483805,
|
||||||
"node": null,
|
|
||||||
"actor": "server0.conn0.netEvent36",
|
"actor": "server0.conn0.netEvent36",
|
||||||
"discardRequestBody": true,
|
"discardRequestBody": true,
|
||||||
"discardResponseBody": false,
|
"discardResponseBody": false,
|
||||||
|
@ -387,19 +387,34 @@ class WebConsoleUI {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (resource.resourceType === TYPES.NETWORK_EVENT_STACKTRACE) {
|
if (resource.resourceType === TYPES.NETWORK_EVENT_STACKTRACE) {
|
||||||
this.netEventStackTraces.set(resource.channelId, resource);
|
this.netEventStackTraces.set(resource.resourceId, resource);
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (resource.resourceType === TYPES.NETWORK_EVENT) {
|
if (resource.resourceType === TYPES.NETWORK_EVENT) {
|
||||||
// Add the stacktrace
|
// Add the stacktrace
|
||||||
if (this.netEventStackTraces.has(resource.channelId)) {
|
if (this.netEventStackTraces.has(resource.resourceId)) {
|
||||||
const { stacktrace, lastFrame } = this.netEventStackTraces.get(
|
const {
|
||||||
resource.channelId
|
stacktraceAvailable,
|
||||||
);
|
lastFrame,
|
||||||
resource.cause.stacktraceAvailable = stacktrace;
|
targetFront,
|
||||||
|
} = this.netEventStackTraces.get(resource.resourceId);
|
||||||
|
|
||||||
|
resource.cause.stacktraceAvailable = stacktraceAvailable;
|
||||||
resource.cause.lastFrame = lastFrame;
|
resource.cause.lastFrame = lastFrame;
|
||||||
this.netEventStackTraces.delete(resource.channelId);
|
this.netEventStackTraces.delete(resource.resourceId);
|
||||||
|
|
||||||
|
if (
|
||||||
|
this.wrapper?.networkDataProvider?.stackTraceRequestInfoByActorID
|
||||||
|
) {
|
||||||
|
this.wrapper.networkDataProvider.stackTraceRequestInfoByActorID.set(
|
||||||
|
resource.actor,
|
||||||
|
{
|
||||||
|
targetFront,
|
||||||
|
resourceId: resource.resourceId,
|
||||||
|
}
|
||||||
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -60,6 +60,7 @@ class WebConsoleWrapper {
|
|||||||
* @param {WebConsoleUI} webConsoleUI
|
* @param {WebConsoleUI} webConsoleUI
|
||||||
* @param {Toolbox} toolbox
|
* @param {Toolbox} toolbox
|
||||||
* @param {Document} document
|
* @param {Document} document
|
||||||
|
*
|
||||||
*/
|
*/
|
||||||
constructor(parentNode, webConsoleUI, toolbox, document) {
|
constructor(parentNode, webConsoleUI, toolbox, document) {
|
||||||
EventEmitter.decorate(this);
|
EventEmitter.decorate(this);
|
||||||
@ -89,6 +90,7 @@ class WebConsoleWrapper {
|
|||||||
updateRequest: (id, data) => this.batchedRequestUpdates({ id, data }),
|
updateRequest: (id, data) => this.batchedRequestUpdates({ id, data }),
|
||||||
},
|
},
|
||||||
webConsoleFront,
|
webConsoleFront,
|
||||||
|
resourceWatcher: this.hud.resourceWatcher,
|
||||||
});
|
});
|
||||||
|
|
||||||
return new Promise(resolve => {
|
return new Promise(resolve => {
|
||||||
|
@ -17,5 +17,6 @@ DevToolsModules(
|
|||||||
'network-observer.js',
|
'network-observer.js',
|
||||||
'network-response-listener.js',
|
'network-response-listener.js',
|
||||||
'stack-trace-collector.js',
|
'stack-trace-collector.js',
|
||||||
|
'stack-traces-actor.js',
|
||||||
'websocket-actor.js',
|
'websocket-actor.js',
|
||||||
)
|
)
|
||||||
|
@ -70,10 +70,12 @@ const NetworkEventActor = protocol.ActorClassWithSpec(networkEventSpec, {
|
|||||||
this._isXHR = networkEvent.isXHR;
|
this._isXHR = networkEvent.isXHR;
|
||||||
|
|
||||||
this._cause = networkEvent.cause;
|
this._cause = networkEvent.cause;
|
||||||
this._cause.stacktraceAvailable = !!(
|
// Lets remove the last frame here as
|
||||||
this._stackTrace &&
|
// it is passed from the the server by the NETWORK_EVENT_STACKTRACE
|
||||||
(typeof this._stackTrace == "boolean" || this._stackTrace.length)
|
// resource type. This done here for backward compatibility.
|
||||||
);
|
if (this._cause.lastFrame) {
|
||||||
|
delete this._cause.lastFrame;
|
||||||
|
}
|
||||||
|
|
||||||
this._fromCache = networkEvent.fromCache;
|
this._fromCache = networkEvent.fromCache;
|
||||||
this._fromServiceWorker = networkEvent.fromServiceWorker;
|
this._fromServiceWorker = networkEvent.fromServiceWorker;
|
||||||
@ -81,7 +83,7 @@ const NetworkEventActor = protocol.ActorClassWithSpec(networkEventSpec, {
|
|||||||
networkEvent.isThirdPartyTrackingResource;
|
networkEvent.isThirdPartyTrackingResource;
|
||||||
this._referrerPolicy = networkEvent.referrerPolicy;
|
this._referrerPolicy = networkEvent.referrerPolicy;
|
||||||
this._channelId = networkEvent.channelId;
|
this._channelId = networkEvent.channelId;
|
||||||
|
this._serial = networkEvent.serial;
|
||||||
this._blockedReason = networkEvent.blockedReason;
|
this._blockedReason = networkEvent.blockedReason;
|
||||||
this._blockingExtension = networkEvent.blockingExtension;
|
this._blockingExtension = networkEvent.blockingExtension;
|
||||||
|
|
||||||
@ -119,7 +121,9 @@ const NetworkEventActor = protocol.ActorClassWithSpec(networkEventSpec, {
|
|||||||
referrerPolicy: this._referrerPolicy,
|
referrerPolicy: this._referrerPolicy,
|
||||||
blockedReason: this._blockedReason,
|
blockedReason: this._blockedReason,
|
||||||
blockingExtension: this._blockingExtension,
|
blockingExtension: this._blockingExtension,
|
||||||
channelId: this._channelId,
|
// For websocket requests the serial is used instead of the channel id.
|
||||||
|
stacktraceResourceId:
|
||||||
|
this._cause.type == "websocket" ? this._serial : this._channelId,
|
||||||
updates: [],
|
updates: [],
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
@ -260,23 +264,6 @@ const NetworkEventActor = protocol.ActorClassWithSpec(networkEventSpec, {
|
|||||||
};
|
};
|
||||||
},
|
},
|
||||||
|
|
||||||
/**
|
|
||||||
* The "getStackTrace" packet type handler.
|
|
||||||
*
|
|
||||||
* @return object
|
|
||||||
* The response packet - stack trace.
|
|
||||||
*/
|
|
||||||
async getStackTrace() {
|
|
||||||
const stacktrace = this._stackTrace;
|
|
||||||
if (stacktrace && typeof stacktrace == "boolean") {
|
|
||||||
this._stackTrace = [];
|
|
||||||
}
|
|
||||||
|
|
||||||
return {
|
|
||||||
stacktrace,
|
|
||||||
};
|
|
||||||
},
|
|
||||||
|
|
||||||
/** ****************************************************************
|
/** ****************************************************************
|
||||||
* Listeners for new network event data coming from NetworkMonitor.
|
* Listeners for new network event data coming from NetworkMonitor.
|
||||||
******************************************************************/
|
******************************************************************/
|
||||||
@ -491,10 +478,8 @@ const NetworkEventActor = protocol.ActorClassWithSpec(networkEventSpec, {
|
|||||||
if (this.isDestroyed()) {
|
if (this.isDestroyed()) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
this._response.responseCache = content.responseCache;
|
||||||
this._onEventUpdate("responseCache", {
|
this._onEventUpdate("responseCache", {});
|
||||||
responseCache: content.responseCache,
|
|
||||||
});
|
|
||||||
},
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -837,6 +837,7 @@ NetworkObserver.prototype = {
|
|||||||
}
|
}
|
||||||
if (wsChannel) {
|
if (wsChannel) {
|
||||||
event.url = wsChannel.URI.spec;
|
event.url = wsChannel.URI.spec;
|
||||||
|
event.serial = wsChannel.serial;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
59
devtools/server/actors/network-monitor/stack-traces-actor.js
Normal file
59
devtools/server/actors/network-monitor/stack-traces-actor.js
Normal file
@ -0,0 +1,59 @@
|
|||||||
|
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||||
|
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||||
|
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||||
|
|
||||||
|
"use strict";
|
||||||
|
|
||||||
|
const { ActorClassWithSpec, Actor } = require("devtools/shared/protocol");
|
||||||
|
const { stackTracesSpec } = require("devtools/shared/specs/stacktraces");
|
||||||
|
|
||||||
|
loader.lazyRequireGetter(
|
||||||
|
this,
|
||||||
|
"WebConsoleUtils",
|
||||||
|
"devtools/server/actors/webconsole/utils",
|
||||||
|
true
|
||||||
|
);
|
||||||
|
|
||||||
|
const {
|
||||||
|
TYPES: { NETWORK_EVENT_STACKTRACE },
|
||||||
|
getResourceWatcher,
|
||||||
|
} = require("devtools/server/actors/resources/index");
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Manages the stacktraces of the network
|
||||||
|
*
|
||||||
|
* @constructor
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
const StackTracesActor = ActorClassWithSpec(stackTracesSpec, {
|
||||||
|
initialize(conn, targetActor) {
|
||||||
|
Actor.prototype.initialize.call(this, conn);
|
||||||
|
this.targetActor = targetActor;
|
||||||
|
},
|
||||||
|
|
||||||
|
destroy(conn) {
|
||||||
|
Actor.prototype.destroy.call(this, conn);
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The "getStackTrace" packet type handler.
|
||||||
|
*
|
||||||
|
* @return object
|
||||||
|
* The response packet - stack trace.
|
||||||
|
*/
|
||||||
|
getStackTrace(resourceId) {
|
||||||
|
const networkEventStackTraceWatcher = getResourceWatcher(
|
||||||
|
this.targetActor,
|
||||||
|
NETWORK_EVENT_STACKTRACE
|
||||||
|
);
|
||||||
|
if (!networkEventStackTraceWatcher) {
|
||||||
|
throw new Error("Not listening for network event stacktraces");
|
||||||
|
}
|
||||||
|
const stacktrace = networkEventStackTraceWatcher.getStackTrace(resourceId);
|
||||||
|
return {
|
||||||
|
stacktrace: WebConsoleUtils.removeFramesAboveDebuggerEval(stacktrace),
|
||||||
|
};
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
exports.StackTracesActor = StackTracesActor;
|
@ -15,6 +15,7 @@ const TYPES = {
|
|||||||
PLATFORM_MESSAGE: "platform-message",
|
PLATFORM_MESSAGE: "platform-message",
|
||||||
NETWORK_EVENT: "network-event",
|
NETWORK_EVENT: "network-event",
|
||||||
STYLESHEET: "stylesheet",
|
STYLESHEET: "stylesheet",
|
||||||
|
NETWORK_EVENT_STACKTRACE: "network-event-stacktrace",
|
||||||
};
|
};
|
||||||
exports.TYPES = TYPES;
|
exports.TYPES = TYPES;
|
||||||
|
|
||||||
@ -49,6 +50,9 @@ const FrameTargetResources = augmentResourceDictionary({
|
|||||||
[TYPES.STYLESHEET]: {
|
[TYPES.STYLESHEET]: {
|
||||||
path: "devtools/server/actors/resources/stylesheets",
|
path: "devtools/server/actors/resources/stylesheets",
|
||||||
},
|
},
|
||||||
|
[TYPES.NETWORK_EVENT_STACKTRACE]: {
|
||||||
|
path: "devtools/server/actors/resources/network-events-stacktraces",
|
||||||
|
},
|
||||||
});
|
});
|
||||||
const ParentProcessResources = augmentResourceDictionary({
|
const ParentProcessResources = augmentResourceDictionary({
|
||||||
[TYPES.NETWORK_EVENT]: {
|
[TYPES.NETWORK_EVENT]: {
|
||||||
|
@ -15,6 +15,7 @@ DevToolsModules(
|
|||||||
'document-event.js',
|
'document-event.js',
|
||||||
'error-messages.js',
|
'error-messages.js',
|
||||||
'index.js',
|
'index.js',
|
||||||
|
'network-events-stacktraces.js',
|
||||||
'network-events.js',
|
'network-events.js',
|
||||||
'platform-messages.js',
|
'platform-messages.js',
|
||||||
'stylesheets.js',
|
'stylesheets.js',
|
||||||
|
203
devtools/server/actors/resources/network-events-stacktraces.js
Normal file
203
devtools/server/actors/resources/network-events-stacktraces.js
Normal file
@ -0,0 +1,203 @@
|
|||||||
|
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||||
|
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||||
|
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||||
|
|
||||||
|
"use strict";
|
||||||
|
|
||||||
|
const {
|
||||||
|
TYPES: { NETWORK_EVENT_STACKTRACE },
|
||||||
|
} = require("devtools/server/actors/resources/index");
|
||||||
|
|
||||||
|
const { Ci, components } = require("chrome");
|
||||||
|
const Services = require("Services");
|
||||||
|
|
||||||
|
loader.lazyRequireGetter(
|
||||||
|
this,
|
||||||
|
"ChannelEventSinkFactory",
|
||||||
|
"devtools/server/actors/network-monitor/channel-event-sink",
|
||||||
|
true
|
||||||
|
);
|
||||||
|
|
||||||
|
loader.lazyRequireGetter(
|
||||||
|
this,
|
||||||
|
"matchRequest",
|
||||||
|
"devtools/server/actors/network-monitor/network-observer",
|
||||||
|
true
|
||||||
|
);
|
||||||
|
|
||||||
|
class NetworkEventStackTracesWatcher {
|
||||||
|
/**
|
||||||
|
* Start watching for all network event's stack traces related to a given Target actor.
|
||||||
|
*
|
||||||
|
* @param TargetActor targetActor
|
||||||
|
* The target actor from which we should observe the strack traces
|
||||||
|
* @param Object options
|
||||||
|
* Dictionary object with following attributes:
|
||||||
|
* - onAvailable: mandatory
|
||||||
|
* This will be called for each resource.
|
||||||
|
*/
|
||||||
|
async watch(targetActor, { onAvailable }) {
|
||||||
|
Services.obs.addObserver(this, "http-on-opening-request");
|
||||||
|
Services.obs.addObserver(this, "document-on-opening-request");
|
||||||
|
Services.obs.addObserver(this, "network-monitor-alternate-stack");
|
||||||
|
ChannelEventSinkFactory.getService().registerCollector(this);
|
||||||
|
|
||||||
|
this.targetActor = targetActor;
|
||||||
|
this.onStackTraceAvailable = onAvailable;
|
||||||
|
|
||||||
|
this.stacktraces = new Map();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Stop watching for network event's strack traces related to a given Target Actor.
|
||||||
|
*
|
||||||
|
* @param TargetActor targetActor
|
||||||
|
* The target actor from which we should stop observing the strack traces
|
||||||
|
*/
|
||||||
|
destroy(targetActor) {
|
||||||
|
Services.obs.removeObserver(this, "http-on-opening-request");
|
||||||
|
Services.obs.removeObserver(this, "document-on-opening-request");
|
||||||
|
Services.obs.removeObserver(this, "network-monitor-alternate-stack");
|
||||||
|
ChannelEventSinkFactory.getService().unregisterCollector(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
onChannelRedirect(oldChannel, newChannel, flags) {
|
||||||
|
// We can be called with any nsIChannel, but are interested only in HTTP channels
|
||||||
|
try {
|
||||||
|
oldChannel.QueryInterface(Ci.nsIHttpChannel);
|
||||||
|
newChannel.QueryInterface(Ci.nsIHttpChannel);
|
||||||
|
} catch (ex) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const oldId = oldChannel.channelId;
|
||||||
|
const stacktrace = this.stacktraces.get(oldId);
|
||||||
|
if (stacktrace) {
|
||||||
|
this._setStackTrace(newChannel.channelId, stacktrace);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
observe(subject, topic, data) {
|
||||||
|
let channel, id;
|
||||||
|
try {
|
||||||
|
// We need to QI nsIHttpChannel in order to load the interface's
|
||||||
|
// methods / attributes for later code that could assume we are dealing
|
||||||
|
// with a nsIHttpChannel.
|
||||||
|
channel = subject.QueryInterface(Ci.nsIHttpChannel);
|
||||||
|
id = channel.channelId;
|
||||||
|
} catch (e1) {
|
||||||
|
try {
|
||||||
|
channel = subject.QueryInterface(Ci.nsIIdentChannel);
|
||||||
|
id = channel.channelId;
|
||||||
|
} catch (e2) {
|
||||||
|
// WebSocketChannels do not have IDs, so use the serial. When a WebSocket is
|
||||||
|
// opened in a content process, a channel is created locally but the HTTP
|
||||||
|
// channel for the connection lives entirely in the parent process. When
|
||||||
|
// the server code running in the parent sees that HTTP channel, it will
|
||||||
|
// look for the creation stack using the websocket's serial.
|
||||||
|
try {
|
||||||
|
channel = subject.QueryInterface(Ci.nsIWebSocketChannel);
|
||||||
|
id = channel.serial;
|
||||||
|
} catch (e3) {
|
||||||
|
// Channels which don't implement the above interfaces can appear here,
|
||||||
|
// such as nsIFileChannel. Ignore these channels.
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// XXX: is window the best filter to use?
|
||||||
|
if (!matchRequest(channel, { window: this.targetActor.window })) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this.stacktraces.has(id)) {
|
||||||
|
// 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;
|
||||||
|
}
|
||||||
|
|
||||||
|
const stacktrace = [];
|
||||||
|
switch (topic) {
|
||||||
|
case "http-on-opening-request":
|
||||||
|
case "document-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?.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._setStackTrace(id, stacktrace);
|
||||||
|
}
|
||||||
|
|
||||||
|
_setStackTrace(resourceId, stacktrace) {
|
||||||
|
this.stacktraces.set(resourceId, stacktrace);
|
||||||
|
this.onStackTraceAvailable([
|
||||||
|
{
|
||||||
|
resourceType: NETWORK_EVENT_STACKTRACE,
|
||||||
|
resourceId,
|
||||||
|
targetFront: this.targetFront,
|
||||||
|
stacktraceAvailable: stacktrace && stacktrace.length > 0,
|
||||||
|
lastFrame:
|
||||||
|
stacktrace && stacktrace.length > 0 ? stacktrace[0] : undefined,
|
||||||
|
},
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
|
||||||
|
getStackTrace(id) {
|
||||||
|
let stacktrace = [];
|
||||||
|
if (this.stacktraces.has(id)) {
|
||||||
|
stacktrace = this.stacktraces.get(id);
|
||||||
|
this.stacktraces.delete(id);
|
||||||
|
}
|
||||||
|
return stacktrace;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
module.exports = NetworkEventStackTracesWatcher;
|
@ -30,15 +30,12 @@ class NetworkEventWatcher {
|
|||||||
* This will be called for each resource.
|
* This will be called for each resource.
|
||||||
* - onUpdated: optional function
|
* - onUpdated: optional function
|
||||||
* This would be called multiple times for each resource.
|
* This would be called multiple times for each resource.
|
||||||
* - onDestroyed: optional function
|
|
||||||
* This would be called multiple times for each resource.
|
|
||||||
*/
|
*/
|
||||||
async watch(watcherActor, { onAvailable, onUpdated, onDestroyed }) {
|
async watch(watcherActor, { onAvailable, onUpdated }) {
|
||||||
this.networkEvents = new Map();
|
this.networkEvents = new Map();
|
||||||
this.watcherActor = watcherActor;
|
this.watcherActor = watcherActor;
|
||||||
this.onNetworkEventAvailable = onAvailable;
|
this.onNetworkEventAvailable = onAvailable;
|
||||||
this.onNetworkEventUpdated = onUpdated;
|
this.onNetworkEventUpdated = onUpdated;
|
||||||
this.onNeworkEventDestroyed = onDestroyed;
|
|
||||||
|
|
||||||
this.listener = new NetworkObserver(
|
this.listener = new NetworkObserver(
|
||||||
{ browserId: watcherActor.browserId },
|
{ browserId: watcherActor.browserId },
|
||||||
@ -58,7 +55,7 @@ class NetworkEventWatcher {
|
|||||||
this,
|
this,
|
||||||
{
|
{
|
||||||
onNetworkEventUpdate: this.onNetworkEventUpdate.bind(this),
|
onNetworkEventUpdate: this.onNetworkEventUpdate.bind(this),
|
||||||
onNetworkEventDestroy: this.onNeworkEventDestroyed,
|
onNetworkEventDestroy: this.onNetworkEventDestroy.bind(this),
|
||||||
},
|
},
|
||||||
event
|
event
|
||||||
);
|
);
|
||||||
@ -141,6 +138,12 @@ class NetworkEventWatcher {
|
|||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
onNetworkEventDestroy(channelId) {
|
||||||
|
if (this.networkEvents.has(channelId)) {
|
||||||
|
this.networkEvents.delete(channelId);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Stop watching for network event related to a given Watcher Actor.
|
* Stop watching for network event related to a given Watcher Actor.
|
||||||
*
|
*
|
||||||
|
@ -275,6 +275,14 @@ const ActorRegistry = {
|
|||||||
constructor: "ManifestActor",
|
constructor: "ManifestActor",
|
||||||
type: { target: true },
|
type: { target: true },
|
||||||
});
|
});
|
||||||
|
this.registerModule(
|
||||||
|
"devtools/server/actors/network-monitor/stack-traces-actor",
|
||||||
|
{
|
||||||
|
prefix: "stacktraces",
|
||||||
|
constructor: "StackTracesActor",
|
||||||
|
type: { target: true },
|
||||||
|
}
|
||||||
|
);
|
||||||
},
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -117,6 +117,8 @@ exports.WatcherActor = protocol.ActorClassWithSpec(watcherSpec, {
|
|||||||
[Resources.TYPES.PLATFORM_MESSAGE]: true,
|
[Resources.TYPES.PLATFORM_MESSAGE]: true,
|
||||||
[Resources.TYPES.NETWORK_EVENT]:
|
[Resources.TYPES.NETWORK_EVENT]:
|
||||||
enableServerWatcher && hasBrowserElement,
|
enableServerWatcher && hasBrowserElement,
|
||||||
|
[Resources.TYPES.NETWORK_EVENT_STACKTRACE]:
|
||||||
|
enableServerWatcher && hasBrowserElement,
|
||||||
[Resources.TYPES.STYLESHEET]:
|
[Resources.TYPES.STYLESHEET]:
|
||||||
enableServerWatcher && hasBrowserElement,
|
enableServerWatcher && hasBrowserElement,
|
||||||
},
|
},
|
||||||
|
@ -19,8 +19,8 @@ module.exports = async function({
|
|||||||
onAvailable([
|
onAvailable([
|
||||||
{
|
{
|
||||||
resourceType: ResourceWatcher.TYPES.NETWORK_EVENT_STACKTRACE,
|
resourceType: ResourceWatcher.TYPES.NETWORK_EVENT_STACKTRACE,
|
||||||
channelId: actor.channelId,
|
resourceId: actor.channelId,
|
||||||
stacktrace: actor.cause.stacktraceAvailable,
|
stacktraceAvailable: actor.cause.stacktraceAvailable,
|
||||||
lastFrame: actor.cause.lastFrame,
|
lastFrame: actor.cause.lastFrame,
|
||||||
},
|
},
|
||||||
]);
|
]);
|
||||||
|
@ -39,9 +39,7 @@ module.exports = async function({
|
|||||||
const resource = {
|
const resource = {
|
||||||
resourceId: actor.channelId,
|
resourceId: actor.channelId,
|
||||||
resourceType: ResourceWatcher.TYPES.NETWORK_EVENT,
|
resourceType: ResourceWatcher.TYPES.NETWORK_EVENT,
|
||||||
_type: "NetworkEvent",
|
|
||||||
timeStamp: actor.timeStamp,
|
timeStamp: actor.timeStamp,
|
||||||
node: null,
|
|
||||||
actor: actor.actor,
|
actor: actor.actor,
|
||||||
discardRequestBody: true,
|
discardRequestBody: true,
|
||||||
discardResponseBody: true,
|
discardResponseBody: true,
|
||||||
@ -61,7 +59,10 @@ module.exports = async function({
|
|||||||
referrerPolicy: actor.referrerPolicy,
|
referrerPolicy: actor.referrerPolicy,
|
||||||
blockedReason: actor.blockedReason,
|
blockedReason: actor.blockedReason,
|
||||||
blockingExtension: actor.blockingExtension,
|
blockingExtension: actor.blockingExtension,
|
||||||
channelId: actor.channelId,
|
stacktraceResourceId:
|
||||||
|
actor.cause.type == "websocket"
|
||||||
|
? actor.url.replace(/^http/, "ws")
|
||||||
|
: actor.channelId,
|
||||||
updates: [],
|
updates: [],
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -280,7 +280,7 @@ class ResourceWatcher {
|
|||||||
// ...request existing resource and new one to come from this one target
|
// ...request existing resource and new one to come from this one target
|
||||||
// *but* only do that for backward compat, where we don't have the watcher API
|
// *but* only do that for backward compat, where we don't have the watcher API
|
||||||
// (See bug 1626647)
|
// (See bug 1626647)
|
||||||
if (this._hasWatcherSupport(resourceType)) {
|
if (this.hasWatcherSupport(resourceType)) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
await this._watchResourcesForTarget(targetFront, resourceType);
|
await this._watchResourcesForTarget(targetFront, resourceType);
|
||||||
@ -601,7 +601,7 @@ class ResourceWatcher {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
_hasWatcherSupport(resourceType) {
|
hasWatcherSupport(resourceType) {
|
||||||
return this.watcher?.traits?.resources?.[resourceType];
|
return this.watcher?.traits?.resources?.[resourceType];
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -632,7 +632,7 @@ class ResourceWatcher {
|
|||||||
|
|
||||||
// If the server supports the Watcher API and the Watcher supports
|
// If the server supports the Watcher API and the Watcher supports
|
||||||
// this resource type, use this API
|
// this resource type, use this API
|
||||||
if (this._hasWatcherSupport(resourceType)) {
|
if (this.hasWatcherSupport(resourceType)) {
|
||||||
await this.watcher.watchResources([resourceType]);
|
await this.watcher.watchResources([resourceType]);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@ -712,7 +712,7 @@ class ResourceWatcher {
|
|||||||
|
|
||||||
// If the server supports the Watcher API and the Watcher supports
|
// If the server supports the Watcher API and the Watcher supports
|
||||||
// this resource type, use this API
|
// this resource type, use this API
|
||||||
if (this._hasWatcherSupport(resourceType)) {
|
if (this.hasWatcherSupport(resourceType)) {
|
||||||
this.watcher.unwatchResources([resourceType]);
|
this.watcher.unwatchResources([resourceType]);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -14,7 +14,7 @@ const TEST_URI = `${URL_ROOT_SSL}network_document.html`;
|
|||||||
const REQUEST_STUB = {
|
const REQUEST_STUB = {
|
||||||
code: `await fetch("/request_post_0.html", { method: "POST" });`,
|
code: `await fetch("/request_post_0.html", { method: "POST" });`,
|
||||||
expected: {
|
expected: {
|
||||||
stacktrace: true,
|
stacktraceAvailable: true,
|
||||||
lastFrame: {
|
lastFrame: {
|
||||||
filename:
|
filename:
|
||||||
"https://example.com/browser/devtools/shared/resources/tests/network_document.html",
|
"https://example.com/browser/devtools/shared/resources/tests/network_document.html",
|
||||||
|
@ -209,6 +209,11 @@ const Types = (exports.__TypesForTests = [
|
|||||||
spec: "devtools/shared/specs/source",
|
spec: "devtools/shared/specs/source",
|
||||||
front: "devtools/client/fronts/source",
|
front: "devtools/client/fronts/source",
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
types: ["stacktraces"],
|
||||||
|
spec: "devtools/shared/specs/stacktraces",
|
||||||
|
front: "devtools/client/fronts/stacktraces",
|
||||||
|
},
|
||||||
{
|
{
|
||||||
types: [
|
types: [
|
||||||
"cookies",
|
"cookies",
|
||||||
|
@ -45,6 +45,7 @@ DevToolsModules(
|
|||||||
'root.js',
|
'root.js',
|
||||||
'screenshot.js',
|
'screenshot.js',
|
||||||
'source.js',
|
'source.js',
|
||||||
|
'stacktraces.js',
|
||||||
'storage.js',
|
'storage.js',
|
||||||
'string.js',
|
'string.js',
|
||||||
'styles.js',
|
'styles.js',
|
||||||
|
20
devtools/shared/specs/stacktraces.js
Normal file
20
devtools/shared/specs/stacktraces.js
Normal file
@ -0,0 +1,20 @@
|
|||||||
|
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||||
|
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||||
|
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||||
|
|
||||||
|
"use strict";
|
||||||
|
|
||||||
|
const { generateActorSpec, RetVal, Arg } = require("devtools/shared/protocol");
|
||||||
|
|
||||||
|
const stackTracesSpec = generateActorSpec({
|
||||||
|
typeName: "stacktraces",
|
||||||
|
methods: {
|
||||||
|
getStackTrace: {
|
||||||
|
request: { resourceId: Arg(0) },
|
||||||
|
// stacktrace is an "array:string", but not always.
|
||||||
|
response: RetVal("json"),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
exports.stackTracesSpec = stackTracesSpec;
|
Loading…
Reference in New Issue
Block a user