gecko-dev/dom/base/test/browser_use_counters.js

349 строки
11 KiB
JavaScript

/* -*- Mode: javascript; tab-width: 2; indent-tabs-mode: nil; js-indent-level: 2 -*- */
requestLongerTimeout(2);
const gHttpTestRoot = "http://example.com/browser/dom/base/test/";
/**
* Enable local telemetry recording for the duration of the tests.
*/
var gOldContentCanRecord = false;
var gOldParentCanRecord = false;
add_task(async function test_initialize() {
let Telemetry = Cc["@mozilla.org/base/telemetry;1"].getService(
Ci.nsITelemetry
);
gOldParentCanRecord = Telemetry.canRecordExtended;
Telemetry.canRecordExtended = true;
await SpecialPowers.pushPrefEnv({
set: [
// Because canRecordExtended is a per-process variable, we need to make sure
// that all of the pages load in the same content process. Limit the number
// of content processes to at most 1 (or 0 if e10s is off entirely).
["dom.ipc.processCount", 1],
["layout.css.use-counters.enabled", true],
["layout.css.use-counters-unimplemented.enabled", true],
],
});
gOldContentCanRecord = await SpecialPowers.spawn(
gBrowser.selectedBrowser,
[],
function() {
let telemetry = Cc["@mozilla.org/base/telemetry;1"].getService(
Ci.nsITelemetry
);
let old = telemetry.canRecordExtended;
telemetry.canRecordExtended = true;
return old;
}
);
info("canRecord for content: " + gOldContentCanRecord);
});
add_task(async function() {
const TESTS = [
// Check that use counters are incremented by SVGs loaded directly in iframes.
{
type: "iframe",
filename: "file_use_counter_svg_getElementById.svg",
counters: [{ name: "SVGSVGELEMENT_GETELEMENTBYID" }],
},
{
type: "iframe",
filename: "file_use_counter_svg_currentScale.svg",
counters: [
{ name: "SVGSVGELEMENT_CURRENTSCALE_getter" },
{ name: "SVGSVGELEMENT_CURRENTSCALE_setter" },
],
},
{
type: "iframe",
filename: "file_use_counter_style.html",
counters: [
// Check for longhands.
{ name: "CSS_PROPERTY_BackgroundImage" },
// Check for shorthands.
{ name: "CSS_PROPERTY_Padding" },
// Check for aliases.
{ name: "CSS_PROPERTY_MozTransform" },
// Check for counted unknown properties.
{ name: "CSS_PROPERTY_WebkitPaddingStart" },
],
},
// Check that even loads from the imglib cache update use counters. The
// images should still be there, because we just loaded them in the last
// set of tests. But we won't get updated counts for the document
// counters, because we won't be re-parsing the SVG documents.
{
type: "iframe",
filename: "file_use_counter_svg_getElementById.svg",
counters: [{ name: "SVGSVGELEMENT_GETELEMENTBYID" }],
check_documents: false,
},
{
type: "iframe",
filename: "file_use_counter_svg_currentScale.svg",
counters: [
{ name: "SVGSVGELEMENT_CURRENTSCALE_getter" },
{ name: "SVGSVGELEMENT_CURRENTSCALE_setter" },
],
check_documents: false,
},
// Check that use counters are incremented by SVGs loaded as images.
// Note that SVG images are not permitted to execute script, so we can only
// check for properties here.
{
type: "img",
filename: "file_use_counter_svg_getElementById.svg",
counters: [{ name: "CSS_PROPERTY_Fill" }],
},
{
type: "img",
filename: "file_use_counter_svg_currentScale.svg",
counters: [{ name: "CSS_PROPERTY_Fill" }],
},
// Check that use counters are incremented by directly loading SVGs
// that reference patterns defined in another SVG file.
{
type: "direct",
filename: "file_use_counter_svg_fill_pattern.svg",
counters: [{ name: "CSS_PROPERTY_FillOpacity", xfail: true }],
},
// Check that use counters are incremented by directly loading SVGs
// that reference patterns defined in the same file or in data: URLs.
{
type: "direct",
filename: "file_use_counter_svg_fill_pattern_internal.svg",
counters: [{ name: "CSS_PROPERTY_FillOpacity" }],
},
// Check that use counters are incremented in a display:none iframe.
{
type: "undisplayed-iframe",
filename: "file_use_counter_svg_currentScale.svg",
counters: [{ name: "SVGSVGELEMENT_CURRENTSCALE_getter" }],
},
// Check that a document that comes out of the bfcache reports any new use
// counters recorded on it.
{
type: "direct",
filename: "file_use_counter_bfcache.html",
waitForExplicitFinish: true,
counters: [{ name: "SVGSVGELEMENT_GETELEMENTBYID" }],
},
// // data: URLs don't correctly propagate to their referring document yet.
// {
// type: "direct",
// filename: "file_use_counter_svg_fill_pattern_data.svg",
// counters: [
// { name: "PROPERTY_FILL_OPACITY" },
// ],
// },
];
for (let test of TESTS) {
let file = test.filename;
info(`checking ${file} (${test.type})`);
let newTab = BrowserTestUtils.addTab(gBrowser, "about:blank");
gBrowser.selectedTab = newTab;
newTab.linkedBrowser.stop();
// Hold on to the current values of the telemetry histograms we're
// interested in.
let before = await grabHistogramsFromContent(
test.counters.map(c => c.name)
);
// Load the test file in the new tab, either directly or via
// file_use_counter_outer{,_display_none}.html, depending on the test type.
let url, targetElement;
switch (test.type) {
case "iframe":
url = gHttpTestRoot + "file_use_counter_outer.html";
targetElement = "content";
break;
case "undisplayed-iframe":
url = gHttpTestRoot + "file_use_counter_outer_display_none.html";
targetElement = "content";
break;
case "img":
url = gHttpTestRoot + "file_use_counter_outer.html";
targetElement = "display";
break;
case "direct":
url = gHttpTestRoot + file;
targetElement = null;
break;
default:
throw `unexpected type ${test.type}`;
}
BrowserTestUtils.loadURI(gBrowser.selectedBrowser, url);
await BrowserTestUtils.browserLoaded(gBrowser.selectedBrowser);
if (test.waitForExplicitFinish) {
if (test.type != "direct") {
throw new Error(
`cannot use waitForExplicitFinish with test type ${test.type}`
);
}
// Wait until the tab changes its hash to indicate it has finished.
await BrowserTestUtils.waitForLocationChange(gBrowser, url + "#finished");
}
if (targetElement) {
// Inject our desired file into the target element of the newly-loaded page.
await SpecialPowers.spawn(
gBrowser.selectedBrowser,
[{ file, targetElement }],
function(opts) {
let target = content.document.getElementById(opts.targetElement);
target.src = opts.file;
return new Promise(resolve => {
let listener = event => {
event.target.removeEventListener("load", listener, true);
resolve();
};
target.addEventListener("load", listener, true);
});
}
);
}
// Tear down the page.
let tabClosed = BrowserTestUtils.waitForTabClosing(newTab);
gBrowser.removeTab(newTab);
await tabClosed;
// Grab histograms again.
let after = await grabHistogramsFromContent(
test.counters.map(c => c.name),
before.sentinel
);
// Compare before and after.
for (let counter of test.counters) {
let name = counter.name;
let value = counter.value ?? 1;
if (!counter.xfail) {
is(
after.page[name],
before.page[name] + value,
`page counts for ${name} after are correct`
);
is(
after.document[name],
before.document[name] + value,
`document counts for ${name} after are correct`
);
}
}
if (test.check_documents ?? true) {
ok(
after.toplevel_docs >= before.toplevel_docs + 1,
"top level destroyed document counts are correct"
);
// 2 documents for "img" tests: one for the outer html page containing the
// <img> element, and one for the SVG image itself.
ok(
after.docs >= before.docs + (test.type == "img" ? 2 : 1),
"destroyed document counts are correct"
);
}
}
});
add_task(async function() {
let Telemetry = Cc["@mozilla.org/base/telemetry;1"].getService(
Ci.nsITelemetry
);
Telemetry.canRecordExtended = gOldParentCanRecord;
await SpecialPowers.spawn(
gBrowser.selectedBrowser,
[{ oldCanRecord: gOldContentCanRecord }],
async function(arg) {
await new Promise(resolve => {
let telemetry = Cc["@mozilla.org/base/telemetry;1"].getService(
Ci.nsITelemetry
);
telemetry.canRecordExtended = arg.oldCanRecord;
resolve();
});
}
);
});
async function grabHistogramsFromContent(names, prev_sentinel = null) {
// We don't have any way to get a notification when telemetry from the
// document that was just closed has been reported. So instead, we
// repeatedly poll for telemetry until we see that a specific use counter
// histogram (CSS_PROPERTY_MarkerMid, the "sentinel") that likely is not
// used by any other document that's open has been incremented.
let telemetry = Cc["@mozilla.org/base/telemetry;1"].getService(
Ci.nsITelemetry
);
let gatheredHistograms;
return BrowserTestUtils.waitForCondition(
function() {
// Document use counters are reported in the content process (when e10s
// is enabled), and page use counters are reported in the parent process.
let snapshots = telemetry.getSnapshotForHistograms("main", false);
let checkGet = probe => {
// When e10s is disabled, all histograms are reported in the parent
// process. Otherwise, all page use counters are reported in the parent
// and document use counters are reported in the content process.
let process =
!Services.appinfo.browserTabsRemoteAutostart ||
probe.endsWith("_PAGE") ||
probe == "TOP_LEVEL_CONTENT_DOCUMENTS_DESTROYED"
? "parent"
: "content";
return snapshots[process][probe] ? snapshots[process][probe].sum : 0;
};
let page = Object.fromEntries(
names.map(name => [name, checkGet(`USE_COUNTER2_${name}_PAGE`)])
);
let document = Object.fromEntries(
names.map(name => [name, checkGet(`USE_COUNTER2_${name}_DOCUMENT`)])
);
gatheredHistograms = {
page,
document,
docs: checkGet("CONTENT_DOCUMENTS_DESTROYED"),
toplevel_docs: checkGet("TOP_LEVEL_CONTENT_DOCUMENTS_DESTROYED"),
sentinel: {
doc: checkGet("USE_COUNTER2_CSS_PROPERTY_MarkerMid_DOCUMENT"),
page: checkGet("USE_COUNTER2_CSS_PROPERTY_MarkerMid_PAGE"),
},
};
let sentinelChanged =
!prev_sentinel ||
(prev_sentinel.doc != gatheredHistograms.sentinel.doc &&
prev_sentinel.page != gatheredHistograms.sentinel.page);
return sentinelChanged;
},
"grabHistogramsFromContent",
100,
Infinity
).then(
() => gatheredHistograms,
function(msg) {
throw msg;
}
);
}