Bug 1664214 - In about:processes, only display root subframes;r=florian

To decrease visual clutter, we stop displaying frames that are purely in-process subframes of the tab.

Differential Revision: https://phabricator.services.mozilla.com/D89878
This commit is contained in:
David Teller 2020-09-14 10:26:39 +00:00
Родитель 7fce8717c1
Коммит ff4e70cada
3 изменённых файлов: 234 добавлений и 186 удалений

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

@ -4444,6 +4444,8 @@ pref("dom.clients.openwindow_favors_same_process", true);
pref("toolkit.aboutPerformance.showInternals", true);
#endif
// If `true`, about:processes shows in-process subframes.
pref("toolkit.aboutProcesses.showAllSubframes", false);
// If `true`, about:processes shows thread information.
pref("toolkit.aboutProcesses.showThreads", false);

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

@ -32,6 +32,9 @@ const { WebExtensionPolicy } = Cu.getGlobalForObject(Services);
const SHOW_THREADS = Services.prefs.getBoolPref(
"toolkit.aboutProcesses.showThreads"
);
const SHOW_ALL_SUBFRAMES = Services.prefs.getBoolPref(
"toolkit.aboutProcesses.showAllSubframes"
);
/**
* Returns a Promise that's resolved after the next turn of the event loop.
@ -993,8 +996,10 @@ var Control = {
let winRow;
for (let win of process.windows) {
winRow = View.appendDOMWindowRow(win, process);
winRow.win = win;
if (SHOW_ALL_SUBFRAMES || win.tab || win.isProcessRoot) {
winRow = View.appendDOMWindowRow(win, process);
winRow.win = win;
}
}
if (SHOW_THREADS) {

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

@ -265,201 +265,242 @@ function extractProcessDetails(row) {
add_task(async function testAboutProcesses() {
info("Setting up about:processes");
// Test twice, once without `showThreads`, once with it.
for (let showThreads of [false, true]) {
// Test twice, once without `showAllSubframes`, once with it.
for (let showAllFrames of [false, true]) {
Services.prefs.setBoolPref(
"toolkit.aboutProcesses.showThreads",
showThreads
"toolkit.aboutProcesses.showAllSubframes",
showAllFrames
);
// The tab we're testing.
let tabAboutProcesses = (gBrowser.selectedTab = BrowserTestUtils.addTab(
gBrowser,
"about:processes"
));
await BrowserTestUtils.browserLoaded(tabAboutProcesses.linkedBrowser);
// Test twice, once without `showThreads`, once with it.
for (let showThreads of [false, true]) {
Services.prefs.setBoolPref(
"toolkit.aboutProcesses.showThreads",
showThreads
);
// The tab we're testing.
let tabAboutProcesses = (gBrowser.selectedTab = BrowserTestUtils.addTab(
gBrowser,
"about:processes"
));
await BrowserTestUtils.browserLoaded(tabAboutProcesses.linkedBrowser);
info("Setting up example.com");
// Another tab that we'll pretend is hung.
let tabHung = BrowserTestUtils.addTab(gBrowser, "http://example.com");
await BrowserTestUtils.browserLoaded(tabHung.linkedBrowser);
info("Setting up fake process hang detector");
let hungChildID = tabHung.linkedBrowser.frameLoader.childID;
let doc = tabAboutProcesses.linkedBrowser.contentDocument;
let tbody = doc.getElementById("process-tbody");
// Keep informing about:processes that `tabHung` is hung.
// Note: this is a background task, do not `await` it.
let fakeProcessHangMonitor = async function() {
for (let i = 0; i < 100; ++i) {
if (!tabHung.linkedBrowser) {
// Let's stop spamming as soon as we can.
return;
}
// eslint-disable-next-line mozilla/no-arbitrary-setTimeout
await new Promise(resolve => setTimeout(resolve, 300));
Services.obs.notifyObservers(
{
childID: hungChildID,
hangType: Ci.nsIHangReport.PLUGIN_HANG,
pluginName: "Fake plug-in",
QueryInterface: ChromeUtils.generateQI(["nsIHangReport"]),
},
"process-hang-report"
);
}
};
fakeProcessHangMonitor();
info("Waiting for the first update of about:processes");
// Wait until the table has first been populated.
await TestUtils.waitForCondition(() => tbody.childElementCount);
info("Waiting for the second update of about:processes");
// And wait for another update using a mutation observer, to give our newly created test tab some time
// to burn some CPU.
await new Promise(resolve => {
let observer = new doc.ownerGlobal.MutationObserver(() => {
observer.disconnect();
resolve();
info("Setting up example.com");
// Another tab that we'll pretend is hung.
let tabHung = BrowserTestUtils.addTab(gBrowser, "http://example.com");
await BrowserTestUtils.browserLoaded(tabHung.linkedBrowser);
await SpecialPowers.spawn(tabHung.linkedBrowser, [], async () => {
// Open an in-process iframe to test toolkit.aboutProcesses.showAllSubframes
let frame = content.document.createElement("iframe");
content.document.body.appendChild(frame);
});
observer.observe(tbody, { childList: true });
});
info("Looking at the contents of about:processes");
let processesToBeFound = [
// The browser process.
{
name: "browser",
type: ["browser"],
predicate: row => row.process.type == "browser",
},
// The hung process.
{
name: "hung",
type: ["web", "webIsolated"],
predicate: row =>
row.classList.contains("hung") && row.classList.contains("process"),
},
// Any non-hung process
{
name: "hung",
type: ["web", "webIsolated"],
predicate: row =>
!row.classList.contains("hung") &&
row.classList.contains("process") &&
row.process.type == "web",
},
];
for (let finder of processesToBeFound) {
info(`Running sanity tests on ${finder.name}`);
let row = tbody.firstChild;
while (row) {
if (finder.predicate(row)) {
break;
}
row = row.nextSibling;
}
Assert.ok(row, `found a table row for ${finder.name}`);
let {
memoryResidentContent,
cpuContent,
pidContent,
typeContent,
threads,
} = extractProcessDetails(row);
info("Setting up fake process hang detector");
let hungChildID = tabHung.linkedBrowser.frameLoader.childID;
info("Sanity checks: type");
Assert.ok(
finder.type.includes(typeContent),
`Type ${typeContent} should be one of ${finder.type}`
);
let doc = tabAboutProcesses.linkedBrowser.contentDocument;
let tbody = doc.getElementById("process-tbody");
info("Sanity checks: pid");
let pid = Number.parseInt(pidContent);
Assert.ok(pid > 0, `Checking pid ${pidContent}`);
Assert.equal(pid, row.process.pid);
info("Sanity checks: memory resident");
testMemory(
memoryResidentContent,
row.process.totalResidentUniqueSize,
row.process.deltaResidentUniqueSize,
HARDCODED_ASSUMPTIONS_PROCESS
);
info("Sanity checks: CPU (Total)");
testCpu(
cpuContent,
row.process.totalCpu,
row.process.slopeCpu,
HARDCODED_ASSUMPTIONS_PROCESS
);
if (!showThreads) {
Assert.equal(
threads,
null,
"In hidden threads mode, we shouldn't have any thread summary"
);
} else {
info("Sanity checks: number of threads");
let numberOfThreads = Number.parseInt(threads.numberContent);
Assert.ok(
numberOfThreads >=
HARDCODED_ASSUMPTIONS_PROCESS.minimalNumberOfThreads
);
Assert.ok(
numberOfThreads <=
HARDCODED_ASSUMPTIONS_PROCESS.maximalNumberOfThreads
);
Assert.equal(numberOfThreads, row.process.threads.length);
info("Testing that we can open the list of threads");
let twisty = threads.row.getElementsByClassName("twisty")[0];
twisty.click();
let numberOfThreadsFound = 0;
for (
let threadRow = threads.row.nextSibling;
threadRow && threadRow.classList.contains("thread");
threadRow = threadRow.nextSibling
) {
let children = threadRow.children;
let cpuContent = children[2].textContent;
let tidContent = document.l10n.getAttributes(children[0].children[0])
.args.tid;
++numberOfThreadsFound;
info("Sanity checks: tid");
let tid = Number.parseInt(tidContent);
Assert.notEqual(tid, 0, "The tid should be set");
Assert.equal(tid, threadRow.thread.tid, "Displayed tid is correct");
info("Sanity checks: CPU (User and Kernel)");
testCpu(
cpuContent,
threadRow.thread.totalCpu,
threadRow.thread.slopeCpu,
HARDCODED_ASSUMPTIONS_THREAD
// Keep informing about:processes that `tabHung` is hung.
// Note: this is a background task, do not `await` it.
let fakeProcessHangMonitor = async function() {
for (let i = 0; i < 100; ++i) {
if (!tabHung.linkedBrowser) {
// Let's stop spamming as soon as we can.
return;
}
// eslint-disable-next-line mozilla/no-arbitrary-setTimeout
await new Promise(resolve => setTimeout(resolve, 300));
Services.obs.notifyObservers(
{
childID: hungChildID,
hangType: Ci.nsIHangReport.PLUGIN_HANG,
pluginName: "Fake plug-in",
QueryInterface: ChromeUtils.generateQI(["nsIHangReport"]),
},
"process-hang-report"
);
}
Assert.equal(
numberOfThreads,
numberOfThreadsFound,
"Found as many threads as expected"
);
}
}
};
fakeProcessHangMonitor();
info("Additional sanity check for all processes");
for (let row of document.getElementsByClassName("process")) {
let { pidContent, typeContent } = extractProcessDetails(row);
Assert.equal(typeContent, row.process.type);
Assert.equal(Number.parseInt(pidContent), row.process.pid);
info("Waiting for the first update of about:processes");
// Wait until the table has first been populated.
await TestUtils.waitForCondition(() => tbody.childElementCount);
info("Waiting for the second update of about:processes");
// And wait for another update using a mutation observer, to give our newly created test tab some time
// to burn some CPU.
await new Promise(resolve => {
let observer = new doc.ownerGlobal.MutationObserver(() => {
observer.disconnect();
resolve();
});
observer.observe(tbody, { childList: true });
});
info("Looking at the contents of about:processes");
let processesToBeFound = [
// The browser process.
{
name: "browser",
type: ["browser"],
predicate: row => row.process.type == "browser",
},
// The hung process.
{
name: "hung",
type: ["web", "webIsolated"],
predicate: row =>
row.classList.contains("hung") && row.classList.contains("process"),
},
// Any non-hung process
{
name: "hung",
type: ["web", "webIsolated"],
predicate: row =>
!row.classList.contains("hung") &&
row.classList.contains("process") &&
row.process.type == "web",
},
];
for (let finder of processesToBeFound) {
info(`Running sanity tests on ${finder.name}`);
let row = tbody.firstChild;
while (row) {
if (finder.predicate(row)) {
break;
}
row = row.nextSibling;
}
Assert.ok(row, `found a table row for ${finder.name}`);
let {
memoryResidentContent,
cpuContent,
pidContent,
typeContent,
threads,
} = extractProcessDetails(row);
info("Sanity checks: type");
Assert.ok(
finder.type.includes(typeContent),
`Type ${typeContent} should be one of ${finder.type}`
);
info("Sanity checks: pid");
let pid = Number.parseInt(pidContent);
Assert.ok(pid > 0, `Checking pid ${pidContent}`);
Assert.equal(pid, row.process.pid);
info("Sanity checks: memory resident");
testMemory(
memoryResidentContent,
row.process.totalResidentUniqueSize,
row.process.deltaResidentUniqueSize,
HARDCODED_ASSUMPTIONS_PROCESS
);
info("Sanity checks: CPU (Total)");
testCpu(
cpuContent,
row.process.totalCpu,
row.process.slopeCpu,
HARDCODED_ASSUMPTIONS_PROCESS
);
// Testing threads.
if (!showThreads) {
info("In this mode, we shouldn't display any threads");
Assert.equal(
threads,
null,
"In hidden threads mode, we shouldn't have any thread summary"
);
} else {
info("Sanity checks: number of threads");
let numberOfThreads = Number.parseInt(threads.numberContent);
Assert.ok(
numberOfThreads >=
HARDCODED_ASSUMPTIONS_PROCESS.minimalNumberOfThreads
);
Assert.ok(
numberOfThreads <=
HARDCODED_ASSUMPTIONS_PROCESS.maximalNumberOfThreads
);
Assert.equal(numberOfThreads, row.process.threads.length);
info("Testing that we can open the list of threads");
let twisty = threads.row.getElementsByClassName("twisty")[0];
twisty.click();
let numberOfThreadsFound = 0;
for (
let threadRow = threads.row.nextSibling;
threadRow && threadRow.classList.contains("thread");
threadRow = threadRow.nextSibling
) {
let children = threadRow.children;
let cpuContent = children[2].textContent;
let tidContent = document.l10n.getAttributes(
children[0].children[0]
).args.tid;
++numberOfThreadsFound;
info("Sanity checks: tid");
let tid = Number.parseInt(tidContent);
Assert.notEqual(tid, 0, "The tid should be set");
Assert.equal(tid, threadRow.thread.tid, "Displayed tid is correct");
info("Sanity checks: CPU (User and Kernel)");
testCpu(
cpuContent,
threadRow.thread.totalCpu,
threadRow.thread.slopeCpu,
HARDCODED_ASSUMPTIONS_THREAD
);
}
Assert.equal(
numberOfThreads,
numberOfThreadsFound,
"Found as many threads as expected"
);
}
// Testing subframes.
let foundAtLeastOneInProcessSubframe = false;
for (let row of doc.getElementsByClassName("window")) {
let subframe = row.win;
if (subframe.tab) {
continue;
}
let url = document.l10n.getAttributes(row.children[0].children[0])
.args.url;
Assert.equal(url, subframe.documentURI.spec);
if (!subframe.isProcessRoot) {
foundAtLeastOneInProcessSubframe = true;
}
}
if (showAllFrames) {
Assert.ok(
foundAtLeastOneInProcessSubframe,
"Found at least one about:blank in-process subframe"
);
} else {
Assert.ok(
!foundAtLeastOneInProcessSubframe,
"We shouldn't have any about:blank in-process subframe"
);
}
}
info("Additional sanity check for all processes");
for (let row of document.getElementsByClassName("process")) {
let { pidContent, typeContent } = extractProcessDetails(row);
Assert.equal(typeContent, row.process.type);
Assert.equal(Number.parseInt(pidContent), row.process.pid);
}
BrowserTestUtils.removeTab(tabAboutProcesses);
BrowserTestUtils.removeTab(tabHung);
}
BrowserTestUtils.removeTab(tabAboutProcesses);
BrowserTestUtils.removeTab(tabHung);
}
Services.prefs.clearUserPref("toolkit.aboutProcesses.showThreads");