Bug 1637363 - [remote] Use frameId of the related browsing context for Network events. r=remote-protocol-reviewers,maja_zf

Differential Revision: https://phabricator.services.mozilla.com/D76835
This commit is contained in:
Henrik Skupin 2020-05-29 12:36:26 +00:00
Родитель 9b6b44fb8b
Коммит a557087998
15 изменённых файлов: 550 добавлений и 282 удалений

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

@ -37,6 +37,13 @@ class Page extends ContentProcessDomain {
this.scriptsToEvaluateOnLoad = new Map();
this.worldsToEvaluateOnLoad = new Set();
// This map is used to keep a reference to the loader id for
// those Page events, which do not directly rely on
// Network events. This might be a temporary solution until
// the Network observer could be queried for that. But right
// now this lives in the parent process.
this.frameIdToLoaderId = new Map();
this._onFrameAttached = this._onFrameAttached.bind(this);
this._onFrameDetached = this._onFrameDetached.bind(this);
this._onFrameNavigated = this._onFrameNavigated.bind(this);
@ -309,13 +316,10 @@ class Page extends ContentProcessDomain {
return;
}
const timestamp = Date.now() / 1000;
const frameId = target.defaultView.docShell.browsingContext.id.toString();
const loaderId = this.frameIdToLoaderId.get(frameId);
const timestamp = Date.now() / 1000;
const url = target.location.href;
const loaderId =
this._lastRequest?.frameId == frameId
? this._lastRequest?.loaderId
: null;
switch (type) {
case "DOMContentLoaded":
@ -351,7 +355,9 @@ class Page extends ContentProcessDomain {
}
_updateLoaderId(data) {
this._lastRequest = data;
const { frameId, loaderId } = data;
this.frameIdToLoaderId.set(frameId, loaderId);
}
_contentRect() {

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

@ -378,7 +378,7 @@ class Network extends Domain {
_onRequest(eventName, httpChannel, data) {
const wrappedChannel = ChannelWrapper.get(httpChannel);
const topFrame = getLoadContext(httpChannel).topFrameElement;
const request = {
url: httpChannel.URI.spec,
urlFragment: undefined,
@ -401,16 +401,15 @@ class Network extends Domain {
initiator: undefined,
redirectResponse: undefined,
type: LOAD_CAUSE_STRINGS[data.cause] || "unknown",
// Bug 1637363 - Add subframe support
frameId: topFrame.browsingContext?.id.toString(),
frameId: data.frameId,
hasUserGesture: undefined,
});
}
_onResponse(eventName, httpChannel, data) {
const wrappedChannel = ChannelWrapper.get(httpChannel);
const topFrame = getLoadContext(httpChannel).topFrameElement;
const headers = headersAsObject(data.headers);
this.emit("Network.responseReceived", {
requestId: data.requestId,
loaderId: data.loaderId,
@ -434,31 +433,11 @@ class Network extends Domain {
// unknown, neutral, insecure, secure, info, insecure-broken
securityState: "unknown",
},
// Bug 1637363 - Add subframe support
frameId: topFrame.browsingContext?.id.toString(),
frameId: data.frameId,
});
}
}
function getLoadContext(httpChannel) {
let loadContext = null;
try {
if (httpChannel.notificationCallbacks) {
loadContext = httpChannel.notificationCallbacks.getInterface(
Ci.nsILoadContext
);
}
} catch (e) {}
try {
if (!loadContext && httpChannel.loadGroup) {
loadContext = httpChannel.loadGroup.notificationCallbacks.getInterface(
Ci.nsILoadContext
);
}
} catch (e) {}
return loadContext;
}
/**
* Creates a CDP Network.Cookie from our internal cookie values
*

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

@ -747,7 +747,7 @@ class Page extends Domain {
}
this.executeInChild("_updateLoaderId", {
loaderId: data.loaderId,
frameId: this.session.browsingContext.id.toString(),
frameId: data.frameId,
});
}
}

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

@ -54,6 +54,7 @@ class NetworkObserver {
ChannelEventSinkFactory.getService().registerCollector(this);
this._redirectMap = new Map();
this._windowIdToFrameIdMap = new Map();
// Request interception state.
this._browserSuspendedChannels = new Map();
@ -191,18 +192,15 @@ class NetworkObserver {
} catch (ex) {
return;
}
const httpChannel = channel.QueryInterface(Ci.nsIHttpChannel);
const loadContext = getLoadContext(httpChannel);
if (
!loadContext ||
!this._browserSessionCount.has(loadContext.topFrameElement) ||
!loadContext.topFrameElement.currentURI
) {
const browser = loadContext?.topFrameElement;
if (!loadContext || !this._browserSessionCount.has(browser)) {
return;
}
const extraHeaders = this._extraHTTPHeaders.get(
loadContext.topFrameElement
);
const extraHeaders = this._extraHTTPHeaders.get(browser);
if (extraHeaders) {
for (const header of extraHeaders) {
httpChannel.setRequestHeader(
@ -215,18 +213,18 @@ class NetworkObserver {
const causeType = httpChannel.loadInfo
? httpChannel.loadInfo.externalContentPolicyType
: Ci.nsIContentPolicy.TYPE_OTHER;
const suspendedChannels = this._browserSuspendedChannels.get(
loadContext.topFrameElement
);
const suspendedChannels = this._browserSuspendedChannels.get(browser);
if (suspendedChannels) {
httpChannel.suspend();
suspendedChannels.set(requestId(httpChannel), httpChannel);
}
const oldChannel = this._redirectMap.get(httpChannel);
this._redirectMap.delete(httpChannel);
// Install response body hooks.
new ResponseBodyListener(this, loadContext.topFrameElement, httpChannel);
new ResponseBodyListener(this, browser, httpChannel);
this.emit("request", httpChannel, {
url: httpChannel.URI.spec,
@ -239,11 +237,14 @@ class NetworkObserver {
isNavigationRequest: httpChannel.isMainDocumentChannel,
cause: causeType,
causeString: causeTypeToString(causeType),
frameId: this.frameId(httpChannel, causeType),
// clients expect loaderId == requestId for document navigation
loaderId:
causeType == Ci.nsIContentPolicy.TYPE_DOCUMENT
? requestId(httpChannel)
: undefined,
loaderId: [
Ci.nsIContentPolicy.TYPE_DOCUMENT,
Ci.nsIContentPolicy.TYPE_SUBDOCUMENT,
].includes(causeType)
? requestId(httpChannel)
: undefined,
});
}
@ -279,11 +280,14 @@ class NetworkObserver {
statusText: httpChannel.responseStatusText,
cause: causeType,
causeString: causeTypeToString(causeType),
frameId: this.frameId(httpChannel, causeType),
// clients expect loaderId == requestId for document navigation
loaderId:
causeType == Ci.nsIContentPolicy.TYPE_DOCUMENT
? requestId(httpChannel)
: undefined,
loaderId: [
Ci.nsIContentPolicy.TYPE_DOCUMENT,
Ci.nsIContentPolicy.TYPE_SUBDOCUMENT,
].includes(causeType)
? requestId(httpChannel)
: undefined,
});
}
@ -341,6 +345,36 @@ class NetworkObserver {
this.dispose();
}
}
/**
* Returns the frameId of the current httpChannel.
*
* Only Document and Subdocument requests contain a reference
* to the browsing context, and as such the id, which is used
* as frameId. To be able to send a frameId for resource requests
* a map has to be kept up-to-date to retrieve that id from the
* window id. Hereby window ids are unique for all requests
* belonging to the same frame, but differ between frames.
*/
frameId(httpChannel, causeType) {
const loadContext = getLoadContext(httpChannel);
const wrappedChannel = ChannelWrapper.get(httpChannel);
// Document requests indicate a fresh navigation cycle.
// As such cleanup all the old window id references.
if (causeType == Ci.nsIContentPolicy.TYPE_DOCUMENT) {
this._windowIdToFrameIdMap.clear();
}
let frameId = loadContext.id?.toString();
if (frameId) {
this._windowIdToFrameIdMap.set(wrappedChannel.windowId, frameId);
} else {
frameId = this._windowIdToFrameIdMap.get(wrappedChannel.windowId);
}
return frameId;
}
}
const protocolVersionNames = {

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

@ -551,7 +551,7 @@ class RecordEvents {
* @param {number=} timeout
* Timeout in milliseconds. Defaults to 1000.
*
* @return {Array<{ eventName, payload }>} Recorded events
* @return {Array<{ eventName, payload, index }>} Recorded events
*/
async record(timeout = TIMEOUT_EVENTS) {
await Promise.race([Promise.all(this.promises), timeoutPromise(timeout)]);
@ -561,17 +561,29 @@ class RecordEvents {
return this.events;
}
/**
* Filter events based on predicate
*
* @param {Function} predicate
*
* @return {Array<{ eventName, payload, index }>}
* The list of events matching the filter.
*/
filter(predicate) {
return this.events.filter(predicate);
}
/**
* Find first occurrence of the given event.
*
* @param {string} eventName
*
* @return {object} The event payload, if any.
* @return {{ eventName, payload, index }} The event, if any.
*/
findEvent(eventName) {
const event = this.events.find(el => el.eventName == eventName);
if (event) {
return event.payload;
return event;
}
return {};
}
@ -581,13 +593,11 @@ class RecordEvents {
*
* @param {string} eventName
*
* @return {Array<object>}
* The events payload, if any.
* @return {Array<{ eventName, payload, index }>}
* The events, if any.
*/
findEvents(eventName) {
return this.events
.filter(event => event.eventName == eventName)
.map(event => event.payload);
return this.events.filter(event => event.eventName == eventName);
}
/**

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

@ -9,8 +9,10 @@ support-files =
!/remote/test/browser/head.js
head.js
doc_empty.html
doc_frameset.html
doc_networkEvents.html
file_networkEvents.js
file_framesetEvents.js
sjs-cookies.sjs
[browser_deleteCookies.js]

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

@ -6,88 +6,52 @@
// Test order and consistency of Network/Page events as a whole.
// Details of specific events are checked in event-specific test files.
const PAGE_URL =
"http://example.com/browser/remote/test/browser/network/doc_networkEvents.html";
const JS_URL =
"http://example.com/browser/remote/test/browser/network/file_networkEvents.js";
const BASE_PATH = "http://example.com/browser/remote/test/browser/network";
const FRAMESET_URL = `${BASE_PATH}/doc_frameset.html`;
const FRAMESET_JS_URL = `${BASE_PATH}/file_framesetEvents.js`;
const PAGE_URL = `${BASE_PATH}/doc_networkEvents.html`;
const PAGE_JS_URL = `${BASE_PATH}/file_networkEvents.js`;
add_task(async function documentNavigationWithScriptResource({ client }) {
const { Page, Network } = client;
await Network.enable();
await Page.enable();
await Page.setLifecycleEventsEnabled({ enabled: true });
const { history, urlToEvents } = configureHistory(client);
const navigateDone = history.addPromise("Page.navigate");
const { frameId } = await Page.navigate({ url: PAGE_URL }).then(navigateDone);
ok(frameId, "Page.navigate returned a frameId");
info("Wait for events");
const events = await history.record();
is(events.length, 8, "Expected number of events");
const eventNames = events.map(
item => `${item.eventName}(${item.payload.type || item.payload.name})`
);
info(`Received events: ${eventNames}`);
const documentEvents = urlToEvents.get(PAGE_URL);
const resourceEvents = urlToEvents.get(JS_URL);
is(
2,
documentEvents.length,
"Expected number of Network events for document"
);
is(
2,
resourceEvents.length,
"Expected number of Network events for resource"
add_task(async function eventsForTopFrameNavigation({ client }) {
const { history, frameId: frameIdNav } = await prepareTest(
client,
FRAMESET_URL,
10
);
const docRequest = documentEvents[0].event;
is(docRequest.request.url, PAGE_URL, "Got the doc request");
is(docRequest.documentURL, PAGE_URL, "documenURL matches request url");
const lifeCycleEvents = history.findEvents("Page.lifecycleEvent");
const firstLifecycle = history.indexOf("Page.lifecycleEvent");
ok(
firstLifecycle > documentEvents[1].index,
"First lifecycle event is after document response"
);
for (const e of lifeCycleEvents) {
is(
e.loaderId,
docRequest.loaderId,
`${e.name} lifecycle event has same loaderId as document request`
);
}
const documentEvents = filterEventsByType(history, "Document");
const scriptEvents = filterEventsByType(history, "Script");
const subdocumentEvents = filterEventsByType(history, "Subdocument");
const resourceRequest = resourceEvents[0].event;
is(resourceRequest.documentURL, PAGE_URL, "documentURL is trigger document");
is(resourceRequest.request.url, JS_URL, "Got the JS request");
ok(
documentEvents[0].index < resourceEvents[0].index,
"Document request received before resource request"
);
const navigateStep = history.indexOf("Page.navigate");
ok(
navigateStep > documentEvents[1].index,
"Page.navigate returns after document response"
);
ok(
navigateStep < resourceEvents[0].index,
"Page.navigate returns before resource request"
);
ok(
navigateStep < firstLifecycle,
"Page.navigate returns before first lifecycle event"
);
is(documentEvents.length, 2, "Expected number of Document events");
is(subdocumentEvents.length, 2, "Expected number of Subdocument events");
is(scriptEvents.length, 4, "Expected number of Script events");
const docResponse = documentEvents[1].event;
is(docResponse.response.url, PAGE_URL, "Got the doc response");
is(
docRequest.frameId,
docResponse.frameId,
"Doc response frame id matches that of doc request"
);
ok(!!docResponse.response.headers.server, "Doc response has headers");
const navigatedEvents = history.findEvents("Page.navigate");
is(navigatedEvents.length, 1, "Expected number of navigate done events");
const frameAttachedEvents = history.findEvents("Page.frameAttached");
is(frameAttachedEvents.length, 1, "Expected number of frame attached events");
// network events for document and script
assertEventOrder(documentEvents[0], documentEvents[1]);
assertEventOrder(documentEvents[1], navigatedEvents[0], {
ignoreTimestamps: true,
});
assertEventOrder(navigatedEvents[0], scriptEvents[0], {
ignoreTimestamps: true,
});
assertEventOrder(scriptEvents[0], scriptEvents[1]);
const docRequest = documentEvents[0].payload;
is(docRequest.documentURL, FRAMESET_URL, "documenURL matches target url");
is(docRequest.frameId, frameIdNav, "Got the expected frame id");
is(docRequest.request.url, FRAMESET_URL, "Got the Document request");
const docResponse = documentEvents[1].payload;
is(docResponse.frameId, frameIdNav, "Got the expected frame id");
is(docResponse.response.url, FRAMESET_URL, "Got the Document response");
ok(!!docResponse.response.headers.server, "Document response has headers");
// TODO? response reports extra request header "upgrade-insecure-requests":"1"
// Assert.deepEqual(
// docResponse.response.requestHeaders,
@ -95,54 +59,99 @@ add_task(async function documentNavigationWithScriptResource({ client }) {
// "Response event reports same request headers as request event"
// );
ok(
docRequest.timestamp <= docResponse.timestamp,
"Document request happens before document response"
const scriptRequest = scriptEvents[0].payload;
is(
scriptRequest.documentURL,
FRAMESET_URL,
"documentURL is trigger document"
);
const resourceResponse = resourceEvents[1].event;
is(resourceResponse.response.url, JS_URL, "Got the resource response");
is(scriptRequest.frameId, frameIdNav, "Got the expected frame id");
is(scriptRequest.request.url, FRAMESET_JS_URL, "Got the Script request");
const scriptResponse = scriptEvents[1].payload;
is(scriptResponse.frameId, frameIdNav, "Got the expected frame id");
todo(
resourceResponse.loaderId === docRequest.loaderId,
scriptResponse.loaderId === docRequest.loaderId,
"The same loaderId is used for dependent responses (Bug 1637838)"
);
ok(!!resourceResponse.frameId, "Resource response has a frame id");
is(
docRequest.frameId,
resourceResponse.frameId,
"Resource response frame id matches that of doc request"
);
is(scriptResponse.response.url, FRAMESET_JS_URL, "Got the Script response");
Assert.deepEqual(
resourceResponse.response.requestHeaders,
resourceRequest.request.headers,
scriptResponse.response.requestHeaders,
scriptRequest.request.headers,
"Response event reports same request headers as request event"
);
ok(
resourceRequest.timestamp <= resourceResponse.timestamp,
"Document request happens before document response"
// frame is attached after all resources of the document have been loaded
// and before sub document starts loading
assertEventOrder(scriptEvents[1], frameAttachedEvents[0], {
ignoreTimestamps: true,
});
assertEventOrder(frameAttachedEvents[0], subdocumentEvents[0], {
ignoreTimestamps: true,
});
const {
frameId: frameIdSubFrame,
parentFrameId,
} = frameAttachedEvents[0].payload;
is(parentFrameId, frameIdNav, "Got expected parent frame id");
// network events for subdocument and script
assertEventOrder(subdocumentEvents[0], subdocumentEvents[1]);
assertEventOrder(subdocumentEvents[1], scriptEvents[2]);
assertEventOrder(scriptEvents[2], scriptEvents[3]);
const subdocRequest = subdocumentEvents[0].payload;
is(
subdocRequest.documentURL,
FRAMESET_URL,
"documentURL is trigger document"
);
is(subdocRequest.frameId, frameIdSubFrame, "Got the expected frame id");
is(subdocRequest.request.url, PAGE_URL, "Got the Subdocument request");
const subdocResponse = subdocumentEvents[1].payload;
is(subdocResponse.frameId, frameIdSubFrame, "Got the expected frame id");
is(subdocResponse.response.url, PAGE_URL, "Got the Subdocument response");
const subscriptRequest = scriptEvents[2].payload;
is(subscriptRequest.documentURL, PAGE_URL, "documentURL is trigger document");
is(subscriptRequest.frameId, frameIdSubFrame, "Got the expected frame id");
is(subscriptRequest.request.url, PAGE_JS_URL, "Got the Script request");
const subscriptResponse = scriptEvents[3].payload;
is(subscriptResponse.frameId, frameIdSubFrame, "Got the expected frame id");
is(subscriptResponse.response.url, PAGE_JS_URL, "Got the Script response");
todo(
subscriptResponse.loaderId === subdocRequest.loaderId,
"The same loaderId is used for dependent responses (Bug 1637838)"
);
Assert.deepEqual(
subscriptResponse.response.requestHeaders,
subscriptRequest.request.headers,
"Response event reports same request headers as request event"
);
const lifeCycleEvents = history
.findEvents("Page.lifecycleEvent")
.map(event => event.payload);
for (const { name, loaderId } of lifeCycleEvents) {
is(
loaderId,
docRequest.loaderId,
`${name} lifecycle event has same loaderId as Document request`
);
}
});
function configureHistory(client) {
async function prepareTest(client, url, totalCount) {
const REQUEST = "Network.requestWillBeSent";
const RESPONSE = "Network.responseReceived";
const LIFECYCLE = "Page.lifecycleEvent";
const FRAMEATTACHED = "Page.frameAttached";
const LIFECYCLE = "Page.livecycleEvent";
const { Network, Page } = client;
const history = new RecordEvents(8);
const urlToEvents = new Map();
function updateUrlToEvents(kind) {
return ({ payload, index, eventName }) => {
const url = payload[kind]?.url;
if (!url) {
return;
}
if (!urlToEvents.get(url)) {
urlToEvents.set(url, [{ index, event: payload, eventName }]);
} else {
urlToEvents.get(url).push({ index, event: payload, eventName });
}
};
}
const history = new RecordEvents(totalCount);
history.addRecorder({
event: Network.requestWillBeSent,
@ -150,7 +159,6 @@ function configureHistory(client) {
messageFn: payload => {
return `Received ${REQUEST} for ${payload.request?.url}`;
},
callback: updateUrlToEvents("request"),
});
history.addRecorder({
@ -159,7 +167,14 @@ function configureHistory(client) {
messageFn: payload => {
return `Received ${RESPONSE} for ${payload.response?.url}`;
},
callback: updateUrlToEvents("response"),
});
history.addRecorder({
event: Page.frameAttached,
eventName: FRAMEATTACHED,
messageFn: ({ frameId, parentFrameId: parentId }) => {
return `Received ${FRAMEATTACHED} frame=${frameId} parent=${parentId}`;
},
});
history.addRecorder({
@ -170,5 +185,18 @@ function configureHistory(client) {
},
});
return { history, urlToEvents };
await Network.enable();
await Page.enable();
const navigateDone = history.addPromise("Page.navigate");
const { frameId } = await Page.navigate({ url }).then(navigateDone);
ok(frameId, "Page.navigate returned a frameId");
info("Wait for events");
const events = await history.record();
info(`Received events: ${events.map(getDescriptionForEvent)}`);
is(events.length, totalCount, "Received expected number of events");
return { history, frameId };
}

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

@ -3,10 +3,11 @@
"use strict";
const PAGE_URL =
"http://example.com/browser/remote/test/browser/network/doc_networkEvents.html";
const JS_URL =
"http://example.com/browser/remote/test/browser/network/file_networkEvents.js";
const BASE_PATH = "http://example.com/browser/remote/test/browser/network";
const FRAMESET_URL = `${BASE_PATH}/doc_frameset.html`;
const FRAMESET_JS_URL = `${BASE_PATH}/file_framesetEvents.js`;
const PAGE_URL = `${BASE_PATH}/doc_networkEvents.html`;
const PAGE_JS_URL = `${BASE_PATH}/file_networkEvents.js`;
add_task(async function noEventsWhenNetworkDomainDisabled({ client }) {
const history = configureHistory(client, 0);
@ -30,56 +31,109 @@ add_task(async function noEventsAfterNetworkDomainDisabled({ client }) {
add_task(async function documentNavigationWithResource({ client }) {
const { Page, Network } = client;
await Network.enable();
const history = configureHistory(client, 2);
const { frameId: frameIdNav } = await Page.navigate({ url: PAGE_URL });
await Network.enable();
await Page.enable();
const history = configureHistory(client, 4);
const frameAttached = Page.frameAttached();
const { frameId: frameIdNav } = await Page.navigate({ url: FRAMESET_URL });
const { frameId: frameIdSubFrame } = await frameAttached;
ok(frameIdNav, "Page.navigate returned a frameId");
info("Wait for Network events");
const events = await history.record();
is(events.length, 2, "Expected number of Network.requestWillBeSent events");
is(events.length, 4, "Expected number of Network.requestWillBeSent events");
// Check top-level document request
const docRequest = events[0].payload;
is(docRequest.request.url, PAGE_URL, "Got the doc request");
is(docRequest.documentURL, PAGE_URL, "documenURL matches request url");
is(docRequest.type, "Document", "The doc request has 'Document' type");
is(docRequest.request.method, "GET", "The doc request has 'GET' method");
is(docRequest.type, "Document", "Document request has the expected type");
is(docRequest.documentURL, FRAMESET_URL, "documentURL matches requested url");
is(docRequest.frameId, frameIdNav, "Got the expected frame id");
is(docRequest.request.url, FRAMESET_URL, "Got the Document request");
is(docRequest.request.method, "GET", "Has the expected request method");
is(
docRequest.requestId,
docRequest.loaderId,
"The doc request has requestId = loaderId"
"The request id is equal to the loader id"
);
is(
docRequest.frameId,
frameIdNav,
"Doc request returns same frameId as Page.navigate"
);
is(docRequest.request.headers.host, "example.com", "Doc request has headers");
const resourceRequest = events[1].payload;
is(resourceRequest.documentURL, PAGE_URL, "documentURL is trigger document");
is(resourceRequest.request.url, JS_URL, "Got the JS request");
is(
resourceRequest.request.headers.host,
docRequest.request.headers.host,
"example.com",
"Doc request has headers"
"Document request has headers"
);
is(resourceRequest.type, "Script", "The page request has 'Script' type");
is(resourceRequest.request.method, "GET", "The doc request has 'GET' method");
// Check top-level script request
const scriptRequest = events[1].payload;
is(scriptRequest.type, "Script", "Script request has the expected type");
is(
docRequest.frameId,
frameIdNav,
"Resource request returns same frameId as Page.navigate"
scriptRequest.documentURL,
FRAMESET_URL,
"documentURL is trigger document for the script request"
);
is(scriptRequest.frameId, frameIdNav, "Got the expected frame id");
is(scriptRequest.request.url, FRAMESET_JS_URL, "Got the Script request");
is(scriptRequest.request.method, "GET", "Has the expected request method");
is(
scriptRequest.request.headers.host,
"example.com",
"Script request has headers"
);
todo(
resourceRequest.loaderId === docRequest.loaderId,
scriptRequest.loaderId === docRequest.loaderId,
"The same loaderId is used for dependent requests (Bug 1637838)"
);
ok(
docRequest.timestamp <= resourceRequest.timestamp,
"Document request happens before resource request"
assertEventOrder(events[0], events[1]);
// Check subdocument request
const subdocRequest = events[2].payload;
is(
subdocRequest.type,
"Subdocument",
"Subdocument request has the expected type"
);
is(subdocRequest.documentURL, FRAMESET_URL, "documenURL matches request url");
is(subdocRequest.frameId, frameIdSubFrame, "Got the expected frame id");
is(
subdocRequest.requestId,
subdocRequest.loaderId,
"The request id is equal to the loader id"
);
is(subdocRequest.request.url, PAGE_URL, "Got the Subdocument request");
is(subdocRequest.request.method, "GET", "Has the expected request method");
is(
subdocRequest.request.headers.host,
"example.com",
"Subdocument request has headers"
);
assertEventOrder(events[1], events[2]);
// Check script request (frame)
const subscriptRequest = events[3].payload;
is(subscriptRequest.type, "Script", "Script request has the expected type");
is(
subscriptRequest.documentURL,
PAGE_URL,
"documentURL is trigger document for the script request"
);
is(subscriptRequest.frameId, frameIdSubFrame, "Got the expected frame id");
todo(
subscriptRequest.loaderId === docRequest.loaderId,
"The same loaderId is used for dependent requests (Bug 1637838)"
);
is(subscriptRequest.request.url, PAGE_JS_URL, "Got the Script request");
is(
subscriptRequest.request.method,
"GET",
"Script request has the expected method"
);
is(
subscriptRequest.request.headers.host,
"example.com",
"Script request has headers"
);
assertEventOrder(events[2], events[3]);
});
function configureHistory(client, total) {
@ -92,7 +146,7 @@ function configureHistory(client, total) {
event: Network.requestWillBeSent,
eventName: REQUEST,
messageFn: payload => {
return `Received ${REQUEST} for ${payload.request?.url}`;
return `Received ${REQUEST} for ${payload.request.url}`;
},
});

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

@ -3,10 +3,11 @@
"use strict";
const PAGE_URL =
"http://example.com/browser/remote/test/browser/network/doc_networkEvents.html";
const JS_URL =
"http://example.com/browser/remote/test/browser/network/file_networkEvents.js";
const BASE_PATH = "http://example.com/browser/remote/test/browser/network";
const FRAMESET_URL = `${BASE_PATH}/doc_frameset.html`;
const FRAMESET_JS_URL = `${BASE_PATH}/file_framesetEvents.js`;
const PAGE_URL = `${BASE_PATH}/doc_networkEvents.html`;
const PAGE_JS_URL = `${BASE_PATH}/file_networkEvents.js`;
add_task(async function noEventsWhenNetworkDomainDisabled({ client }) {
const history = configureHistory(client, 0);
@ -30,88 +31,191 @@ add_task(async function noEventsAfterNetworkDomainDisabled({ client }) {
add_task(async function documentNavigationWithResource({ client }) {
const { Page, Network } = client;
await Network.enable();
const history = configureHistory(client, 2);
const { frameId: frameIdNav } = await Page.navigate({ url: PAGE_URL });
await Network.enable();
await Page.enable();
const history = configureHistory(client, 4);
const frameAttached = Page.frameAttached();
const { frameId: frameIdNav } = await Page.navigate({ url: FRAMESET_URL });
const { frameId: frameIdSubframe } = await frameAttached;
ok(frameIdNav, "Page.navigate returned a frameId");
info("Wait for Network events");
const events = await history.record();
is(events.length, 2, "Expected number of Network.responseReceived events");
is(events.length, 4, "Expected number of Network.responseReceived events");
// Check top-level document response
const docResponse = events[0].payload;
is(docResponse.response.url, PAGE_URL, "Got the doc response");
is(
docResponse.response.mimeType,
"text/html",
"Doc response has expected mimeType"
);
is(docResponse.type, "Document", "The doc response has 'Document' type");
is(docResponse.type, "Document", "Document response has expected type");
is(docResponse.frameId, frameIdNav, "Got the expected frame id");
is(
docResponse.requestId,
docResponse.loaderId,
"The doc request has requestId = loaderId"
"The response id is equal to the loader id"
);
is(docResponse.response.url, FRAMESET_URL, "Got the Document response");
is(
docResponse.frameId,
frameIdNav,
"Doc response returns same frameId as Page.navigate"
docResponse.response.mimeType,
"text/html",
"Document response has expected mimeType"
);
ok(!!docResponse.response.headers.server, "Document response has headers");
is(docResponse.response.status, 200, "Document response has expected status");
is(
docResponse.response.statusText,
"OK",
"Document response has expected status text"
);
ok(!!docResponse.response.headers.server, "Doc response has headers");
is(docResponse.response.status, 200, "Doc response status is 200");
is(docResponse.response.statusText, "OK", "Doc response status is OK");
if (docResponse.response.fromDiskCache === false) {
is(
docResponse.response.remoteIPAddress,
"127.0.0.1",
"Doc response has an IP address"
"Document response has the expected IP address"
);
ok(
typeof docResponse.response.remotePort == "number",
"Doc response has a remotePort"
"Document response has a remotePort"
);
}
is(
docResponse.response.protocol,
"http/1.1",
"Doc response has expected protocol"
"Document response has expected protocol"
);
const resourceResponse = events[1].payload;
is(resourceResponse.response.url, JS_URL, "Got the resource response");
// Check top-level script response
const scriptResponse = events[1].payload;
is(scriptResponse.type, "Script", "Script response has expected type");
is(scriptResponse.frameId, frameIdNav, "Got the expected frame id");
is(scriptResponse.response.url, FRAMESET_JS_URL, "Got the Script response");
is(
resourceResponse.response.mimeType,
scriptResponse.response.mimeType,
"application/x-javascript",
"Resource response has expected mimeType"
"Script response has expected mimeType"
);
ok(!!scriptResponse.response.headers.server, "Script response has headers");
is(
scriptResponse.response.status,
200,
"Script response has the expected status"
);
is(
resourceResponse.type,
"Script",
"The resource response has 'Script' type"
scriptResponse.response.statusText,
"OK",
"Script response has the expected status text"
);
ok(!!resourceResponse.frameId, "Resource response has a frame id");
ok(
!!resourceResponse.response.headers.server,
"Resource response has headers"
);
is(resourceResponse.response.status, 200, "Resource response status is 200");
is(resourceResponse.response.statusText, "OK", "Response status is OK");
if (resourceResponse.response.fromDiskCache === false) {
if (scriptResponse.response.fromDiskCache === false) {
is(
resourceResponse.response.remoteIPAddress,
scriptResponse.response.remoteIPAddress,
docResponse.response.remoteIPAddress,
"Resource response has same IP address and doc response"
"Script response has same IP address as document response"
);
ok(
typeof resourceResponse.response.remotePort == "number",
"Resource response has a remotePort"
typeof scriptResponse.response.remotePort == "number",
"Script response has a remotePort"
);
}
is(
resourceResponse.response.protocol,
scriptResponse.response.protocol,
"http/1.1",
"Resource response has expected protocol"
"Script response has the expected protocol"
);
// Check subdocument response
const frameDocResponse = events[2].payload;
is(
frameDocResponse.type,
"Subdocument",
"Subdocument response has expected type"
);
is(frameDocResponse.frameId, frameIdSubframe, "Got the expected frame id");
is(
frameDocResponse.requestId,
frameDocResponse.loaderId,
"The response id is equal to the loader id"
);
is(
frameDocResponse.response.url,
PAGE_URL,
"Got the expected Document response"
);
is(
frameDocResponse.response.mimeType,
"text/html",
"Document response has expected mimeType"
);
ok(
!!frameDocResponse.response.headers.server,
"Subdocument response has headers"
);
is(
frameDocResponse.response.status,
200,
"Subdocument response has expected status"
);
is(
frameDocResponse.response.statusText,
"OK",
"Subdocument response has expected status text"
);
if (frameDocResponse.response.fromDiskCache === false) {
is(
frameDocResponse.response.remoteIPAddress,
"127.0.0.1",
"Subdocument response has the expected IP address"
);
ok(
typeof frameDocResponse.response.remotePort == "number",
"Subdocument response has a remotePort"
);
}
is(
frameDocResponse.response.protocol,
"http/1.1",
"Subdocument response has expected protocol"
);
// Check frame script response
const frameScriptResponse = events[3].payload;
is(frameScriptResponse.type, "Script", "Script response has expected type");
is(frameScriptResponse.frameId, frameIdSubframe, "Got the expected frame id");
is(frameScriptResponse.response.url, PAGE_JS_URL, "Got the Script response");
is(
frameScriptResponse.response.mimeType,
"application/x-javascript",
"Script response has expected mimeType"
);
ok(
!!frameScriptResponse.response.headers.server,
"Script response has headers"
);
is(
frameScriptResponse.response.status,
200,
"Script response has the expected status"
);
is(
frameScriptResponse.response.statusText,
"OK",
"Script response has the expected status text"
);
if (frameScriptResponse.response.fromDiskCache === false) {
is(
frameScriptResponse.response.remoteIPAddress,
docResponse.response.remoteIPAddress,
"Script response has same IP address as document response"
);
ok(
typeof frameScriptResponse.response.remotePort == "number",
"Script response has a remotePort"
);
}
is(
frameScriptResponse.response.protocol,
"http/1.1",
"Script response has the expected protocol"
);
});
@ -125,7 +229,7 @@ function configureHistory(client, total) {
event: Network.responseReceived,
eventName: RESPONSE,
messageFn: payload => {
return `Received ${RESPONSE} for ${payload.response?.url}`;
return `Received ${RESPONSE} for ${payload.response.url}`;
},
});
return history;

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

@ -0,0 +1,10 @@
<!DOCTYPE html>
<html>
<head>
<title>Frameset for Network events</title>
<script type="text/javascript" src="file_framesetEvents.js"></script>
</head>
<body>
<iframe src="doc_networkEvents.html"></iframe>
</body>
</html>

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

@ -2,8 +2,8 @@
<html>
<head>
<title>Test page for Network events</title>
<script type="text/javascript" src="file_networkEvents.js"></script>
</head>
<body>
<script type="text/javascript" src="file_networkEvents.js"></script>
</body>
</html>

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

@ -0,0 +1,2 @@
// Test file to emit Network events.
var foo = true;

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

@ -43,6 +43,29 @@ function assertCookie(cookie, expected = {}) {
Assert.deepEqual(cookie, expectedCookie);
}
function assertEventOrder(first, second, options = {}) {
const { ignoreTimestamps = false } = options;
const firstDescription = getDescriptionForEvent(first);
const secondDescription = getDescriptionForEvent(second);
ok(
first.index < second.index,
`${firstDescription} received before ${secondDescription})`
);
if (!ignoreTimestamps) {
ok(
first.payload.timestamp <= second.payload.timestamp,
`Timestamp of ${firstDescription}) is earlier than ${secondDescription})`
);
}
}
function filterEventsByType(history, type) {
return history.filter(event => event.payload.type == type);
}
function getCookies() {
return Services.cookies.cookies.map(cookie => {
const data = {
@ -70,3 +93,9 @@ function getCookies() {
return data;
});
}
function getDescriptionForEvent(event) {
const { eventName, payload } = event;
return `${eventName}(${payload.type || payload.name || payload.frameId})`;
}

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

@ -157,7 +157,9 @@ add_task(async function contextCreatedAfterNavigation({ client }) {
],
});
const contexts = history.findEvents(CREATED).map(event => event.context);
const contexts = history
.findEvents(CREATED)
.map(event => event.payload.context);
const defaultContext = contexts[0];
const isolatedContext = contexts[1];
is(defaultContext.auxData.isDefault, true, "Default context is default");
@ -196,11 +198,11 @@ add_task(async function contextDestroyedForNavigation({ client }) {
const destroyed = history
.findEvents(DESTROYED)
.map(event => event.executionContextId);
.map(event => event.payload.executionContextId);
ok(destroyed.includes(isolatedContext.id), "Isolated context destroyed");
ok(destroyed.includes(defaultContext.id), "Default context destroyed");
const { context: newContext } = history.findEvent(CREATED);
const { context: newContext } = history.findEvent(CREATED).payload;
is(newContext.auxData.isDefault, true, "The new context is a default one");
ok(!!newContext.id, "The new context has an id");
ok(
@ -254,7 +256,7 @@ add_task(async function contextsForFramesetNavigation({ client }) {
const contextsCreated = historyTo
.findEvents(CREATED)
.map(event => event.context);
.map(event => event.payload.context);
const parentDefaultContextCreated = contextsCreated[0];
const frameDefaultContextCreated = contextsCreated[1];
const parentIsolatedContextCreated = contextsCreated[2];
@ -295,7 +297,7 @@ add_task(async function contextsForFramesetNavigation({ client }) {
const contextsDestroyed = historyFrom
.findEvents(DESTROYED)
.map(event => event.executionContextId);
.map(event => event.payload.executionContextId);
contextsCreated.forEach(context => {
ok(
contextsDestroyed.includes(context.id),
@ -303,7 +305,7 @@ add_task(async function contextsForFramesetNavigation({ client }) {
);
});
const { context: newContext } = historyFrom.findEvent(CREATED);
const { context: newContext } = historyFrom.findEvent(CREATED).payload;
is(newContext.auxData.isDefault, true, "The new context is a default one");
ok(!!newContext.id, "The new context has an id");
ok(

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

@ -40,14 +40,16 @@ add_task(async function eventsWhenNavigatingWithNoFrames({ client }) {
const { frameId } = await Page.navigate({ url: DOC });
await assertEventOrder({ history });
const { executionContextId: destroyedId } = history.findEvent(DESTROYED);
const { executionContextId: destroyedId } = history.findEvent(
DESTROYED
).payload;
is(
destroyedId,
previousContext.id,
"The destroyed event reports the previous context id"
);
const { context: contextCreated } = history.findEvent(CREATED);
const { context: contextCreated } = history.findEvent(CREATED).payload;
checkDefaultContext(contextCreated);
isnot(
contextCreated.id,
@ -75,7 +77,9 @@ add_task(async function eventsWhenNavigatingFrameSet({ client }) {
expectedEvents: [DESTROYED, CLEARED, CREATED, CREATED],
});
const { executionContextId: destroyedId } = historyTo.findEvent(DESTROYED);
const { executionContextId: destroyedId } = historyTo.findEvent(
DESTROYED
).payload;
is(
destroyedId,
previousContext.id,
@ -83,8 +87,8 @@ add_task(async function eventsWhenNavigatingFrameSet({ client }) {
);
const contexts = historyTo.findEvents(CREATED);
const createdTopContext = contexts[0].context;
const createdFrameContext = contexts[1].context;
const createdTopContext = contexts[0].payload.context;
const createdFrameContext = contexts[1].payload.context;
checkDefaultContext(createdTopContext);
isnot(
@ -120,17 +124,17 @@ add_task(async function eventsWhenNavigatingFrameSet({ client }) {
const destroyedContextIds = historyFrom.findEvents(DESTROYED);
is(
destroyedContextIds[0].executionContextId,
destroyedContextIds[0].payload.executionContextId,
createdTopContext.id,
"The destroyed event reports the previous context id"
);
is(
destroyedContextIds[1].executionContextId,
destroyedContextIds[1].payload.executionContextId,
createdFrameContext.id,
"The destroyed event reports the previous frame's context id"
);
const { context: contextCreated } = historyFrom.findEvent(CREATED);
const { context: contextCreated } = historyFrom.findEvent(CREATED).payload;
checkDefaultContext(contextCreated);
isnot(
contextCreated.id,
@ -159,14 +163,16 @@ add_task(async function eventsWhenNavigatingBackWithNoFrames({ client }) {
gBrowser.selectedBrowser.goBack();
await assertEventOrder({ history });
const { executionContextId: destroyedId } = history.findEvent(DESTROYED);
const { executionContextId: destroyedId } = history.findEvent(
DESTROYED
).payload;
is(
destroyedId,
createdContext.id,
"The destroyed event reports the current context id"
);
const { context } = history.findEvent(CREATED);
const { context } = history.findEvent(CREATED).payload;
checkDefaultContext(context);
is(
context.origin,
@ -214,14 +220,14 @@ add_task(async function eventsWhenReloadingPageWithNoFrames({ client }) {
await assertEventOrder({ history });
const { executionContextId } = history.findEvent(DESTROYED);
const { executionContextId } = history.findEvent(DESTROYED).payload;
is(
executionContextId,
previousContext.id,
"The destroyed event reports the previous context id"
);
const { context } = history.findEvent(CREATED);
const { context } = history.findEvent(CREATED).payload;
checkDefaultContext(context);
is(
context.auxData.frameId,
@ -248,14 +254,16 @@ add_task(async function eventsWhenNavigatingByLocationWithNoFrames({ client }) {
});
await assertEventOrder({ history });
const { executionContextId: destroyedId } = history.findEvent(DESTROYED);
const { executionContextId: destroyedId } = history.findEvent(
DESTROYED
).payload;
is(
destroyedId,
previousContext.id,
"The destroyed event reports the previous context id"
);
const { context: createdContext } = history.findEvent(CREATED);
const { context: createdContext } = history.findEvent(CREATED).payload;
checkDefaultContext(createdContext);
is(
createdContext.auxData.frameId,
@ -283,12 +291,12 @@ function recordContextEvents(Runtime, total) {
history.addRecorder({
event: Runtime.executionContextCreated,
eventName: CREATED,
messageFn: payload => {
messageFn: ({ context }) => {
return (
`Received ${CREATED} for id ${payload.context.id}` +
` type: ${payload.context.auxData.type}` +
` name: ${payload.context.name}` +
` origin: ${payload.context.origin}`
`Received ${CREATED} for id ${context.id}` +
` type: ${context.auxData.type}` +
` name: ${context.name}` +
` origin: ${context.origin}`
);
},
});