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

305 строки
9.5 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" }],
},
// // 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}`);
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.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 "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 (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() {
let snapshots;
if (Services.appinfo.browserTabsRemoteAutostart) {
snapshots = telemetry.getSnapshotForHistograms("main", false).content;
} else {
snapshots = telemetry.getSnapshotForHistograms("main", false).parent;
}
let checkGet = probe => {
return snapshots[probe] ? snapshots[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: checkGet("USE_COUNTER2_CSS_PROPERTY_MarkerMid_DOCUMENT"),
};
return prev_sentinel !== gatheredHistograms.sentinel;
},
"grabHistogramsFromContent",
100,
Infinity
).then(
() => gatheredHistograms,
function(msg) {
throw msg;
}
);
}