зеркало из https://github.com/mozilla/gecko-dev.git
Bug 1628423 - Distinguish favicon loads from lazy loading images. r=Honza
Note that it seems unpredictable when we receive the favicon request in the network monitor, for example, on a Linux machine, the favicon request for a static link element after the stylesheet link element in html_cause-test-page.html it happens after the XHR request was received. So instead of using a static link favicon element, we add a favicon link dynamically in tests. Unfortunately there is no handy way to tell a favicon request has done in contents, so we use LinkHandlerParent utility here. Also note that the favicon request is triggered in FaviconLoader.jsm, which means the call stack should NOT be shown (bug 1280266). For now, we intentionally specify the stack and use todo for the case. Differential Revision: https://phabricator.services.mozilla.com/D70572
This commit is contained in:
Родитель
10353690c9
Коммит
9616df4a8b
|
@ -93,6 +93,22 @@ const EXPECTED_REQUESTS = [
|
|||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
method: "GET",
|
||||
url: EXAMPLE_URL + "favicon_request",
|
||||
causeType: "img",
|
||||
causeUri: INITIATOR_URL,
|
||||
// the favicon request is triggered in FaviconLoader.jsm module, it should
|
||||
// NOT be shown in the stack (bug 1280266). For now we intentionally
|
||||
// specify the file and the line number to be properly sorted.
|
||||
// NOTE: The line number can be an arbitrary number greater than 0.
|
||||
stack: [
|
||||
{
|
||||
file: "resource:///modules/FaviconLoader.jsm",
|
||||
line: Number.MAX_SAFE_INTEGER,
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
method: "GET",
|
||||
url: EXAMPLE_URL + "lazy_img_request",
|
||||
|
@ -112,7 +128,7 @@ const EXPECTED_REQUESTS = [
|
|||
url: EXAMPLE_URL + "beacon_request",
|
||||
causeType: "beacon",
|
||||
causeUri: INITIATOR_URL,
|
||||
stack: [{ fn: "performBeaconRequest", file: INITIATOR_URL, line: 70 }],
|
||||
stack: [{ fn: "performBeaconRequest", file: INITIATOR_URL, line: 80 }],
|
||||
},
|
||||
];
|
||||
|
||||
|
@ -142,6 +158,9 @@ add_task(async function() {
|
|||
|
||||
const wait = waitForNetworkEvents(monitor, EXPECTED_REQUESTS.length);
|
||||
BrowserTestUtils.loadURI(tab.linkedBrowser, INITIATOR_URL);
|
||||
|
||||
registerFaviconNotifier(tab.linkedBrowser);
|
||||
|
||||
await wait;
|
||||
|
||||
// For all expected requests
|
||||
|
@ -180,6 +199,7 @@ add_task(async function() {
|
|||
|
||||
const expectedOrder = EXPECTED_REQUESTS.sort(initiatorSortPredicate).map(
|
||||
r => {
|
||||
let isChromeFrames = false;
|
||||
const lastFrameExists = !!r.stack;
|
||||
let initiator = "";
|
||||
let lineNumber = "";
|
||||
|
@ -187,16 +207,24 @@ add_task(async function() {
|
|||
const { file, line: _lineNumber } = r.stack[0];
|
||||
initiator = getUrlBaseName(file);
|
||||
lineNumber = ":" + _lineNumber;
|
||||
isChromeFrames = file.startsWith("resource:///");
|
||||
}
|
||||
const causeStr = lastFrameExists ? " (" + r.causeType + ")" : r.causeType;
|
||||
return initiator + lineNumber + causeStr;
|
||||
return {
|
||||
initiatorStr: initiator + lineNumber + causeStr,
|
||||
isChromeFrames,
|
||||
};
|
||||
}
|
||||
);
|
||||
|
||||
expectedOrder.forEach((expectedInitiator, i) => {
|
||||
const request = getSortedRequests(store.getState())[i];
|
||||
let initiator;
|
||||
if (request.cause.stacktraceAvailable) {
|
||||
// In cases of chrome frames, we shouldn't have stack.
|
||||
if (
|
||||
request.cause.stacktraceAvailable &&
|
||||
!expectedInitiator.isChromeFrames
|
||||
) {
|
||||
const { filename, lineNumber } = request.cause.lastFrame;
|
||||
initiator =
|
||||
getUrlBaseName(filename) +
|
||||
|
@ -208,11 +236,20 @@ add_task(async function() {
|
|||
} else {
|
||||
initiator = request.cause.type;
|
||||
}
|
||||
is(
|
||||
initiator,
|
||||
expectedInitiator,
|
||||
`The request #${i} has the expected initiator after sorting`
|
||||
);
|
||||
|
||||
if (expectedInitiator.isChromeFrames) {
|
||||
todo_is(
|
||||
initiator,
|
||||
expectedInitiator.initiatorStr,
|
||||
`The request #${i} has the expected initiator after sorting`
|
||||
);
|
||||
} else {
|
||||
is(
|
||||
initiator,
|
||||
expectedInitiator.initiatorStr,
|
||||
`The request #${i} has the expected initiator after sorting`
|
||||
);
|
||||
}
|
||||
});
|
||||
|
||||
await teardown(monitor);
|
||||
|
|
|
@ -6,7 +6,7 @@
|
|||
verifyRequestItemTarget, waitFor, waitForDispatch, testFilterButtons,
|
||||
performRequestsInContent, waitForNetworkEvents, selectIndexAndWaitForSourceEditor,
|
||||
testColumnsAlignment, hideColumn, showColumn, performRequests, waitForRequestData,
|
||||
toggleBlockedUrl */
|
||||
toggleBlockedUrl, registerFaviconNotifier */
|
||||
|
||||
"use strict";
|
||||
|
||||
|
@ -16,6 +16,10 @@ Services.scriptloader.loadSubScript(
|
|||
this
|
||||
);
|
||||
|
||||
const { LinkHandlerParent } = ChromeUtils.import(
|
||||
"resource:///actors/LinkHandlerParent.jsm"
|
||||
);
|
||||
|
||||
const {
|
||||
getFormattedIPAndPort,
|
||||
getFormattedTime,
|
||||
|
@ -1131,26 +1135,33 @@ function validateRequests(requests, monitor) {
|
|||
// if "stack" is array, check the details about the top stack frames
|
||||
if (Array.isArray(stack)) {
|
||||
stack.forEach((frame, j) => {
|
||||
is(
|
||||
stacktrace[j].functionName,
|
||||
frame.fn,
|
||||
`Request #${i} has the correct function on JS stack frame #${j}`
|
||||
);
|
||||
is(
|
||||
stacktrace[j].filename.split("/").pop(),
|
||||
frame.file.split("/").pop(),
|
||||
`Request #${i} has the correct file on JS stack frame #${j}`
|
||||
);
|
||||
is(
|
||||
stacktrace[j].lineNumber,
|
||||
frame.line,
|
||||
`Request #${i} has the correct line number on JS stack frame #${j}`
|
||||
);
|
||||
is(
|
||||
stacktrace[j].asyncCause,
|
||||
frame.asyncCause,
|
||||
`Request #${i} has the correct async cause on JS stack frame #${j}`
|
||||
);
|
||||
// If the `fn` is "*", it means the request is triggered from chrome
|
||||
// resources, e.g. `resource:///modules/XX.jsm`, so we skip checking
|
||||
// the function name for now (bug 1280266).
|
||||
if (frame.file.startsWith("resource:///")) {
|
||||
todo(false, "Requests from chrome resource should not be included");
|
||||
} else {
|
||||
is(
|
||||
stacktrace[j].functionName,
|
||||
frame.fn,
|
||||
`Request #${i} has the correct function on JS stack frame #${j}`
|
||||
);
|
||||
is(
|
||||
stacktrace[j].filename.split("/").pop(),
|
||||
frame.file.split("/").pop(),
|
||||
`Request #${i} has the correct file on JS stack frame #${j}`
|
||||
);
|
||||
is(
|
||||
stacktrace[j].lineNumber,
|
||||
frame.line,
|
||||
`Request #${i} has the correct line number on JS stack frame #${j}`
|
||||
);
|
||||
is(
|
||||
stacktrace[j].asyncCause,
|
||||
frame.asyncCause,
|
||||
`Request #${i} has the correct async cause on JS stack frame #${j}`
|
||||
);
|
||||
}
|
||||
});
|
||||
}
|
||||
} else {
|
||||
|
@ -1225,6 +1236,27 @@ function clickElement(element, monitor) {
|
|||
EventUtils.synthesizeMouseAtCenter(element, {}, monitor.panelWin);
|
||||
}
|
||||
|
||||
/**
|
||||
* Register a listener to be notified when a favicon finished loading and
|
||||
* dispatch a "devtools:test:favicon" event to the favicon's link element.
|
||||
*
|
||||
* @param {Browser} browser
|
||||
* Target browser to observe the favicon load.
|
||||
*/
|
||||
function registerFaviconNotifier(browser) {
|
||||
const listener = async (name, data) => {
|
||||
if (name == "SetIcon" || name == "SetFailedIcon") {
|
||||
await SpecialPowers.spawn(browser, [], async () => {
|
||||
content.document
|
||||
.querySelector("link[rel='icon']")
|
||||
.dispatchEvent(new content.CustomEvent("devtools:test:favicon"));
|
||||
});
|
||||
LinkHandlerParent.removeListenerForTests(listener);
|
||||
}
|
||||
};
|
||||
LinkHandlerParent.addListenerForTests(listener);
|
||||
}
|
||||
|
||||
/**
|
||||
* Predicates used when sorting items.
|
||||
*
|
||||
|
|
|
@ -50,6 +50,16 @@
|
|||
});
|
||||
}
|
||||
|
||||
function performFavicon() {
|
||||
return new Promise(function (resolve) {
|
||||
const link = document.createElement("link");
|
||||
link.rel = "icon";
|
||||
link.href = "favicon_request";
|
||||
document.querySelector("head").appendChild(link);
|
||||
link.addEventListener("devtools:test:favicon", resolve);
|
||||
});
|
||||
}
|
||||
|
||||
function performLazyLoadingImage() {
|
||||
return new Promise(function (resolve) {
|
||||
const lazyImgs = document.querySelectorAll("img[loading='lazy']");
|
||||
|
@ -75,6 +85,7 @@
|
|||
await performFetchRequest();
|
||||
await performPromiseFetchRequest();
|
||||
await performTimeoutFetchRequest();
|
||||
await performFavicon();
|
||||
await performLazyLoadingImage();
|
||||
|
||||
// Finally, send a beacon request
|
||||
|
|
|
@ -708,7 +708,11 @@ NetworkObserver.prototype = {
|
|||
}
|
||||
|
||||
event.cause = {
|
||||
type: causeTypeToString(causeType, channel.loadFlags),
|
||||
type: causeTypeToString(
|
||||
causeType,
|
||||
channel.loadFlags,
|
||||
channel.loadInfo.internalContentPolicyType
|
||||
),
|
||||
loadingDocumentUri: causeUri,
|
||||
stacktrace,
|
||||
};
|
||||
|
@ -1501,11 +1505,11 @@ const LOAD_CAUSE_STRINGS = {
|
|||
[Ci.nsIContentPolicy.TYPE_WEB_MANIFEST]: "webManifest",
|
||||
};
|
||||
|
||||
function causeTypeToString(causeType, loadFlags) {
|
||||
function causeTypeToString(causeType, loadFlags, internalContentPolicyType) {
|
||||
let prefix = "";
|
||||
if (
|
||||
(causeType == Ci.nsIContentPolicy.TYPE_IMAGESET ||
|
||||
causeType == Ci.nsIContentPolicy.TYPE_IMAGE) &&
|
||||
internalContentPolicyType == Ci.nsIContentPolicy.TYPE_INTERNAL_IMAGE) &&
|
||||
loadFlags & Ci.nsIRequest.LOAD_BACKGROUND
|
||||
) {
|
||||
prefix = "lazy-";
|
||||
|
|
Загрузка…
Ссылка в новой задаче