/* -*- 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 for -moz-appearance related use counters. { type: "iframe", filename: "file_use_counter_appearance.html", counters: [ { name: "APPEARANCE_WIDGET_BUTTON", value: 0 }, { name: "APPEARANCE_NONWIDGET_BUTTON", value: 0 }, { name: "APPEARANCE_WIDGET_TEXTFIELD", value: 0 }, { name: "APPEARANCE_NONWIDGET_TEXTFIELD", value: 0 }, { name: "APPEARANCE_WIDGET_RADIO", value: 0 }, { name: "APPEARANCE_NONWIDGET_RADIO", value: 0 }, { name: "APPEARANCE_WIDGET_CHECKBOX" }, { name: "APPEARANCE_NONWIDGET_CHECKBOX", value: 0 }, { name: "APPEARANCE_WIDGET_MENULISTBUTTON", value: 0 }, { name: "APPEARANCE_NONWIDGET_MENULISTBUTTON" }, { name: "APPEARANCE_WIDGET_METER", value: 0 }, { name: "APPEARANCE_NONWIDGET_METER", value: 0 }, { name: "APPEARANCE_WIDGET_TEXTAREA", value: 0 }, { name: "APPEARANCE_NONWIDGET_TEXTAREA", value: 0 }, { name: "APPEARANCE_WIDGET_RANGE", value: 0 }, { name: "APPEARANCE_NONWIDGET_RANGE", value: 0 }, { name: "APPEARANCE_OVERRIDDEN_RANGE" }, { name: "APPEARANCE_WIDGET_NUMBERINPUT", value: 0 }, { name: "APPEARANCE_NONWIDGET_NUMBERINPUT", value: 0 }, { name: "APPEARANCE_OVERRIDDEN_NUMBERINPUT", value: 0 }, ], }, // 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 // 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; } ); }