Bug 1662736 - [devtools] Simplify the network request payload r=ochameau

Differential Revision: https://phabricator.services.mozilla.com/D94457
This commit is contained in:
Hubert Boma Manilla 2020-11-13 15:17:48 +00:00
Родитель 0951f9218a
Коммит a70ec38159
24 изменённых файлов: 423 добавлений и 823 удалений

Просмотреть файл

@ -325,12 +325,12 @@ class FirefoxConnector {
}
navigate() {
if (this.dataProvider.isPayloadQueueEmpty()) {
if (!this.dataProvider.hasPendingRequests()) {
this.onReloaded();
return;
}
const listener = () => {
if (this.dataProvider && !this.dataProvider.isPayloadQueueEmpty()) {
if (this.dataProvider && this.dataProvider.hasPendingRequests()) {
return;
}
if (this.owner) {

Просмотреть файл

@ -43,8 +43,8 @@ class FirefoxDataProvider {
// Map of the stacktrace information keyed by the actor id's
this.stackTraceRequestInfoByActorID = new Map();
// Internal properties
this.payloadQueue = new Map();
// For tracking unfinished requests
this.pendingRequests = new Set();
// Map[key string => Promise] used by `requestData` to prevent requesting the same
// request data twice.
@ -84,32 +84,14 @@ class FirefoxDataProvider {
* Add a new network request to application state.
*
* @param {string} id request id
* @param {object} data data payload will be added to application state
* @param {object} resource resource payload will be added to application state
*/
async addRequest(id, data) {
const { startedDateTime, ...payload } = data;
// Insert blocked reason in the payload queue as well, as we'll need it later
// when deciding if the request is complete.
this.pushRequestToQueue(id, {
blockedReason: payload.blockedReason,
});
async addRequest(id, resource) {
// Add to the pending requests which helps when deciding if the request is complete.
this.pendingRequests.add(id);
if (this.actionsEnabled && this.actions.addRequest) {
await this.actions.addRequest(
id,
{
...payload,
// Convert the received date/time string to a unix timestamp.
startedMs: Date.parse(startedDateTime),
// Compatibility code to support Firefox 58 and earlier that always
// send stack-trace immediately on networkEvent message.
// FF59+ supports fetching the traces lazily via requestData.
stacktrace: payload.cause.stacktrace,
},
true
);
await this.actions.addRequest(id, resource, true);
}
this.emit(EVENTS.REQUEST_ADDED, id);
@ -287,25 +269,10 @@ class FirefoxDataProvider {
/**
* Public API used by the Toolbox: Tells if there is still any pending request.
*
* @return {boolean} returns true if the payload queue is empty
* @return {boolean} returns true if pending requests still exist in the queue.
*/
isPayloadQueueEmpty() {
return this.payloadQueue.size === 0;
}
/**
* Merge upcoming networkEventUpdate payload into existing one.
*
* @param {string} id request actor id
* @param {object} payload request data payload
*/
pushRequestToQueue(id, payload) {
let payloadFromQueue = this.payloadQueue.get(id);
if (!payloadFromQueue) {
payloadFromQueue = {};
this.payloadQueue.set(id, payloadFromQueue);
}
Object.assign(payloadFromQueue, payload);
hasPendingRequests() {
return this.pendingRequests.size > 0;
}
/**
@ -359,22 +326,7 @@ class FirefoxDataProvider {
* @param {object} resource The network event resource
*/
async onNetworkResourceAvailable(resource) {
const {
actor,
cause,
fromCache,
fromServiceWorker,
isXHR,
request: { method, url },
response: { bodySize, ...responseProps },
startedDateTime,
isThirdPartyTrackingResource,
referrerPolicy,
blockedReason,
blockingExtension,
resourceId,
stacktraceResourceId,
} = resource;
const { actor, stacktraceResourceId } = resource;
// Check if a stacktrace resource exists for this network resource.
// The stacktrace event is expected to happen before the network
@ -385,8 +337,10 @@ class FirefoxDataProvider {
lastFrame,
targetFront,
} = this.stackTraces.get(stacktraceResourceId);
cause.stacktraceAvailable = stacktraceAvailable;
cause.lastFrame = lastFrame;
resource.cause.stacktraceAvailable = stacktraceAvailable;
resource.cause.lastFrame = lastFrame;
this.stackTraces.delete(stacktraceResourceId);
// We retrieve preliminary information about the stacktrace from the
// NETWORK_EVENT_STACKTRACE resource via `this.stackTraces` Map,
@ -398,45 +352,7 @@ class FirefoxDataProvider {
stacktraceResourceId,
});
}
// For resources from the resource watcher cache no updates are going to be fired
// as the resource already contains all the updated props. These need to be set so
// the UI knows the data is available on the backend.
const available = {};
[
"eventTimings",
"requestHeaders",
"requestPostData",
"responseHeaders",
"responseStart",
"responseContent",
"securityInfo",
"responseCache",
"responseCookies",
].forEach(updateType => {
if (resource.updates.includes(updateType)) {
available[`${updateType}Available`] = true;
}
});
await this.addRequest(actor, {
cause,
fromCache,
fromServiceWorker,
isXHR,
method,
startedDateTime,
url,
isThirdPartyTrackingResource,
referrerPolicy,
blockedReason,
blockingExtension,
resourceId,
mimeType: resource?.content?.mimeType,
contentSize: bodySize,
...responseProps,
...available,
});
await this.addRequest(actor, resource);
this.emitForTests(TEST_EVENTS.NETWORK_EVENT, resource);
}
@ -445,66 +361,23 @@ class FirefoxDataProvider {
*
* @param {object} resource The updated network event resource.
*/
async onNetworkResourceUpdated(resource, update) {
switch (update.updateType) {
case "securityInfo":
this.pushRequestToQueue(resource.actor, {
securityState: resource.securityState,
isRacing: resource.isRacing,
});
break;
case "responseStart":
this.pushRequestToQueue(resource.actor, {
httpVersion: resource.response.httpVersion,
remoteAddress: resource.response.remoteAddress,
remotePort: resource.response.remotePort,
status: resource.response.status,
statusText: resource.response.statusText,
headersSize: resource.response.headersSize,
waitingTime: resource.response.waitingTime,
});
async onNetworkResourceUpdated(resource) {
// Identify the channel as SSE if mimeType is event-stream.
if (resource.response.content.mimeType?.includes("text/event-stream")) {
if (resource?.mimeType?.includes("text/event-stream")) {
await this.setEventStreamFlag(resource.actor);
}
this.emitForTests(
TEST_EVENTS.STARTED_RECEIVING_RESPONSE,
resource.actor
);
break;
case "responseContent":
this.pushRequestToQueue(resource.actor, {
contentSize: resource.response.bodySize,
transferredSize: resource.response.transferredSize,
mimeType: resource.response.content.mimeType,
blockingExtension: resource.blockingExtension,
blockedReason: resource.blockedReason,
});
break;
case "eventTimings":
// Total time doesn't have to be always set e.g. net provider enhancer
// in Console panel is using this method to fetch data when network log
// is expanded. So, make sure to not push undefined into the payload queue
// (it could overwrite an existing value).
if (typeof resource.totalTime !== "undefined") {
this.pushRequestToQueue(resource.actor, {
totalTime: resource.totalTime,
});
}
break;
this.pendingRequests.delete(resource.actor);
if (this.actionsEnabled && this.actions.updateRequest) {
await this.actions.updateRequest(resource.actor, resource, true);
}
// This available field helps knowing when/if updateType property is arrived
// and can be requested via `requestData`
this.pushRequestToQueue(resource.actor, {
[`${update.updateType}Available`]: true,
});
await this.onPayloadDataReceived(resource);
// This event is fired only once per request, once all the properties are fetched
// from `onNetworkResourceUpdated`. There should be no more RDP requests after this.
// Note that this event might be consumed by extension so, emit it in production
// release as well.
this.emitForTests(TEST_EVENTS.NETWORK_EVENT_UPDATED, resource.actor);
this.emit(EVENTS.PAYLOAD_READY, resource);
}
/**
@ -564,42 +437,6 @@ class FirefoxDataProvider {
// TODO: Emit an event for test here
}
/**
* Notify actions when events from onNetworkResourceUpdated are done, updated network event
* resources contain initial network info for each updateType and then we can invoke
* requestData to fetch its corresponded data lazily.
* Once all updateTypes of updated network event resource are available, we flush the merged
* request payload from pending queue and then update the component.
*/
async onPayloadDataReceived(resource) {
const payload = this.payloadQueue.get(resource.actor) || {};
// For blocked requests, we should only expect the request portions and not
// the response portions to be available.
if (!payload.requestHeadersAvailable || !payload.requestCookiesAvailable) {
return;
}
// For unblocked requests, we should wait for all major portions to be available.
if (
!payload.blockedReason &&
(!payload.eventTimingsAvailable || !payload.responseContentAvailable)
) {
return;
}
this.payloadQueue.delete(resource.actor);
if (this.actionsEnabled && this.actions.updateRequest) {
await this.actions.updateRequest(resource.actor, payload, true);
}
// This event is fired only once per request, once all the properties are fetched
// from `onNetworkResourceUpdated`. There should be no more RDP requests after this.
// Note that this event might be consumed by extension so, emit it in production
// release as well.
this.emit(EVENTS.PAYLOAD_READY, resource);
}
/**
* Public connector API to lazily request HTTP details from the backend.
*

Просмотреть файл

@ -170,12 +170,7 @@ HarCollector.prototype = {
for (const resource of resources) {
trace.log("HarCollector.onNetworkEvent; ", resource);
const {
actor,
startedDateTime,
request: { method, url },
isXHR,
} = resource;
const { actor, startedDateTime, method, url, isXHR } = resource;
const startTime = Date.parse(startedDateTime);
if (this.firstRequestStart == -1) {
@ -211,7 +206,7 @@ HarCollector.prototype = {
},
onResourceUpdated: function(updates) {
for (const { resource, update } of updates) {
for (const { resource } of updates) {
// Skip events from unknown actors (not in the list).
// It can happen when there are zombie requests received after
// the target is closed or multiple tabs are attached through
@ -221,84 +216,84 @@ HarCollector.prototype = {
return;
}
trace.log(
"HarCollector.onNetworkEventUpdate; " + update.updateType,
resource
);
const includeResponseBodies = Services.prefs.getBoolPref(
"devtools.netmonitor.har.includeResponseBodies"
);
[
{
type: "eventTimings",
method: "getEventTimings",
callbackName: "onEventTimings",
},
{
type: "requestHeaders",
method: "getRequestHeaders",
callbackName: "onRequestHeaders",
},
{
type: "requestPostData",
method: "getRequestPostData",
callbackName: "onRequestPostData",
},
{
type: "responseHeaders",
method: "getResponseHeaders",
callbackName: "onResponseHeaders",
},
{ type: "responseStart" },
{
type: "responseContent",
method: "getResponseContent",
callbackName: "onResponseContent",
},
{
type: "requestCookies",
method: "getRequestCookies",
callbackName: "onRequestCookies",
},
{
type: "responseCookies",
method: "getResponseCookies",
callbackName: "onResponseCookies",
},
].forEach(updateType => {
trace.log(
"HarCollector.onNetworkEventUpdate; " + updateType.type,
resource
);
let request;
switch (update.updateType) {
case "requestHeaders":
request = this.getData(
resource.actor,
"getRequestHeaders",
this.onRequestHeaders
);
break;
case "requestCookies":
request = this.getData(
resource.actor,
"getRequestCookies",
this.onRequestCookies
);
break;
case "requestPostData":
request = this.getData(
resource.actor,
"getRequestPostData",
this.onRequestPostData
);
break;
case "responseHeaders":
request = this.getData(
resource.actor,
"getResponseHeaders",
this.onResponseHeaders
);
break;
case "responseCookies":
request = this.getData(
resource.actor,
"getResponseCookies",
this.onResponseCookies
);
break;
case "responseStart":
file.httpVersion = resource.response.httpVersion;
file.status = resource.response.status;
file.statusText = resource.response.statusText;
break;
case "responseContent":
if (resource[`${updateType.type}Available`]) {
if (updateType.type == "responseStart") {
file.httpVersion = resource.httpVersion;
file.status = resource.status;
file.statusText = resource.statusText;
} else if (updateType.type == "responseContent") {
file.contentSize = resource.contentSize;
file.mimeType = resource.mimeType;
file.transferredSize = resource.transferredSize;
if (includeResponseBodies) {
request = this.getData(
resource.actor,
"getResponseContent",
this.onResponseContent
updateType.method,
this[updateType.callbackName]
);
}
break;
case "eventTimings":
} else {
request = this.getData(
resource.actor,
"getEventTimings",
this.onEventTimings
updateType.method,
this[updateType.callbackName]
);
break;
}
}
if (request) {
this.requests.push(request);
}
this.resetPageLoadTimeout();
});
}
},

Просмотреть файл

@ -5,6 +5,9 @@
"use strict";
const { TIMING_KEYS } = require("devtools/client/netmonitor/src/constants");
const {
getUrlDetails,
} = require("devtools/client/netmonitor/src/utils/request-utils");
var guid = 0;
@ -47,6 +50,7 @@ HarImporter.prototype = {
startedMs: startedMs,
method: entry.request.method,
url: entry.request.url,
urlDetails: getUrlDetails(entry.request.url),
isXHR: false,
cause: {
loadingDocumentUri: "",

Просмотреть файл

@ -5,7 +5,6 @@
"use strict";
const {
getUrlDetails,
processNetworkUpdates,
} = require("devtools/client/netmonitor/src/utils/request-utils");
const {
@ -152,11 +151,12 @@ function requestsReducer(state = Requests(), action) {
function addRequest(state, action) {
const nextState = { ...state };
// The target front is not used and cannot be serialized by redux
// eslint-disable-next-line no-unused-vars
const { targetFront, ...requestData } = action.data;
const newRequest = {
id: action.id,
...action.data,
urlDetails: getUrlDetails(action.data.url),
...requestData,
};
nextState.requests = [...state.requests, newRequest];

Просмотреть файл

@ -585,26 +585,25 @@ async function getMessagePayload(payload, getLongString) {
/**
* This helper function is used for additional processing of
* incoming network update packets. It's used by Network and
* Console panel reducers.
* incoming network update packets. It makes sure the only valid
* update properties and the values are correct.
* It's used by Network and Console panel reducers.
* @param {object} update
* The new update payload
* @param {object} request
* The current request in the state
*/
function processNetworkUpdates(update) {
const result = {};
const newRequest = {};
for (const [key, value] of Object.entries(update)) {
if (UPDATE_PROPS.includes(key)) {
result[key] = value;
switch (key) {
case "totalTime":
result.totalTime = update.totalTime;
break;
case "requestPostData":
result.requestHeadersFromUploadStream = value.uploadHeaders;
break;
newRequest[key] = value;
if (key == "requestPostData") {
newRequest.requestHeadersFromUploadStream = value.uploadHeaders;
}
}
}
return result;
return newRequest;
}
/**

Просмотреть файл

@ -80,16 +80,17 @@ function NetworkEventMessage({
source,
type,
level,
request,
url,
method,
isXHR,
timeStamp,
blockedReason,
httpVersion,
status,
statusText,
totalTime,
} = message;
const { response = {}, totalTime } = networkMessageUpdate;
const { httpVersion, status, statusText } = response;
const topLevelClasses = ["cm-s-mozilla"];
if (isMessageNetworkError(message)) {
topLevelClasses.push("error");
@ -139,7 +140,7 @@ function NetworkEventMessage({
const onToggle = (messageId, e) => {
const shouldOpenLink = (isMacOS && e.metaKey) || (!isMacOS && e.ctrlKey);
if (shouldOpenLink) {
serviceContainer.openLink(request.url, e);
serviceContainer.openLink(url, e);
e.stopPropagation();
} else if (open) {
dispatch(actions.messageClose(messageId));
@ -149,27 +150,24 @@ function NetworkEventMessage({
};
// Message body components.
const method = dom.span({ className: "method" }, request.method);
const requestMethod = dom.span({ className: "method" }, method);
const xhr = isXHR
? dom.span({ className: "xhr" }, l10n.getStr("webConsoleXhrIndicator"))
: null;
const requestUrl = dom.span(
{ className: "url", title: request.url },
request.url
);
const requestUrl = dom.span({ className: "url", title: url }, url);
const statusBody = statusInfo
? dom.a({ className: "status" }, statusInfo)
: null;
const messageBody = [xhr, method, requestUrl, statusBody];
const messageBody = [xhr, requestMethod, requestUrl, statusBody];
// API consumed by Net monitor UI components. Most of the method
// are not needed in context of the Console panel (atm) and thus
// let's just provide empty implementation.
// Individual methods might be implemented step by step as needed.
const connector = {
viewSourceInDebugger: (url, line, column) => {
serviceContainer.onViewSourceInDebugger({ url, line, column });
viewSourceInDebugger: (srcUrl, line, column) => {
serviceContainer.onViewSourceInDebugger({ url: srcUrl, line, column });
},
getLongString: grip => {
return serviceContainer.getLongString(grip);
@ -210,6 +208,7 @@ function NetworkEventMessage({
})
);
const request = { url, method };
return Message({
dispatch,
messageId: id,

Просмотреть файл

@ -51,9 +51,7 @@ function enableNetProvider(webConsoleUI) {
const message = updates[action.id];
if (message && !message.openedOnce && message.source == "network") {
dataProvider.onNetworkResourceAvailable(message);
message.updates.forEach(updateType => {
dataProvider.onNetworkResourceUpdated(message, { updateType });
});
dataProvider.onNetworkResourceUpdated(message);
}
}
@ -69,9 +67,7 @@ function enableNetProvider(webConsoleUI) {
const open = allMessages.includes(actor);
if (open) {
const message = getMessage(newState, actor);
message.updates.forEach(updateType => {
dataProvider.onNetworkResourceUpdated(message, { updateType });
});
dataProvider.onNetworkResourceUpdated(message);
}
}
}

Просмотреть файл

@ -1359,7 +1359,7 @@ function passSearchFilters(message, filters) {
// Look for a match in location.
isTextInFrame(matchStr, message.frame) ||
// Look for a match in net events.
isTextInNetEvent(matchStr, message.request) ||
isTextInNetEvent(matchStr, message) ||
// Look for a match in stack-trace.
isTextInStackTrace(matchStr, message.stacktrace) ||
// Look for a match in messageText.
@ -1445,12 +1445,10 @@ function isTextInParameter(matchStr, parameter) {
/**
* Returns true if given text is included in provided net event grip.
*/
function isTextInNetEvent(matchStr, request) {
if (!request) {
function isTextInNetEvent(matchStr, { method, url } = {}) {
if (!method && !url) {
return false;
}
const { method, url } = request;
return matchStr(method) || matchStr(url);
}

Просмотреть файл

@ -32,6 +32,8 @@ add_task(async function() {
// clear the browser console.
await clearOutput(hud);
await waitForTick();
await safeCloseBrowserConsole();
});
async function testMessages(hud) {

Просмотреть файл

@ -24,9 +24,9 @@ add_task(async function task() {
const currentTab = gBrowser.selectedTab;
const target = await TargetFactory.forTab(currentTab);
const toolbox = gDevTools.getToolbox(target);
const panel = toolbox.getCurrentPanel().panelWin;
const monitor = toolbox.getCurrentPanel();
const netReady = monitor.panelWin.api.once("NetMonitor:PayloadReady");
const netReady = panel.api.once("NetMonitor:PayloadReady");
// Fire an XHR POST request.
await SpecialPowers.spawn(gBrowser.selectedBrowser, [], function() {
@ -46,19 +46,7 @@ add_task(async function task() {
const urlNode = messageNode.querySelector(".url");
info("Network message found.");
const onReady = new Promise(resolve => {
let count = 0;
function onPayloadReady(updateCount) {
count += updateCount;
// Wait for all NETWORK_REQUEST updated events
// Otherwise we may still be having pending request
if (count == 7) {
hud.ui.off("network-request-payload-ready", onPayloadReady);
resolve();
}
}
hud.ui.on("network-request-payload-ready", onPayloadReady);
});
const onReady = hud.ui.once("network-request-payload-ready");
// Expand network log
urlNode.click();
@ -66,6 +54,7 @@ add_task(async function task() {
await onReady;
info("network-request-payload-ready received");
await testNetworkMessage(messageNode);
await waitForLazyRequests(toolbox);
});

Просмотреть файл

@ -27,6 +27,9 @@ registerCleanupFunction(async () => {
});
add_task(async function task() {
// Make sure the filter to show all the requests is set
await pushPref("devtools.netmonitor.filters", '["all"]');
// Test that the request appears in the console.
const hud = await openNewTabAndConsole(TEST_URI);
const currentTab = gBrowser.selectedTab;
@ -65,11 +68,14 @@ async function testNetmonitor(toolbox) {
);
store.dispatch(Actions.batchEnable(false));
const requestItem = document.querySelector(".request-list-item");
await waitUntil(() => store.getState().requests.requests.length > 0);
// Lets also wait until all the event timings data requested
// has completed and the column is rendered.
await waitForDOM(requestItem, ".requests-list-timings-total");
await waitForDOM(
document,
".request-list-item:first-child .requests-list-timings-total"
);
is(
store.getState().requests.requests.length,

Просмотреть файл

@ -40,15 +40,7 @@ add_task(async function() {
let failed = false;
for (const [key, packet] of generatedStubs) {
// packet.updates are handle by the webconsole front, and can be updated after
// we cleaned the packet, so the order isn't guaranteed. Let's sort the array
// here so the test doesn't fail.
const existingPacket = existingStubs.stubPackets.get(key);
if (packet.updates && existingPacket.updates) {
packet.updates.sort();
existingPacket.updates.sort();
}
const packetStr = JSON.stringify(packet, null, 2);
const existingPacketStr = JSON.stringify(existingPacket, null, 2);
is(packetStr, existingPacketStr, `"${key}" packet has expected value`);
@ -67,7 +59,6 @@ async function generateNetworkEventStubs() {
const tab = await addTab(TEST_URI);
const resourceWatcher = await createResourceWatcherForTab(tab);
const stacktraces = new Map();
let addNetworkStub = function() {};
let addNetworkUpdateStub = function() {};
@ -110,7 +101,6 @@ async function generateNetworkEventStubs() {
);
for (const [key, code] of getCommands()) {
const noExpectedUpdates = 7;
const networkEventDone = new Promise(resolve => {
addNetworkStub = resource => {
stubs.set(key, getCleanedPacket(key, getOrderedResource(resource)));
@ -118,12 +108,8 @@ async function generateNetworkEventStubs() {
};
});
const networkEventUpdateDone = new Promise(resolve => {
let updateCount = 0;
addNetworkUpdateStub = resource => {
const updateKey = `${key} update`;
// make sure all the updates have been happened
if (updateCount >= noExpectedUpdates) {
// make sure the network event stub contains all the updates
stubs.set(key, getCleanedPacket(key, getOrderedResource(resource)));
stubs.set(
updateKey,
@ -132,11 +118,7 @@ async function generateNetworkEventStubs() {
// Hand-picking only what we need should prevent this.
getCleanedPacket(updateKey, getOrderedResource(resource))
);
resolve();
} else {
updateCount++;
}
};
});
@ -169,15 +151,23 @@ async function generateNetworkEventStubs() {
function getOrderedResource(resource) {
return {
resourceType: resource.resourceType,
_type: resource._type,
timeStamp: resource.timeStamp,
node: resource.node,
actor: resource.actor,
startedDateTime: resource.startedDateTime,
request: resource.request,
method: resource.method,
url: resource.url,
isXHR: resource.isXHR,
cause: resource.cause,
response: resource.response,
httpVersion: resource.httpVersion,
status: resource.status,
statusText: resource.statusText,
headersSize: resource.headersSize,
remoteAddress: resource.remoteAddress,
remotePort: resource.remotePort,
mimeType: resource.mimeType,
waitingTime: resource.waitingTime,
contentSize: resource.contentSize,
transferredSize: resource.transferredSize,
timings: resource.timings,
private: resource.private,
fromCache: resource.fromCache,
@ -185,10 +175,11 @@ function getOrderedResource(resource) {
isThirdPartyTrackingResource: resource.isThirdPartyTrackingResource,
referrerPolicy: resource.referrerPolicy,
blockedReason: resource.blockedReason,
blockingExtension: resource.blockingExtension,
channelId: resource.channelId,
updates: resource.updates,
totalTime: resource.totalTime,
securityState: resource.securityState,
responseCache: resource.responseCache,
isRacing: resource.isRacing,
};
}

Просмотреть файл

@ -1704,7 +1704,12 @@ function toggleLayout(hud) {
async function waitForLazyRequests(toolbox) {
const { wrapper } = toolbox.getCurrentPanel().hud.ui;
return waitUntil(() => {
return !wrapper.networkDataProvider.lazyRequestData.size;
return (
!wrapper.networkDataProvider.lazyRequestData.size &&
// Make sure that batched request updates are all complete
// as they trigger late lazy data requests.
!wrapper.queuedRequestUpdates.length
);
});
}

Просмотреть файл

@ -296,29 +296,8 @@ function getCleanedPacket(key, packet) {
res.actor = existingPacket.actor;
}
if (res?.request?.headersSize && existingPacket?.request?.headersSize) {
res.request.headersSize = existingPacket.request.headersSize;
}
if (res?.response?.headersSize && existingPacket?.response?.headersSize) {
res.response.headersSize = existingPacket.response.headersSize;
}
if (res?.response?.bodySize && existingPacket?.response?.bodySize) {
res.response.bodySize = existingPacket.response.bodySize;
}
if (
res?.response?.transferredSize &&
existingPacket?.response?.transferredSize
) {
res.response.transferredSize = existingPacket.response.transferredSize;
}
if (res?.response?.waitingTime && existingPacket?.response?.waitingTime) {
res.response.waitingTime = existingPacket.response.waitingTime;
}
if (res.updates && Array.isArray(res.updates)) {
res.updates.sort();
if (res.waitingTime && existingPacket.waitingTime) {
res.waitingTime = existingPacket.waitingTime;
}
if (res.helperResult) {

Просмотреть файл

@ -23,15 +23,11 @@ rawPackets.set(`GET request`, {
"timeStamp": 1572867483805,
"actor": "server0.conn0.netEvent4",
"startedDateTime": "2019-11-04T11:06:34.542Z",
"request": {
"url": "http://example.com/inexistent.html",
"method": "GET",
"headersSize": 385
},
"url": "http://example.com/inexistent.html",
"isXHR": false,
"cause": {
"type": "img",
"loadingDocumentUri": "http://example.com/browser/devtools/client/webconsole/test/browser/stub-generators/test-network-event.html",
"stacktraceAvailable": true,
"lastFrame": {
"filename": "http://example.com/browser/devtools/client/webconsole/test/browser/stub-generators/test-network-event.html",
@ -41,34 +37,20 @@ rawPackets.set(`GET request`, {
"asyncCause": null
}
},
"response": {
"httpVersion": "HTTP/1.1",
"status": "404",
"statusText": "Not Found",
"headersSize": 160,
"remoteAddress": "127.0.0.1",
"remotePort": 8888,
"content": {
"mimeType": "text/html; charset=utf-8"
},
"mimeType": "text/html; charset=utf-8",
"waitingTime": 1,
"bodySize": 418,
"transferredSize": 578
},
"contentSize": 418,
"transferredSize": 578,
"timings": {},
"private": false,
"isThirdPartyTrackingResource": false,
"referrerPolicy": "no-referrer-when-downgrade",
"updates": [
"eventTimings",
"requestCookies",
"requestHeaders",
"responseContent",
"responseCookies",
"responseHeaders",
"responseStart",
"securityInfo"
],
"blockedReason": 0,
"totalTime": 2,
"securityState": "insecure",
"isRacing": false
@ -79,15 +61,11 @@ rawPackets.set(`GET request update`, {
"timeStamp": 1572867483805,
"actor": "server0.conn0.netEvent5",
"startedDateTime": "2020-07-07T14:41:14.572Z",
"request": {
"url": "http://example.com/inexistent.html",
"method": "GET",
"headersSize": 385
},
"url": "http://example.com/inexistent.html",
"isXHR": false,
"cause": {
"type": "img",
"loadingDocumentUri": "http://example.com/browser/devtools/client/webconsole/test/browser/stub-generators/test-network-event.html",
"stacktraceAvailable": true,
"lastFrame": {
"filename": "http://example.com/browser/devtools/client/webconsole/test/browser/stub-generators/test-network-event.html",
@ -97,34 +75,20 @@ rawPackets.set(`GET request update`, {
"asyncCause": null
}
},
"response": {
"httpVersion": "HTTP/1.1",
"status": "404",
"statusText": "Not Found",
"headersSize": 160,
"remoteAddress": "127.0.0.1",
"remotePort": 8888,
"content": {
"mimeType": "text/html; charset=utf-8"
},
"mimeType": "text/html; charset=utf-8",
"waitingTime": 1,
"bodySize": 418,
"transferredSize": 578
},
"contentSize": 418,
"transferredSize": 578,
"timings": {},
"private": false,
"isThirdPartyTrackingResource": false,
"referrerPolicy": "no-referrer-when-downgrade",
"updates": [
"eventTimings",
"requestCookies",
"requestHeaders",
"responseContent",
"responseCookies",
"responseHeaders",
"responseStart",
"securityInfo"
],
"blockedReason": 0,
"totalTime": 3,
"securityState": "insecure",
"isRacing": false
@ -135,15 +99,11 @@ rawPackets.set(`XHR GET request`, {
"timeStamp": 1572867483805,
"actor": "server0.conn0.netEvent21",
"startedDateTime": "2020-07-07T14:41:14.612Z",
"request": {
"url": "http://example.com/inexistent.html",
"method": "GET",
"headersSize": 385
},
"url": "http://example.com/inexistent.html",
"isXHR": true,
"cause": {
"type": "xhr",
"loadingDocumentUri": "http://example.com/browser/devtools/client/webconsole/test/browser/stub-generators/test-network-event.html",
"stacktraceAvailable": true,
"lastFrame": {
"filename": "http://example.com/browser/devtools/client/webconsole/test/browser/stub-generators/test-network-event.html",
@ -153,34 +113,20 @@ rawPackets.set(`XHR GET request`, {
"asyncCause": null
}
},
"response": {
"httpVersion": "HTTP/1.1",
"status": "404",
"statusText": "Not Found",
"headersSize": 160,
"remoteAddress": "127.0.0.1",
"remotePort": 8888,
"content": {
"mimeType": "text/html; charset=utf-8"
},
"waitingTime": 2,
"bodySize": 418,
"transferredSize": 578
},
"mimeType": "text/html; charset=utf-8",
"waitingTime": 1,
"contentSize": 418,
"transferredSize": 578,
"timings": {},
"private": false,
"isThirdPartyTrackingResource": false,
"referrerPolicy": "no-referrer-when-downgrade",
"updates": [
"eventTimings",
"requestCookies",
"requestHeaders",
"responseContent",
"responseCookies",
"responseHeaders",
"responseStart",
"securityInfo"
],
"blockedReason": 0,
"totalTime": 1,
"securityState": "insecure",
"isRacing": false
@ -190,15 +136,11 @@ rawPackets.set(`XHR GET request update`, {
"resourceType": "network-event",
"timeStamp": 1572867483805,
"actor": "server0.conn0.netEvent20",
"request": {
"url": "http://example.com/inexistent.html",
"method": "GET",
"headersSize": 385
},
"url": "http://example.com/inexistent.html",
"isXHR": true,
"cause": {
"type": "xhr",
"loadingDocumentUri": "http://example.com/browser/devtools/client/webconsole/test/browser/stub-generators/test-network-event.html",
"stacktraceAvailable": true,
"lastFrame": {
"filename": "http://example.com/browser/devtools/client/webconsole/test/browser/stub-generators/test-network-event.html",
@ -208,34 +150,20 @@ rawPackets.set(`XHR GET request update`, {
"asyncCause": null
}
},
"response": {
"httpVersion": "HTTP/1.1",
"status": "404",
"statusText": "Not Found",
"headersSize": 160,
"remoteAddress": "127.0.0.1",
"remotePort": 8888,
"content": {
"mimeType": "text/html; charset=utf-8"
},
"waitingTime": 2,
"bodySize": 418,
"transferredSize": 578
},
"mimeType": "text/html; charset=utf-8",
"waitingTime": 1,
"contentSize": 418,
"transferredSize": 578,
"timings": {},
"private": false,
"isThirdPartyTrackingResource": false,
"referrerPolicy": "no-referrer-when-downgrade",
"updates": [
"eventTimings",
"requestCookies",
"requestHeaders",
"responseContent",
"responseCookies",
"responseHeaders",
"responseStart",
"securityInfo"
],
"blockedReason": 0,
"totalTime": 1,
"securityState": "insecure",
"isRacing": false
@ -246,15 +174,11 @@ rawPackets.set(`XHR POST request`, {
"timeStamp": 1572867483805,
"actor": "server0.conn0.netEvent36",
"startedDateTime": "2019-11-04T11:06:35.007Z",
"request": {
"url": "http://example.com/inexistent.html",
"method": "POST",
"headersSize": 385
},
"url": "http://example.com/inexistent.html",
"isXHR": true,
"cause": {
"type": "xhr",
"loadingDocumentUri": "http://example.com/browser/devtools/client/webconsole/test/browser/stub-generators/test-network-event.html",
"stacktraceAvailable": true,
"lastFrame": {
"filename": "http://example.com/browser/devtools/client/webconsole/test/browser/stub-generators/test-network-event.html",
@ -264,34 +188,20 @@ rawPackets.set(`XHR POST request`, {
"asyncCause": null
}
},
"response": {
"httpVersion": "HTTP/1.1",
"status": "404",
"statusText": "Not Found",
"headersSize": 160,
"remoteAddress": "127.0.0.1",
"remotePort": 8888,
"content": {
"mimeType": "text/html; charset=utf-8"
},
"waitingTime": 2,
"bodySize": 418,
"transferredSize": 578
},
"mimeType": "text/html; charset=utf-8",
"waitingTime": 1,
"contentSize": 418,
"transferredSize": 578,
"timings": {},
"private": false,
"isThirdPartyTrackingResource": false,
"referrerPolicy": "no-referrer-when-downgrade",
"updates": [
"eventTimings",
"requestCookies",
"requestHeaders",
"responseContent",
"responseCookies",
"responseHeaders",
"responseStart",
"securityInfo"
],
"blockedReason": 0,
"totalTime": 1,
"securityState": "insecure",
"isRacing": false
@ -301,15 +211,11 @@ rawPackets.set(`XHR POST request update`, {
"resourceType": "network-event",
"timeStamp": 1572867483805,
"actor": "server0.conn0.netEvent36",
"request": {
"url": "http://example.com/inexistent.html",
"method": "POST",
"headersSize": 385
},
"url": "http://example.com/inexistent.html",
"isXHR": true,
"cause": {
"type": "xhr",
"loadingDocumentUri": "http://example.com/browser/devtools/client/webconsole/test/browser/stub-generators/test-network-event.html",
"stacktraceAvailable": true,
"lastFrame": {
"filename": "http://example.com/browser/devtools/client/webconsole/test/browser/stub-generators/test-network-event.html",
@ -319,34 +225,20 @@ rawPackets.set(`XHR POST request update`, {
"asyncCause": null
}
},
"response": {
"httpVersion": "HTTP/1.1",
"status": "404",
"statusText": "Not Found",
"headersSize": 160,
"remoteAddress": "127.0.0.1",
"remotePort": 8888,
"content": {
"mimeType": "text/html; charset=utf-8"
},
"waitingTime": 2,
"bodySize": 418,
"transferredSize": 578
},
"mimeType": "text/html; charset=utf-8",
"waitingTime": 1,
"contentSize": 418,
"transferredSize": 578,
"timings": {},
"private": false,
"isThirdPartyTrackingResource": false,
"referrerPolicy": "no-referrer-when-downgrade",
"updates": [
"eventTimings",
"requestCookies",
"requestHeaders",
"responseContent",
"responseCookies",
"responseHeaders",
"responseStart",
"securityInfo"
],
"blockedReason": 0,
"totalTime": 2,
"securityState": "insecure",
"isRacing": false

Просмотреть файл

@ -6,9 +6,6 @@
const Services = require("Services");
const l10n = require("devtools/client/webconsole/utils/l10n");
const {
getUrlDetails,
} = require("devtools/client/netmonitor/src/utils/request-utils");
const {
ResourceWatcher,
} = require("devtools/shared/resources/resource-watcher");
@ -362,24 +359,7 @@ function transformCSSMessageResource(cssMessageResource) {
}
function transformNetworkEventResource(networkEventResource) {
return new NetworkEventMessage({
targetFront: networkEventResource.targetFront,
actor: networkEventResource.actor,
isXHR: networkEventResource.isXHR,
request: networkEventResource.request,
response: networkEventResource.response,
timeStamp: networkEventResource.timeStamp,
totalTime: networkEventResource.totalTime,
url: networkEventResource.request.url,
urlDetails: getUrlDetails(networkEventResource.request.url),
method: networkEventResource.request.method,
updates: networkEventResource.updates,
cause: networkEventResource.cause,
private: networkEventResource.private,
securityState: networkEventResource.securityState,
chromeContext: networkEventResource.chromeContext,
blockedReason: networkEventResource.blockedReason,
});
return new NetworkEventMessage(networkEventResource);
}
function transformEvaluationResultPacket(packet) {
@ -790,8 +770,8 @@ function getNaturalOrder(messageA, messageB) {
function isMessageNetworkError(message) {
return (
message.source === MESSAGE_SOURCE.NETWORK &&
message?.response?.status &&
message.response.status.toString().match(/^[4,5]\d\d$/)
message?.status &&
message?.status.toString().match(/^[4,5]\d\d$/)
);
}

Просмотреть файл

@ -423,32 +423,13 @@ class WebConsoleUI {
}
_onResourceUpdated(updates) {
const messages = [];
for (const { resource } of updates) {
if (
const messageUpdates = updates
.filter(
({ resource }) =>
resource.resourceType == this.hud.resourceWatcher.TYPES.NETWORK_EVENT
) {
// network-message-updated will emit when all the update message arrives.
// Since we can't ensure the order of the network update, we check
// that message.updates has all we need.
// Note that 'requestPostData' is sent only for POST requests, so we need
// to count with that.
const NUMBER_OF_NETWORK_UPDATE = 8;
let expectedLength = NUMBER_OF_NETWORK_UPDATE;
if (resource.updates.includes("responseCache")) {
expectedLength++;
}
if (resource.updates.includes("requestPostData")) {
expectedLength++;
}
if (resource.updates.length === expectedLength) {
messages.push(resource);
}
}
}
this.wrapper.dispatchMessagesUpdate(messages);
)
.map(({ resource }) => resource);
this.wrapper.dispatchMessagesUpdate(messageUpdates);
}
/**

Просмотреть файл

@ -30,23 +30,30 @@ module.exports = async function({
}
const webConsoleFront = await targetFront.getFront("console");
const _resources = new Map();
const resources = new Map();
function onNetworkEvent(packet) {
const actor = packet.eventActor;
const resource = {
resources.set(actor.actor, {
resourceId: actor.channelId,
resourceType: ResourceWatcher.TYPES.NETWORK_EVENT,
isBlocked: !!actor.blockedReason,
types: [],
resourceUpdates: {},
});
onAvailable([
{
resourceId: actor.channelId,
resourceType: ResourceWatcher.TYPES.NETWORK_EVENT,
timeStamp: actor.timeStamp,
actor: actor.actor,
startedDateTime: actor.startedDateTime,
request: {
url: actor.url,
method: actor.method,
},
isXHR: actor.isXHR,
cause: actor.cause,
response: {},
cause: { type: actor.cause.type },
timings: {},
private: actor.private,
fromCache: actor.fromCache,
@ -59,61 +66,44 @@ module.exports = async function({
actor.cause.type == "websocket"
? actor.url.replace(/^http/, "ws")
: actor.channelId,
updates: [],
};
// Lets remove the stacktrace info here as
// it is passed from the the server by the NETWORK_EVENT_STACKTRACE
// resource type.
delete resource.cause.stacktraceAvailable;
delete resource.cause.lastFrame;
_resources.set(actor.actor, resource);
onAvailable([resource]);
},
]);
}
function onNetworkEventUpdate(packet) {
const resource = _resources.get(packet.from);
const resource = resources.get(packet.from);
if (!resource) {
return;
}
const updateType = packet.updateType;
const resourceUpdates = {};
resourceUpdates.updates = [...resource.updates, updateType];
const {
types,
resourceUpdates,
resourceId,
resourceType,
isBlocked,
} = resource;
switch (updateType) {
case "requestHeaders":
resourceUpdates.request = Object.assign({}, resource.request, {
headersSize: packet.headersSize,
});
break;
switch (packet.updateType) {
case "requestPostData":
resourceUpdates.request = Object.assign({}, resource.request, {
bodySize: packet.dataSize,
});
resourceUpdates.contentSize = packet.dataSize;
break;
case "responseStart":
resourceUpdates.response = Object.assign({}, resource.response, {
httpVersion: packet.response.httpVersion,
status: packet.response.status,
statusText: packet.response.statusText,
headersSize: packet.response.headersSize,
remoteAddress: packet.response.remoteAddress,
remotePort: packet.response.remotePort,
content: {
mimeType: packet.response.mimeType,
},
waitingTime: packet.response.waitingTime,
});
resourceUpdates.httpVersion = packet.response.httpVersion;
resourceUpdates.status = packet.response.status;
resourceUpdates.statusText = packet.response.statusText;
resourceUpdates.remoteAddress = packet.response.remoteAddress;
resourceUpdates.remotePort = packet.response.remotePort;
resourceUpdates.mimeType = packet.response.mimeType;
resourceUpdates.waitingTime = packet.response.waitingTime;
break;
case "responseContent":
resourceUpdates.response = Object.assign({}, resource.response, {
bodySize: packet.contentSize,
transferredSize: packet.transferredSize,
content: { mimeType: packet.mimeType },
});
resourceUpdates.contentSize = packet.contentSize;
resourceUpdates.transferredSize = packet.transferredSize;
resourceUpdates.mimeType = packet.mimeType;
resourceUpdates.blockingExtension = packet.blockingExtension;
resourceUpdates.blockedReason = packet.blockedReason;
break;
case "eventTimings":
resourceUpdates.totalTime = packet.totalTime;
@ -123,40 +113,40 @@ module.exports = async function({
resourceUpdates.isRacing = packet.isRacing;
break;
case "responseCache":
resourceUpdates.response = Object.assign({}, resource.response, {
responseCache: packet.responseCache,
});
resourceUpdates.responseCache = packet.responseCache;
break;
}
// Update local resource.
Object.assign(resource, resourceUpdates);
resourceUpdates[`${packet.updateType}Available`] = true;
types.push(packet.updateType);
if (isBlocked) {
// Blocked requests
if (
!types.includes("requestHeaders") ||
!types.includes("requestCookies")
) {
return;
}
} else if (
// Un-blocked requests
!types.includes("requestHeaders") ||
!types.includes("requestCookies") ||
!types.includes("eventTimings") ||
!types.includes("responseContent")
) {
return;
}
onUpdated([
{
resourceType: resource.resourceType,
resourceId: resource.resourceId,
resourceType,
resourceId,
resourceUpdates,
updateType,
},
]);
if (resource.blockedReason) {
// Blocked requests
if (
resource.updates.includes("requestHeaders") &&
resource.updates.includes("requestCookies")
) {
_resources.delete(resource.actor);
}
} else if (
resource.updates.includes("requestHeaders") &&
resource.updates.includes("requestCookies") &&
resource.updates.includes("eventTimings") &&
resource.updates.includes("responseContent")
) {
_resources.delete(resource.actor);
}
resources.delete(resource.actor);
}
webConsoleFront.on("serverNetworkEvent", onNetworkEvent);

Просмотреть файл

@ -847,4 +847,6 @@ const ResourceTransformers = {
.ROOT_NODE]: require("devtools/shared/resources/transformers/root-node"),
[ResourceWatcher.TYPES
.SESSION_STORAGE]: require("devtools/shared/resources/transformers/storage-session-storage.js"),
[ResourceWatcher.TYPES
.NETWORK_EVENT]: require("devtools/shared/resources/transformers/network-events"),
};

Просмотреть файл

@ -35,7 +35,6 @@ support-files =
[browser_resources_getAllResources.js]
[browser_resources_network_event_stacktraces.js]
[browser_resources_network_events.js]
skip-if = os == "linux" #Bug 1655183
[browser_resources_platform_messages.js]
[browser_resources_root_node.js]
[browser_resources_several_resources.js]

Просмотреть файл

@ -18,63 +18,36 @@ add_task(async function() {
await testNetworkEventResourcesWithExistingResources();
await testNetworkEventResourcesWithoutExistingResources();
// These tests would be enabled when the server-side work is done. See Bug 1644191
// info("Test network events server listener");
// await pushPref("devtools.testing.enableServerWatcherSupport", true);
// await testNetworkEventResources();
// await testNetworkEventResourcesWithIgnoreExistingResources();
// await testNetworkEventResourcesWithExistingResources();
// await testNetworkEventResourcesWithoutExistingResources();
});
const UPDATES = [
"requestHeaders",
"requestCookies",
"responseStart",
"securityInfo",
"responseHeaders",
"responseCookies",
"eventTimings",
"responseContent",
];
async function testNetworkEventResourcesWithExistingResources() {
info(`Tests for network event resources with the existing resources`);
await testNetworkEventResources({
ignoreExistingResources: false,
// 1 available event fired, for the existing resource in the cache.
// 1 available event fired, when live request is created.
expectedOnAvailableCounts: 2,
// 8 update events fired, when live request is updated.
expectedOnUpdatedCounts: 8,
totalExpectedOnAvailableCounts: 2,
// 1 update events fired, when live request is updated.
totalExpectedOnUpdatedCounts: 1,
expectedResourcesOnAvailable: {
[`${EXAMPLE_DOMAIN}existing_post.html`]: {
[`${EXAMPLE_DOMAIN}cached_post.html`]: {
resourceType: ResourceWatcher.TYPES.NETWORK_EVENT,
request: {
url: `${EXAMPLE_DOMAIN}existing_post.html`,
method: "POST",
},
// gets reset based on the type of request
updates: [],
},
[`${EXAMPLE_DOMAIN}live_get.html`]: {
resourceType: ResourceWatcher.TYPES.NETWORK_EVENT,
request: {
url: `${EXAMPLE_DOMAIN}live_get.html`,
method: "GET",
},
// Throttling makes us receive the available event
// after processing all the updates events
updates: UPDATES,
},
},
expectedResourcesOnUpdated: {
[`${EXAMPLE_DOMAIN}live_get.html`]: {
resourceType: ResourceWatcher.TYPES.NETWORK_EVENT,
request: {
url: `${EXAMPLE_DOMAIN}live_get.html`,
method: "GET",
},
updates: UPDATES,
},
},
});
}
@ -84,30 +57,20 @@ async function testNetworkEventResourcesWithoutExistingResources() {
await testNetworkEventResources({
ignoreExistingResources: true,
// 1 available event fired, when live request is created.
expectedOnAvailableCounts: 1,
// 8 update events fired, when live request is updated.
expectedOnUpdatedCounts: 8,
totalExpectedOnAvailableCounts: 1,
// 1 update events fired, when live request is updated.
totalExpectedOnUpdatedCounts: 1,
expectedResourcesOnAvailable: {
[`${EXAMPLE_DOMAIN}live_get.html`]: {
resourceType: ResourceWatcher.TYPES.NETWORK_EVENT,
request: {
url: `${EXAMPLE_DOMAIN}live_get.html`,
method: "GET",
},
// Throttling makes us receive the available event
// after processing all the updates events
updates: UPDATES,
},
},
expectedResourcesOnUpdated: {
[`${EXAMPLE_DOMAIN}live_get.html`]: {
resourceType: ResourceWatcher.TYPES.NETWORK_EVENT,
request: {
url: `${EXAMPLE_DOMAIN}live_get.html`,
method: "GET",
},
updates: UPDATES,
},
},
});
}
@ -118,55 +81,35 @@ async function testNetworkEventResources(options) {
tab
);
const actualResourcesOnAvailable = {};
const actualResourcesOnUpdated = {};
info(
`Trigger some network requests *before* calling ResourceWatcher.watchResources
in order to assert the behavior of already existing network events.`
);
let onResourceAvailable = () => {};
let onResourceUpdated = () => {};
const waitOnAllExpectedUpdatesForExistingRequests = new Promise(resolve => {
const existingRequestUrl = `${EXAMPLE_DOMAIN}existing_post.html`;
// Lets make sure there is already a network event resource in the cache.
const waitOnRequestForResourceWatcherCache = new Promise(resolve => {
onResourceAvailable = resources => {
for (const resource of resources) {
// A blocked request would only have two updates so lets also resolve here
if (
resource.request.url == existingRequestUrl &&
resource.blockedReason &&
resource.updates.length == 2
) {
// Reset the updates expectation as the request is blocked
if (options.expectedResourcesOnAvailable[resource.request.url]) {
options.expectedResourcesOnAvailable[
resource.request.url
].updates = [...resource.updates];
}
resolve();
}
is(
resource.resourceType,
ResourceWatcher.TYPES.NETWORK_EVENT,
"Received a network event resource"
);
}
};
onResourceUpdated = updates => {
for (const { resource } of updates) {
// Wait until all the update events have fired for the existing request.
// Handle both blocked and unblocked requests
if (
resource.request.url == existingRequestUrl &&
(resource.updates.length == 8 ||
(resource.blockedReason && resource.updates.length == 2))
) {
// Makes sure the expectation always correct (for either blocked or unblocked requests)
if (options.expectedResourcesOnAvailable[resource.request.url]) {
options.expectedResourcesOnAvailable[
resource.request.url
].updates = [...resource.updates];
}
is(
resource.resourceType,
ResourceWatcher.TYPES.NETWORK_EVENT,
"Received a network update event resource"
);
resolve();
}
}
};
resourceWatcher
@ -177,23 +120,29 @@ async function testNetworkEventResources(options) {
.then(() => {
// We can only trigger the requests once `watchResources` settles, otherwise the
// thread might be paused.
triggerNetworkRequests(tab.linkedBrowser, EXISTING_REQUESTS_COMMANDS);
triggerNetworkRequests(tab.linkedBrowser, [cachedRequest]);
});
});
await waitOnAllExpectedUpdatesForExistingRequests;
await waitOnRequestForResourceWatcherCache;
const actualResourcesOnAvailable = {};
const actualResourcesOnUpdated = {};
let {
expectedOnAvailableCounts,
expectedOnUpdatedCounts,
totalExpectedOnAvailableCounts,
totalExpectedOnUpdatedCounts,
expectedResourcesOnAvailable,
expectedResourcesOnUpdated,
ignoreExistingResources,
} = options;
const waitForAllOnAvailableEvents = waitUntil(
() => expectedOnAvailableCounts == 0
const waitForAllExpectedOnAvailableEvents = waitUntil(
() => totalExpectedOnAvailableCounts == 0
);
const waitForAllOnUpdatedEvents = waitUntil(
() => expectedOnUpdatedCounts == 0
const waitForAllExpectedOnUpdatedEvents = waitUntil(
() => totalExpectedOnUpdatedCounts == 0
);
const onAvailable = resources => {
@ -203,13 +152,12 @@ async function testNetworkEventResources(options) {
ResourceWatcher.TYPES.NETWORK_EVENT,
"Received a network event resource"
);
actualResourcesOnAvailable[resource.request.url] = {
actualResourcesOnAvailable[resource.url] = {
resourceId: resource.resourceId,
resourceType: resource.resourceType,
request: resource.request,
updates: [...resource.updates],
method: resource.method,
};
expectedOnAvailableCounts--;
totalExpectedOnAvailableCounts--;
}
};
@ -220,13 +168,12 @@ async function testNetworkEventResources(options) {
ResourceWatcher.TYPES.NETWORK_EVENT,
"Received a network update event resource"
);
actualResourcesOnUpdated[resource.request.url] = {
actualResourcesOnUpdated[resource.url] = {
resourceId: resource.resourceId,
resourceType: resource.resourceType,
request: resource.request,
updates: [...resource.updates],
method: resource.method,
};
expectedOnUpdatedCounts--;
totalExpectedOnUpdatedCounts--;
}
};
@ -240,14 +187,17 @@ async function testNetworkEventResources(options) {
`Trigger the rest of the requests *after* calling ResourceWatcher.watchResources
in order to assert the behavior of live network events.`
);
await triggerNetworkRequests(tab.linkedBrowser, LIVE_REQUESTS_COMMANDS);
await triggerNetworkRequests(tab.linkedBrowser, [liveRequest]);
await Promise.all([waitForAllOnAvailableEvents, waitForAllOnUpdatedEvents]);
await Promise.all([
waitForAllExpectedOnAvailableEvents,
waitForAllExpectedOnUpdatedEvents,
]);
info("Check the resources on available");
is(
Object.keys(actualResourcesOnAvailable).length,
Object.keys(options.expectedResourcesOnAvailable).length,
Object.keys(expectedResourcesOnAvailable).length,
"Got the expected number of network events fired onAvailable"
);
@ -259,8 +209,8 @@ async function testNetworkEventResources(options) {
);
// assert the resources emitted when the network event is created
for (const key in options.expectedResourcesOnAvailable) {
const expected = options.expectedResourcesOnAvailable[key];
for (const key in expectedResourcesOnAvailable) {
const expected = expectedResourcesOnAvailable[key];
const actual = actualResourcesOnAvailable[key];
assertResources(actual, expected);
}
@ -269,20 +219,15 @@ async function testNetworkEventResources(options) {
is(
Object.keys(actualResourcesOnUpdated).length,
Object.keys(options.expectedResourcesOnUpdated).length,
Object.keys(expectedResourcesOnUpdated).length,
"Got the expected number of network events fired onUpdated"
);
// assert the resources emitted when the network event is updated
for (const key in options.expectedResourcesOnUpdated) {
const expected = options.expectedResourcesOnUpdated[key];
for (const key in expectedResourcesOnUpdated) {
const expected = expectedResourcesOnUpdated[key];
const actual = actualResourcesOnUpdated[key];
assertResources(actual, expected);
is(
actual.updates.length,
expected.updates.length,
"The number of updates is correct"
);
}
await resourceWatcher.unwatchResources(
@ -312,14 +257,8 @@ function assertResources(actual, expected) {
expected.resourceType,
"The resource type is correct"
);
is(actual.request.url, expected.request.url, "The url is correct");
is(actual.request.method, expected.request.method, "The method is correct");
is(actual.method, expected.method, "The method is correct");
}
const EXISTING_REQUESTS_COMMANDS = [
`await fetch("/existing_post.html", { method: "POST" });`,
];
const LIVE_REQUESTS_COMMANDS = [
`await fetch("/live_get.html", { method: "GET" });`,
];
const cachedRequest = `await fetch("/cached_post.html", { method: "POST" });`;
const liveRequest = `await fetch("/live_get.html", { method: "GET" });`;

Просмотреть файл

@ -5,6 +5,7 @@
DevToolsModules(
"console-messages.js",
"error-messages.js",
"network-events.js",
"root-node.js",
"storage-local-storage.js",
"storage-session-storage.js",

Просмотреть файл

@ -0,0 +1,16 @@
/* 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 {
getUrlDetails,
// eslint-disable-next-line mozilla/reject-some-requires
} = require("devtools/client/netmonitor/src/utils/request-utils");
module.exports = function({ resource }) {
resource.urlDetails = getUrlDetails(resource.url);
resource.startedMs = Date.parse(resource.startedDateTime);
return resource;
};