diff --git a/browser/actors/RFPHelperParent.sys.mjs b/browser/actors/RFPHelperParent.sys.mjs deleted file mode 100644 index 0e4e3e8be64..00000000000 --- a/browser/actors/RFPHelperParent.sys.mjs +++ /dev/null @@ -1,33 +0,0 @@ -1; /* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */ -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ - -import { XPCOMUtils } from "resource://gre/modules/XPCOMUtils.sys.mjs"; - -const lazy = {}; -ChromeUtils.defineESModuleGetters(lazy, { - RFPHelper: "resource://gre/modules/RFPHelper.sys.mjs", -}); - -const kPrefLetterboxing = "privacy.resistFingerprinting.letterboxing"; - -XPCOMUtils.defineLazyPreferenceGetter( - lazy, - "isLetterboxingEnabled", - kPrefLetterboxing, - false -); - -export class RFPHelperParent extends JSWindowActorParent { - receiveMessage(aMessage) { - if ( - lazy.isLetterboxingEnabled && - aMessage.name == "Letterboxing:ContentSizeUpdated" - ) { - let browser = this.browsingContext.top.embedderElement; - let window = browser.ownerGlobal; - lazy.RFPHelper.contentSizeUpdated(window); - } - } -} diff --git a/browser/app/profile/firefox.js b/browser/app/profile/firefox.js index 48981f6d8b5..9e4caaee415 100644 --- a/browser/app/profile/firefox.js +++ b/browser/app/profile/firefox.js @@ -1945,7 +1945,7 @@ pref("browser.newtabpage.activity-stream.discoverystream.spocMessageVariant", "" pref("browser.newtabpage.activity-stream.discoverystream.sendToPocket.enabled", true); // Pref enabling content reporting -pref("browser.newtabpage.activity-stream.discoverystream.reportContent.enabled", false); +pref("browser.newtabpage.activity-stream.discoverystream.reportAds.enabled", false); // List of regions that do not get stories, regardless of locale-list-config. pref("browser.newtabpage.activity-stream.discoverystream.region-stories-block", ""); diff --git a/browser/components/asrouter/tests/browser/browser_asrouter_experimentsAPILoader.js b/browser/components/asrouter/tests/browser/browser_asrouter_experimentsAPILoader.js index 5a053c1a6b6..374b21c2d80 100644 --- a/browser/components/asrouter/tests/browser/browser_asrouter_experimentsAPILoader.js +++ b/browser/components/asrouter/tests/browser/browser_asrouter_experimentsAPILoader.js @@ -297,7 +297,7 @@ add_task(async function test_forceEnrollUpdatesMessages() { await assertMessageInState("xman_test_message"); - await ExperimentManager.unenroll(`optin-${experiment.slug}`, "cleanup"); + await ExperimentManager.unenroll(`optin-${experiment.slug}`); await SpecialPowers.popPrefEnv(); await cleanup(); }); diff --git a/browser/components/uitour/UITourChild.sys.mjs b/browser/components/uitour/UITourChild.sys.mjs index 969b6923520..2df3b6e4c51 100644 --- a/browser/components/uitour/UITourChild.sys.mjs +++ b/browser/components/uitour/UITourChild.sys.mjs @@ -2,12 +2,11 @@ * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ -const PREF_TEST_ORIGINS = "browser.uitour.testingOrigins"; -const UITOUR_PERMISSION = "uitour"; +import { UITourUtils } from "moz-src:///browser/components/uitour/UITourUtils.sys.mjs"; export class UITourChild extends JSWindowActorChild { handleEvent(event) { - if (!this.ensureTrustedOrigin()) { + if (!UITourUtils.ensureTrustedOrigin(this.manager)) { return; } @@ -18,62 +17,6 @@ export class UITourChild extends JSWindowActorChild { }); } - isTestingOrigin(aURI) { - let testingOrigins = Services.prefs.getStringPref(PREF_TEST_ORIGINS, ""); - if (!testingOrigins) { - return false; - } - - // Allow any testing origins (comma-seperated). - for (let origin of testingOrigins.split(/\s*,\s*/)) { - try { - let testingURI = Services.io.newURI(origin); - if (aURI.prePath == testingURI.prePath) { - return true; - } - } catch (ex) { - console.error(ex); - } - } - return false; - } - - // This function is copied from UITour.sys.mjs. - isSafeScheme(aURI) { - let allowedSchemes = new Set(["https", "about"]); - if (!allowedSchemes.has(aURI.scheme)) { - return false; - } - - return true; - } - - ensureTrustedOrigin() { - if (this.browsingContext.top != this.browsingContext) { - return false; - } - - let uri = this.document.documentURIObject; - - if (uri.schemeIs("chrome")) { - return true; - } - - if (!this.isSafeScheme(uri)) { - return false; - } - - let permission = Services.perms.testPermissionFromPrincipal( - this.document.nodePrincipal, - UITOUR_PERMISSION - ); - if (permission == Services.perms.ALLOW_ACTION) { - return true; - } - - return this.isTestingOrigin(uri); - } - receiveMessage(aMessage) { switch (aMessage.name) { case "UITour:SendPageCallback": @@ -86,7 +29,7 @@ export class UITourChild extends JSWindowActorChild { } sendPageEvent(type, detail) { - if (!this.ensureTrustedOrigin()) { + if (!UITourUtils.ensureTrustedOrigin(this.manager)) { return; } diff --git a/browser/components/uitour/UITourParent.sys.mjs b/browser/components/uitour/UITourParent.sys.mjs index 56222251074..61506d358db 100644 --- a/browser/components/uitour/UITourParent.sys.mjs +++ b/browser/components/uitour/UITourParent.sys.mjs @@ -3,9 +3,13 @@ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ import { UITour } from "moz-src:///browser/components/uitour/UITour.sys.mjs"; +import { UITourUtils } from "moz-src:///browser/components/uitour/UITourUtils.sys.mjs"; export class UITourParent extends JSWindowActorParent { receiveMessage(message) { + if (!UITourUtils.ensureTrustedOrigin(this.manager)) { + return; + } switch (message.name) { case "UITour:onPageEvent": if (this.manager.rootFrameLoader) { diff --git a/browser/components/uitour/UITourUtils.sys.mjs b/browser/components/uitour/UITourUtils.sys.mjs new file mode 100644 index 00000000000..b390b2b53dc --- /dev/null +++ b/browser/components/uitour/UITourUtils.sys.mjs @@ -0,0 +1,84 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +const PREF_TEST_ORIGINS = "browser.uitour.testingOrigins"; +const UITOUR_PERMISSION = "uitour"; + +export let UITourUtils = { + /** + * Check if we've got a testing origin. + * + * @param {nsIURI} uri + * The URI to check + * @returns {boolean} + * Whether or not it's a testing origin. + */ + isTestingOrigin(uri) { + let testingOrigins = Services.prefs.getStringPref(PREF_TEST_ORIGINS, ""); + if (!testingOrigins) { + return false; + } + + // Allow any testing origins (comma-seperated). + for (let origin of testingOrigins.split(/\s*,\s*/)) { + try { + let testingURI = Services.io.newURI(origin); + if (uri.prePath == testingURI.prePath) { + return true; + } + } catch (ex) { + console.error(ex); + } + } + return false; + }, + + /** + * + * @param {WindowGlobalChild|WindowGlobalParent} windowGlobal + * The parent/child representation of a window global to check if we can + * use UITour. + * @returns {boolean} + * Whether or not we can use UITour here. + */ + ensureTrustedOrigin(windowGlobal) { + // If we're not top-most or no longer current, bail out immediately. + if (windowGlobal.browsingContext.parent || !windowGlobal.isCurrentGlobal) { + return false; + } + + let principal, uri; + // We can get either a WindowGlobalParent or WindowGlobalChild, depending on + // what process we're called in, and determining the secure context-ness and + // principal + URI needs different approaches based on this. + if (WindowGlobalParent.isInstance(windowGlobal)) { + if (!windowGlobal.browsingContext.secureBrowserUI?.isSecureContext) { + return false; + } + principal = windowGlobal.documentPrincipal; + uri = windowGlobal.documentURI; + } else { + if (!windowGlobal.contentWindow?.isSecureContext) { + return false; + } + let document = windowGlobal.contentWindow.document; + principal = document?.nodePrincipal; + uri = document?.documentURIObject; + } + + if (!principal) { + return false; + } + + let permission = Services.perms.testPermissionFromPrincipal( + principal, + UITOUR_PERMISSION + ); + if (permission == Services.perms.ALLOW_ACTION) { + return true; + } + + return uri && this.isTestingOrigin(uri); + }, +}; diff --git a/browser/components/uitour/moz.build b/browser/components/uitour/moz.build index 3a891ae10a0..e679af7e23e 100644 --- a/browser/components/uitour/moz.build +++ b/browser/components/uitour/moz.build @@ -2,7 +2,12 @@ # License, v. 2.0. If a copy of the MPL was not distributed with this # file, You can obtain one at http://mozilla.org/MPL/2.0/. -MOZ_SRC_FILES += ["UITour.sys.mjs", "UITourChild.sys.mjs", "UITourParent.sys.mjs"] +MOZ_SRC_FILES += [ + "UITour.sys.mjs", + "UITourChild.sys.mjs", + "UITourParent.sys.mjs", + "UITourUtils.sys.mjs", +] BROWSER_CHROME_MANIFESTS += [ "test/browser.toml", diff --git a/browser/components/uitour/test/browser_UITour_private_browsing.js b/browser/components/uitour/test/browser_UITour_private_browsing.js index 0c5a52f6674..d69bb30fbb9 100644 --- a/browser/components/uitour/test/browser_UITour_private_browsing.js +++ b/browser/components/uitour/test/browser_UITour_private_browsing.js @@ -16,6 +16,10 @@ add_task(async function test_privatebrowsing_window() { const ABOUT_ORIGIN_WITH_UITOUR_DEFAULT = "about:newtab"; const HTTPS_ORIGIN_WITH_UITOUR_DEFAULT = "https://www.mozilla.org"; + let { UITourUtils } = ChromeUtils.importESModule( + "moz-src:///browser/components/uitour/UITourUtils.sys.mjs" + ); + let win = await BrowserTestUtils.openNewBrowserWindow({ private: true }); let browser = win.gBrowser.selectedBrowser; @@ -26,10 +30,21 @@ add_task(async function test_privatebrowsing_window() { BrowserTestUtils.startLoadingURIString(browser, uri); await BrowserTestUtils.browserLoaded(browser); + Assert.ok( + UITourUtils.ensureTrustedOrigin( + browser.browsingContext.currentWindowGlobal + ), + "Page should be considered trusted for UITour in the parent." + ); + await SpecialPowers.spawn(browser, [], async () => { let actor = content.windowGlobalChild.getActor("UITour"); + // eslint-disable-next-line no-shadow + let { UITourUtils } = ChromeUtils.importESModule( + "moz-src:///browser/components/uitour/UITourUtils.sys.mjs" + ); Assert.ok( - actor.ensureTrustedOrigin(), + UITourUtils.ensureTrustedOrigin(actor.manager), "Page should be considered trusted for UITour." ); }); diff --git a/browser/components/urlbar/docs/firefox-suggest-telemetry.rst b/browser/components/urlbar/docs/firefox-suggest-telemetry.rst index 964d9165b29..83c37123d81 100644 --- a/browser/components/urlbar/docs/firefox-suggest-telemetry.rst +++ b/browser/components/urlbar/docs/firefox-suggest-telemetry.rst @@ -158,11 +158,16 @@ The following scalars are recorded for Firefox Suggest. For general information on scalar telemetry in Firefox, see the :doc:`/toolkit/components/telemetry/collection/scalars` document. -browser.ui.interaction.preferences_panePrivacy -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +browser.ui.interaction.preferences_paneSearch +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ This keyed scalar is incremented each time the user clicks a Firefox Suggest -checkbox or toggle switch in the preferences UI. Keys are the following: +checkbox or toggle switch in the preferences UI. + +Note: These are also recorded in different forms under the +``contextual.services.quicksuggest.*`` telemetry described below. + +Keys are the following: :firefoxSuggestBestMatch: This key is incremented when the "Top pick" checkbox is clicked. In 120 this @@ -173,10 +178,10 @@ checkbox or toggle switch in the preferences UI. Keys are the following: :firefoxSuggestDataCollectionToggle: This key is incremented when the toggle switch for data collection is clicked. -:firefoxSuggestNonsponsoredToggle: +:firefoxSuggestNonsponsored: This key is incremented when the toggle switch for non-sponsored suggestions is clicked. -:firefoxSuggestSponsoredToggle: +:firefoxSuggestSponsored: This key is incremented when the toggle switch for sponsored suggestions is clicked. @@ -194,10 +199,18 @@ Changelog Removed ``firefoxSuggestBestMatch`` and ``firefoxSuggestBestMatchLearnMore``. [Bug 1857391_] + Firefox 123.0 + Recording moved from ``browser.ui.interaction.preferences_panePrivacy`` to + ``browser.ui.interaction.preferences_paneSearch``. [Bug 1852048_] + ``firefoxSuggestNonsponsoredToggle`` was renamed to ``firefoxSuggestNonsponsored`` + ``firefoxSuggestSponsoredToggle`` was renamed to ``firefoxSuggestSponsored`` + `` + .. _1735976: https://bugzilla.mozilla.org/show_bug.cgi?id=1735976 .. _1755100: https://bugzilla.mozilla.org/show_bug.cgi?id=1755100 .. _1756917: https://bugzilla.mozilla.org/show_bug.cgi?id=1756917 .. _1857391: https://bugzilla.mozilla.org/show_bug.cgi?id=1857391 +.. _1852048: https://bugzilla.mozilla.org/show_bug.cgi?id=1852048 contextual.services.quicksuggest.block_dynamic_wikipedia ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ diff --git a/browser/components/urlbar/docs/telemetry.rst b/browser/components/urlbar/docs/telemetry.rst index 90f0836cb7e..486cf9e6877 100644 --- a/browser/components/urlbar/docs/telemetry.rst +++ b/browser/components/urlbar/docs/telemetry.rst @@ -291,6 +291,10 @@ urlbar.searchmode.* Used when the user selects a keyword offer result. - ``oneoff`` Used when the user selects a one-off engine in the Urlbar. + - ``searchbutton`` + Used when the user entered search mode via the unified search button. + Added in Firefox 133, but the unified search button was not enabled in + release until 136. - ``shortcut`` Used when the user enters search mode with a keyboard shortcut or menu bar item (e.g. ``Accel+K``). @@ -365,6 +369,14 @@ urlbar.searchmode.* Please note that symbols cannot trigger the ``urlbar.searchmode.keywordoffer`` telemetry, as symbols are only valid for typed. [Bug `1919180`_] + Firefox 133 + Added ``urlbar.searchmode.searchbutton``: + - This new probe is for accesses to search mode from the unified search + button. The button was released in Firefox 136 and replaced the previous + one-off buttons (``urlbar.searchmode.oneoff``). + + Added Glean equivalents of the probes as labeled counters. + urlbar.picked.* ~~~~~~~~~~~~~~~ diff --git a/browser/extensions/newtab/content-src/components/DiscoveryStreamBase/DiscoveryStreamBase.jsx b/browser/extensions/newtab/content-src/components/DiscoveryStreamBase/DiscoveryStreamBase.jsx index cfea19640b1..526d84051d5 100644 --- a/browser/extensions/newtab/content-src/components/DiscoveryStreamBase/DiscoveryStreamBase.jsx +++ b/browser/extensions/newtab/content-src/components/DiscoveryStreamBase/DiscoveryStreamBase.jsx @@ -253,8 +253,9 @@ export class _DiscoveryStreamBase extends React.PureComponent { const { config } = this.props.DiscoveryStream; const topicSelectionEnabled = this.props.Prefs.values["discoverystream.topicSelection.enabled"]; - const reportContentEnabled = - this.props.Prefs.values["discoverystream.reportContent.enabled"]; + const reportAdsEnabled = + this.props.Prefs.values["discoverystream.reportAds.enabled"]; + const spocsEnabled = this.props.Prefs.values["unifiedAds.spocs.enabled"]; // Allow rendering without extracting special components if (!config.collapsible) { @@ -319,16 +320,19 @@ export class _DiscoveryStreamBase extends React.PureComponent { } } - // Render a DS-style TopSites then the rest if any in a collapsible section const { DiscoveryStream } = this.props; + return ( {this.props.DiscoveryStream.isPrivacyInfoModalVisible && ( )} - {reportContentEnabled && ( + + {/* Reporting stories/articles will only be available in sections, not the default card grid */} + {((reportAdsEnabled && spocsEnabled) || sectionsEnabled) && ( )} + {topSites && this.renderLayout([ { diff --git a/browser/extensions/newtab/content-src/components/DiscoveryStreamComponents/AdBanner/AdBanner.jsx b/browser/extensions/newtab/content-src/components/DiscoveryStreamComponents/AdBanner/AdBanner.jsx index fd38ededfe2..d503f8b32a8 100644 --- a/browser/extensions/newtab/content-src/components/DiscoveryStreamComponents/AdBanner/AdBanner.jsx +++ b/browser/extensions/newtab/content-src/components/DiscoveryStreamComponents/AdBanner/AdBanner.jsx @@ -49,6 +49,7 @@ export const AdBanner = ({ }; const sectionsEnabled = prefs["discoverystream.sections.enabled"]; + const showAdReporting = prefs["discoverystream.reportAds.enabled"]; const { width: imgWidth, height: imgHeight } = getDimensions(spoc.format); @@ -89,7 +90,7 @@ export const AdBanner = ({ spoc={spoc} position={row} type={type} - prefs={prefs} + showAdReporting={showAdReporting} /> ({ id: "newtab-menu-manage-sponsored-content", action: ac.OnlyToMain({ type: at.SETTINGS_OPEN }), + userEvent: "OPEN_NEWTAB_PREFS", }), OurSponsorsAndYourPrivacy: () => ({ id: "newtab-menu-our-sponsors-and-your-privacy", @@ -524,6 +525,7 @@ export const LinkMenuOptions = { url: "https://support.mozilla.org/kb/pocket-sponsored-stories-new-tabs", }, }), + userEvent: "CLICK_PRIVACY_INFO", }), ReportAd: site => { return { diff --git a/browser/extensions/newtab/data/content/activity-stream.bundle.js b/browser/extensions/newtab/data/content/activity-stream.bundle.js index 98876b69db2..db5e7b1feb2 100644 --- a/browser/extensions/newtab/data/content/activity-stream.bundle.js +++ b/browser/extensions/newtab/data/content/activity-stream.bundle.js @@ -2202,6 +2202,7 @@ const LinkMenuOptions = { ManageSponsoredContent: () => ({ id: "newtab-menu-manage-sponsored-content", action: actionCreators.OnlyToMain({ type: actionTypes.SETTINGS_OPEN }), + userEvent: "OPEN_NEWTAB_PREFS", }), OurSponsorsAndYourPrivacy: () => ({ id: "newtab-menu-our-sponsors-and-your-privacy", @@ -2211,6 +2212,7 @@ const LinkMenuOptions = { url: "https://support.mozilla.org/kb/pocket-sponsored-stories-new-tabs", }, }), + userEvent: "CLICK_PRIVACY_INFO", }), ReportAd: site => { return { @@ -2437,19 +2439,16 @@ class _DSLinkMenu extends (external_React_default()).PureComponent { dispatch } = this.props; let TOP_STORIES_CONTEXT_MENU_OPTIONS; - const PREF_REPORT_CONTENT_ENABLED = "discoverystream.reportContent.enabled"; + const PREF_REPORT_ADS_ENABLED = "discoverystream.reportAds.enabled"; const prefs = this.props.Prefs.values; - const showReporting = prefs[PREF_REPORT_CONTENT_ENABLED]; + const showAdsReporting = prefs[PREF_REPORT_ADS_ENABLED]; const isSpoc = this.props.card_type === "spoc"; if (isSpoc) { - TOP_STORIES_CONTEXT_MENU_OPTIONS = ["BlockUrl", ...(showReporting ? ["ReportAd"] : []), "ManageSponsoredContent", "OurSponsorsAndYourPrivacy"]; + TOP_STORIES_CONTEXT_MENU_OPTIONS = ["BlockUrl", ...(showAdsReporting ? ["ReportAd"] : []), "ManageSponsoredContent", "OurSponsorsAndYourPrivacy"]; } else { const saveToPocketOptions = this.props.pocket_button_enabled ? ["CheckArchiveFromPocket", "CheckSavedToPocket"] : []; - TOP_STORIES_CONTEXT_MENU_OPTIONS = ["CheckBookmark", ...(showReporting && this.props.section ? ["ReportContent"] : []), ...saveToPocketOptions, "Separator", "OpenInNewWindow", "OpenInPrivateWindow", "Separator", "BlockUrl"]; + TOP_STORIES_CONTEXT_MENU_OPTIONS = ["CheckBookmark", ...(this.props.section ? ["ReportContent"] : []), ...saveToPocketOptions, "Separator", "OpenInNewWindow", "OpenInPrivateWindow", "Separator", "BlockUrl"]; } - - // eslint-disable-next-line no-console - console.log("dslinkmenu prop", this.props); const type = this.props.type || "DISCOVERY_STREAM"; const title = this.props.title || this.props.source; return /*#__PURE__*/external_React_default().createElement("div", { @@ -4521,6 +4520,7 @@ function ListFeed({ * @param spoc * @param position * @param type + * @param showAdReporting * @returns {Element} * @constructor */ @@ -4529,11 +4529,9 @@ function AdBannerContextMenu({ spoc, position, type, - prefs + showAdReporting }) { - const PREF_REPORT_CONTENT_ENABLED = "discoverystream.reportContent.enabled"; - const showReporting = prefs[PREF_REPORT_CONTENT_ENABLED]; - const ADBANNER_CONTEXT_MENU_OPTIONS = ["BlockAdUrl", ...(showReporting ? ["ReportAd"] : []), "ManageSponsoredContent", "OurSponsorsAndYourPrivacy"]; + const ADBANNER_CONTEXT_MENU_OPTIONS = ["BlockAdUrl", ...(showAdReporting ? ["ReportAd"] : []), "ManageSponsoredContent", "OurSponsorsAndYourPrivacy"]; const [showContextMenu, setShowContextMenu] = (0,external_React_namespaceObject.useState)(false); const onClick = e => { e.preventDefault(); @@ -4633,6 +4631,7 @@ const AdBanner = ({ }; }; const sectionsEnabled = prefs["discoverystream.sections.enabled"]; + const showAdReporting = prefs["discoverystream.reportAds.enabled"]; const { width: imgWidth, height: imgHeight @@ -4675,7 +4674,7 @@ const AdBanner = ({ spoc: spoc, position: row, type: type, - prefs: prefs + showAdReporting: showAdReporting }), /*#__PURE__*/external_React_default().createElement(SafeAnchor, { className: "ad-banner-link", url: spoc.url, @@ -11412,7 +11411,8 @@ class _DiscoveryStreamBase extends (external_React_default()).PureComponent { config } = this.props.DiscoveryStream; const topicSelectionEnabled = this.props.Prefs.values["discoverystream.topicSelection.enabled"]; - const reportContentEnabled = this.props.Prefs.values["discoverystream.reportContent.enabled"]; + const reportAdsEnabled = this.props.Prefs.values["discoverystream.reportAds.enabled"]; + const spocsEnabled = this.props.Prefs.values["unifiedAds.spocs.enabled"]; // Allow rendering without extracting special components if (!config.collapsible) { @@ -11474,14 +11474,12 @@ class _DiscoveryStreamBase extends (external_React_default()).PureComponent { sectionTitle = "Editor’s Picks"; } } - - // Render a DS-style TopSites then the rest if any in a collapsible section const { DiscoveryStream } = this.props; return /*#__PURE__*/external_React_default().createElement((external_React_default()).Fragment, null, this.props.DiscoveryStream.isPrivacyInfoModalVisible && /*#__PURE__*/external_React_default().createElement(DSPrivacyModal, { dispatch: this.props.dispatch - }), reportContentEnabled && /*#__PURE__*/external_React_default().createElement(ReportContent, { + }), (reportAdsEnabled && spocsEnabled || sectionsEnabled) && /*#__PURE__*/external_React_default().createElement(ReportContent, { spocs: DiscoveryStream.spocs }), topSites && this.renderLayout([{ width: 12, diff --git a/browser/extensions/newtab/lib/ActivityStream.sys.mjs b/browser/extensions/newtab/lib/ActivityStream.sys.mjs index 1c54cb46629..63a33eda837 100644 --- a/browser/extensions/newtab/lib/ActivityStream.sys.mjs +++ b/browser/extensions/newtab/lib/ActivityStream.sys.mjs @@ -610,10 +610,9 @@ export const PREFS_CONFIG = new Map([ }, ], [ - "discoverystream.reportContent.enabled", + "discoverystream.reportAds.enabled", { - title: - "Boolean flag to enable reporting content and ads from the context menu", + title: "Boolean flag to enable reporting ads from the context menu", value: false, }, ], diff --git a/browser/extensions/newtab/test/unit/content-src/components/DiscoveryStreamComponents/AdBannerContextMenu.test.jsx b/browser/extensions/newtab/test/unit/content-src/components/DiscoveryStreamComponents/AdBannerContextMenu.test.jsx index af53ed513d5..7ca3d134359 100644 --- a/browser/extensions/newtab/test/unit/content-src/components/DiscoveryStreamComponents/AdBannerContextMenu.test.jsx +++ b/browser/extensions/newtab/test/unit/content-src/components/DiscoveryStreamComponents/AdBannerContextMenu.test.jsx @@ -11,9 +11,7 @@ describe("", () => { spoc: { url: "https://www.test.com/", shim: "aaabbbcccddd" }, position: 1, type: "billboard", - prefs: { - "discoverystream.reportContent.enabled": true, - }, + prefs: {}, }; beforeEach(() => { @@ -53,8 +51,34 @@ describe("", () => { ].forEach(prop => assert.property(linkMenuProps, prop)); }); - it("should pass through the correct menu options to LinkMenu for ad banners", () => { - const reportPref = props.prefs["discoverystream.reportContent.enabled"]; + it("should pass through the correct menu options to LinkMenu for ad banners with reporting INCLUDED", () => { + const propsWithReporting = { + ...props, + showAdReporting: true, + }; + wrapper = shallow(); + wrapper.find("moz-button").simulate("click", { + preventDefault: () => {}, + }); + const linkMenuProps = wrapper.find(LinkMenu).props(); + + const linkMenuOptions = [ + "BlockAdUrl", + "ReportAd", + "ManageSponsoredContent", + "OurSponsorsAndYourPrivacy", + ]; + + assert.deepEqual(linkMenuProps.options, linkMenuOptions); + }); + + it("should pass through correct menu options to LinkMenu for ad banner with reporting EXCLUDED", () => { + const propsWithoutReporting = { + ...props, + showAdReporting: false, + }; + + wrapper = shallow(); wrapper.find("moz-button").simulate("click", { preventDefault: () => {}, }); @@ -66,18 +90,7 @@ describe("", () => { "OurSponsorsAndYourPrivacy", ]; - const optionsWithReporting = [ - "BlockAdUrl", - "ReportAd", - "ManageSponsoredContent", - "OurSponsorsAndYourPrivacy", - ]; - - const expectedOptions = reportPref - ? optionsWithReporting - : linkMenuOptions; - - assert.deepEqual(linkMenuProps.options, expectedOptions); + assert.deepEqual(linkMenuProps.options, linkMenuOptions); }); }); }); diff --git a/browser/locales/l10n-changesets.json b/browser/locales/l10n-changesets.json index 4727347b2a6..567d2e10eb7 100644 --- a/browser/locales/l10n-changesets.json +++ b/browser/locales/l10n-changesets.json @@ -17,7 +17,7 @@ "win64-aarch64-devedition", "win64-devedition" ], - "revision": "22ddd09d5b7bf939862d445984bc2c3095dc2503" + "revision": "467538f140e3a848feafd5eba60654350dcce57f" }, "af": { "pin": false, @@ -37,7 +37,7 @@ "win64-aarch64-devedition", "win64-devedition" ], - "revision": "22ddd09d5b7bf939862d445984bc2c3095dc2503" + "revision": "467538f140e3a848feafd5eba60654350dcce57f" }, "an": { "pin": false, @@ -57,7 +57,7 @@ "win64-aarch64-devedition", "win64-devedition" ], - "revision": "22ddd09d5b7bf939862d445984bc2c3095dc2503" + "revision": "467538f140e3a848feafd5eba60654350dcce57f" }, "ar": { "pin": false, @@ -77,7 +77,7 @@ "win64-aarch64-devedition", "win64-devedition" ], - "revision": "22ddd09d5b7bf939862d445984bc2c3095dc2503" + "revision": "467538f140e3a848feafd5eba60654350dcce57f" }, "ast": { "pin": false, @@ -97,7 +97,7 @@ "win64-aarch64-devedition", "win64-devedition" ], - "revision": "22ddd09d5b7bf939862d445984bc2c3095dc2503" + "revision": "467538f140e3a848feafd5eba60654350dcce57f" }, "az": { "pin": false, @@ -117,7 +117,7 @@ "win64-aarch64-devedition", "win64-devedition" ], - "revision": "22ddd09d5b7bf939862d445984bc2c3095dc2503" + "revision": "467538f140e3a848feafd5eba60654350dcce57f" }, "be": { "pin": false, @@ -137,7 +137,7 @@ "win64-aarch64-devedition", "win64-devedition" ], - "revision": "22ddd09d5b7bf939862d445984bc2c3095dc2503" + "revision": "467538f140e3a848feafd5eba60654350dcce57f" }, "bg": { "pin": false, @@ -157,7 +157,7 @@ "win64-aarch64-devedition", "win64-devedition" ], - "revision": "22ddd09d5b7bf939862d445984bc2c3095dc2503" + "revision": "467538f140e3a848feafd5eba60654350dcce57f" }, "bn": { "pin": false, @@ -177,7 +177,7 @@ "win64-aarch64-devedition", "win64-devedition" ], - "revision": "22ddd09d5b7bf939862d445984bc2c3095dc2503" + "revision": "467538f140e3a848feafd5eba60654350dcce57f" }, "bo": { "pin": false, @@ -197,7 +197,7 @@ "win64-aarch64-devedition", "win64-devedition" ], - "revision": "22ddd09d5b7bf939862d445984bc2c3095dc2503" + "revision": "467538f140e3a848feafd5eba60654350dcce57f" }, "br": { "pin": false, @@ -217,7 +217,7 @@ "win64-aarch64-devedition", "win64-devedition" ], - "revision": "22ddd09d5b7bf939862d445984bc2c3095dc2503" + "revision": "467538f140e3a848feafd5eba60654350dcce57f" }, "brx": { "pin": false, @@ -237,7 +237,7 @@ "win64-aarch64-devedition", "win64-devedition" ], - "revision": "22ddd09d5b7bf939862d445984bc2c3095dc2503" + "revision": "467538f140e3a848feafd5eba60654350dcce57f" }, "bs": { "pin": false, @@ -257,7 +257,7 @@ "win64-aarch64-devedition", "win64-devedition" ], - "revision": "22ddd09d5b7bf939862d445984bc2c3095dc2503" + "revision": "467538f140e3a848feafd5eba60654350dcce57f" }, "ca": { "pin": false, @@ -277,7 +277,7 @@ "win64-aarch64-devedition", "win64-devedition" ], - "revision": "22ddd09d5b7bf939862d445984bc2c3095dc2503" + "revision": "467538f140e3a848feafd5eba60654350dcce57f" }, "ca-valencia": { "pin": false, @@ -297,7 +297,7 @@ "win64-aarch64-devedition", "win64-devedition" ], - "revision": "22ddd09d5b7bf939862d445984bc2c3095dc2503" + "revision": "467538f140e3a848feafd5eba60654350dcce57f" }, "cak": { "pin": false, @@ -317,7 +317,7 @@ "win64-aarch64-devedition", "win64-devedition" ], - "revision": "22ddd09d5b7bf939862d445984bc2c3095dc2503" + "revision": "467538f140e3a848feafd5eba60654350dcce57f" }, "ckb": { "pin": false, @@ -337,7 +337,7 @@ "win64-aarch64-devedition", "win64-devedition" ], - "revision": "22ddd09d5b7bf939862d445984bc2c3095dc2503" + "revision": "467538f140e3a848feafd5eba60654350dcce57f" }, "cs": { "pin": false, @@ -357,7 +357,7 @@ "win64-aarch64-devedition", "win64-devedition" ], - "revision": "22ddd09d5b7bf939862d445984bc2c3095dc2503" + "revision": "467538f140e3a848feafd5eba60654350dcce57f" }, "cy": { "pin": false, @@ -377,7 +377,7 @@ "win64-aarch64-devedition", "win64-devedition" ], - "revision": "22ddd09d5b7bf939862d445984bc2c3095dc2503" + "revision": "467538f140e3a848feafd5eba60654350dcce57f" }, "da": { "pin": false, @@ -397,7 +397,7 @@ "win64-aarch64-devedition", "win64-devedition" ], - "revision": "22ddd09d5b7bf939862d445984bc2c3095dc2503" + "revision": "467538f140e3a848feafd5eba60654350dcce57f" }, "de": { "pin": false, @@ -417,7 +417,7 @@ "win64-aarch64-devedition", "win64-devedition" ], - "revision": "22ddd09d5b7bf939862d445984bc2c3095dc2503" + "revision": "467538f140e3a848feafd5eba60654350dcce57f" }, "dsb": { "pin": false, @@ -437,7 +437,7 @@ "win64-aarch64-devedition", "win64-devedition" ], - "revision": "22ddd09d5b7bf939862d445984bc2c3095dc2503" + "revision": "467538f140e3a848feafd5eba60654350dcce57f" }, "el": { "pin": false, @@ -457,7 +457,7 @@ "win64-aarch64-devedition", "win64-devedition" ], - "revision": "22ddd09d5b7bf939862d445984bc2c3095dc2503" + "revision": "467538f140e3a848feafd5eba60654350dcce57f" }, "en-CA": { "pin": false, @@ -477,7 +477,7 @@ "win64-aarch64-devedition", "win64-devedition" ], - "revision": "22ddd09d5b7bf939862d445984bc2c3095dc2503" + "revision": "467538f140e3a848feafd5eba60654350dcce57f" }, "en-GB": { "pin": false, @@ -497,7 +497,7 @@ "win64-aarch64-devedition", "win64-devedition" ], - "revision": "22ddd09d5b7bf939862d445984bc2c3095dc2503" + "revision": "467538f140e3a848feafd5eba60654350dcce57f" }, "eo": { "pin": false, @@ -517,7 +517,7 @@ "win64-aarch64-devedition", "win64-devedition" ], - "revision": "22ddd09d5b7bf939862d445984bc2c3095dc2503" + "revision": "467538f140e3a848feafd5eba60654350dcce57f" }, "es-AR": { "pin": false, @@ -537,7 +537,7 @@ "win64-aarch64-devedition", "win64-devedition" ], - "revision": "22ddd09d5b7bf939862d445984bc2c3095dc2503" + "revision": "467538f140e3a848feafd5eba60654350dcce57f" }, "es-CL": { "pin": false, @@ -557,7 +557,7 @@ "win64-aarch64-devedition", "win64-devedition" ], - "revision": "22ddd09d5b7bf939862d445984bc2c3095dc2503" + "revision": "467538f140e3a848feafd5eba60654350dcce57f" }, "es-ES": { "pin": false, @@ -577,7 +577,7 @@ "win64-aarch64-devedition", "win64-devedition" ], - "revision": "22ddd09d5b7bf939862d445984bc2c3095dc2503" + "revision": "467538f140e3a848feafd5eba60654350dcce57f" }, "es-MX": { "pin": false, @@ -597,7 +597,7 @@ "win64-aarch64-devedition", "win64-devedition" ], - "revision": "22ddd09d5b7bf939862d445984bc2c3095dc2503" + "revision": "467538f140e3a848feafd5eba60654350dcce57f" }, "et": { "pin": false, @@ -617,7 +617,7 @@ "win64-aarch64-devedition", "win64-devedition" ], - "revision": "22ddd09d5b7bf939862d445984bc2c3095dc2503" + "revision": "467538f140e3a848feafd5eba60654350dcce57f" }, "eu": { "pin": false, @@ -637,7 +637,7 @@ "win64-aarch64-devedition", "win64-devedition" ], - "revision": "22ddd09d5b7bf939862d445984bc2c3095dc2503" + "revision": "467538f140e3a848feafd5eba60654350dcce57f" }, "fa": { "pin": false, @@ -657,7 +657,7 @@ "win64-aarch64-devedition", "win64-devedition" ], - "revision": "22ddd09d5b7bf939862d445984bc2c3095dc2503" + "revision": "467538f140e3a848feafd5eba60654350dcce57f" }, "ff": { "pin": false, @@ -677,7 +677,7 @@ "win64-aarch64-devedition", "win64-devedition" ], - "revision": "22ddd09d5b7bf939862d445984bc2c3095dc2503" + "revision": "467538f140e3a848feafd5eba60654350dcce57f" }, "fi": { "pin": false, @@ -697,7 +697,7 @@ "win64-aarch64-devedition", "win64-devedition" ], - "revision": "22ddd09d5b7bf939862d445984bc2c3095dc2503" + "revision": "467538f140e3a848feafd5eba60654350dcce57f" }, "fr": { "pin": false, @@ -717,7 +717,7 @@ "win64-aarch64-devedition", "win64-devedition" ], - "revision": "22ddd09d5b7bf939862d445984bc2c3095dc2503" + "revision": "467538f140e3a848feafd5eba60654350dcce57f" }, "fur": { "pin": false, @@ -737,7 +737,7 @@ "win64-aarch64-devedition", "win64-devedition" ], - "revision": "22ddd09d5b7bf939862d445984bc2c3095dc2503" + "revision": "467538f140e3a848feafd5eba60654350dcce57f" }, "fy-NL": { "pin": false, @@ -757,7 +757,7 @@ "win64-aarch64-devedition", "win64-devedition" ], - "revision": "22ddd09d5b7bf939862d445984bc2c3095dc2503" + "revision": "467538f140e3a848feafd5eba60654350dcce57f" }, "ga-IE": { "pin": false, @@ -777,7 +777,7 @@ "win64-aarch64-devedition", "win64-devedition" ], - "revision": "22ddd09d5b7bf939862d445984bc2c3095dc2503" + "revision": "467538f140e3a848feafd5eba60654350dcce57f" }, "gd": { "pin": false, @@ -797,7 +797,7 @@ "win64-aarch64-devedition", "win64-devedition" ], - "revision": "22ddd09d5b7bf939862d445984bc2c3095dc2503" + "revision": "467538f140e3a848feafd5eba60654350dcce57f" }, "gl": { "pin": false, @@ -817,7 +817,7 @@ "win64-aarch64-devedition", "win64-devedition" ], - "revision": "22ddd09d5b7bf939862d445984bc2c3095dc2503" + "revision": "467538f140e3a848feafd5eba60654350dcce57f" }, "gn": { "pin": false, @@ -837,7 +837,7 @@ "win64-aarch64-devedition", "win64-devedition" ], - "revision": "22ddd09d5b7bf939862d445984bc2c3095dc2503" + "revision": "467538f140e3a848feafd5eba60654350dcce57f" }, "gu-IN": { "pin": false, @@ -857,7 +857,7 @@ "win64-aarch64-devedition", "win64-devedition" ], - "revision": "22ddd09d5b7bf939862d445984bc2c3095dc2503" + "revision": "467538f140e3a848feafd5eba60654350dcce57f" }, "he": { "pin": false, @@ -877,7 +877,7 @@ "win64-aarch64-devedition", "win64-devedition" ], - "revision": "22ddd09d5b7bf939862d445984bc2c3095dc2503" + "revision": "467538f140e3a848feafd5eba60654350dcce57f" }, "hi-IN": { "pin": false, @@ -897,7 +897,7 @@ "win64-aarch64-devedition", "win64-devedition" ], - "revision": "22ddd09d5b7bf939862d445984bc2c3095dc2503" + "revision": "467538f140e3a848feafd5eba60654350dcce57f" }, "hr": { "pin": false, @@ -917,7 +917,7 @@ "win64-aarch64-devedition", "win64-devedition" ], - "revision": "22ddd09d5b7bf939862d445984bc2c3095dc2503" + "revision": "467538f140e3a848feafd5eba60654350dcce57f" }, "hsb": { "pin": false, @@ -937,7 +937,7 @@ "win64-aarch64-devedition", "win64-devedition" ], - "revision": "22ddd09d5b7bf939862d445984bc2c3095dc2503" + "revision": "467538f140e3a848feafd5eba60654350dcce57f" }, "hu": { "pin": false, @@ -957,7 +957,7 @@ "win64-aarch64-devedition", "win64-devedition" ], - "revision": "22ddd09d5b7bf939862d445984bc2c3095dc2503" + "revision": "467538f140e3a848feafd5eba60654350dcce57f" }, "hy-AM": { "pin": false, @@ -977,7 +977,7 @@ "win64-aarch64-devedition", "win64-devedition" ], - "revision": "22ddd09d5b7bf939862d445984bc2c3095dc2503" + "revision": "467538f140e3a848feafd5eba60654350dcce57f" }, "hye": { "pin": false, @@ -997,7 +997,7 @@ "win64-aarch64-devedition", "win64-devedition" ], - "revision": "22ddd09d5b7bf939862d445984bc2c3095dc2503" + "revision": "467538f140e3a848feafd5eba60654350dcce57f" }, "ia": { "pin": false, @@ -1017,7 +1017,7 @@ "win64-aarch64-devedition", "win64-devedition" ], - "revision": "22ddd09d5b7bf939862d445984bc2c3095dc2503" + "revision": "467538f140e3a848feafd5eba60654350dcce57f" }, "id": { "pin": false, @@ -1037,7 +1037,7 @@ "win64-aarch64-devedition", "win64-devedition" ], - "revision": "22ddd09d5b7bf939862d445984bc2c3095dc2503" + "revision": "467538f140e3a848feafd5eba60654350dcce57f" }, "is": { "pin": false, @@ -1057,7 +1057,7 @@ "win64-aarch64-devedition", "win64-devedition" ], - "revision": "22ddd09d5b7bf939862d445984bc2c3095dc2503" + "revision": "467538f140e3a848feafd5eba60654350dcce57f" }, "it": { "pin": false, @@ -1077,7 +1077,7 @@ "win64-aarch64-devedition", "win64-devedition" ], - "revision": "22ddd09d5b7bf939862d445984bc2c3095dc2503" + "revision": "467538f140e3a848feafd5eba60654350dcce57f" }, "ja": { "pin": false, @@ -1095,7 +1095,7 @@ "win64-aarch64-devedition", "win64-devedition" ], - "revision": "22ddd09d5b7bf939862d445984bc2c3095dc2503" + "revision": "467538f140e3a848feafd5eba60654350dcce57f" }, "ja-JP-mac": { "pin": false, @@ -1103,7 +1103,7 @@ "macosx64", "macosx64-devedition" ], - "revision": "22ddd09d5b7bf939862d445984bc2c3095dc2503" + "revision": "467538f140e3a848feafd5eba60654350dcce57f" }, "ka": { "pin": false, @@ -1123,7 +1123,7 @@ "win64-aarch64-devedition", "win64-devedition" ], - "revision": "22ddd09d5b7bf939862d445984bc2c3095dc2503" + "revision": "467538f140e3a848feafd5eba60654350dcce57f" }, "kab": { "pin": false, @@ -1143,7 +1143,7 @@ "win64-aarch64-devedition", "win64-devedition" ], - "revision": "22ddd09d5b7bf939862d445984bc2c3095dc2503" + "revision": "467538f140e3a848feafd5eba60654350dcce57f" }, "kk": { "pin": false, @@ -1163,7 +1163,7 @@ "win64-aarch64-devedition", "win64-devedition" ], - "revision": "22ddd09d5b7bf939862d445984bc2c3095dc2503" + "revision": "467538f140e3a848feafd5eba60654350dcce57f" }, "km": { "pin": false, @@ -1183,7 +1183,7 @@ "win64-aarch64-devedition", "win64-devedition" ], - "revision": "22ddd09d5b7bf939862d445984bc2c3095dc2503" + "revision": "467538f140e3a848feafd5eba60654350dcce57f" }, "kn": { "pin": false, @@ -1203,7 +1203,7 @@ "win64-aarch64-devedition", "win64-devedition" ], - "revision": "22ddd09d5b7bf939862d445984bc2c3095dc2503" + "revision": "467538f140e3a848feafd5eba60654350dcce57f" }, "ko": { "pin": false, @@ -1223,7 +1223,7 @@ "win64-aarch64-devedition", "win64-devedition" ], - "revision": "22ddd09d5b7bf939862d445984bc2c3095dc2503" + "revision": "467538f140e3a848feafd5eba60654350dcce57f" }, "lij": { "pin": false, @@ -1243,7 +1243,7 @@ "win64-aarch64-devedition", "win64-devedition" ], - "revision": "22ddd09d5b7bf939862d445984bc2c3095dc2503" + "revision": "467538f140e3a848feafd5eba60654350dcce57f" }, "lo": { "pin": false, @@ -1263,7 +1263,7 @@ "win64-aarch64-devedition", "win64-devedition" ], - "revision": "22ddd09d5b7bf939862d445984bc2c3095dc2503" + "revision": "467538f140e3a848feafd5eba60654350dcce57f" }, "lt": { "pin": false, @@ -1283,7 +1283,7 @@ "win64-aarch64-devedition", "win64-devedition" ], - "revision": "22ddd09d5b7bf939862d445984bc2c3095dc2503" + "revision": "467538f140e3a848feafd5eba60654350dcce57f" }, "ltg": { "pin": false, @@ -1303,7 +1303,7 @@ "win64-aarch64-devedition", "win64-devedition" ], - "revision": "22ddd09d5b7bf939862d445984bc2c3095dc2503" + "revision": "467538f140e3a848feafd5eba60654350dcce57f" }, "lv": { "pin": false, @@ -1323,7 +1323,7 @@ "win64-aarch64-devedition", "win64-devedition" ], - "revision": "22ddd09d5b7bf939862d445984bc2c3095dc2503" + "revision": "467538f140e3a848feafd5eba60654350dcce57f" }, "meh": { "pin": false, @@ -1343,7 +1343,7 @@ "win64-aarch64-devedition", "win64-devedition" ], - "revision": "22ddd09d5b7bf939862d445984bc2c3095dc2503" + "revision": "467538f140e3a848feafd5eba60654350dcce57f" }, "mk": { "pin": false, @@ -1363,7 +1363,7 @@ "win64-aarch64-devedition", "win64-devedition" ], - "revision": "22ddd09d5b7bf939862d445984bc2c3095dc2503" + "revision": "467538f140e3a848feafd5eba60654350dcce57f" }, "ml": { "pin": false, @@ -1383,7 +1383,7 @@ "win64-aarch64-devedition", "win64-devedition" ], - "revision": "22ddd09d5b7bf939862d445984bc2c3095dc2503" + "revision": "467538f140e3a848feafd5eba60654350dcce57f" }, "mr": { "pin": false, @@ -1403,7 +1403,7 @@ "win64-aarch64-devedition", "win64-devedition" ], - "revision": "22ddd09d5b7bf939862d445984bc2c3095dc2503" + "revision": "467538f140e3a848feafd5eba60654350dcce57f" }, "ms": { "pin": false, @@ -1423,7 +1423,7 @@ "win64-aarch64-devedition", "win64-devedition" ], - "revision": "22ddd09d5b7bf939862d445984bc2c3095dc2503" + "revision": "467538f140e3a848feafd5eba60654350dcce57f" }, "my": { "pin": false, @@ -1443,7 +1443,7 @@ "win64-aarch64-devedition", "win64-devedition" ], - "revision": "22ddd09d5b7bf939862d445984bc2c3095dc2503" + "revision": "467538f140e3a848feafd5eba60654350dcce57f" }, "nb-NO": { "pin": false, @@ -1463,7 +1463,7 @@ "win64-aarch64-devedition", "win64-devedition" ], - "revision": "22ddd09d5b7bf939862d445984bc2c3095dc2503" + "revision": "467538f140e3a848feafd5eba60654350dcce57f" }, "ne-NP": { "pin": false, @@ -1483,7 +1483,7 @@ "win64-aarch64-devedition", "win64-devedition" ], - "revision": "22ddd09d5b7bf939862d445984bc2c3095dc2503" + "revision": "467538f140e3a848feafd5eba60654350dcce57f" }, "nl": { "pin": false, @@ -1503,7 +1503,7 @@ "win64-aarch64-devedition", "win64-devedition" ], - "revision": "22ddd09d5b7bf939862d445984bc2c3095dc2503" + "revision": "467538f140e3a848feafd5eba60654350dcce57f" }, "nn-NO": { "pin": false, @@ -1523,7 +1523,7 @@ "win64-aarch64-devedition", "win64-devedition" ], - "revision": "22ddd09d5b7bf939862d445984bc2c3095dc2503" + "revision": "467538f140e3a848feafd5eba60654350dcce57f" }, "oc": { "pin": false, @@ -1543,7 +1543,7 @@ "win64-aarch64-devedition", "win64-devedition" ], - "revision": "22ddd09d5b7bf939862d445984bc2c3095dc2503" + "revision": "467538f140e3a848feafd5eba60654350dcce57f" }, "pa-IN": { "pin": false, @@ -1563,7 +1563,7 @@ "win64-aarch64-devedition", "win64-devedition" ], - "revision": "22ddd09d5b7bf939862d445984bc2c3095dc2503" + "revision": "467538f140e3a848feafd5eba60654350dcce57f" }, "pl": { "pin": false, @@ -1583,7 +1583,7 @@ "win64-aarch64-devedition", "win64-devedition" ], - "revision": "22ddd09d5b7bf939862d445984bc2c3095dc2503" + "revision": "467538f140e3a848feafd5eba60654350dcce57f" }, "pt-BR": { "pin": false, @@ -1603,7 +1603,7 @@ "win64-aarch64-devedition", "win64-devedition" ], - "revision": "22ddd09d5b7bf939862d445984bc2c3095dc2503" + "revision": "467538f140e3a848feafd5eba60654350dcce57f" }, "pt-PT": { "pin": false, @@ -1623,7 +1623,7 @@ "win64-aarch64-devedition", "win64-devedition" ], - "revision": "22ddd09d5b7bf939862d445984bc2c3095dc2503" + "revision": "467538f140e3a848feafd5eba60654350dcce57f" }, "rm": { "pin": false, @@ -1643,7 +1643,7 @@ "win64-aarch64-devedition", "win64-devedition" ], - "revision": "22ddd09d5b7bf939862d445984bc2c3095dc2503" + "revision": "467538f140e3a848feafd5eba60654350dcce57f" }, "ro": { "pin": false, @@ -1663,7 +1663,7 @@ "win64-aarch64-devedition", "win64-devedition" ], - "revision": "22ddd09d5b7bf939862d445984bc2c3095dc2503" + "revision": "467538f140e3a848feafd5eba60654350dcce57f" }, "ru": { "pin": false, @@ -1683,7 +1683,7 @@ "win64-aarch64-devedition", "win64-devedition" ], - "revision": "22ddd09d5b7bf939862d445984bc2c3095dc2503" + "revision": "467538f140e3a848feafd5eba60654350dcce57f" }, "sat": { "pin": false, @@ -1703,7 +1703,7 @@ "win64-aarch64-devedition", "win64-devedition" ], - "revision": "22ddd09d5b7bf939862d445984bc2c3095dc2503" + "revision": "467538f140e3a848feafd5eba60654350dcce57f" }, "sc": { "pin": false, @@ -1723,7 +1723,7 @@ "win64-aarch64-devedition", "win64-devedition" ], - "revision": "22ddd09d5b7bf939862d445984bc2c3095dc2503" + "revision": "467538f140e3a848feafd5eba60654350dcce57f" }, "scn": { "pin": false, @@ -1743,7 +1743,7 @@ "win64-aarch64-devedition", "win64-devedition" ], - "revision": "22ddd09d5b7bf939862d445984bc2c3095dc2503" + "revision": "467538f140e3a848feafd5eba60654350dcce57f" }, "sco": { "pin": false, @@ -1763,7 +1763,7 @@ "win64-aarch64-devedition", "win64-devedition" ], - "revision": "22ddd09d5b7bf939862d445984bc2c3095dc2503" + "revision": "467538f140e3a848feafd5eba60654350dcce57f" }, "si": { "pin": false, @@ -1783,7 +1783,7 @@ "win64-aarch64-devedition", "win64-devedition" ], - "revision": "22ddd09d5b7bf939862d445984bc2c3095dc2503" + "revision": "467538f140e3a848feafd5eba60654350dcce57f" }, "sk": { "pin": false, @@ -1803,7 +1803,7 @@ "win64-aarch64-devedition", "win64-devedition" ], - "revision": "22ddd09d5b7bf939862d445984bc2c3095dc2503" + "revision": "467538f140e3a848feafd5eba60654350dcce57f" }, "skr": { "pin": false, @@ -1823,7 +1823,7 @@ "win64-aarch64-devedition", "win64-devedition" ], - "revision": "22ddd09d5b7bf939862d445984bc2c3095dc2503" + "revision": "467538f140e3a848feafd5eba60654350dcce57f" }, "sl": { "pin": false, @@ -1843,7 +1843,7 @@ "win64-aarch64-devedition", "win64-devedition" ], - "revision": "22ddd09d5b7bf939862d445984bc2c3095dc2503" + "revision": "467538f140e3a848feafd5eba60654350dcce57f" }, "son": { "pin": false, @@ -1863,7 +1863,7 @@ "win64-aarch64-devedition", "win64-devedition" ], - "revision": "22ddd09d5b7bf939862d445984bc2c3095dc2503" + "revision": "467538f140e3a848feafd5eba60654350dcce57f" }, "sq": { "pin": false, @@ -1883,7 +1883,7 @@ "win64-aarch64-devedition", "win64-devedition" ], - "revision": "22ddd09d5b7bf939862d445984bc2c3095dc2503" + "revision": "467538f140e3a848feafd5eba60654350dcce57f" }, "sr": { "pin": false, @@ -1903,7 +1903,7 @@ "win64-aarch64-devedition", "win64-devedition" ], - "revision": "22ddd09d5b7bf939862d445984bc2c3095dc2503" + "revision": "467538f140e3a848feafd5eba60654350dcce57f" }, "sv-SE": { "pin": false, @@ -1923,7 +1923,7 @@ "win64-aarch64-devedition", "win64-devedition" ], - "revision": "22ddd09d5b7bf939862d445984bc2c3095dc2503" + "revision": "467538f140e3a848feafd5eba60654350dcce57f" }, "szl": { "pin": false, @@ -1943,7 +1943,7 @@ "win64-aarch64-devedition", "win64-devedition" ], - "revision": "22ddd09d5b7bf939862d445984bc2c3095dc2503" + "revision": "467538f140e3a848feafd5eba60654350dcce57f" }, "ta": { "pin": false, @@ -1963,7 +1963,7 @@ "win64-aarch64-devedition", "win64-devedition" ], - "revision": "22ddd09d5b7bf939862d445984bc2c3095dc2503" + "revision": "467538f140e3a848feafd5eba60654350dcce57f" }, "te": { "pin": false, @@ -1983,7 +1983,7 @@ "win64-aarch64-devedition", "win64-devedition" ], - "revision": "22ddd09d5b7bf939862d445984bc2c3095dc2503" + "revision": "467538f140e3a848feafd5eba60654350dcce57f" }, "tg": { "pin": false, @@ -2003,7 +2003,7 @@ "win64-aarch64-devedition", "win64-devedition" ], - "revision": "22ddd09d5b7bf939862d445984bc2c3095dc2503" + "revision": "467538f140e3a848feafd5eba60654350dcce57f" }, "th": { "pin": false, @@ -2023,7 +2023,7 @@ "win64-aarch64-devedition", "win64-devedition" ], - "revision": "22ddd09d5b7bf939862d445984bc2c3095dc2503" + "revision": "467538f140e3a848feafd5eba60654350dcce57f" }, "tl": { "pin": false, @@ -2043,7 +2043,7 @@ "win64-aarch64-devedition", "win64-devedition" ], - "revision": "22ddd09d5b7bf939862d445984bc2c3095dc2503" + "revision": "467538f140e3a848feafd5eba60654350dcce57f" }, "tr": { "pin": false, @@ -2063,7 +2063,7 @@ "win64-aarch64-devedition", "win64-devedition" ], - "revision": "22ddd09d5b7bf939862d445984bc2c3095dc2503" + "revision": "467538f140e3a848feafd5eba60654350dcce57f" }, "trs": { "pin": false, @@ -2083,7 +2083,7 @@ "win64-aarch64-devedition", "win64-devedition" ], - "revision": "22ddd09d5b7bf939862d445984bc2c3095dc2503" + "revision": "467538f140e3a848feafd5eba60654350dcce57f" }, "uk": { "pin": false, @@ -2103,7 +2103,7 @@ "win64-aarch64-devedition", "win64-devedition" ], - "revision": "22ddd09d5b7bf939862d445984bc2c3095dc2503" + "revision": "467538f140e3a848feafd5eba60654350dcce57f" }, "ur": { "pin": false, @@ -2123,7 +2123,7 @@ "win64-aarch64-devedition", "win64-devedition" ], - "revision": "22ddd09d5b7bf939862d445984bc2c3095dc2503" + "revision": "467538f140e3a848feafd5eba60654350dcce57f" }, "uz": { "pin": false, @@ -2143,7 +2143,7 @@ "win64-aarch64-devedition", "win64-devedition" ], - "revision": "22ddd09d5b7bf939862d445984bc2c3095dc2503" + "revision": "467538f140e3a848feafd5eba60654350dcce57f" }, "vi": { "pin": false, @@ -2163,7 +2163,7 @@ "win64-aarch64-devedition", "win64-devedition" ], - "revision": "22ddd09d5b7bf939862d445984bc2c3095dc2503" + "revision": "467538f140e3a848feafd5eba60654350dcce57f" }, "wo": { "pin": false, @@ -2183,7 +2183,7 @@ "win64-aarch64-devedition", "win64-devedition" ], - "revision": "22ddd09d5b7bf939862d445984bc2c3095dc2503" + "revision": "467538f140e3a848feafd5eba60654350dcce57f" }, "xh": { "pin": false, @@ -2203,7 +2203,7 @@ "win64-aarch64-devedition", "win64-devedition" ], - "revision": "22ddd09d5b7bf939862d445984bc2c3095dc2503" + "revision": "467538f140e3a848feafd5eba60654350dcce57f" }, "zh-CN": { "pin": false, @@ -2223,7 +2223,7 @@ "win64-aarch64-devedition", "win64-devedition" ], - "revision": "22ddd09d5b7bf939862d445984bc2c3095dc2503" + "revision": "467538f140e3a848feafd5eba60654350dcce57f" }, "zh-TW": { "pin": false, @@ -2243,6 +2243,6 @@ "win64-aarch64-devedition", "win64-devedition" ], - "revision": "22ddd09d5b7bf939862d445984bc2c3095dc2503" + "revision": "467538f140e3a848feafd5eba60654350dcce57f" } } \ No newline at end of file diff --git a/dom/base/nsGlobalWindowInner.cpp b/dom/base/nsGlobalWindowInner.cpp index 7b5f63b61c5..05cdc124858 100644 --- a/dom/base/nsGlobalWindowInner.cpp +++ b/dom/base/nsGlobalWindowInner.cpp @@ -2849,7 +2849,7 @@ void nsPIDOMWindowInner::TryToCacheTopInnerWindow() { } } -void nsPIDOMWindowInner::UpdateActiveIndexedDBDatabaseCount(int32_t aDelta) { +void nsGlobalWindowInner::UpdateActiveIndexedDBDatabaseCount(int32_t aDelta) { MOZ_ASSERT(NS_IsMainThread()); if (aDelta == 0) { @@ -2865,7 +2865,7 @@ void nsPIDOMWindowInner::UpdateActiveIndexedDBDatabaseCount(int32_t aDelta) { counter += aDelta; } -bool nsGlobalWindowInner::HasActiveIndexedDBDatabases() { +bool nsGlobalWindowInner::HasActiveIndexedDBDatabases() const { MOZ_ASSERT(NS_IsMainThread()); return mTopInnerWindow ? mTopInnerWindow->mNumOfIndexedDBDatabases > 0 diff --git a/dom/base/nsGlobalWindowInner.h b/dom/base/nsGlobalWindowInner.h index effa95d67df..9e6ae30c93e 100644 --- a/dom/base/nsGlobalWindowInner.h +++ b/dom/base/nsGlobalWindowInner.h @@ -335,12 +335,15 @@ class nsGlobalWindowInner final : public mozilla::dom::EventTarget, void Freeze(bool aIncludeSubWindows = true); void Thaw(bool aIncludeSubWindows = true); virtual bool IsFrozen() const override; - virtual bool HasActiveIndexedDBDatabases() override; + virtual bool HasActiveIndexedDBDatabases() const override; virtual bool HasActivePeerConnections() override; virtual bool HasOpenWebSockets() const override; virtual bool HasScheduledNormalOrHighPriorityWebTasks() const override; void SyncStateFromParentWindow(); virtual void UpdateWebSocketCount(int32_t aDelta) override; + // Increase/Decrease the number of active IndexedDB databases for the + // decision making of timeout-throttling. + virtual void UpdateActiveIndexedDBDatabaseCount(int32_t aDelta) override; // Called on the current inner window of a browsing context when its // background state changes according to selected tab or visibility of the diff --git a/dom/base/nsIGlobalObject.h b/dom/base/nsIGlobalObject.h index 281e41fe1c2..33419699591 100644 --- a/dom/base/nsIGlobalObject.h +++ b/dom/base/nsIGlobalObject.h @@ -336,7 +336,7 @@ class nsIGlobalObject : public nsISupports { } // Return true if there is any active IndexedDB databases which could block // timeout-throttling. - virtual bool HasActiveIndexedDBDatabases() { return false; } + virtual bool HasActiveIndexedDBDatabases() const { return false; } /** * Check whether the active peer connection count is non-zero. */ @@ -352,6 +352,9 @@ class nsIGlobalObject : public nsISupports { } virtual void UpdateWebSocketCount(int32_t aDelta) {}; + // Increase/Decrease the number of active IndexedDB databases for the + // decision making of timeout-throttling. + virtual void UpdateActiveIndexedDBDatabaseCount(int32_t aDelta) {} /** * Report a localized error message to the error console. Currently this diff --git a/dom/base/nsPIDOMWindow.h b/dom/base/nsPIDOMWindow.h index b8e612086ad..53c53254799 100644 --- a/dom/base/nsPIDOMWindow.h +++ b/dom/base/nsPIDOMWindow.h @@ -340,10 +340,6 @@ class nsPIDOMWindowInner : public mozIDOMWindow { // indexedDB counters. void TryToCacheTopInnerWindow(); - // Increase/Decrease the number of active IndexedDB databases for the - // decision making of timeout-throttling. - void UpdateActiveIndexedDBDatabaseCount(int32_t aDelta); - mozilla::Maybe GetClientInfo() const; mozilla::Maybe GetClientState() const; mozilla::Maybe GetController() const; diff --git a/dom/chrome-webidl/WindowGlobalActors.webidl b/dom/chrome-webidl/WindowGlobalActors.webidl index ead070a2fec..54b47719782 100644 --- a/dom/chrome-webidl/WindowGlobalActors.webidl +++ b/dom/chrome-webidl/WindowGlobalActors.webidl @@ -178,6 +178,7 @@ interface WindowGlobalChild { readonly attribute boolean isInProcess; readonly attribute BrowsingContext browsingContext; readonly attribute WindowContext windowContext; + readonly attribute WindowProxy? contentWindow; readonly attribute boolean isCurrentGlobal; diff --git a/dom/indexedDB/IDBFactory.cpp b/dom/indexedDB/IDBFactory.cpp index 860b14e5720..a17d45fdac2 100644 --- a/dom/indexedDB/IDBFactory.cpp +++ b/dom/indexedDB/IDBFactory.cpp @@ -404,8 +404,8 @@ void IDBFactory::UpdateActiveDatabaseCount(int32_t aDelta) { (mActiveDatabaseCount + aDelta) < mActiveDatabaseCount); mActiveDatabaseCount += aDelta; - if (nsGlobalWindowInner* win = GetOwnerWindow()) { - win->UpdateActiveIndexedDBDatabaseCount(aDelta); + if (nsIGlobalObject* global = GetOwnerGlobal()) { + global->UpdateActiveIndexedDBDatabaseCount(aDelta); } } diff --git a/dom/ipc/WindowGlobalChild.cpp b/dom/ipc/WindowGlobalChild.cpp index 356f12605e1..de149d7126c 100644 --- a/dom/ipc/WindowGlobalChild.cpp +++ b/dom/ipc/WindowGlobalChild.cpp @@ -258,6 +258,13 @@ dom::BrowsingContext* WindowGlobalChild::BrowsingContext() { return mWindowContext->GetBrowsingContext(); } +Nullable WindowGlobalChild::GetContentWindow() { + if (IsCurrentGlobal()) { + return WindowProxyHolder(BrowsingContext()); + } + return nullptr; +} + uint64_t WindowGlobalChild::InnerWindowId() { return mWindowContext->InnerWindowId(); } diff --git a/dom/ipc/WindowGlobalChild.h b/dom/ipc/WindowGlobalChild.h index b90f692ddcd..b5f207a5b9c 100644 --- a/dom/ipc/WindowGlobalChild.h +++ b/dom/ipc/WindowGlobalChild.h @@ -14,6 +14,7 @@ #include "nsWrapperCache.h" #include "mozilla/dom/Document.h" #include "mozilla/dom/WindowGlobalActor.h" +#include "mozilla/dom/WindowProxyHolder.h" class nsGlobalWindowInner; class nsDocShell; @@ -53,6 +54,8 @@ class WindowGlobalChild final : public WindowGlobalActor, dom::WindowContext* WindowContext() const { return mWindowContext; } nsGlobalWindowInner* GetWindowGlobal() const { return mWindowGlobal; } + Nullable GetContentWindow(); + // Has this actor been shut down bool IsClosed() { return !CanSend(); } void Destroy(); diff --git a/dom/quota/ActorsParent.cpp b/dom/quota/ActorsParent.cpp index 28d0936938d..7422444b83f 100644 --- a/dom/quota/ActorsParent.cpp +++ b/dom/quota/ActorsParent.cpp @@ -2699,12 +2699,7 @@ void QuotaManager::UpdateOriginAccessTime( MutexAutoUnlock autoUnlock(mQuotaMutex); - auto op = CreateSaveOriginAccessTimeOp(WrapMovingNotNullUnchecked(this), - aOriginMetadata, timestamp); - - RegisterNormalOriginOp(*op); - - op->RunImmediately(); + SaveOriginAccessTime(aOriginMetadata, timestamp); } } @@ -6589,6 +6584,21 @@ RefPtr QuotaManager::InitializeAllTemporaryOrigins() { return promise; } +RefPtr QuotaManager::SaveOriginAccessTime( + const OriginMetadata& aOriginMetadata, int64_t aTimestamp) { + AssertIsOnOwningThread(); + MOZ_ASSERT(aOriginMetadata.mPersistenceType != PERSISTENCE_TYPE_PERSISTENT); + + auto saveOriginAccessTimeOp = CreateSaveOriginAccessTimeOp( + WrapMovingNotNullUnchecked(this), aOriginMetadata, aTimestamp); + + RegisterNormalOriginOp(*saveOriginAccessTimeOp); + + saveOriginAccessTimeOp->RunImmediately(); + + return saveOriginAccessTimeOp->OnResults(); +} + RefPtr QuotaManager::GetUsage( bool aGetAll, RefPtr aOnCancelPromise) { AssertIsOnOwningThread(); diff --git a/dom/quota/OriginOperations.cpp b/dom/quota/OriginOperations.cpp index 6c997a13ce2..ffdd087aa7a 100644 --- a/dom/quota/OriginOperations.cpp +++ b/dom/quota/OriginOperations.cpp @@ -114,7 +114,7 @@ class FinalizeOriginEvictionOp : public OriginOperationBase { }; class SaveOriginAccessTimeOp - : public OpenStorageDirectoryHelper { + : public OpenStorageDirectoryHelper> { const OriginMetadata mOriginMetadata; int64_t mTimestamp; @@ -129,8 +129,6 @@ class SaveOriginAccessTimeOp AssertIsOnOwningThread(); } - NS_INLINE_DECL_REFCOUNTING(SaveOriginAccessTimeOp, override) - private: ~SaveOriginAccessTimeOp() = default; @@ -138,7 +136,7 @@ class SaveOriginAccessTimeOp virtual nsresult DoDirectoryWork(QuotaManager& aQuotaManager) override; - virtual void SendResults() override; + bool UnwrapResolveValue() override { return true; } void CloseDirectory() override; }; @@ -1006,7 +1004,7 @@ RefPtr CreateFinalizeOriginEvictionOp( std::move(aLocks)); } -RefPtr CreateSaveOriginAccessTimeOp( +RefPtr> CreateSaveOriginAccessTimeOp( MovingNotNull> aQuotaManager, const OriginMetadata& aOriginMetadata, int64_t aTimestamp) { return MakeRefPtr(std::move(aQuotaManager), @@ -1331,8 +1329,6 @@ nsresult SaveOriginAccessTimeOp::DoDirectoryWork(QuotaManager& aQuotaManager) { return NS_OK; } -void SaveOriginAccessTimeOp::SendResults() {} - void SaveOriginAccessTimeOp::CloseDirectory() { AssertIsOnOwningThread(); diff --git a/dom/quota/OriginOperations.h b/dom/quota/OriginOperations.h index dd3d1c54a44..50fdcd169e6 100644 --- a/dom/quota/OriginOperations.h +++ b/dom/quota/OriginOperations.h @@ -45,7 +45,7 @@ RefPtr CreateFinalizeOriginEvictionOp( MovingNotNull> aQuotaManager, nsTArray>&& aLocks); -RefPtr CreateSaveOriginAccessTimeOp( +RefPtr> CreateSaveOriginAccessTimeOp( MovingNotNull> aQuotaManager, const OriginMetadata& aOriginMetadata, int64_t aTimestamp); diff --git a/dom/quota/QuotaManager.h b/dom/quota/QuotaManager.h index 365b42ca20c..1d6beca4071 100644 --- a/dom/quota/QuotaManager.h +++ b/dom/quota/QuotaManager.h @@ -510,6 +510,9 @@ class QuotaManager final : public BackgroundThreadObject { public: RefPtr InitializeAllTemporaryOrigins(); + RefPtr SaveOriginAccessTime( + const OriginMetadata& aOriginMetadata, int64_t aTimestamp); + RefPtr GetUsage( bool aGetAll, RefPtr aOnCancelPromise = nullptr); diff --git a/dom/quota/test/gtest/TestQuotaManager.cpp b/dom/quota/test/gtest/TestQuotaManager.cpp index 16dacfa0b4e..a11af94c847 100644 --- a/dom/quota/test/gtest/TestQuotaManager.cpp +++ b/dom/quota/test/gtest/TestQuotaManager.cpp @@ -17,6 +17,7 @@ #include "mozilla/dom/quota/UniversalDirectoryLock.h" #include "mozilla/gtest/MozAssertions.h" #include "nsFmtString.h" +#include "prtime.h" #include "QuotaManagerDependencyFixture.h" #include "QuotaManagerTestHelpers.h" @@ -1989,6 +1990,39 @@ TEST_F(TestQuotaManager, ASSERT_NO_FATAL_FAILURE(ShutdownStorage()); } +// Tests the availability of SaveOriginAccessTime and verifies that calling it +// does not trigger temporary storage or origin initialization. +TEST_F(TestQuotaManager, SaveOriginAccessTime_Simple) { + auto testOriginMetadata = GetTestOriginMetadata(); + + ASSERT_NO_FATAL_FAILURE(ShutdownStorage()); + + ASSERT_NO_FATAL_FAILURE(AssertStorageNotInitialized()); + ASSERT_NO_FATAL_FAILURE(AssertTemporaryStorageNotInitialized()); + ASSERT_NO_FATAL_FAILURE( + AssertTemporaryOriginNotInitialized(testOriginMetadata)); + + PerformOnBackgroundThread([testOriginMetadata]() { + QuotaManager* quotaManager = QuotaManager::Get(); + ASSERT_TRUE(quotaManager); + + { + int64_t timestamp = PR_Now(); + + auto value = Await( + quotaManager->SaveOriginAccessTime(testOriginMetadata, timestamp)); + ASSERT_TRUE(value.IsResolve()); + } + }); + + ASSERT_NO_FATAL_FAILURE(AssertStorageInitialized()); + ASSERT_NO_FATAL_FAILURE(AssertTemporaryStorageNotInitialized()); + ASSERT_NO_FATAL_FAILURE( + AssertTemporaryOriginNotInitialized(testOriginMetadata)); + + ASSERT_NO_FATAL_FAILURE(ShutdownStorage()); +} + // Test simple ClearStoragesForOrigin. TEST_F(TestQuotaManager, ClearStoragesForOrigin_Simple) { ASSERT_NO_FATAL_FAILURE(ShutdownStorage()); diff --git a/dom/security/nsContentSecurityUtils.cpp b/dom/security/nsContentSecurityUtils.cpp index 13f24b160fd..be117589712 100644 --- a/dom/security/nsContentSecurityUtils.cpp +++ b/dom/security/nsContentSecurityUtils.cpp @@ -1959,7 +1959,6 @@ void nsContentSecurityUtils::AssertChromePageHasCSP(Document* aDocument) { "chrome://global/content/appPicker.xhtml"_ns, "chrome://global/content/backgroundPageThumbs.xhtml"_ns, "chrome://global/content/megalist/megalist.html"_ns, - "chrome://global/content/selectDialog.xhtml"_ns, // Test files "chrome://mochikit/"_ns, "chrome://mochitests/"_ns, diff --git a/dom/webgpu/ExternalTexture.cpp b/dom/webgpu/ExternalTexture.cpp index d972097e3a5..491548ff941 100644 --- a/dom/webgpu/ExternalTexture.cpp +++ b/dom/webgpu/ExternalTexture.cpp @@ -37,7 +37,8 @@ UniquePtr ExternalTexture::Create( UniquePtr texture; #ifdef XP_WIN - texture = ExternalTextureD3D11::Create(aWidth, aHeight, aFormat, aUsage); + texture = ExternalTextureD3D11::Create(aParent, aDeviceId, aWidth, aHeight, + aFormat, aUsage); #elif defined(XP_LINUX) && !defined(MOZ_WIDGET_ANDROID) texture = ExternalTextureDMABuf::Create(aParent, aDeviceId, aWidth, aHeight, aFormat, aUsage); diff --git a/dom/webgpu/ExternalTexture.h b/dom/webgpu/ExternalTexture.h index c84ba30844f..5653f3e5944 100644 --- a/dom/webgpu/ExternalTexture.h +++ b/dom/webgpu/ExternalTexture.h @@ -43,6 +43,7 @@ class ExtTex : public ObjectBase { void Cleanup() {} }; +class ExternalTextureD3D11; class ExternalTextureDMABuf; class ExternalTextureMacIOSurface; class WebGPUParent; @@ -62,10 +63,7 @@ class ExternalTexture { const ffi::WGPUTextureUsages aUsage); virtual ~ExternalTexture(); - virtual void* GetExternalTextureHandle() { return nullptr; } - - virtual Maybe ToSurfaceDescriptor( - Maybe& aFenceInfo) = 0; + virtual Maybe ToSurfaceDescriptor() = 0; virtual void GetSnapshot(const ipc::Shmem& aDestShmem, const gfx::IntSize& aSize) {} @@ -76,6 +74,8 @@ class ExternalTexture { return nullptr; } + virtual ExternalTextureD3D11* AsExternalTextureD3D11() { return nullptr; } + gfx::IntSize GetSize() { return gfx::IntSize(mWidth, mHeight); } void SetSubmissionIndex(uint64_t aSubmissionIndex); @@ -114,8 +114,7 @@ class ExternalTextureReadBackPresent final : public ExternalTexture { const ffi::WGPUTextureUsages aUsage); virtual ~ExternalTextureReadBackPresent(); - Maybe ToSurfaceDescriptor( - Maybe& aFenceInfo) override { + Maybe ToSurfaceDescriptor() override { return Nothing(); } }; diff --git a/dom/webgpu/ExternalTextureD3D11.cpp b/dom/webgpu/ExternalTextureD3D11.cpp index 09830abc027..e5be9c0f9a4 100644 --- a/dom/webgpu/ExternalTextureD3D11.cpp +++ b/dom/webgpu/ExternalTextureD3D11.cpp @@ -9,15 +9,40 @@ #include "mozilla/gfx/DeviceManagerDx.h" #include "mozilla/gfx/Logging.h" +#include "mozilla/layers/FenceD3D11.h" +#include "mozilla/layers/GpuProcessD3D11FencesHolderMap.h" #include "mozilla/layers/ImageDataSerializer.h" +#include "mozilla/webgpu/WebGPUParent.h" namespace mozilla::webgpu { // static UniquePtr ExternalTextureD3D11::Create( + WebGPUParent* aParent, const ffi::WGPUDeviceId aDeviceId, const uint32_t aWidth, const uint32_t aHeight, const struct ffi::WGPUTextureFormat aFormat, const ffi::WGPUTextureUsages aUsage) { + auto* fencesHolderMap = layers::GpuProcessD3D11FencesHolderMap::Get(); + if (!fencesHolderMap) { + MOZ_ASSERT_UNREACHABLE("unexpected to be called"); + gfxCriticalNoteOnce << "Failed to get FencesHolderMap"; + return nullptr; + } + + RefPtr fenceHandle = + aParent->GetDeviceFenceHandle(aDeviceId); + if (!fenceHandle) { + gfxCriticalNoteOnce << "Failed to get fenceHandle"; + return nullptr; + } + + RefPtr fence = + layers::FenceD3D11::CreateFromHandle(fenceHandle); + if (!fence) { + gfxCriticalNoteOnce << "Failed create FenceD3D11"; + return nullptr; + } + const RefPtr d3d11Device = gfx::DeviceManagerDx::Get()->GetCompositorDevice(); if (!d3d11Device) { @@ -53,55 +78,74 @@ UniquePtr ExternalTextureD3D11::Create( texture->QueryInterface((IDXGIResource1**)getter_AddRefs(resource)); if (!resource) { gfxCriticalNoteOnce << "Failed to get IDXGIResource"; - return 0; + return nullptr; } HANDLE sharedHandle; hr = resource->CreateSharedHandle( nullptr, DXGI_SHARED_RESOURCE_READ | DXGI_SHARED_RESOURCE_WRITE, nullptr, &sharedHandle); - if (FAILED(hr)) { + if (FAILED(hr) || !sharedHandle) { gfxCriticalNoteOnce << "GetSharedHandle failed: " << gfx::hexa(hr); - return 0; + return nullptr; } RefPtr handle = new gfx::FileHandleWrapper(UniqueFileHandle(sharedHandle)); + auto fencesHolderId = layers::GpuProcessFencesHolderId::GetNext(); + fencesHolderMap->Register(fencesHolderId); + return MakeUnique(aWidth, aHeight, aFormat, aUsage, - texture, std::move(handle)); + texture, std::move(handle), + fencesHolderId, std::move(fence)); } ExternalTextureD3D11::ExternalTextureD3D11( const uint32_t aWidth, const uint32_t aHeight, const struct ffi::WGPUTextureFormat aFormat, const ffi::WGPUTextureUsages aUsage, const RefPtr aTexture, - RefPtr&& aSharedHandle) + RefPtr&& aSharedHandle, + const layers::GpuProcessFencesHolderId aFencesHolderId, + RefPtr&& aWriteFence) : ExternalTexture(aWidth, aHeight, aFormat, aUsage), mTexture(aTexture), - mSharedHandle(std::move(aSharedHandle)) { + mSharedHandle(std::move(aSharedHandle)), + mFencesHolderId(aFencesHolderId), + mWriteFence(std::move(aWriteFence)) { MOZ_ASSERT(mTexture); } ExternalTextureD3D11::~ExternalTextureD3D11() {} void* ExternalTextureD3D11::GetExternalTextureHandle() { - if (!mSharedHandle) { - return nullptr; - } + RefPtr device; + mTexture->GetDevice(getter_AddRefs(device)); + auto* fencesHolderMap = layers::GpuProcessD3D11FencesHolderMap::Get(); + MOZ_ASSERT(fencesHolderMap); + + // XXX deliver fences to wgpu + fencesHolderMap->WaitAllFencesAndForget(mFencesHolderId, device); return mSharedHandle->GetHandle(); } -Maybe ExternalTextureD3D11::ToSurfaceDescriptor( - Maybe& aFenceInfo) { +Maybe ExternalTextureD3D11::ToSurfaceDescriptor() { + MOZ_ASSERT(mSubmissionIndex > 0); + + mWriteFence->Update(mSubmissionIndex); + + auto* fencesHolderMap = layers::GpuProcessD3D11FencesHolderMap::Get(); + MOZ_ASSERT(fencesHolderMap); + fencesHolderMap->SetWriteFence(mFencesHolderId, mWriteFence); + const auto format = gfx::SurfaceFormat::B8G8R8A8; return Some(layers::SurfaceDescriptorD3D10( mSharedHandle, /* gpuProcessTextureId */ Nothing(), /* arrayIndex */ 0, format, gfx::IntSize(mWidth, mHeight), gfx::ColorSpace2::SRGB, gfx::ColorRange::FULL, - /* hasKeyedMutex */ false, aFenceInfo)); + /* hasKeyedMutex */ false, Some(mFencesHolderId))); } void ExternalTextureD3D11::GetSnapshot(const ipc::Shmem& aDestShmem, diff --git a/dom/webgpu/ExternalTextureD3D11.h b/dom/webgpu/ExternalTextureD3D11.h index 74f4cbdc863..0ffe8c3dc30 100644 --- a/dom/webgpu/ExternalTextureD3D11.h +++ b/dom/webgpu/ExternalTextureD3D11.h @@ -13,11 +13,16 @@ struct ID3D11Texture2D; namespace mozilla { +namespace layers { +class FenceD3D11; +} // namespace layers + namespace webgpu { class ExternalTextureD3D11 final : public ExternalTexture { public: static UniquePtr Create( + WebGPUParent* aParent, const ffi::WGPUDeviceId aDeviceId, const uint32_t aWidth, const uint32_t aHeight, const struct ffi::WGPUTextureFormat aFormat, const ffi::WGPUTextureUsages aUsage); @@ -26,20 +31,25 @@ class ExternalTextureD3D11 final : public ExternalTexture { const struct ffi::WGPUTextureFormat aFormat, const ffi::WGPUTextureUsages aUsage, const RefPtr aTexture, - RefPtr&& aSharedHandle); + RefPtr&& aSharedHandle, + const layers::GpuProcessFencesHolderId aFencesHolderId, + RefPtr&& aWriteFence); virtual ~ExternalTextureD3D11(); - void* GetExternalTextureHandle() override; + void* GetExternalTextureHandle(); - Maybe ToSurfaceDescriptor( - Maybe& aFenceInfo) override; + Maybe ToSurfaceDescriptor() override; void GetSnapshot(const ipc::Shmem& aDestShmem, const gfx::IntSize& aSize) override; + ExternalTextureD3D11* AsExternalTextureD3D11() override { return this; } + protected: const RefPtr mTexture; const RefPtr mSharedHandle; + const layers::GpuProcessFencesHolderId mFencesHolderId; + const RefPtr mWriteFence; }; } // namespace webgpu diff --git a/dom/webgpu/ExternalTextureDMABuf.cpp b/dom/webgpu/ExternalTextureDMABuf.cpp index 7eda87479be..c375968ee15 100644 --- a/dom/webgpu/ExternalTextureDMABuf.cpp +++ b/dom/webgpu/ExternalTextureDMABuf.cpp @@ -97,10 +97,7 @@ ExternalTextureDMABuf::ExternalTextureDMABuf( ExternalTextureDMABuf::~ExternalTextureDMABuf() {} -void* ExternalTextureDMABuf::GetExternalTextureHandle() { return nullptr; } - -Maybe ExternalTextureDMABuf::ToSurfaceDescriptor( - Maybe& aFenceInfo) { +Maybe ExternalTextureDMABuf::ToSurfaceDescriptor() { layers::SurfaceDescriptor sd; if (!mSurface->Serialize(sd)) { return Nothing(); diff --git a/dom/webgpu/ExternalTextureDMABuf.h b/dom/webgpu/ExternalTextureDMABuf.h index 374b4c1fbfc..b65cc00db40 100644 --- a/dom/webgpu/ExternalTextureDMABuf.h +++ b/dom/webgpu/ExternalTextureDMABuf.h @@ -35,10 +35,7 @@ class ExternalTextureDMABuf final : public ExternalTexture { const layers::SurfaceDescriptorDMABuf& aSurfaceDescriptor); virtual ~ExternalTextureDMABuf(); - void* GetExternalTextureHandle() override; - - Maybe ToSurfaceDescriptor( - Maybe& aFenceInfo) override; + Maybe ToSurfaceDescriptor() override; void GetSnapshot(const ipc::Shmem& aDestShmem, const gfx::IntSize& aSize) override; diff --git a/dom/webgpu/ExternalTextureMacIOSurface.cpp b/dom/webgpu/ExternalTextureMacIOSurface.cpp index 3f6c863959d..6324b85fecf 100644 --- a/dom/webgpu/ExternalTextureMacIOSurface.cpp +++ b/dom/webgpu/ExternalTextureMacIOSurface.cpp @@ -55,17 +55,12 @@ ExternalTextureMacIOSurface::ExternalTextureMacIOSurface( ExternalTextureMacIOSurface::~ExternalTextureMacIOSurface() {} -void* ExternalTextureMacIOSurface::GetExternalTextureHandle() { - return nullptr; -} - uint32_t ExternalTextureMacIOSurface::GetIOSurfaceId() { return mSurface->GetIOSurfaceID(); } Maybe -ExternalTextureMacIOSurface::ToSurfaceDescriptor( - Maybe& aFenceInfo) { +ExternalTextureMacIOSurface::ToSurfaceDescriptor() { MOZ_ASSERT(mSubmissionIndex > 0); RefPtr gpuFence; diff --git a/dom/webgpu/ExternalTextureMacIOSurface.h b/dom/webgpu/ExternalTextureMacIOSurface.h index 031e9193759..0f6b1023274 100644 --- a/dom/webgpu/ExternalTextureMacIOSurface.h +++ b/dom/webgpu/ExternalTextureMacIOSurface.h @@ -32,10 +32,7 @@ class ExternalTextureMacIOSurface final : public ExternalTexture { RefPtr&& aSurface); virtual ~ExternalTextureMacIOSurface(); - void* GetExternalTextureHandle() override; - - Maybe ToSurfaceDescriptor( - Maybe& aFenceInfo) override; + Maybe ToSurfaceDescriptor() override; void GetSnapshot(const ipc::Shmem& aDestShmem, const gfx::IntSize& aSize) override; diff --git a/dom/webgpu/ipc/WebGPUParent.cpp b/dom/webgpu/ipc/WebGPUParent.cpp index 61e36037c3a..033355e7b72 100644 --- a/dom/webgpu/ipc/WebGPUParent.cpp +++ b/dom/webgpu/ipc/WebGPUParent.cpp @@ -22,6 +22,7 @@ #if defined(XP_WIN) # include "mozilla/gfx/DeviceManagerDx.h" +# include "mozilla/webgpu/ExternalTextureD3D11.h" #endif #if defined(XP_LINUX) && !defined(MOZ_WIDGET_ANDROID) @@ -86,7 +87,12 @@ extern void* wgpu_server_get_external_texture_handle(void* aParam, void* sharedHandle = nullptr; #ifdef XP_WIN - sharedHandle = texture->GetExternalTextureHandle(); + auto* textureD3D11 = texture->AsExternalTextureD3D11(); + if (!textureD3D11) { + MOZ_ASSERT_UNREACHABLE("unexpected to be called"); + return nullptr; + } + sharedHandle = textureD3D11->GetExternalTextureHandle(); if (!sharedHandle) { MOZ_ASSERT_UNREACHABLE("unexpected to be called"); gfxCriticalNoteOnce << "Failed to get shared handle"; @@ -1428,19 +1434,11 @@ void WebGPUParent::PostExternalTexture( const auto surfaceFormat = gfx::SurfaceFormat::B8G8R8A8; const auto size = aExternalTexture->GetSize(); - const auto index = aExternalTexture->GetSubmissionIndex(); - MOZ_ASSERT(index != 0); RefPtr data = lookup->second.get(); - Maybe fenceInfo; - auto it = mDeviceFenceHandles.find(data->mDeviceId); - if (it != mDeviceFenceHandles.end()) { - fenceInfo = Some(gfx::FenceInfo(it->second, index)); - } - Maybe desc = - aExternalTexture->ToSurfaceDescriptor(fenceInfo); + aExternalTexture->ToSurfaceDescriptor(); if (!desc) { MOZ_ASSERT_UNREACHABLE("unexpected to be called"); return; @@ -1456,6 +1454,15 @@ void WebGPUParent::PostExternalTexture( } } +RefPtr WebGPUParent::GetDeviceFenceHandle( + const RawId aDeviceId) { + auto it = mDeviceFenceHandles.find(aDeviceId); + if (it == mDeviceFenceHandles.end()) { + return nullptr; + } + return it->second; +} + ipc::IPCResult WebGPUParent::RecvSwapChainPresent( RawId aTextureId, RawId aCommandEncoderId, const layers::RemoteTextureId& aRemoteTextureId, diff --git a/dom/webgpu/ipc/WebGPUParent.h b/dom/webgpu/ipc/WebGPUParent.h index fe7162ec4f4..ff6d749e09c 100644 --- a/dom/webgpu/ipc/WebGPUParent.h +++ b/dom/webgpu/ipc/WebGPUParent.h @@ -190,6 +190,8 @@ class WebGPUParent final : public PWebGPUParent, public SupportsWeakPtr { return mActiveDeviceIds.Contains(aDeviceId); } + RefPtr GetDeviceFenceHandle(const RawId aDeviceId); + private: static void MapCallback(uint8_t* aUserData, ffi::WGPUBufferMapAsyncStatus aStatus); diff --git a/dom/workers/WorkerScope.h b/dom/workers/WorkerScope.h index a663e9897be..cbe7c1fc6db 100644 --- a/dom/workers/WorkerScope.h +++ b/dom/workers/WorkerScope.h @@ -218,8 +218,20 @@ class WorkerGlobalScopeBase : public DOMEventTargetHelper, mNumOfOpenWebSockets += aDelta; } + // Increase/Decrease the number of active IndexedDB databases for the + // decision making of timeout-throttling. + void UpdateActiveIndexedDBDatabaseCount(int32_t aDelta) override { + AssertIsOnWorkerThread(); + mNumOfIndexedDBDatabases += aDelta; + } + bool HasOpenWebSockets() const override { return mNumOfOpenWebSockets; } + bool HasActiveIndexedDBDatabases() const override { + AssertIsOnWorkerThread(); + return mNumOfIndexedDBDatabases; + } + void TriggerUpdateCCFlag() override { mWorkerPrivate->UpdateCCFlag(WorkerPrivate::CCFlag::EligibleForTimeout); } @@ -247,6 +259,7 @@ class WorkerGlobalScopeBase : public DOMEventTargetHelper, #endif mozilla::UniquePtr mTimeoutManager; uint32_t mNumOfOpenWebSockets{}; + uint32_t mNumOfIndexedDBDatabases{}; }; namespace workerinternals { diff --git a/editor/libeditor/tests/mochitest.toml b/editor/libeditor/tests/mochitest.toml index e8ab9288b2f..3b6f6191f08 100644 --- a/editor/libeditor/tests/mochitest.toml +++ b/editor/libeditor/tests/mochitest.toml @@ -444,6 +444,7 @@ skip-if = ["os == 'android'"] # Needs interaction with the scrollbar ["test_editing_UI_in_plaintext-only.html"] ["test_execCommandPaste_noTarget.html"] +skip-if = ["display == 'wayland'"] # Bug 1935188 ["test_focus_caret_navigation_between_nested_editors.html"] diff --git a/editor/libeditor/tests/test_bug578771.html b/editor/libeditor/tests/test_bug578771.html index 5cf7b23e525..1a2414fb5d3 100644 --- a/editor/libeditor/tests/test_bug578771.html +++ b/editor/libeditor/tests/test_bug578771.html @@ -31,13 +31,13 @@ https://bugzilla.mozilla.org/show_bug.cgi?id=578771 ce.focus(); synthesizeMouse(elem, 5, 5, {clickCount: 2 }); - ok(elem.selectionStart == 0 && elem.selectionEnd == 7, - " Double-clicking on another " + elemTag + " works correctly"); + is(elem.selectionStart, 0, `${elemTag} selectionStart after double-click`); + is(elem.selectionEnd, 7, `${elemTag} selectionEnd after double-click`); ce.focus(); synthesizeMouse(elem, 5, 5, {clickCount: 3 }); - ok(elem.selectionStart == 0 && elem.selectionEnd == 14, - "Triple-clicking on another " + elemTag + " works correctly"); + is(elem.selectionStart, 0, `${elemTag} selectionStart after triple-click`); + is(elem.selectionEnd, 14, `${elemTag} selectionEnd after triple-click`); } // Avoid platform selection differences SimpleTest.waitForFocus(function() { diff --git a/gfx/gl/SharedSurfaceANGLE.cpp b/gfx/gl/SharedSurfaceANGLE.cpp index 54200bb5c33..e61ba5fdbaf 100644 --- a/gfx/gl/SharedSurfaceANGLE.cpp +++ b/gfx/gl/SharedSurfaceANGLE.cpp @@ -177,7 +177,7 @@ SharedSurface_ANGLEShareHandle::ToSurfaceDescriptor() { mSharedHandle, /* gpuProcessTextureId */ Nothing(), /* arrayIndex */ 0, format, mDesc.size, mDesc.colorSpace, gfx::ColorRange::FULL, /* hasKeyedMutex */ true, - /* fenceInfo */ Nothing())); + /* fencesHolderId */ Nothing())); } //////////////////////////////////////////////////////////////////////////////// diff --git a/gfx/gl/SharedSurfaceD3D11Interop.cpp b/gfx/gl/SharedSurfaceD3D11Interop.cpp index c39cd0d4699..b6c86afd73e 100644 --- a/gfx/gl/SharedSurfaceD3D11Interop.cpp +++ b/gfx/gl/SharedSurfaceD3D11Interop.cpp @@ -452,7 +452,7 @@ SharedSurface_D3D11Interop::ToSurfaceDescriptor() { mData.dxgiHandle, /* gpuProcessTextureId */ Nothing(), /* arrayIndex */ 0, format, mDesc.size, mDesc.colorSpace, gfx::ColorRange::FULL, /* hasKeyedMutex */ true, - /* fenceInfo */ Nothing())); + /* fencesHolderId */ Nothing())); } ////////////////////////////////////////////////////////////////////////////////////////// diff --git a/gfx/ipc/FileHandleWrapper.h b/gfx/ipc/FileHandleWrapper.h index 6ca905bd617..b507b5d1a16 100644 --- a/gfx/ipc/FileHandleWrapper.h +++ b/gfx/ipc/FileHandleWrapper.h @@ -43,20 +43,6 @@ class FileHandleWrapper { const mozilla::UniqueFileHandle mHandle; }; -struct FenceInfo { - FenceInfo() = default; - FenceInfo(FileHandleWrapper* aFenceHandle, uint64_t aFenceValue) - : mFenceHandle(aFenceHandle), mFenceValue(aFenceValue) {} - - bool operator==(const FenceInfo& aOther) const { - return mFenceHandle == aOther.mFenceHandle && - mFenceValue == aOther.mFenceValue; - } - - RefPtr mFenceHandle; - uint64_t mFenceValue = 0; -}; - } // namespace gfx } // namespace mozilla diff --git a/gfx/ipc/GfxMessageUtils.h b/gfx/ipc/GfxMessageUtils.h index 5ebd5b2c025..484be9d6804 100644 --- a/gfx/ipc/GfxMessageUtils.h +++ b/gfx/ipc/GfxMessageUtils.h @@ -1203,24 +1203,6 @@ struct ParamTraits { } }; -template <> -struct ParamTraits { - typedef mozilla::gfx::FenceInfo paramType; - - static void Write(MessageWriter* aWriter, const paramType& aParam) { - WriteParam(aWriter, aParam.mFenceHandle); - WriteParam(aWriter, aParam.mFenceValue); - } - - static bool Read(MessageReader* aReader, paramType* aResult) { - if (!ReadParam(aReader, &aResult->mFenceHandle) || - !ReadParam(aReader, &aResult->mFenceValue)) { - return false; - } - return true; - } -}; - } // namespace IPC namespace mozilla { diff --git a/gfx/layers/d3d11/FenceD3D11.cpp b/gfx/layers/d3d11/FenceD3D11.cpp index 0486872b5ee..01230865ca2 100644 --- a/gfx/layers/d3d11/FenceD3D11.cpp +++ b/gfx/layers/d3d11/FenceD3D11.cpp @@ -63,6 +63,11 @@ RefPtr FenceD3D11::Create(ID3D11Device* aDevice) { /* static */ RefPtr FenceD3D11::CreateFromHandle( RefPtr aHandle) { + MOZ_ASSERT(aHandle); + + if (!aHandle) { + return nullptr; + } // Opening shared handle is deferred. return new FenceD3D11(aHandle); } @@ -125,10 +130,6 @@ RefPtr FenceD3D11::CloneFromHandle() { return fence; } -gfx::FenceInfo FenceD3D11::GetFenceInfo() const { - return gfx::FenceInfo(mHandle, mFenceValue); -} - bool FenceD3D11::IncrementAndSignal() { MOZ_ASSERT(mDevice); MOZ_ASSERT(mSignalFence); diff --git a/gfx/layers/d3d11/FenceD3D11.h b/gfx/layers/d3d11/FenceD3D11.h index 54c191a0ca2..69919a782d6 100644 --- a/gfx/layers/d3d11/FenceD3D11.h +++ b/gfx/layers/d3d11/FenceD3D11.h @@ -62,8 +62,6 @@ class FenceD3D11 final : public Fence { uint64_t GetFenceValue() const { return mFenceValue; } - gfx::FenceInfo GetFenceInfo() const; - const RefPtr mHandle; protected: diff --git a/gfx/layers/d3d11/GpuProcessD3D11FencesHolderMap.cpp b/gfx/layers/d3d11/GpuProcessD3D11FencesHolderMap.cpp index 55ab2924520..94bfd145f3c 100644 --- a/gfx/layers/d3d11/GpuProcessD3D11FencesHolderMap.cpp +++ b/gfx/layers/d3d11/GpuProcessD3D11FencesHolderMap.cpp @@ -17,13 +17,13 @@ StaticAutoPtr /* static */ void GpuProcessD3D11FencesHolderMap::Init() { - MOZ_ASSERT(XRE_IsGPUProcess()); + MOZ_ASSERT(XRE_IsGPUProcess() || XRE_IsParentProcess()); sInstance = new GpuProcessD3D11FencesHolderMap(); } /* static */ void GpuProcessD3D11FencesHolderMap::Shutdown() { - MOZ_ASSERT(XRE_IsGPUProcess()); + MOZ_ASSERT(XRE_IsGPUProcess() || XRE_IsParentProcess()); sInstance = nullptr; } diff --git a/gfx/layers/d3d11/TextureD3D11.cpp b/gfx/layers/d3d11/TextureD3D11.cpp index 3d5a8020ab7..7528ea471f0 100644 --- a/gfx/layers/d3d11/TextureD3D11.cpp +++ b/gfx/layers/d3d11/TextureD3D11.cpp @@ -425,7 +425,7 @@ bool D3D11TextureData::SerializeSpecific( *aOutDesc = SurfaceDescriptorD3D10( mSharedHandle, mGpuProcessTextureId, mArrayIndex, mFormat, mSize, mColorSpace, mColorRange, /* hasKeyedMutex */ mHasKeyedMutex, - /* fenceInfo */ Nothing()); + /* fencesHolderId */ Nothing()); return true; } @@ -921,9 +921,7 @@ DXGITextureHostD3D11::DXGITextureHostD3D11( mSize(aDescriptor.size()), mFormat(aDescriptor.format()), mHasKeyedMutex(aDescriptor.hasKeyedMutex()), - mAcquireFenceInfo(aDescriptor.fenceInfo().isSome() - ? aDescriptor.fenceInfo().ref() - : gfx::FenceInfo()), + mFencesHolderId(aDescriptor.fencesHolderId()), mColorSpace(aDescriptor.colorSpace()), mColorRange(aDescriptor.colorRange()) {} @@ -1182,7 +1180,7 @@ void DXGITextureHostD3D11::CreateRenderTexture( RefPtr texture = new wr::RenderDXGITextureHost( mHandle, mGpuProcessTextureId, mArrayIndex, mFormat, mColorSpace, - mColorRange, mSize, mHasKeyedMutex, mAcquireFenceInfo); + mColorRange, mSize, mHasKeyedMutex, mFencesHolderId); if (mFlags & TextureFlags::SOFTWARE_DECODED_VIDEO) { texture->SetIsSoftwareDecodedVideo(); } diff --git a/gfx/layers/d3d11/TextureD3D11.h b/gfx/layers/d3d11/TextureD3D11.h index 86debb7b62a..f49bfa97e51 100644 --- a/gfx/layers/d3d11/TextureD3D11.h +++ b/gfx/layers/d3d11/TextureD3D11.h @@ -26,7 +26,6 @@ namespace mozilla { namespace gfx { class FileHandleWrapper; -struct FenceInfo; } // namespace gfx namespace gl { @@ -398,7 +397,7 @@ class DXGITextureHostD3D11 : public TextureHost { const gfx::IntSize mSize; const gfx::SurfaceFormat mFormat; const bool mHasKeyedMutex; - const gfx::FenceInfo mAcquireFenceInfo; + const Maybe mFencesHolderId; const gfx::ColorSpace2 mColorSpace; const gfx::ColorRange mColorRange; }; diff --git a/gfx/layers/d3d11/TextureHostWrapperD3D11.cpp b/gfx/layers/d3d11/TextureHostWrapperD3D11.cpp index efcb75776dc..a6900e0faf2 100644 --- a/gfx/layers/d3d11/TextureHostWrapperD3D11.cpp +++ b/gfx/layers/d3d11/TextureHostWrapperD3D11.cpp @@ -257,7 +257,7 @@ RefPtr TextureHostWrapperD3D11::CreateFromBufferTexture( auto descD3D10 = SurfaceDescriptorD3D10( nullptr, Some(id), /* arrayIndex */ 0, gfx::SurfaceFormat::NV12, size, colorSpace, - colorRange, /* hasKeyedMutex */ false, /* fenceInfo */ Nothing()); + colorRange, /* hasKeyedMutex */ false, /* fencesHolderId */ Nothing()); RefPtr textureHostD3D11 = new DXGITextureHostD3D11(flags, descD3D10); diff --git a/gfx/layers/ipc/LayersSurfaces.ipdlh b/gfx/layers/ipc/LayersSurfaces.ipdlh index e5b279306a8..5e24ddeab4c 100644 --- a/gfx/layers/ipc/LayersSurfaces.ipdlh +++ b/gfx/layers/ipc/LayersSurfaces.ipdlh @@ -22,7 +22,6 @@ using mozilla::gfx::SurfaceFormat from "mozilla/gfx/Types.h"; using mozilla::gfx::IntRect from "mozilla/gfx/Rect.h"; using mozilla::gfx::IntSize from "mozilla/gfx/Point.h"; using mozilla::gfx::Matrix4x4 from "mozilla/gfx/Matrix.h"; -using mozilla::gfx::FenceInfo from "mozilla/gfx/FileHandleWrapper.h"; [RefCounted] using mozilla::gfx::FileHandleWrapper from "mozilla/gfx/FileHandleWrapper.h"; using gfxImageFormat from "gfxTypes.h"; using mozilla::layers::MaybeVideoBridgeSource from "mozilla/layers/VideoBridgeUtils.h"; @@ -48,7 +47,7 @@ namespace layers { ColorSpace2 colorSpace; ColorRange colorRange; bool hasKeyedMutex; - FenceInfo? fenceInfo; + GpuProcessFencesHolderId? fencesHolderId; }; [Comparable] struct SurfaceDescriptorDXGIYCbCr { diff --git a/gfx/thebes/gfxPlatform.cpp b/gfx/thebes/gfxPlatform.cpp index 39c76fab144..38e847aa2a0 100644 --- a/gfx/thebes/gfxPlatform.cpp +++ b/gfx/thebes/gfxPlatform.cpp @@ -77,6 +77,7 @@ #if defined(XP_WIN) # include "gfxWindowsPlatform.h" +# include "mozilla/layers/GpuProcessD3D11FencesHolderMap.h" # include "mozilla/widget/WinWindowOcclusionTracker.h" #elif defined(XP_DARWIN) # include "gfxPlatformMac.h" @@ -1336,6 +1337,9 @@ void gfxPlatform::InitLayersIPC() { } #endif if (!gfxConfig::IsEnabled(Feature::GPU_PROCESS)) { +#if defined(XP_WIN) + GpuProcessD3D11FencesHolderMap::Init(); +#endif RemoteTextureMap::Init(); wr::RenderThread::Start(GPUProcessManager::Get()->AllocateNamespace()); image::ImageMemoryReporter::InitForWebRender(); @@ -1391,6 +1395,7 @@ void gfxPlatform::ShutdownLayersIPC() { StaticPrefs::GetPrefName_gfx_webrender_blob_tile_size())); } #if defined(XP_WIN) + GpuProcessD3D11FencesHolderMap::Shutdown(); widget::WinWindowOcclusionTracker::ShutDown(); #endif } else { @@ -3974,6 +3979,9 @@ void gfxPlatform::DisableGPUProcess() { "FEATURE_FAILURE_DISABLED_BY_GPU_PROCESS_DISABLED"_ns); } +#if defined(XP_WIN) + GpuProcessD3D11FencesHolderMap::Init(); +#endif RemoteTextureMap::Init(); // We need to initialize the parent process to prepare for WebRender if we // did not end up disabling it, despite losing the GPU process. diff --git a/gfx/webrender_bindings/RenderD3D11TextureHost.cpp b/gfx/webrender_bindings/RenderD3D11TextureHost.cpp index 928b9499b94..a6a55a139b4 100644 --- a/gfx/webrender_bindings/RenderD3D11TextureHost.cpp +++ b/gfx/webrender_bindings/RenderD3D11TextureHost.cpp @@ -30,7 +30,7 @@ RenderDXGITextureHost::RenderDXGITextureHost( const uint32_t aArrayIndex, const gfx::SurfaceFormat aFormat, const gfx::ColorSpace2 aColorSpace, const gfx::ColorRange aColorRange, const gfx::IntSize aSize, bool aHasKeyedMutex, - const gfx::FenceInfo& aAcquireFenceInfo) + const Maybe& aFencesHolderId) : mHandle(aHandle), mGpuProcessTextureId(aGpuProcessTextureId), mArrayIndex(aArrayIndex), @@ -42,7 +42,7 @@ RenderDXGITextureHost::RenderDXGITextureHost( mColorRange(aColorRange), mSize(aSize), mHasKeyedMutex(aHasKeyedMutex), - mAcquireFenceInfo(aAcquireFenceInfo), + mFencesHolderId(aFencesHolderId), mLocked(false) { MOZ_COUNT_CTOR_INHERITED(RenderDXGITextureHost, RenderTextureHost); MOZ_ASSERT((mFormat != gfx::SurfaceFormat::NV12 && @@ -367,19 +367,20 @@ wr::WrExternalImage RenderDXGITextureHost::Lock(uint8_t aChannelIndex, } bool RenderDXGITextureHost::LockInternal() { - if (!mLocked) { - if (mAcquireFenceInfo.mFenceHandle) { - if (!mAcquireFence) { - mAcquireFence = layers::FenceD3D11::CreateFromHandle( - mAcquireFenceInfo.mFenceHandle); - } - if (mAcquireFence) { - MOZ_ASSERT(mAcquireFenceInfo.mFenceHandle == mAcquireFence->mHandle); + MOZ_ASSERT(mTexture); - mAcquireFence->Update(mAcquireFenceInfo.mFenceValue); - RefPtr d3d11Device = - gfx::DeviceManagerDx::Get()->GetCompositorDevice(); - mAcquireFence->Wait(d3d11Device); + if (!mLocked) { + if (mFencesHolderId.isSome()) { + auto* fencesHolderMap = layers::GpuProcessD3D11FencesHolderMap::Get(); + if (!fencesHolderMap) { + MOZ_ASSERT_UNREACHABLE("unexpected to be called"); + return false; + } + RefPtr device; + mTexture->GetDevice(getter_AddRefs(device)); + + if (!fencesHolderMap->WaitWriteFence(mFencesHolderId.ref(), device)) { + return false; } } if (mKeyedMutex) { @@ -468,7 +469,7 @@ gfx::IntSize RenderDXGITextureHost::GetSize(uint8_t aChannelIndex) const { bool RenderDXGITextureHost::SyncObjectNeeded() { return mGpuProcessTextureId.isNothing() && !mHasKeyedMutex && - !mAcquireFenceInfo.mFenceHandle; + mFencesHolderId.isNothing(); } RenderDXGIYCbCrTextureHost::RenderDXGIYCbCrTextureHost( @@ -641,12 +642,12 @@ bool RenderDXGIYCbCrTextureHost::EnsureD3D11Texture2D(ID3D11Device* aDevice) { bool RenderDXGIYCbCrTextureHost::LockInternal() { if (!mLocked) { - auto* fenceHolderMap = layers::GpuProcessD3D11FencesHolderMap::Get(); - if (!fenceHolderMap) { + auto* fencesHolderMap = layers::GpuProcessD3D11FencesHolderMap::Get(); + if (!fencesHolderMap) { MOZ_ASSERT_UNREACHABLE("unexpected to be called"); return false; } - if (!fenceHolderMap->WaitWriteFence(mFencesHolderId, mDevice)) { + if (!fencesHolderMap->WaitWriteFence(mFencesHolderId, mDevice)) { return false; } mLocked = true; diff --git a/gfx/webrender_bindings/RenderD3D11TextureHost.h b/gfx/webrender_bindings/RenderD3D11TextureHost.h index fc6d55cc9b7..061571d5237 100644 --- a/gfx/webrender_bindings/RenderD3D11TextureHost.h +++ b/gfx/webrender_bindings/RenderD3D11TextureHost.h @@ -32,7 +32,7 @@ class RenderDXGITextureHost final : public RenderTextureHostSWGL { const uint32_t aArrayIndex, const gfx::SurfaceFormat aFormat, const gfx::ColorSpace2 aColorSpace, const gfx::ColorRange aColorRange, const gfx::IntSize aSize, const bool aHasKeyedMutex, - const gfx::FenceInfo& aAcquireFenceInfo); + const Maybe& aFencesHolderId); wr::WrExternalImage Lock(uint8_t aChannelIndex, gl::GLContext* aGL) override; void Unlock() override; @@ -121,15 +121,13 @@ class RenderDXGITextureHost final : public RenderTextureHostSWGL { bool mIsSoftwareDecodedVideo = false; - RefPtr mAcquireFence; - public: const gfx::SurfaceFormat mFormat; const gfx::ColorSpace2 mColorSpace; const gfx::ColorRange mColorRange; const gfx::IntSize mSize; const bool mHasKeyedMutex; - const gfx::FenceInfo mAcquireFenceInfo; + const Maybe mFencesHolderId; private: bool mLocked; diff --git a/gfx/wr/webrender/src/scene_building.rs b/gfx/wr/webrender/src/scene_building.rs index 22ac36f751f..d45e514422c 100644 --- a/gfx/wr/webrender/src/scene_building.rs +++ b/gfx/wr/webrender/src/scene_building.rs @@ -393,10 +393,15 @@ impl PictureChainBuilder { // If no picture was created for this stacking context, create a // pass-through wrapper now. This is only needed in 1-2 edge cases // now, and will be removed as a follow up. + + // If the picture is snapshotted, it needs to have a surface rather + // than being pass-through. + let composite_mode = snapshot.map(|_| PictureCompositeMode::Blit(BlitReason::SNAPSHOT)); + let pic_index = PictureIndex(prim_store.pictures .alloc() .init(PicturePrimitive::new_image( - None, + composite_mode, Picture3DContext::Out, self.flags, prim_list, @@ -644,6 +649,10 @@ impl<'a> SceneBuilder<'a> { &builder.interners, ); + for pic_index in &builder.snapshot_pictures { + builder.picture_graph.add_root(*pic_index); + } + // Add all the tile cache pictures as roots of the picture graph for pic_index in &tile_cache_pictures { builder.picture_graph.add_root(*pic_index); @@ -2618,7 +2627,10 @@ impl<'a> SceneBuilder<'a> { // 3d render context. for child_pic_index in &prim_list.child_pictures { let child_pic = &mut self.prim_store.pictures[child_pic_index.0]; - child_pic.composite_mode = None; + let needs_surface = child_pic.snapshot.is_some(); + if !needs_surface { + child_pic.composite_mode = None; + } child_pic.context_3d = Picture3DContext::Out; } diff --git a/layout/base/nsRefreshDriver.cpp b/layout/base/nsRefreshDriver.cpp index 1b052da2d9a..a58efeabe0c 100644 --- a/layout/base/nsRefreshDriver.cpp +++ b/layout/base/nsRefreshDriver.cpp @@ -839,11 +839,17 @@ class VsyncRefreshDriverTimer : public RefreshDriverTimer { sMostRecentHighRate = rate; } +#ifdef DEBUG // On 32-bit Windows we sometimes get times where TimeStamp::Now() is not // monotonic because the underlying system apis produce non-monontonic - // results. (bug 1306896) -#if !defined(_WIN32) + // results; see bug 1306896. + // On Wayland, vsync timestamp might not precisely match system time; see + // bug 1958043. +# if defined(_WIN32) || defined(MOZ_WAYLAND) + Unused << NS_WARN_IF(aVsyncTimestamp > tickStart); +# else MOZ_ASSERT(aVsyncTimestamp <= tickStart); +# endif #endif bool shouldGiveNonVSyncTasksMoreTime = ShouldGiveNonVsyncTasksMoreTime(); diff --git a/layout/generic/nsIFrame.cpp b/layout/generic/nsIFrame.cpp index 09c9dd43e52..8b190c2bb55 100644 --- a/layout/generic/nsIFrame.cpp +++ b/layout/generic/nsIFrame.cpp @@ -4615,6 +4615,10 @@ nsresult nsIFrame::GetDataForTableSelection( } static bool IsEditingHost(const nsIFrame* aFrame) { + if (aFrame->Style()->GetPseudoType() == + PseudoStyleType::mozTextControlEditingRoot) { + return true; + } nsIContent* content = aFrame->GetContent(); return content && content->IsEditingHost(); } @@ -4666,8 +4670,6 @@ bool nsIFrame::ShouldHaveLineIfEmpty() const { break; case PseudoStyleType::scrolledContent: return GetParent()->ShouldHaveLineIfEmpty(); - case PseudoStyleType::mozTextControlEditingRoot: - return true; case PseudoStyleType::buttonContent: // HTML quirk. return GetContent()->IsHTMLElement(nsGkAtoms::input); @@ -5460,6 +5462,23 @@ struct MOZ_STACK_CLASS FrameContentRange { int32_t end; }; +static bool IsRelevantBlockFrame(const nsIFrame* aFrame) { + if (!aFrame->IsBlockOutside()) { + return false; + } + if (aFrame->GetContent()->IsInNativeAnonymousSubtree()) { + // This helps skipping things like scrollbar parts. + return false; + } + auto pseudoType = aFrame->Style()->GetPseudoType(); + if (PseudoStyle::IsAnonBox(pseudoType)) { + // Table cell contents should be considered block boundaries for this + // purpose. + return pseudoType == PseudoStyleType::cellContent; + } + return true; +} + // Retrieve the content offsets of a frame static FrameContentRange GetRangeForFrame(const nsIFrame* aFrame) { nsIContent* content = aFrame->GetContent(); @@ -5486,7 +5505,7 @@ static FrameContentRange GetRangeForFrame(const nsIFrame* aFrame) { MOZ_ASSERT(!content->IsBeingRemoved()); nsIContent* parent = content->GetParent(); - if (aFrame->IsBlockOutside() || !parent) { + if (IsRelevantBlockFrame(aFrame) || !parent) { return FrameContentRange(content, 0, content->GetChildCount()); } @@ -9385,7 +9404,7 @@ static nsContentAndOffset FindLineBreakingFrame(nsIFrame* aFrame, // the content of the inline frames they were created from. The // first/last child of such frames is the real block frame we're // looking for. - if ((aFrame->IsBlockOutside() && + if ((IsRelevantBlockFrame(aFrame) && !aFrame->HasAnyStateBits(NS_FRAME_PART_OF_IBSPLIT)) || aFrame->IsBrFrame()) { nsIContent* content = aFrame->GetContent(); @@ -9427,7 +9446,7 @@ nsresult nsIFrame::PeekOffsetForParagraph(PeekOffsetStruct* aPos) { nsIFrame* frame = this; nsContentAndOffset blockFrameOrBR; blockFrameOrBR.mContent = nullptr; - bool reachedLimit = frame->IsBlockOutside() || IsEditingHost(frame); + bool reachedLimit = IsRelevantBlockFrame(frame) || IsEditingHost(frame); auto traverse = [&aPos](nsIFrame* current) { return aPos->mDirection == eDirPrevious ? current->GetPrevSibling() @@ -9463,7 +9482,8 @@ nsresult nsIFrame::PeekOffsetForParagraph(PeekOffsetStruct* aPos) { break; } frame = parent; - reachedLimit = frame && (frame->IsBlockOutside() || IsEditingHost(frame)); + reachedLimit = + frame && (IsRelevantBlockFrame(frame) || IsEditingHost(frame)); } if (reachedLimit) { // no "stop frame" found diff --git a/layout/generic/test/test_selection_tripleclick.html b/layout/generic/test/test_selection_tripleclick.html index a912eb46863..62ad6b94843 100644 --- a/layout/generic/test/test_selection_tripleclick.html +++ b/layout/generic/test/test_selection_tripleclick.html @@ -5,6 +5,7 @@

Some code with text with code in it

@@ -15,6 +16,8 @@ some more code with pre-formatted whitespace that should be selected line by line +

Some code with text with code in it

+ - - - - - -
diff --git a/testing/web-platform/tests/IndexedDB/key_valid.any.js b/testing/web-platform/tests/IndexedDB/key_valid.any.js new file mode 100644 index 00000000000..8c015d331ba --- /dev/null +++ b/testing/web-platform/tests/IndexedDB/key_valid.any.js @@ -0,0 +1,68 @@ +// META: global=window,worker +// META: title=Valid key +// META: script=resources/support.js + +// Spec: https://w3c.github.io/IndexedDB/#key-construct + +'use strict'; + +const valid_key = (desc, key) => { + async_test(t => { + let db; + const open_rq = createdb(t); + open_rq.onupgradeneeded = t.step_func(e => { + db = e.target.result; + const store = db.createObjectStore('store'); + assert_true(store.add('value', key) instanceof IDBRequest); + + const store2 = db.createObjectStore('store2', { + keyPath: ['x', 'keypath'], + }); + assert_true(store2.add({x: 'v', keypath: key}) instanceof IDBRequest); + }); + + open_rq.onsuccess = t.step_func(e => { + const rq = + db.transaction('store', 'readonly').objectStore('store').get(key); + rq.onsuccess = t.step_func(e => { + assert_equals(e.target.result, 'value'); + const rq2 = + db.transaction('store2', 'readonly').objectStore('store2').get([ + 'v', key + ]); + rq2.onsuccess = t.step_func(e => { + assert_equals(e.target.result.x, 'v'); + assert_key_equals(e.target.result.keypath, key); + t.done(); + }); + }); + }); + }, 'Valid key - ' + desc); +}; + +// Date +valid_key('new Date()', new Date()); +valid_key('new Date(0)', new Date(0)); + +// Array +valid_key('[]', []); +valid_key('new Array()', new Array()); + +valid_key('["undefined"]', ['undefined']); + +// Float +valid_key('Infinity', Infinity); +valid_key('-Infinity', -Infinity); +valid_key('0', 0); +valid_key('1.5', 1.5); +valid_key('3e38', 3e38); +valid_key('3e-38', 3e38); + +// String +valid_key('"foo"', 'foo'); +valid_key('"\\n"', '\n'); +valid_key('""', ''); +valid_key('"\\""', '"'); +valid_key('"\\u1234"', '\u1234'); +valid_key('"\\u0000"', '\u0000'); +valid_key('"NaN"', 'NaN'); diff --git a/testing/web-platform/tests/IndexedDB/key_valid.html b/testing/web-platform/tests/IndexedDB/key_valid.html deleted file mode 100644 index 0cca54cdbb3..00000000000 --- a/testing/web-platform/tests/IndexedDB/key_valid.html +++ /dev/null @@ -1,75 +0,0 @@ - - - - -Valid key - - - - - - - - - -
diff --git a/testing/web-platform/tests/IndexedDB/keyorder.any.js b/testing/web-platform/tests/IndexedDB/keyorder.any.js new file mode 100644 index 00000000000..fe3eb11fa92 --- /dev/null +++ b/testing/web-platform/tests/IndexedDB/keyorder.any.js @@ -0,0 +1,169 @@ +// META: global=window,worker +// META: title=Key sort order +// META: script=resources/support.js + +// Spec: https://w3c.github.io/IndexedDB/#key-construct + +'use strict'; + +const global_db = createdb_for_multiple_tests(); + +const keysort = (desc, unsorted, expected) => { + async_test(t => { + const store_name = 'store-' + Date.now() + Math.random(); + + // The database test + const open_rq = global_db.setTest(t); + open_rq.onupgradeneeded = t.step_func(e => { + const db = e.target.result; + const objStore = db.createObjectStore(store_name); + + for (let i = 0; i < unsorted.length; i++) + objStore.add('value', unsorted[i]); + }); + + open_rq.onsuccess = t.step_func(e => { + const db = e.target.result; + const actual_keys = []; + const rq = + db.transaction(store_name).objectStore(store_name).openCursor(); + + rq.onsuccess = t.step_func(e => { + const cursor = e.target.result; + + if (cursor) { + actual_keys.push(cursor.key); + cursor.continue(); + } else { + assert_key_equals(actual_keys, expected, 'keyorder array'); + assert_equals(actual_keys.length, expected.length, 'array length'); + + t.done(); + } + }); + }); + }, `Database readback sort - ${desc}`); + + // The IDBKey.cmp test + test(() => { + const sorted = unsorted.slice(0).sort((a, b) => indexedDB.cmp(a, b)); + assert_key_equals(sorted, expected, 'sorted array'); + }, `IDBKey.cmp sort - ${desc}`); +}; + +const now = new Date(); +const one_sec_ago = new Date(now - 1000); +const one_min_future = new Date(now.getTime() + 1000 * 60); + +keysort('String < Array', [[0], 'yo', '', []], ['', 'yo', [], [0]]); + +keysort( + 'float < String', [Infinity, 'yo', 0, '', 100], + [0, 100, Infinity, '', 'yo']); + +keysort( + 'float < Date', [now, 0, 9999999999999, -0.22], + [-0.22, 0, 9999999999999, now]); + +keysort( + 'float < Date < String < Array', [[], '', now, [0], '-1', 0, 9999999999999], + [0, 9999999999999, now, '', '-1', [], [0]]); + +keysort( + 'Date(1 sec ago) < Date(now) < Date(1 minute in future)', + [now, one_sec_ago, one_min_future], [one_sec_ago, now, one_min_future]); + +keysort( + '-1.1 < 1 < 1.01337 < 1.013373 < 2', [1.013373, 2, 1.01337, -1.1, 1], + [-1.1, 1, 1.01337, 1.013373, 2]); + +keysort( + '-Infinity < -0.01 < 0 < Infinity', [0, -0.01, -Infinity, Infinity], + [-Infinity, -0.01, 0, Infinity]); + +keysort( + '"" < "a" < "ab" < "b" < "ba"', ['a', 'ba', '', 'b', 'ab'], + ['', 'a', 'ab', 'b', 'ba']); + +keysort( + 'Arrays', [[[0]], [0], [], [0, 0], [0, [0]]], + [[], [0], [0, 0], [0, [0]], [[0]]]); + +const big_array = []; +const bigger_array = []; +for (let i = 0; i < 10000; i++) { + big_array.push(i); + bigger_array.push(i); +} +bigger_array.push(0); + +keysort( + 'Array.length: 10,000 < Array.length: 10,001', + [bigger_array, [0, 2, 3], [0], [9], big_array], + [[0], big_array, bigger_array, [0, 2, 3], [9]]); + +keysort( + 'Infinity inside arrays', + [ + [Infinity, 1], + [Infinity, Infinity], + [1, 1], + [1, Infinity], + [1, -Infinity], + [-Infinity, Infinity], + ], + [ + [-Infinity, Infinity], + [1, -Infinity], + [1, 1], + [1, Infinity], + [Infinity, 1], + [Infinity, Infinity], + ]); + +keysort( + 'Test different stuff at once', + [ + now, + [0, []], + 'test', + 1, + ['a', [1, [-1]]], + ['b', 'a'], + [0, 2, 'c'], + ['a', [1, 2]], + [], + [0, [], 3], + ['a', 'b'], + [1, 2], + ['a', 'b', 'c'], + one_sec_ago, + [0, 'b', 'c'], + Infinity, + -Infinity, + 2.55, + [0, now], + [1], + ], + [ + -Infinity, + 1, + 2.55, + Infinity, + one_sec_ago, + now, + 'test', + [], + [0, 2, 'c'], + [0, now], + [0, 'b', 'c'], + [0, []], + [0, [], 3], + [1], + [1, 2], + ['a', 'b'], + ['a', 'b', 'c'], + ['a', [1, 2]], + ['a', [1, [-1]]], + ['b', 'a'], + ]); diff --git a/testing/web-platform/tests/IndexedDB/keyorder.htm b/testing/web-platform/tests/IndexedDB/keyorder.htm deleted file mode 100644 index 7e8b3d41264..00000000000 --- a/testing/web-platform/tests/IndexedDB/keyorder.htm +++ /dev/null @@ -1,175 +0,0 @@ - - - -Key sort order - - - - - - - - -
diff --git a/testing/web-platform/tests/ai/language_detection/detector.https.tentative.any.js b/testing/web-platform/tests/ai/language_detection/detector.https.tentative.any.js index 8c02df18cbe..8e4bedd05bb 100644 --- a/testing/web-platform/tests/ai/language_detection/detector.https.tentative.any.js +++ b/testing/web-platform/tests/ai/language_detection/detector.https.tentative.any.js @@ -22,6 +22,10 @@ promise_test(async t => { } }, 'Simple LanguageDetector.detect() call'); +promise_test(async t => { + testMonitor(LanguageDetector.create); +}, 'LanguageDetector.create() notifies its monitor on downloadprogress'); + promise_test(async t => { const controller = new AbortController(); controller.abort(); @@ -82,19 +86,18 @@ promise_test(async t => { detector.measureInputUsage('hello', {signal: controller.signal}); await promise_rejects_dom(t, 'AbortError', measureInputUsagePromise); -}, 'Translator.measureInputUsage() call with an aborted signal.'); +}, 'LanguageDetector.measureInputUsage() call with an aborted signal.'); promise_test(async t => { const detector = await LanguageDetector.create(); await testAbortPromise(t, signal => { return detector.measureInputUsage('hello', {signal}); }); -}, 'Aborting Translator.measureInputUsage().'); +}, 'Aborting LanguageDetector.measureInputUsage().'); promise_test(async () => { - const expected_languages = ['en', 'es']; - const detector = await languageDetector.create({ - expectedInputLanguages: expected_languages - }); - assert_array_equals(detector.expectedInputLanguages(), expected_languages); + const expectedLanguages = ['en', 'es']; + const detector = await LanguageDetector.create( + {expectedInputLanguages: expectedLanguages}); + assert_array_equals(detector.expectedInputLanguages, expectedLanguages); }, 'Creating LanguageDetector with expectedInputLanguages'); diff --git a/testing/web-platform/tests/ai/resources/util.js b/testing/web-platform/tests/ai/resources/util.js index 49d677edae2..6433404580b 100644 --- a/testing/web-platform/tests/ai/resources/util.js +++ b/testing/web-platform/tests/ai/resources/util.js @@ -25,3 +25,33 @@ const testAbortPromise = async (t, method) => { await promise_rejects_exactly(t, err, anotherPromise); } }; + +async function testMonitor(createFunc, options = {}) { + let created = false; + const progressEvents = []; + function monitor(m) { + m.addEventListener('downloadprogress', e => { + // No progress events should be fired after `createFunc` resolves. + assert_false(created); + + progressEvents.push(e); + }); + } + + await createFunc({...options, monitor}); + created = true; + + assert_greater_than_equal(progressEvents.length, 2); + assert_equals(progressEvents.at(0).loaded, 0); + assert_equals(progressEvents.at(-1).loaded, 1); + + let lastProgressEventLoaded = -1; + for (const progressEvent of progressEvents) { + assert_equals(progressEvent.total, 1); + assert_less_than_equal(progressEvent.loaded, progressEvent.total); + + // Progress events should have monotonically increasing `loaded` values. + assert_greater_than(progressEvent.loaded, lastProgressEventLoaded); + lastProgressEventLoaded = progressEvent.loaded; + } +} diff --git a/testing/web-platform/tests/ai/translator/translator_translate.tentative.https.any.js b/testing/web-platform/tests/ai/translator/translator_translate.tentative.https.any.js index 5a800c00c69..a8aad5e03e1 100644 --- a/testing/web-platform/tests/ai/translator/translator_translate.tentative.https.any.js +++ b/testing/web-platform/tests/ai/translator/translator_translate.tentative.https.any.js @@ -108,39 +108,9 @@ promise_test(async t => { }, 'Aborting Translator.translate().'); promise_test(async t => { - let monitorCalled = false; - let createdTranslator = false; - const progressEvents = []; - function monitor(m) { - monitorCalled = true; - - m.addEventListener('downloadprogress', e => { - // No progress events should have been fired after we've created the - // translator. - assert_false(createdTranslator); - - progressEvents.push(e); - }); - } - - await createTranslator({sourceLanguage: 'en', targetLanguage: 'ja', monitor}); - createdTranslator = true; - - // Monitor callback must be called. - assert_true(monitorCalled); - - // Must have at least 2 progress events, one for 0 and one for 1. - assert_greater_than_equal(progressEvents.length, 2); - - // 0 should be the first event and 1 should be the last event. - assert_equals(progressEvents.at(0).loaded, 0); - assert_equals(progressEvents.at(-1).loaded, 1); - - // All progress events must have a total of 1. - for (const progressEvent of progressEvents) { - assert_equals(progressEvent.total, 1); - } -}, 'Translator.create() monitor option is called correctly.'); + await testMonitor( + createTranslator, {sourceLanguage: 'en', targetLanguage: 'ja'}); +}, 'Translator.create() notifies its monitor on downloadprogress'); promise_test(async t => { const translator = diff --git a/testing/web-platform/tests/clear-site-data/clear-cache-bfcache.sub.https.html b/testing/web-platform/tests/clear-site-data/clear-cache-bfcache.sub.https.html new file mode 100644 index 00000000000..61d810f453b --- /dev/null +++ b/testing/web-platform/tests/clear-site-data/clear-cache-bfcache.sub.https.html @@ -0,0 +1,115 @@ + + + +Clear-Site-Data: cache for bfcache + + + + + + + + + diff --git a/testing/web-platform/tests/clear-site-data/support/clear-cache-helper.sub.js b/testing/web-platform/tests/clear-site-data/support/clear-cache-helper.sub.js index 2b2d35abfc5..5db0caf834c 100644 --- a/testing/web-platform/tests/clear-site-data/support/clear-cache-helper.sub.js +++ b/testing/web-platform/tests/clear-site-data/support/clear-cache-helper.sub.js @@ -39,7 +39,7 @@ function getUrl(cacheHelper, { } else { // !second_origin && !subdomain url += "{{hosts[][]}}"; } - url += ":{{ports[https][1]}}"; + url += ":{{ports[https][0]}}"; url += "/clear-site-data/support/clear-site-data-cache.py"; url = new URL(url); let params = new URLSearchParams(); @@ -130,4 +130,3 @@ function testCacheClear(test, params, assert) { openTestPageHelper(test, null, testUrls, 0, assert, resolve) }); } - diff --git a/testing/web-platform/tests/cookies/partitioned-cookies/partitioned-cookies-parallel-iframes.embed.tentative.https.html b/testing/web-platform/tests/cookies/partitioned-cookies/partitioned-cookies-parallel-iframes.embed.tentative.https.html index 68de567afee..e92a15bcc29 100644 --- a/testing/web-platform/tests/cookies/partitioned-cookies/partitioned-cookies-parallel-iframes.embed.tentative.https.html +++ b/testing/web-platform/tests/cookies/partitioned-cookies/partitioned-cookies-parallel-iframes.embed.tentative.https.html @@ -102,6 +102,9 @@ promise_test( async() => { assert_false(iframe2.contentWindow.document.cookie.includes(partitionedCookie), iframe2.contentWindow.document.cookie); + erase_cookie_from_js("partitionedCookie", "Secure; Path=/; SameSite=None; Partitioned"); + erase_cookie_from_js("second", "Secure; Path=/; SameSite=None; Partitioned"); + }, "Partitioned cookies set in same-site contexts are available in other same-site documents."); diff --git a/testing/web-platform/tests/css/CSS2/stacking-context/root-element-creates-stacking-context-ref.html b/testing/web-platform/tests/css/CSS2/stacking-context/root-element-creates-stacking-context-ref.html new file mode 100644 index 00000000000..9bb42041498 --- /dev/null +++ b/testing/web-platform/tests/css/CSS2/stacking-context/root-element-creates-stacking-context-ref.html @@ -0,0 +1,4 @@ + + +
+ diff --git a/testing/web-platform/tests/css/CSS2/stacking-context/root-element-creates-stacking-context.html b/testing/web-platform/tests/css/CSS2/stacking-context/root-element-creates-stacking-context.html new file mode 100644 index 00000000000..be7b027ee79 --- /dev/null +++ b/testing/web-platform/tests/css/CSS2/stacking-context/root-element-creates-stacking-context.html @@ -0,0 +1,9 @@ + + + + + + + + + diff --git a/testing/web-platform/tests/css/css-anchor-position/anchor-position-005-print-ref.html b/testing/web-platform/tests/css/css-anchor-position/anchor-position-005-print-ref.html index 94a1f8e55d3..2c8824bbc35 100644 --- a/testing/web-platform/tests/css/css-anchor-position/anchor-position-005-print-ref.html +++ b/testing/web-platform/tests/css/css-anchor-position/anchor-position-005-print-ref.html @@ -1,5 +1,10 @@ +

There should be a green square below, and no red.

diff --git a/testing/web-platform/tests/css/css-anchor-position/anchor-position-005-print.html b/testing/web-platform/tests/css/css-anchor-position/anchor-position-005-print.html index eeae841405a..98c663e3918 100644 --- a/testing/web-platform/tests/css/css-anchor-position/anchor-position-005-print.html +++ b/testing/web-platform/tests/css/css-anchor-position/anchor-position-005-print.html @@ -3,6 +3,11 @@ +

There should be a green square below, and no red.

diff --git a/testing/web-platform/tests/css/css-borders/tentative/corner-shape/corner-shape-interpolation.html b/testing/web-platform/tests/css/css-borders/tentative/corner-shape/corner-shape-interpolation.html new file mode 100644 index 00000000000..2ba9d43cb1f --- /dev/null +++ b/testing/web-platform/tests/css/css-borders/tentative/corner-shape/corner-shape-interpolation.html @@ -0,0 +1,130 @@ + + +corner-shape interpolation + + + + + + + + + + diff --git a/testing/web-platform/tests/css/css-borders/tentative/corner-shape/corner-shape-notch.html b/testing/web-platform/tests/css/css-borders/tentative/corner-shape/corner-shape-notch.html index 79f64070bd8..30acefe527f 100644 --- a/testing/web-platform/tests/css/css-borders/tentative/corner-shape/corner-shape-notch.html +++ b/testing/web-platform/tests/css/css-borders/tentative/corner-shape/corner-shape-notch.html @@ -10,7 +10,7 @@ height: 100px; border-radius: 25px; box-sizing: border-box; - corner-shape: notch superellipse(0.00001) round superellipse(0); + corner-shape: notch superellipse(-100) round superellipse(-infinity); }
diff --git a/testing/web-platform/tests/css/css-borders/tentative/corner-shape/corner-shape-render-fuzzy.html b/testing/web-platform/tests/css/css-borders/tentative/corner-shape/corner-shape-render-fuzzy.html index 5c276b6d5ac..dd1a32a15ae 100644 --- a/testing/web-platform/tests/css/css-borders/tentative/corner-shape/corner-shape-render-fuzzy.html +++ b/testing/web-platform/tests/css/css-borders/tentative/corner-shape/corner-shape-render-fuzzy.html @@ -7,19 +7,22 @@ - + - - + + - - + + - + + + + +
diff --git a/testing/web-platform/tests/css/css-borders/tentative/corner-shape/corner-shape-straight.html b/testing/web-platform/tests/css/css-borders/tentative/corner-shape/corner-shape-straight.html deleted file mode 100644 index 04cd37b5a31..00000000000 --- a/testing/web-platform/tests/css/css-borders/tentative/corner-shape/corner-shape-straight.html +++ /dev/null @@ -1,17 +0,0 @@ - - -CSS Borders and Box Decorations 4: 'corner-shape: straight' - - - -
diff --git a/testing/web-platform/tests/css/css-borders/tentative/corner-shape/resources/corner-shape.js b/testing/web-platform/tests/css/css-borders/tentative/corner-shape/resources/corner-shape.js index de471f95dfc..1e242e607a0 100644 --- a/testing/web-platform/tests/css/css-borders/tentative/corner-shape/resources/corner-shape.js +++ b/testing/web-platform/tests/css/css-borders/tentative/corner-shape/resources/corner-shape.js @@ -127,7 +127,7 @@ function render_rect_with_corner_shapes(style, ctx, width, height) { ctx.save(); ctx.translate(...offset); ctx.beginPath(); - ctx.lineTo(params['top-right'].inner_rect[0], -spread); + ctx.lineTo(params['top-right'].inner_rect[0], params['top-right'].inner_rect[1]); draw_inner_corner_from_params(params['top-right']); ctx.lineTo(params['top-right'].inner_rect[2], params['top-right'].inner_rect[3]) ctx.lineTo(params['bottom-right'].inner_rect[0], params['bottom-right'].inner_rect[1]) diff --git a/testing/web-platform/tests/css/css-borders/tentative/corner-shape/resources/corner-utils.js b/testing/web-platform/tests/css/css-borders/tentative/corner-shape/resources/corner-utils.js index bacaaa8a6e4..b62fd21501b 100644 --- a/testing/web-platform/tests/css/css-borders/tentative/corner-shape/resources/corner-utils.js +++ b/testing/web-platform/tests/css/css-borders/tentative/corner-shape/resources/corner-utils.js @@ -22,13 +22,10 @@ function compute_inner_curvature(curvature, outer_length, inner_length) { if (curvature === 0) return 0; if (curvature < 1) - return 1 / - compute_inner_curvature(1 / curvature, outer_length, inner_length); + return 1 / compute_inner_curvature(1 / curvature, outer_length, inner_length); const target_length = (inner_length - outer_length) / Math.SQRT2; return Math.log(0.5) / - Math.log( - (Math.pow(0.5, 1 / curvature) * outer_length + target_length) / - inner_length); + Math.log((superellipse(curvature).x * outer_length + target_length) / inner_length); } /** @@ -107,8 +104,6 @@ function resolve_corner_params(style, width, height, outset = null) { return Object.fromEntries( Object.entries(params).map(([corner, {outer, inset}]) => { const outer_rect = outer; - if (outset !== null) - inset = [-outset, -outset]; const shape = style[`corner-${corner}-shape`]; const s1 = Math.sign(outer[2] - outer[0]); const s2 = Math.sign(outer[3] - outer[1]); @@ -120,7 +115,7 @@ function resolve_corner_params(style, width, height, outset = null) { offset.reverse(); } - const inner_rect = [ + let inner_rect = [ outer_rect[0] + inner_offset[0] * offset[0], outer_rect[1] + inner_offset[1] * offset[1], outer_rect[2] + inner_offset[2] * offset[1], @@ -128,7 +123,16 @@ function resolve_corner_params(style, width, height, outset = null) { ]; let inner_shape = shape; - if (shape > 2 || shape < 0.5) { + if (outset) { + const new_width = width + outset * 2; + const new_height = height + outset * 2; + inner_rect = [ + (outer_rect[0] / width) * new_width - outset, + (outer_rect[1] / height) * new_height - outset, + (outer_rect[2] / width) * new_width - outset, + (outer_rect[3] / height) * new_height - outset + ] + } else if (shape > 2 || shape < 0.5) { const outer_length = Math.hypot( outer_rect[2] - outer_rect[0], outer_rect[3] - outer_rect[1]); const inner_length = Math.hypot( diff --git a/testing/web-platform/tests/css/css-borders/tentative/corner-shape/resources/resolve-corner-style.js b/testing/web-platform/tests/css/css-borders/tentative/corner-shape/resources/resolve-corner-style.js index 93e5112ea40..26f18a02a33 100644 --- a/testing/web-platform/tests/css/css-borders/tentative/corner-shape/resources/resolve-corner-style.js +++ b/testing/web-platform/tests/css/css-borders/tentative/corner-shape/resources/resolve-corner-style.js @@ -3,28 +3,33 @@ // found in the LICENSE file. const keywords = { - notch: 0, - scoop: 0.5, - bevel: 1, - round: 2, - squircle: 4, - straight: 1000, + notch: -16, + scoop: -1, + bevel: 0, + round: 1, + squircle: 2, + square: 16, }; function resolve_corner_style(style, w, h) { ['top', 'bottom'].forEach((vSide) => ['left', 'right'].forEach((hSide) => { - let shape = style[`corner-${vSide}-${hSide}-shape`] || + let shape_param = style[`corner-${vSide}-${hSide}-shape`] || style['corner-shape'] || 'round'; - const match = shape.match(/superellipse\((\.?[0-9]+(.[0-9]+)?)\)/); - shape = match ? +match[1] : keywords[shape]; + const match = shape_param.match(/superellipse\((-?(infinity|[0-9]*(\.[0-9]+)?))\)/i); + shape_param = match ? match[1] : keywords[shape_param]; const hWidth = parseFloat(style[`border-${hSide}-width`] || style['border-width'] || 0); const vWidth = parseFloat(style[`border-${vSide}-width`] || style['border-width'] || 0); let radius = style[`border-${vSide}-${hSide}-radius`] || style['border-radius'] || 0; if (!Array.isArray(radius)) radius = [radius, radius]; - if (shape > 1000) + let shape = 0; + if (shape_param >= keywords["square"] || shape_param == "infinity") shape = 1000; + else if (shape_param <= keywords["notch"] || shape_param == "-infinity") + shape = 0; + else + shape = Math.pow(2, shape_param); if (String(radius[0]).endsWith('%')) radius[0] = (parseFloat(radius[0]) * w) / 100; if (String(radius[1]).endsWith('%')) diff --git a/testing/web-platform/tests/css/css-borders/tentative/parsing/corner-shape-computed.html b/testing/web-platform/tests/css/css-borders/tentative/parsing/corner-shape-computed.html index 1d084e23397..e1b458fd06e 100644 --- a/testing/web-platform/tests/css/css-borders/tentative/parsing/corner-shape-computed.html +++ b/testing/web-platform/tests/css/css-borders/tentative/parsing/corner-shape-computed.html @@ -15,12 +15,12 @@ test_computed_value("corner-top-left-shape", "round"); test_computed_value("corner-top-left-shape", "scoop"); test_computed_value("corner-top-left-shape", "superellipse(5)"); test_computed_value("corner-top-left-shape", "superellipse(0.2)"); -test_computed_value("corner-top-left-shape", "superellipse(0)", "notch"); -test_computed_value("corner-top-left-shape", "superellipse(infinity)", "straight"); -test_computed_value("corner-top-left-shape", "superellipse(2)", "round"); -test_computed_value("corner-top-left-shape", "superellipse(1)", "bevel"); -test_computed_value("corner-top-left-shape", "superellipse(4)", "squircle"); -test_computed_value("corner-top-left-shape", "superellipse( .5)", "scoop"); +test_computed_value("corner-top-left-shape", "superellipse(-infinity)", "notch"); +test_computed_value("corner-top-left-shape", "superellipse(infinity)", "square"); +test_computed_value("corner-top-left-shape", "superellipse(1)", "round"); +test_computed_value("corner-top-left-shape", "superellipse(0)", "bevel"); +test_computed_value("corner-top-left-shape", "superellipse(2)", "squircle"); +test_computed_value("corner-top-left-shape", "superellipse( -1)", "scoop"); test_computed_value("corner-top-right-shape", "round"); test_computed_value("corner-top-right-shape", "superellipse(5)"); test_computed_value("corner-bottom-right-shape", "scoop"); @@ -28,14 +28,14 @@ test_computed_value("corner-bottom-left-shape", "superellipse(5)"); test_computed_value("corner-shape", "superellipse(5) round"); test_computed_value("corner-shape", "round"); test_computed_value("corner-shape", "bevel superellipse(0.1) round squircle"); -test_computed_value("corner-shape", "superellipse(0.1) superellipse(3) superellipse(7) superellipse(0.1)"); +test_computed_value("corner-shape", "superellipse(-5) superellipse(3) superellipse(7) superellipse(-5.5)"); test_computed_value("corner-shape", "round round round round", "round"); test_computed_value("corner-shape", "round scoop"); test_computed_value("corner-shape", "round scoop round scoop", "round scoop"); -test_computed_value("corner-shape", "bevel superellipse(2)", "bevel round"); -test_computed_value("corner-shape", "superellipse(0.5) superellipse(3) straight", "scoop superellipse(3) straight"); -test_computed_value("corner-shape", "superellipse(0.5) superellipse(3) superellipse(1)", "scoop superellipse(3) bevel"); -test_computed_value("corner-shape", "bevel superellipse(2) squircle round", "bevel round squircle"); -test_computed_value("corner-shape", "superellipse(0.5) superellipse(3) superellipse(1) superellipse(infinity)", "scoop superellipse(3) bevel straight"); +test_computed_value("corner-shape", "bevel superellipse(1)", "bevel round"); +test_computed_value("corner-shape", "superellipse(-1) superellipse(3) square", "scoop superellipse(3) square"); +test_computed_value("corner-shape", "superellipse(-1) superellipse(3) superellipse(0)", "scoop superellipse(3) bevel"); +test_computed_value("corner-shape", "bevel superellipse(1) squircle round", "bevel round squircle"); +test_computed_value("corner-shape", "superellipse(-1) superellipse(3) superellipse(0) superellipse(infinity)", "scoop superellipse(3) bevel square"); diff --git a/testing/web-platform/tests/css/css-borders/tentative/parsing/corner-shape-invalid.html b/testing/web-platform/tests/css/css-borders/tentative/parsing/corner-shape-invalid.html index 582617a634a..6aee773f4a4 100644 --- a/testing/web-platform/tests/css/css-borders/tentative/parsing/corner-shape-invalid.html +++ b/testing/web-platform/tests/css/css-borders/tentative/parsing/corner-shape-invalid.html @@ -17,6 +17,8 @@ test_invalid_value("corner-shape", "round round round round round"); test_invalid_value("corner-shape", "superellipse(8 8)"); test_invalid_value("corner-shape", "superellipse(,)"); test_invalid_value("corner-shape", "superellipse(4,0.1)"); +test_invalid_value("corner-shape", "straight"); +test_invalid_value("corner-shape", "nonsense"); test_invalid_value("corner-shape", "superellipse(foo)"); test_invalid_value("corner-shape", "superellipse(1 abc)"); test_invalid_value("corner-shape", "superellipse(1) / bevel"); diff --git a/testing/web-platform/tests/css/css-borders/tentative/parsing/corner-shape-valid.html b/testing/web-platform/tests/css/css-borders/tentative/parsing/corner-shape-valid.html index 32aaa89fc5b..0ad14c81a74 100644 --- a/testing/web-platform/tests/css/css-borders/tentative/parsing/corner-shape-valid.html +++ b/testing/web-platform/tests/css/css-borders/tentative/parsing/corner-shape-valid.html @@ -15,7 +15,7 @@ test_valid_value(prop, "notch"); test_valid_value(prop, "bevel"); test_valid_value(prop, "squircle"); - test_valid_value(prop, "straight"); + test_valid_value(prop, "square"); test_valid_value(prop, "superellipse(2)"); test_valid_value(prop, "superellipse(.5)", "superellipse(0.5)"); test_valid_value(prop, "superellipse(7)"); @@ -23,6 +23,9 @@ test_valid_value(prop, "superellipse( 0)", "superellipse(0)"); test_valid_value(prop, "superellipse(2 )", "superellipse(2)"); test_valid_value(prop, "superellipse(infinity)"); + test_valid_value(prop, "superellipse(-infinity)"); + test_valid_value(prop, "superellipse(-0.5)"); + test_valid_value(prop, "superellipse(-4)"); test_valid_value(prop, "superellipse(calc(0.5 * 4))", "superellipse(calc(2))"); } @@ -40,7 +43,8 @@ test_valid_value("corner-shape", "round scoop"); test_valid_value("corner-shape", "round scoop round scoop", "round scoop"); test_valid_value("corner-shape", "bevel superellipse(2)"); - test_valid_value("corner-shape", "superellipse(0.5) superellipse(3) straight"); + test_valid_value("corner-shape", "superellipse(0.5) superellipse(3) square"); + test_valid_value("corner-shape", "superellipse(-0.5) superellipse(3) square superellipse(-30)"); test_valid_value("corner-shape", "superellipse(0.5) superellipse(3) superellipse(1)"); test_valid_value("corner-shape", "bevel superellipse(2) squircle round", "bevel superellipse(2) squircle round"); test_valid_value("corner-shape", "superellipse(0.5) superellipse(3) superellipse(1) superellipse(infinity)"); diff --git a/testing/web-platform/tests/css/css-break/block-001-wm-vlr-print.html b/testing/web-platform/tests/css/css-break/block-001-wm-vlr-print.html index 6cd5ea07c7b..bfb48428dd5 100644 --- a/testing/web-platform/tests/css/css-break/block-001-wm-vlr-print.html +++ b/testing/web-platform/tests/css/css-break/block-001-wm-vlr-print.html @@ -11,6 +11,10 @@ margin: 0.5in; } +:root { + print-color-adjust: exact; +} + html,body { color:black; background-color:white; font:20px/1 monospace; padding:0; margin:0; writing-mode: vertical-lr; diff --git a/testing/web-platform/tests/css/css-break/block-001-wm-vrl-print.html b/testing/web-platform/tests/css/css-break/block-001-wm-vrl-print.html index 6e073121c6a..60ee504dd77 100644 --- a/testing/web-platform/tests/css/css-break/block-001-wm-vrl-print.html +++ b/testing/web-platform/tests/css/css-break/block-001-wm-vrl-print.html @@ -11,6 +11,10 @@ margin: 0.5in; } +:root { + print-color-adjust: exact; +} + html,body { color:black; background-color:white; font:20px/1 monospace; padding:0; margin:0; writing-mode: vertical-rl; diff --git a/testing/web-platform/tests/css/css-break/block-002-wm-vlr-print.html b/testing/web-platform/tests/css/css-break/block-002-wm-vlr-print.html index 99949623c39..d427242dc40 100644 --- a/testing/web-platform/tests/css/css-break/block-002-wm-vlr-print.html +++ b/testing/web-platform/tests/css/css-break/block-002-wm-vlr-print.html @@ -11,6 +11,10 @@ margin: 0.5in; } +:root { + print-color-adjust: exact; +} + html,body { color:black; background-color:white; font:20px/1 monospace; padding:0; margin:0; writing-mode: vertical-lr; diff --git a/testing/web-platform/tests/css/css-break/block-002-wm-vrl-print.html b/testing/web-platform/tests/css/css-break/block-002-wm-vrl-print.html index 084b5325b7a..0ca2b73012f 100644 --- a/testing/web-platform/tests/css/css-break/block-002-wm-vrl-print.html +++ b/testing/web-platform/tests/css/css-break/block-002-wm-vrl-print.html @@ -11,6 +11,10 @@ margin: 0.5in; } +:root { + print-color-adjust: exact; +} + html,body { color:black; background-color:white; font:20px/1 monospace; padding:0; margin:0; writing-mode: vertical-rl; diff --git a/testing/web-platform/tests/css/css-break/break-inside-avoid-multicol-001-print-ref.html b/testing/web-platform/tests/css/css-break/break-inside-avoid-multicol-001-print-ref.html index baf114d35c9..0e6f2500d92 100644 --- a/testing/web-platform/tests/css/css-break/break-inside-avoid-multicol-001-print-ref.html +++ b/testing/web-platform/tests/css/css-break/break-inside-avoid-multicol-001-print-ref.html @@ -10,6 +10,9 @@ size: 5in 3in; margin: 0.5in; } + :root { + print-color-adjust: exact; + } body { margin: 0; } diff --git a/testing/web-platform/tests/css/css-break/break-inside-avoid-multicol-001-print.html b/testing/web-platform/tests/css/css-break/break-inside-avoid-multicol-001-print.html index a97ac9f5cda..0398110fe3d 100644 --- a/testing/web-platform/tests/css/css-break/break-inside-avoid-multicol-001-print.html +++ b/testing/web-platform/tests/css/css-break/break-inside-avoid-multicol-001-print.html @@ -13,6 +13,9 @@ size: 5in 3in; margin: 0.5in; } + :root { + print-color-adjust: exact; + } body { margin: 0; } diff --git a/testing/web-platform/tests/css/css-break/break-nested-float-in-table-001-print-ref.html b/testing/web-platform/tests/css/css-break/break-nested-float-in-table-001-print-ref.html index 3459e25edd6..ffd4196708f 100644 --- a/testing/web-platform/tests/css/css-break/break-nested-float-in-table-001-print-ref.html +++ b/testing/web-platform/tests/css/css-break/break-nested-float-in-table-001-print-ref.html @@ -10,6 +10,10 @@

Test passes if there is two purple rectangles at the start of both page 2 and 3 when printing the page (Ctrl+P, with "print backgrounds" enabled).

diff --git a/testing/web-platform/tests/css/css-break/grid/monolithic-overflow-print.html b/testing/web-platform/tests/css/css-break/grid/monolithic-overflow-print.html index 9b174caf96c..11ce95a34e7 100644 --- a/testing/web-platform/tests/css/css-break/grid/monolithic-overflow-print.html +++ b/testing/web-platform/tests/css/css-break/grid/monolithic-overflow-print.html @@ -2,6 +2,11 @@ +

Test passes if there is two purple rectangles at the start of both page 2 and 3 when printing the page (Ctrl+P, with "print backgrounds" enabled).

diff --git a/testing/web-platform/tests/css/css-break/ink-overflow-001-print.html b/testing/web-platform/tests/css/css-break/ink-overflow-001-print.html index 495153d3d42..7d7e8518999 100644 --- a/testing/web-platform/tests/css/css-break/ink-overflow-001-print.html +++ b/testing/web-platform/tests/css/css-break/ink-overflow-001-print.html @@ -9,6 +9,10 @@ margin: 0.5in; } +:root { + print-color-adjust: exact; +} + html,body { color:black; background-color:white; font:20px/1 monospace; padding:0; margin:0; } diff --git a/testing/web-platform/tests/css/css-break/table/table-fragmentation-001a-print-ref.html b/testing/web-platform/tests/css/css-break/table/table-fragmentation-001a-print-ref.html index d4229ebb873..dd0e61badef 100644 --- a/testing/web-platform/tests/css/css-break/table/table-fragmentation-001a-print-ref.html +++ b/testing/web-platform/tests/css/css-break/table/table-fragmentation-001a-print-ref.html @@ -5,6 +5,9 @@

There should be a green square on the second page, and no red.

diff --git a/testing/web-platform/tests/css/css-break/transform-023-print.html b/testing/web-platform/tests/css/css-break/transform-023-print.html index 7650571abe1..53abb1939bb 100644 --- a/testing/web-platform/tests/css/css-break/transform-023-print.html +++ b/testing/web-platform/tests/css/css-break/transform-023-print.html @@ -2,6 +2,11 @@ +

There should be a green square on the second page, and no red.

diff --git a/testing/web-platform/tests/css/css-break/transform-024-print-ref.html b/testing/web-platform/tests/css/css-break/transform-024-print-ref.html index 766c415a1be..582794ca046 100644 --- a/testing/web-platform/tests/css/css-break/transform-024-print-ref.html +++ b/testing/web-platform/tests/css/css-break/transform-024-print-ref.html @@ -1,6 +1,9 @@
diff --git a/testing/web-platform/tests/css/css-break/transform-024-print.html b/testing/web-platform/tests/css/css-break/transform-024-print.html index 510b4c2e44f..22b6cd12075 100644 --- a/testing/web-platform/tests/css/css-break/transform-024-print.html +++ b/testing/web-platform/tests/css/css-break/transform-024-print.html @@ -3,6 +3,9 @@
diff --git a/testing/web-platform/tests/css/css-color/WEB_FEATURES.yml b/testing/web-platform/tests/css/css-color/WEB_FEATURES.yml index a7e2044aeaa..275611f531e 100644 --- a/testing/web-platform/tests/css/css-color/WEB_FEATURES.yml +++ b/testing/web-platform/tests/css/css-color/WEB_FEATURES.yml @@ -26,3 +26,6 @@ features: - name: hwb files: - hwb-* +- name: relative-color + files: + - relative-* diff --git a/testing/web-platform/tests/css/css-color/parsing/WEB_FEATURES.yml b/testing/web-platform/tests/css/css-color/parsing/WEB_FEATURES.yml index cc8273c2adc..0bb97b1e1d9 100644 --- a/testing/web-platform/tests/css/css-color/parsing/WEB_FEATURES.yml +++ b/testing/web-platform/tests/css/css-color/parsing/WEB_FEATURES.yml @@ -3,3 +3,7 @@ features: files: - "*-color-mix-*" - color-mix-out-of-gamut.html +- name: relative-color + files: + - "relative-*" + - "*-relative-*" diff --git a/testing/web-platform/tests/css/css-flexbox/alignment/flex-content-alignment-with-abspos-001.html b/testing/web-platform/tests/css/css-flexbox/alignment/flex-content-alignment-with-abspos-001.html new file mode 100644 index 00000000000..1a70500f2c5 --- /dev/null +++ b/testing/web-platform/tests/css/css-flexbox/alignment/flex-content-alignment-with-abspos-001.html @@ -0,0 +1,55 @@ + + + +CSS Flex Layout Test: dynamic content alignment with abspos elements. + + + + + + + + + + + + + + + + + +
+
+
+ + + diff --git a/testing/web-platform/tests/css/css-flexbox/break-nested-float-in-flex-item-001-print.html b/testing/web-platform/tests/css/css-flexbox/break-nested-float-in-flex-item-001-print.html index 2fbf939f6a5..29796771451 100644 --- a/testing/web-platform/tests/css/css-flexbox/break-nested-float-in-flex-item-001-print.html +++ b/testing/web-platform/tests/css/css-flexbox/break-nested-float-in-flex-item-001-print.html @@ -13,6 +13,10 @@ - - -
-
-
- - - diff --git a/testing/web-platform/tests/css/css-fonts/parsing/font-stretch-invalid.html b/testing/web-platform/tests/css/css-fonts/parsing/font-stretch-invalid.html deleted file mode 100644 index 0f7f7c88975..00000000000 --- a/testing/web-platform/tests/css/css-fonts/parsing/font-stretch-invalid.html +++ /dev/null @@ -1,21 +0,0 @@ - - - - -CSS Fonts Module Level 4: parsing font-stretch with invalid values - - - - - - - - - - - diff --git a/testing/web-platform/tests/css/css-fonts/parsing/font-stretch-valid.html b/testing/web-platform/tests/css/css-fonts/parsing/font-stretch-valid.html deleted file mode 100644 index 90cde603133..00000000000 --- a/testing/web-platform/tests/css/css-fonts/parsing/font-stretch-valid.html +++ /dev/null @@ -1,32 +0,0 @@ - - - - -CSS Fonts Module Level 4: parsing font-stretch with valid values - - - - - - - - - - diff --git a/testing/web-platform/tests/css/css-fonts/parsing/font-width-computed.html b/testing/web-platform/tests/css/css-fonts/parsing/font-width-computed.html new file mode 100644 index 00000000000..4e38a5d5e9a --- /dev/null +++ b/testing/web-platform/tests/css/css-fonts/parsing/font-width-computed.html @@ -0,0 +1,43 @@ + + + + +CSS Fonts Module Level 4: getComputedStyle().fontWidth + + + + + + + + +
+
+
+ + + diff --git a/testing/web-platform/tests/css/css-fonts/parsing/font-width-invalid.html b/testing/web-platform/tests/css/css-fonts/parsing/font-width-invalid.html new file mode 100644 index 00000000000..7f30eab3e5b --- /dev/null +++ b/testing/web-platform/tests/css/css-fonts/parsing/font-width-invalid.html @@ -0,0 +1,23 @@ + + + + +CSS Fonts Module Level 4: parsing font-width with invalid values + + + + + + + + + + + diff --git a/testing/web-platform/tests/css/css-fonts/parsing/font-width-valid.html b/testing/web-platform/tests/css/css-fonts/parsing/font-width-valid.html new file mode 100644 index 00000000000..44cbb37fd60 --- /dev/null +++ b/testing/web-platform/tests/css/css-fonts/parsing/font-width-valid.html @@ -0,0 +1,34 @@ + + + + +CSS Fonts Module Level 4: parsing font-width with valid values + + + + + + + + + + diff --git a/testing/web-platform/tests/css/css-gaps/tentative/grid/grid-gap-decorations-016-ref.html b/testing/web-platform/tests/css/css-gaps/tentative/grid/grid-gap-decorations-016-ref.html new file mode 100644 index 00000000000..1d368aba9d4 --- /dev/null +++ b/testing/web-platform/tests/css/css-gaps/tentative/grid/grid-gap-decorations-016-ref.html @@ -0,0 +1,111 @@ + + + + +
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ +
+
+
+
+ +
+
+
+
diff --git a/testing/web-platform/tests/css/css-gaps/tentative/grid/grid-gap-decorations-016.html b/testing/web-platform/tests/css/css-gaps/tentative/grid/grid-gap-decorations-016.html new file mode 100644 index 00000000000..a0618e44b56 --- /dev/null +++ b/testing/web-platform/tests/css/css-gaps/tentative/grid/grid-gap-decorations-016.html @@ -0,0 +1,60 @@ + + + CSS Gap Decorations: Grid gaps are painted with multiple line-style values for *-rule-style. + + + + + +
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/testing/web-platform/tests/css/css-gaps/tentative/grid/grid-gap-decorations-017-ref.html b/testing/web-platform/tests/css/css-gaps/tentative/grid/grid-gap-decorations-017-ref.html new file mode 100644 index 00000000000..2aa226eb092 --- /dev/null +++ b/testing/web-platform/tests/css/css-gaps/tentative/grid/grid-gap-decorations-017-ref.html @@ -0,0 +1,135 @@ + + + + +
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ +
+
+
+
+
+ +
+
+
+
+
diff --git a/testing/web-platform/tests/css/css-gaps/tentative/grid/grid-gap-decorations-017.html b/testing/web-platform/tests/css/css-gaps/tentative/grid/grid-gap-decorations-017.html new file mode 100644 index 00000000000..be0f7fc2751 --- /dev/null +++ b/testing/web-platform/tests/css/css-gaps/tentative/grid/grid-gap-decorations-017.html @@ -0,0 +1,69 @@ + + + CSS Gap Decorations: Grid gaps are painted with multiple line-style values for *-rule-style. + + + + + +
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/testing/web-platform/tests/css/css-gaps/tentative/grid/grid-gap-decorations-018-ref.html b/testing/web-platform/tests/css/css-gaps/tentative/grid/grid-gap-decorations-018-ref.html new file mode 100644 index 00000000000..6fcc175e68d --- /dev/null +++ b/testing/web-platform/tests/css/css-gaps/tentative/grid/grid-gap-decorations-018-ref.html @@ -0,0 +1,134 @@ + + + + +
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ +
+
+
+
+
+ +
+
+
+
+
diff --git a/testing/web-platform/tests/css/css-gaps/tentative/grid/grid-gap-decorations-018.html b/testing/web-platform/tests/css/css-gaps/tentative/grid/grid-gap-decorations-018.html new file mode 100644 index 00000000000..dc5e2e38e0b --- /dev/null +++ b/testing/web-platform/tests/css/css-gaps/tentative/grid/grid-gap-decorations-018.html @@ -0,0 +1,71 @@ + + + CSS Gap Decorations: Grid gaps are painted with multiple line-width values for *-rule-width. + + + + + +
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/testing/web-platform/tests/css/css-gaps/tentative/grid/grid-gap-decorations-019-ref.html b/testing/web-platform/tests/css/css-gaps/tentative/grid/grid-gap-decorations-019-ref.html new file mode 100644 index 00000000000..0705ba075ab --- /dev/null +++ b/testing/web-platform/tests/css/css-gaps/tentative/grid/grid-gap-decorations-019-ref.html @@ -0,0 +1,137 @@ + + + + +
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ + +
+
+
+
+
+ + +
+
+
+
+
diff --git a/testing/web-platform/tests/css/css-gaps/tentative/grid/grid-gap-decorations-019.html b/testing/web-platform/tests/css/css-gaps/tentative/grid/grid-gap-decorations-019.html new file mode 100644 index 00000000000..637e0467a80 --- /dev/null +++ b/testing/web-platform/tests/css/css-gaps/tentative/grid/grid-gap-decorations-019.html @@ -0,0 +1,71 @@ + + + CSS Gap Decorations: Grid gaps are painted with multiple line-width values for *-rule-width. + + + + + +
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/testing/web-platform/tests/css/css-grid/grid-extrinsically-sized-mutations.html b/testing/web-platform/tests/css/css-grid/grid-extrinsically-sized-mutations.html new file mode 100644 index 00000000000..75600046e82 --- /dev/null +++ b/testing/web-platform/tests/css/css-grid/grid-extrinsically-sized-mutations.html @@ -0,0 +1,121 @@ + + + + + + + + + + + + + + + +
+
+
x x x x
+
+
+ + +
+
+
x x x
+
+
+ + +
+
xx
+
+ + +
+
xx
+
+ + +
+
+
x x x
+
+
+ + +
+
+
xx
+
+
+ + +
+
+
x x x
+
+
+ + +
+
+
xx xx
+
+
+ + + + diff --git a/testing/web-platform/tests/css/css-grid/grid-fragmentation-between-rows-001-print-ref.tentative.html b/testing/web-platform/tests/css/css-grid/grid-fragmentation-between-rows-001-print-ref.tentative.html index 78464712c5c..ba97d33c8b6 100644 --- a/testing/web-platform/tests/css/css-grid/grid-fragmentation-between-rows-001-print-ref.tentative.html +++ b/testing/web-platform/tests/css/css-grid/grid-fragmentation-between-rows-001-print-ref.tentative.html @@ -1,4 +1,9 @@ +
Test passes if there is two purple boxes on both page 1 and page 2 in print mode. (Ctrl+P, with "print backgrounds" enabled)
diff --git a/testing/web-platform/tests/css/css-grid/grid-fragmentation-between-rows-001-print.tentative.html b/testing/web-platform/tests/css/css-grid/grid-fragmentation-between-rows-001-print.tentative.html index 01fd97528fc..af345718e6e 100644 --- a/testing/web-platform/tests/css/css-grid/grid-fragmentation-between-rows-001-print.tentative.html +++ b/testing/web-platform/tests/css/css-grid/grid-fragmentation-between-rows-001-print.tentative.html @@ -2,6 +2,11 @@ +
Test passes if there is two purple boxes on both page 1 and page 2 in print mode. (Ctrl+P, with "print backgrounds" enabled)
diff --git a/testing/web-platform/tests/css/css-images/conic-gradient-angle-negative.html b/testing/web-platform/tests/css/css-images/conic-gradient-angle-negative.html index 5a7030d0ff0..faf9a2d2033 100644 --- a/testing/web-platform/tests/css/css-images/conic-gradient-angle-negative.html +++ b/testing/web-platform/tests/css/css-images/conic-gradient-angle-negative.html @@ -3,7 +3,7 @@ Conic gradient with negative angle parameter - + +
This should be a lime background.
+
This should be a lime background.
+
This should be a lime background.
\ No newline at end of file diff --git a/testing/web-platform/tests/css/css-images/gradient/gradient-infinity-003.html b/testing/web-platform/tests/css/css-images/gradient/gradient-infinity-003.html new file mode 100644 index 00000000000..74008e41691 --- /dev/null +++ b/testing/web-platform/tests/css/css-images/gradient/gradient-infinity-003.html @@ -0,0 +1,27 @@ + + + + + +All boxes should have a lime background. + +
This should be a lime background.
+
This should be a lime background.
+
This should be a lime background.
\ No newline at end of file diff --git a/testing/web-platform/tests/css/css-images/multiple-position-color-stop-conic.html b/testing/web-platform/tests/css/css-images/multiple-position-color-stop-conic.html index 41aa505c879..50ec959e221 100644 --- a/testing/web-platform/tests/css/css-images/multiple-position-color-stop-conic.html +++ b/testing/web-platform/tests/css/css-images/multiple-position-color-stop-conic.html @@ -2,7 +2,7 @@ Conic gradient with a two position color stop - + +
This time, Mark, who had always been the center of attention in +any social gathering, walked into the room uncharacteristically quietly, barely speaking as he settled into a chair.
diff --git a/testing/web-platform/tests/css/css-overflow/line-clamp/block-ellipsis-014.html b/testing/web-platform/tests/css/css-overflow/line-clamp/block-ellipsis-014.html new file mode 100644 index 00000000000..4961cf3a7a2 --- /dev/null +++ b/testing/web-platform/tests/css/css-overflow/line-clamp/block-ellipsis-014.html @@ -0,0 +1,18 @@ + + +CSS Overflow: the block-ellipsis can be placed at hyphenation opportunities, even if the line would not break + + + + + +
This time, Mark, who had always been the center of attention in +any social gathering, walked into the room uncharacteristi­cally quietly, barely speaking as he settled into a chair.
diff --git a/testing/web-platform/tests/css/css-overflow/line-clamp/block-ellipsis-015.html b/testing/web-platform/tests/css/css-overflow/line-clamp/block-ellipsis-015.html new file mode 100644 index 00000000000..0a0046711c0 --- /dev/null +++ b/testing/web-platform/tests/css/css-overflow/line-clamp/block-ellipsis-015.html @@ -0,0 +1,18 @@ + + +CSS Overflow: the block-ellipsis can be inserted at wbr, even if the line would not break there + + + + + +
This time, Mark, who had always been the center of attention in +any social gathering, walked into the room uncharacteristically quietly, barely speaking as he settled into a chair.
diff --git a/testing/web-platform/tests/css/css-overflow/line-clamp/block-ellipsis-016.html b/testing/web-platform/tests/css/css-overflow/line-clamp/block-ellipsis-016.html new file mode 100644 index 00000000000..00e4c601e36 --- /dev/null +++ b/testing/web-platform/tests/css/css-overflow/line-clamp/block-ellipsis-016.html @@ -0,0 +1,19 @@ + + +CSS Overflow: soft wrap opportunities created by overflow-wrap are ignored for inserting block-ellipsis + + + + + +
This time, Mark, who had always been the center of attention in +any social gathering, walked into the room uncharacteristically quietly, barely speaking as he settled into a chair.
diff --git a/testing/web-platform/tests/css/css-overflow/line-clamp/block-ellipsis-017.html b/testing/web-platform/tests/css/css-overflow/line-clamp/block-ellipsis-017.html new file mode 100644 index 00000000000..05f7896f565 --- /dev/null +++ b/testing/web-platform/tests/css/css-overflow/line-clamp/block-ellipsis-017.html @@ -0,0 +1,19 @@ + + +CSS Overflow: soft wrap opportunities created by word-break are not ignored for inserting block-ellipsis + + + + + +
This time, Mark, who had always been the center of attention in +any social gathering, walked into the room uncharacteristically quietly, barely speaking as he settled into a chair.
diff --git a/testing/web-platform/tests/css/css-overflow/line-clamp/block-ellipsis-018.html b/testing/web-platform/tests/css/css-overflow/line-clamp/block-ellipsis-018.html new file mode 100644 index 00000000000..9c4ae82d003 --- /dev/null +++ b/testing/web-platform/tests/css/css-overflow/line-clamp/block-ellipsis-018.html @@ -0,0 +1,54 @@ + + +CSS Overflow: block-ellipsis effect on a fully displaced line's height + + + + + + + +

Test passes if there is a filled green square and no red.

+ +
+
+ XXXX
+ XXXX
+ XXXX
+ XXXX +
+
+ XXXXXXXXX XXX +
+
diff --git a/testing/web-platform/tests/css/css-overflow/line-clamp/block-ellipsis-019.tentative.html b/testing/web-platform/tests/css/css-overflow/line-clamp/block-ellipsis-019.tentative.html new file mode 100644 index 00000000000..afee5d6eddc --- /dev/null +++ b/testing/web-platform/tests/css/css-overflow/line-clamp/block-ellipsis-019.tentative.html @@ -0,0 +1,47 @@ + + +CSS Overflow: block-ellipsis and ::first-letter + + + + + + + +

Test passes if there is a filled green square and no red.

+ +
+
X
+ +
+ + 0000000000
+ 0000000000 +
+
+ +
diff --git a/testing/web-platform/tests/css/css-overflow/line-clamp/block-ellipsis-020.tentative.html b/testing/web-platform/tests/css/css-overflow/line-clamp/block-ellipsis-020.tentative.html new file mode 100644 index 00000000000..473aad392c1 --- /dev/null +++ b/testing/web-platform/tests/css/css-overflow/line-clamp/block-ellipsis-020.tentative.html @@ -0,0 +1,48 @@ + + +CSS Overflow: block-ellipsis and ::first-letter + + + + + + + +

Test passes if there is a filled green square and no red.

+ +
+
X
+ +
+
+ + 0000000000
+ 0000000000 +
+
+
+
diff --git a/testing/web-platform/tests/css/css-overflow/line-clamp/block-ellipsis-021.tentative.html b/testing/web-platform/tests/css/css-overflow/line-clamp/block-ellipsis-021.tentative.html new file mode 100644 index 00000000000..517245ac8b0 --- /dev/null +++ b/testing/web-platform/tests/css/css-overflow/line-clamp/block-ellipsis-021.tentative.html @@ -0,0 +1,50 @@ + + +CSS Overflow: block-ellipsis and ::first-line + + + + + + + +

Test passes if there is a filled green square and no red.

+ +
+
XXXX
XXXX
X
+ +
+ XXXX
+ XXXX +
+ + XXXXXXXXXXXXX
+ XXXXXXXXXXXXX +
+
+
+
diff --git a/testing/web-platform/tests/css/css-overflow/line-clamp/reference/block-ellipsis-014-ref.html b/testing/web-platform/tests/css/css-overflow/line-clamp/reference/block-ellipsis-014-ref.html new file mode 100644 index 00000000000..d135a74cfad --- /dev/null +++ b/testing/web-platform/tests/css/css-overflow/line-clamp/reference/block-ellipsis-014-ref.html @@ -0,0 +1,13 @@ + + +CSS Reference + +
This time, Mark, who had always been the center of attention in +any social gathering, walked into the room uncharacteristi-…
diff --git a/testing/web-platform/tests/css/css-overflow/line-clamp/reference/block-ellipsis-015-ref.html b/testing/web-platform/tests/css/css-overflow/line-clamp/reference/block-ellipsis-015-ref.html new file mode 100644 index 00000000000..baa8dd1a8d7 --- /dev/null +++ b/testing/web-platform/tests/css/css-overflow/line-clamp/reference/block-ellipsis-015-ref.html @@ -0,0 +1,13 @@ + + +CSS Reference + +
This time, Mark, who had always been the center of attention in +any social gathering, walked into the room uncharacteristi…
diff --git a/testing/web-platform/tests/css/css-overflow/line-clamp/reference/block-ellipsis-017-ref.html b/testing/web-platform/tests/css/css-overflow/line-clamp/reference/block-ellipsis-017-ref.html new file mode 100644 index 00000000000..7a801508e2e --- /dev/null +++ b/testing/web-platform/tests/css/css-overflow/line-clamp/reference/block-ellipsis-017-ref.html @@ -0,0 +1,13 @@ + + +CSS Reference + +
This time, Mark, who had always been the center of attention in +any social gathering, walked into the room uncharacteristicall…
diff --git a/testing/web-platform/tests/css/css-overflow/nested-scroll-markers-under-content-visibility-auto-ref.html b/testing/web-platform/tests/css/css-overflow/nested-scroll-markers-under-content-visibility-auto-ref.html new file mode 100644 index 00000000000..afb2d5d65a2 --- /dev/null +++ b/testing/web-platform/tests/css/css-overflow/nested-scroll-markers-under-content-visibility-auto-ref.html @@ -0,0 +1,65 @@ + + +::scroll-markers with content-visibility: auto ancestors and subscrollers (ref) + + + +
+
+
+
+
+
+
+
+
+
+
+
+
+
+ diff --git a/testing/web-platform/tests/css/css-overflow/nested-scroll-markers-under-content-visibility-auto.html b/testing/web-platform/tests/css/css-overflow/nested-scroll-markers-under-content-visibility-auto.html new file mode 100644 index 00000000000..0cb62fec70e --- /dev/null +++ b/testing/web-platform/tests/css/css-overflow/nested-scroll-markers-under-content-visibility-auto.html @@ -0,0 +1,67 @@ + + +::scroll-markers with content-visibility: auto ancestors and subscrollers + + + + + +
+
+
+
+
+
+
+
+
+
+
+
+
+
+ diff --git a/testing/web-platform/tests/css/css-overflow/scroll-marker-activation-crash.html b/testing/web-platform/tests/css/css-overflow/scroll-marker-activation-crash.html new file mode 100644 index 00000000000..1f2360a1943 --- /dev/null +++ b/testing/web-platform/tests/css/css-overflow/scroll-marker-activation-crash.html @@ -0,0 +1,77 @@ + + + +CSS Test: ::scroll-marker crash regression test + + + + + + + +
+
1
+
2
+
3
+
+
+ + diff --git a/testing/web-platform/tests/css/css-page/background-image-only-for-print-ref.html b/testing/web-platform/tests/css/css-page/background-image-only-for-print-ref.html index c3e5d8d1c5e..3271ab98ec6 100644 --- a/testing/web-platform/tests/css/css-page/background-image-only-for-print-ref.html +++ b/testing/web-platform/tests/css/css-page/background-image-only-for-print-ref.html @@ -1,4 +1,9 @@ +

Should print a green rectangle but not display it on screen.

diff --git a/testing/web-platform/tests/css/css-page/background-image-only-for-print.html b/testing/web-platform/tests/css/css-page/background-image-only-for-print.html index f100f7eaeed..cb931a99302 100644 --- a/testing/web-platform/tests/css/css-page/background-image-only-for-print.html +++ b/testing/web-platform/tests/css/css-page/background-image-only-for-print.html @@ -13,6 +13,10 @@ } } + :root { + print-color-adjust: exact; + } + #target { background-image: url("/images/green.png"); height: 50px; diff --git a/testing/web-platform/tests/css/css-page/basic-pagination-001-print-ref.html b/testing/web-platform/tests/css/css-page/basic-pagination-001-print-ref.html index 3487aa6eabb..df3da84637b 100644 --- a/testing/web-platform/tests/css/css-page/basic-pagination-001-print-ref.html +++ b/testing/web-platform/tests/css/css-page/basic-pagination-001-print-ref.html @@ -5,6 +5,9 @@ size: 293px; margin: 5px; } + :root { + print-color-adjust: exact; + } body { margin: 0; background: yellow; diff --git a/testing/web-platform/tests/css/css-page/basic-pagination-001-print.html b/testing/web-platform/tests/css/css-page/basic-pagination-001-print.html index 4bdf2ac6fdb..93293ff4879 100644 --- a/testing/web-platform/tests/css/css-page/basic-pagination-001-print.html +++ b/testing/web-platform/tests/css/css-page/basic-pagination-001-print.html @@ -7,6 +7,9 @@ size: 293px; margin: 5px; } + :root { + print-color-adjust: exact; + } div { break-after: page; } diff --git a/testing/web-platform/tests/css/css-page/basic-pagination-002-print-ref.html b/testing/web-platform/tests/css/css-page/basic-pagination-002-print-ref.html index 90e0e26502b..cf040af63d7 100644 --- a/testing/web-platform/tests/css/css-page/basic-pagination-002-print-ref.html +++ b/testing/web-platform/tests/css/css-page/basic-pagination-002-print-ref.html @@ -5,6 +5,9 @@ size: 293px; margin: 5px; } + :root { + print-color-adjust: exact; + } body { margin: 0; background: yellow; diff --git a/testing/web-platform/tests/css/css-page/basic-pagination-002-print.html b/testing/web-platform/tests/css/css-page/basic-pagination-002-print.html index 4cf0de750da..f2d07730e1c 100644 --- a/testing/web-platform/tests/css/css-page/basic-pagination-002-print.html +++ b/testing/web-platform/tests/css/css-page/basic-pagination-002-print.html @@ -7,6 +7,9 @@ size: 293px; margin: 5px; } + :root { + print-color-adjust: exact; + } body { break-before: page; break-after: page; diff --git a/testing/web-platform/tests/css/css-page/basic-pagination-003-print-ref.html b/testing/web-platform/tests/css/css-page/basic-pagination-003-print-ref.html index 6fd7ef2d453..9d3810213a5 100644 --- a/testing/web-platform/tests/css/css-page/basic-pagination-003-print-ref.html +++ b/testing/web-platform/tests/css/css-page/basic-pagination-003-print-ref.html @@ -5,6 +5,9 @@ size: 293px; margin: 0; } + :root { + print-color-adjust: exact; + } body { margin: 0; } diff --git a/testing/web-platform/tests/css/css-page/basic-pagination-003-print.html b/testing/web-platform/tests/css/css-page/basic-pagination-003-print.html index 56f10d2dc72..6a133059f99 100644 --- a/testing/web-platform/tests/css/css-page/basic-pagination-003-print.html +++ b/testing/web-platform/tests/css/css-page/basic-pagination-003-print.html @@ -7,6 +7,9 @@ size: 293px; margin: 5px; } + :root { + print-color-adjust: exact; + } body { margin: 0; background: yellow; diff --git a/testing/web-platform/tests/css/css-page/basic-pagination-004-print-ref.html b/testing/web-platform/tests/css/css-page/basic-pagination-004-print-ref.html index 3487aa6eabb..df3da84637b 100644 --- a/testing/web-platform/tests/css/css-page/basic-pagination-004-print-ref.html +++ b/testing/web-platform/tests/css/css-page/basic-pagination-004-print-ref.html @@ -5,6 +5,9 @@ size: 293px; margin: 5px; } + :root { + print-color-adjust: exact; + } body { margin: 0; background: yellow; diff --git a/testing/web-platform/tests/css/css-page/basic-pagination-004-print.html b/testing/web-platform/tests/css/css-page/basic-pagination-004-print.html index 0d6f989b185..15e0a7eaf06 100644 --- a/testing/web-platform/tests/css/css-page/basic-pagination-004-print.html +++ b/testing/web-platform/tests/css/css-page/basic-pagination-004-print.html @@ -9,6 +9,9 @@ size: 293px; margin: 5px; } + :root { + print-color-adjust: exact; + } body { margin: 0; background: yellow; diff --git a/testing/web-platform/tests/css/css-page/basic-pagination-005-print.html b/testing/web-platform/tests/css/css-page/basic-pagination-005-print.html index 03c04a41c4d..1429f05f464 100644 --- a/testing/web-platform/tests/css/css-page/basic-pagination-005-print.html +++ b/testing/web-platform/tests/css/css-page/basic-pagination-005-print.html @@ -9,6 +9,9 @@ size: 293px; margin: 5px; } + :root { + print-color-adjust: exact; + } body { margin: 0; background: yellow; diff --git a/testing/web-platform/tests/css/css-page/body-background-slr-print-ref.html b/testing/web-platform/tests/css/css-page/body-background-slr-print-ref.html index d47a59af4d3..ca55f669e27 100644 --- a/testing/web-platform/tests/css/css-page/body-background-slr-print-ref.html +++ b/testing/web-platform/tests/css/css-page/body-background-slr-print-ref.html @@ -1,5 +1,8 @@
diff --git a/testing/web-platform/tests/css/css-page/cssom/dynamic-001-print-ref.html b/testing/web-platform/tests/css/css-page/cssom/dynamic-001-print-ref.html index e697d8e23db..850bf80b720 100644 --- a/testing/web-platform/tests/css/css-page/cssom/dynamic-001-print-ref.html +++ b/testing/web-platform/tests/css/css-page/cssom/dynamic-001-print-ref.html @@ -5,6 +5,9 @@ size: 700px 150px; margin: 0; } + :root { + print-color-adjust: exact; + } body { margin: 0; } diff --git a/testing/web-platform/tests/css/css-page/cssom/dynamic-001-print.html b/testing/web-platform/tests/css/css-page/cssom/dynamic-001-print.html index 90799d3a721..5cdf26454c1 100644 --- a/testing/web-platform/tests/css/css-page/cssom/dynamic-001-print.html +++ b/testing/web-platform/tests/css/css-page/cssom/dynamic-001-print.html @@ -5,6 +5,9 @@

The word "PASS" should be seen below.

diff --git a/testing/web-platform/tests/css/css-page/fixedpos-with-iframe-print.html b/testing/web-platform/tests/css/css-page/fixedpos-with-iframe-print.html index 5102d045c42..215643601b2 100644 --- a/testing/web-platform/tests/css/css-page/fixedpos-with-iframe-print.html +++ b/testing/web-platform/tests/css/css-page/fixedpos-with-iframe-print.html @@ -2,6 +2,11 @@ +

The word "PASS" should be seen below.

diff --git a/testing/web-platform/tests/css/css-page/margin-boxes/auto-margins-002-print-ref.html b/testing/web-platform/tests/css/css-page/margin-boxes/auto-margins-002-print-ref.html index 56adf2a5401..57d966fc46d 100644 --- a/testing/web-platform/tests/css/css-page/margin-boxes/auto-margins-002-print-ref.html +++ b/testing/web-platform/tests/css/css-page/margin-boxes/auto-margins-002-print-ref.html @@ -5,6 +5,9 @@ margin: 0; size: 500px 440px; } + :root { + print-color-adjust: exact; + } body { display: grid; grid-template-columns: 100px auto 100px; diff --git a/testing/web-platform/tests/css/css-page/margin-boxes/auto-margins-002-print.html b/testing/web-platform/tests/css/css-page/margin-boxes/auto-margins-002-print.html index 5b424941cad..28d091ffa1a 100644 --- a/testing/web-platform/tests/css/css-page/margin-boxes/auto-margins-002-print.html +++ b/testing/web-platform/tests/css/css-page/margin-boxes/auto-margins-002-print.html @@ -106,6 +106,9 @@ background: green; } } + :root { + print-color-adjust: exact; + } body { background: white; } diff --git a/testing/web-platform/tests/css/css-page/margin-boxes/background-001-print-ref.html b/testing/web-platform/tests/css/css-page/margin-boxes/background-001-print-ref.html index d43af0ca4bf..af1215c88eb 100644 --- a/testing/web-platform/tests/css/css-page/margin-boxes/background-001-print-ref.html +++ b/testing/web-platform/tests/css/css-page/margin-boxes/background-001-print-ref.html @@ -4,6 +4,9 @@ @page { margin: 0; } + :root { + print-color-adjust: exact; + } body { margin: 0; } diff --git a/testing/web-platform/tests/css/css-page/margin-boxes/background-001-print.html b/testing/web-platform/tests/css/css-page/margin-boxes/background-001-print.html index 66560fce099..0e6db527320 100644 --- a/testing/web-platform/tests/css/css-page/margin-boxes/background-001-print.html +++ b/testing/web-platform/tests/css/css-page/margin-boxes/background-001-print.html @@ -12,6 +12,9 @@ background: url(/images/green.png); } } + :root { + print-color-adjust: exact; + } body { margin: 0; } diff --git a/testing/web-platform/tests/css/css-page/margin-boxes/content-001-print-ref.html b/testing/web-platform/tests/css/css-page/margin-boxes/content-001-print-ref.html index 00c44928daa..ac79c15cc05 100644 --- a/testing/web-platform/tests/css/css-page/margin-boxes/content-001-print-ref.html +++ b/testing/web-platform/tests/css/css-page/margin-boxes/content-001-print-ref.html @@ -5,6 +5,9 @@ size: 400px; margin: 0; } + :root { + print-color-adjust: exact; + } body { margin: 0; } diff --git a/testing/web-platform/tests/css/css-page/margin-boxes/content-001-print.html b/testing/web-platform/tests/css/css-page/margin-boxes/content-001-print.html index 11b5bd6f44d..cf61311491d 100644 --- a/testing/web-platform/tests/css/css-page/margin-boxes/content-001-print.html +++ b/testing/web-platform/tests/css/css-page/margin-boxes/content-001-print.html @@ -4,6 +4,9 @@
diff --git a/testing/web-platform/tests/css/css-page/monolithic-overflow-003-print.html b/testing/web-platform/tests/css/css-page/monolithic-overflow-003-print.html index 76905b180a1..c6bbefddc63 100644 --- a/testing/web-platform/tests/css/css-page/monolithic-overflow-003-print.html +++ b/testing/web-platform/tests/css/css-page/monolithic-overflow-003-print.html @@ -3,6 +3,9 @@
diff --git a/testing/web-platform/tests/css/css-page/monolithic-overflow-004-print.html b/testing/web-platform/tests/css/css-page/monolithic-overflow-004-print.html index ea5a659bd84..e1c492a4220 100644 --- a/testing/web-platform/tests/css/css-page/monolithic-overflow-004-print.html +++ b/testing/web-platform/tests/css/css-page/monolithic-overflow-004-print.html @@ -3,6 +3,9 @@
diff --git a/testing/web-platform/tests/css/css-page/monolithic-overflow-005-print.html b/testing/web-platform/tests/css/css-page/monolithic-overflow-005-print.html index bb0fc212fc0..d690fc3b834 100644 --- a/testing/web-platform/tests/css/css-page/monolithic-overflow-005-print.html +++ b/testing/web-platform/tests/css/css-page/monolithic-overflow-005-print.html @@ -3,6 +3,9 @@
diff --git a/testing/web-platform/tests/css/css-page/monolithic-overflow-006-print.html b/testing/web-platform/tests/css/css-page/monolithic-overflow-006-print.html index 1cbcdbbe1e5..416617ebc65 100644 --- a/testing/web-platform/tests/css/css-page/monolithic-overflow-006-print.html +++ b/testing/web-platform/tests/css/css-page/monolithic-overflow-006-print.html @@ -3,6 +3,9 @@
diff --git a/testing/web-platform/tests/css/css-page/monolithic-overflow-007-print.html b/testing/web-platform/tests/css/css-page/monolithic-overflow-007-print.html index 14a6f051fec..34fa065bb3c 100644 --- a/testing/web-platform/tests/css/css-page/monolithic-overflow-007-print.html +++ b/testing/web-platform/tests/css/css-page/monolithic-overflow-007-print.html @@ -3,6 +3,9 @@
diff --git a/testing/web-platform/tests/css/css-page/monolithic-overflow-008-print.html b/testing/web-platform/tests/css/css-page/monolithic-overflow-008-print.html index ff302dc30ca..576ced54595 100644 --- a/testing/web-platform/tests/css/css-page/monolithic-overflow-008-print.html +++ b/testing/web-platform/tests/css/css-page/monolithic-overflow-008-print.html @@ -3,6 +3,9 @@
diff --git a/testing/web-platform/tests/css/css-page/monolithic-overflow-009-print.html b/testing/web-platform/tests/css/css-page/monolithic-overflow-009-print.html index bdff66e791c..0a62f821178 100644 --- a/testing/web-platform/tests/css/css-page/monolithic-overflow-009-print.html +++ b/testing/web-platform/tests/css/css-page/monolithic-overflow-009-print.html @@ -3,6 +3,9 @@
diff --git a/testing/web-platform/tests/css/css-page/monolithic-overflow-010-print.html b/testing/web-platform/tests/css/css-page/monolithic-overflow-010-print.html index a75c932d30b..325d714e031 100644 --- a/testing/web-platform/tests/css/css-page/monolithic-overflow-010-print.html +++ b/testing/web-platform/tests/css/css-page/monolithic-overflow-010-print.html @@ -3,6 +3,9 @@
diff --git a/testing/web-platform/tests/css/css-page/monolithic-overflow-011-print.html b/testing/web-platform/tests/css/css-page/monolithic-overflow-011-print.html index 973a3aff4dc..b3388892cb9 100644 --- a/testing/web-platform/tests/css/css-page/monolithic-overflow-011-print.html +++ b/testing/web-platform/tests/css/css-page/monolithic-overflow-011-print.html @@ -3,6 +3,9 @@
diff --git a/testing/web-platform/tests/css/css-page/monolithic-overflow-012-print-ref.html b/testing/web-platform/tests/css/css-page/monolithic-overflow-012-print-ref.html index e47ca880f93..2dd7adc644c 100644 --- a/testing/web-platform/tests/css/css-page/monolithic-overflow-012-print-ref.html +++ b/testing/web-platform/tests/css/css-page/monolithic-overflow-012-print-ref.html @@ -1,6 +1,9 @@
diff --git a/testing/web-platform/tests/css/css-page/monolithic-overflow-012-print.html b/testing/web-platform/tests/css/css-page/monolithic-overflow-012-print.html index 53ed74bc728..fb307d60ff6 100644 --- a/testing/web-platform/tests/css/css-page/monolithic-overflow-012-print.html +++ b/testing/web-platform/tests/css/css-page/monolithic-overflow-012-print.html @@ -3,6 +3,9 @@
diff --git a/testing/web-platform/tests/css/css-page/monolithic-overflow-013-print-ref.html b/testing/web-platform/tests/css/css-page/monolithic-overflow-013-print-ref.html index e47ca880f93..2dd7adc644c 100644 --- a/testing/web-platform/tests/css/css-page/monolithic-overflow-013-print-ref.html +++ b/testing/web-platform/tests/css/css-page/monolithic-overflow-013-print-ref.html @@ -1,6 +1,9 @@
diff --git a/testing/web-platform/tests/css/css-page/monolithic-overflow-013-print.html b/testing/web-platform/tests/css/css-page/monolithic-overflow-013-print.html index b9a174879d7..3c16293c19e 100644 --- a/testing/web-platform/tests/css/css-page/monolithic-overflow-013-print.html +++ b/testing/web-platform/tests/css/css-page/monolithic-overflow-013-print.html @@ -3,6 +3,9 @@
diff --git a/testing/web-platform/tests/css/css-page/monolithic-overflow-014-print-ref.html b/testing/web-platform/tests/css/css-page/monolithic-overflow-014-print-ref.html index a74e0d19a55..0aca6e03a48 100644 --- a/testing/web-platform/tests/css/css-page/monolithic-overflow-014-print-ref.html +++ b/testing/web-platform/tests/css/css-page/monolithic-overflow-014-print-ref.html @@ -1,6 +1,9 @@
diff --git a/testing/web-platform/tests/css/css-page/monolithic-overflow-014-print.html b/testing/web-platform/tests/css/css-page/monolithic-overflow-014-print.html index d12ddf4c684..d993acc58d0 100644 --- a/testing/web-platform/tests/css/css-page/monolithic-overflow-014-print.html +++ b/testing/web-platform/tests/css/css-page/monolithic-overflow-014-print.html @@ -3,6 +3,9 @@
diff --git a/testing/web-platform/tests/css/css-page/monolithic-overflow-015-print-ref.html b/testing/web-platform/tests/css/css-page/monolithic-overflow-015-print-ref.html index db44a3ee21e..aa26df11c32 100644 --- a/testing/web-platform/tests/css/css-page/monolithic-overflow-015-print-ref.html +++ b/testing/web-platform/tests/css/css-page/monolithic-overflow-015-print-ref.html @@ -1,6 +1,9 @@
diff --git a/testing/web-platform/tests/css/css-page/monolithic-overflow-015-print.html b/testing/web-platform/tests/css/css-page/monolithic-overflow-015-print.html index d52605605c9..a1b975df3bf 100644 --- a/testing/web-platform/tests/css/css-page/monolithic-overflow-015-print.html +++ b/testing/web-platform/tests/css/css-page/monolithic-overflow-015-print.html @@ -3,6 +3,9 @@

diff --git a/testing/web-platform/tests/css/css-page/monolithic-overflow-016-print-ref.html b/testing/web-platform/tests/css/css-page/monolithic-overflow-016-print-ref.html index a675fb966c5..62ce80590d1 100644 --- a/testing/web-platform/tests/css/css-page/monolithic-overflow-016-print-ref.html +++ b/testing/web-platform/tests/css/css-page/monolithic-overflow-016-print-ref.html @@ -1,6 +1,9 @@
diff --git a/testing/web-platform/tests/css/css-page/monolithic-overflow-017-print.html b/testing/web-platform/tests/css/css-page/monolithic-overflow-017-print.html index c8ce9060fff..0183c82bf62 100644 --- a/testing/web-platform/tests/css/css-page/monolithic-overflow-017-print.html +++ b/testing/web-platform/tests/css/css-page/monolithic-overflow-017-print.html @@ -3,6 +3,9 @@
diff --git a/testing/web-platform/tests/css/css-page/monolithic-overflow-018-print-ref.html b/testing/web-platform/tests/css/css-page/monolithic-overflow-018-print-ref.html index cf31737d6c3..3e4d6c99564 100644 --- a/testing/web-platform/tests/css/css-page/monolithic-overflow-018-print-ref.html +++ b/testing/web-platform/tests/css/css-page/monolithic-overflow-018-print-ref.html @@ -1,6 +1,9 @@
diff --git a/testing/web-platform/tests/css/css-page/monolithic-overflow-018-print.html b/testing/web-platform/tests/css/css-page/monolithic-overflow-018-print.html index 93da11dc62e..fb3164395c9 100644 --- a/testing/web-platform/tests/css/css-page/monolithic-overflow-018-print.html +++ b/testing/web-platform/tests/css/css-page/monolithic-overflow-018-print.html @@ -3,6 +3,9 @@
diff --git a/testing/web-platform/tests/css/css-page/monolithic-overflow-019-print-ref.html b/testing/web-platform/tests/css/css-page/monolithic-overflow-019-print-ref.html index f37740ae7e6..501a8b6a961 100644 --- a/testing/web-platform/tests/css/css-page/monolithic-overflow-019-print-ref.html +++ b/testing/web-platform/tests/css/css-page/monolithic-overflow-019-print-ref.html @@ -1,6 +1,9 @@
diff --git a/testing/web-platform/tests/css/css-page/monolithic-overflow-019-print.html b/testing/web-platform/tests/css/css-page/monolithic-overflow-019-print.html index 3740b5d5b0b..af5ba41bfa6 100644 --- a/testing/web-platform/tests/css/css-page/monolithic-overflow-019-print.html +++ b/testing/web-platform/tests/css/css-page/monolithic-overflow-019-print.html @@ -3,6 +3,9 @@
diff --git a/testing/web-platform/tests/css/css-page/monolithic-overflow-020-print-ref.html b/testing/web-platform/tests/css/css-page/monolithic-overflow-020-print-ref.html index 528c4980de5..6b40bcae957 100644 --- a/testing/web-platform/tests/css/css-page/monolithic-overflow-020-print-ref.html +++ b/testing/web-platform/tests/css/css-page/monolithic-overflow-020-print-ref.html @@ -1,6 +1,9 @@
diff --git a/testing/web-platform/tests/css/css-page/monolithic-overflow-020-print.html b/testing/web-platform/tests/css/css-page/monolithic-overflow-020-print.html index 403d932d643..a296ba4d3d7 100644 --- a/testing/web-platform/tests/css/css-page/monolithic-overflow-020-print.html +++ b/testing/web-platform/tests/css/css-page/monolithic-overflow-020-print.html @@ -3,6 +3,9 @@ +calc-size() and the body fills html quirk + + +
The bottom border of this box should be close to the text, not at the bottom of the viewport.
diff --git a/testing/web-platform/tests/css/css-values/calc-size/calc-size-no-body-height-quirk-001.html b/testing/web-platform/tests/css/css-values/calc-size/calc-size-no-body-height-quirk-001.html new file mode 100644 index 00000000000..70a2670ab87 --- /dev/null +++ b/testing/web-platform/tests/css/css-values/calc-size/calc-size-no-body-height-quirk-001.html @@ -0,0 +1,15 @@ + +calc-size() and the body fills html quirk + + + + + +The bottom border of this box should be close to the text, not at the bottom of the viewport. diff --git a/testing/web-platform/tests/css/css-values/calc-size/calc-size-svg-001-crash.html b/testing/web-platform/tests/css/css-values/calc-size/calc-size-svg-001-crash.html new file mode 100644 index 00000000000..ec1e09a4315 --- /dev/null +++ b/testing/web-platform/tests/css/css-values/calc-size/calc-size-svg-001-crash.html @@ -0,0 +1,10 @@ + + + + diff --git a/testing/web-platform/tests/css/css-view-transitions/capture-with-offscreen-child-translated.html b/testing/web-platform/tests/css/css-view-transitions/capture-with-offscreen-child-translated.html index 925bb8dbc45..d7f4f2b122a 100644 --- a/testing/web-platform/tests/css/css-view-transitions/capture-with-offscreen-child-translated.html +++ b/testing/web-platform/tests/css/css-view-transitions/capture-with-offscreen-child-translated.html @@ -30,6 +30,17 @@ visibility: hidden; animation-duration: 500s; } + +::view-transtion-old(*) { + animation: unset; + opacity: 1; +} + +::view-transtion-new(*) { + animation: unset; + opacity: 0; +} + ::view-transition { background: pink; } diff --git a/testing/web-platform/tests/css/css-view-transitions/capture-with-offscreen-child.html b/testing/web-platform/tests/css/css-view-transitions/capture-with-offscreen-child.html index 7f8085cae2d..576c1a0a4ad 100644 --- a/testing/web-platform/tests/css/css-view-transitions/capture-with-offscreen-child.html +++ b/testing/web-platform/tests/css/css-view-transitions/capture-with-offscreen-child.html @@ -26,6 +26,22 @@ visibility: hidden; animation-duration: 500s; } + +::view-transtion-group(*), +::view-transtion-image-pair(*) { + animation-play-state: paused; +} + +::view-transition-old(*) { + animation: unset; + opacity: 1; +} + +::view-transition-new(*) { + animation: unset; + opacity: 0; +} + ::view-transition { background: pink; } @@ -44,4 +60,5 @@ function runTest() { } onload = () => requestAnimationFrame(() => requestAnimationFrame(runTest)); + diff --git a/testing/web-platform/tests/css/css-view-transitions/capture-with-opacity-zero-child.html b/testing/web-platform/tests/css/css-view-transitions/capture-with-opacity-zero-child.html index 2b0563ea31b..78bd49f24a2 100644 --- a/testing/web-platform/tests/css/css-view-transitions/capture-with-opacity-zero-child.html +++ b/testing/web-platform/tests/css/css-view-transitions/capture-with-opacity-zero-child.html @@ -24,6 +24,22 @@ visibility: hidden; animation-duration: 500s; } + +::view-transtion-group(*), +::view-transtion-image-pair(*) { + animation-play-state: paused; +} + +::view-transition-old(*) { + animation: unset; + opacity: 1; +} + +::view-transition-new(*) { + animation: unset; + opacity: 0; +} + ::view-transition { background: pink; } diff --git a/testing/web-platform/tests/css/css-view-transitions/capture-with-visibility-hidden-child.html b/testing/web-platform/tests/css/css-view-transitions/capture-with-visibility-hidden-child.html index 2d176124996..ba49933390f 100644 --- a/testing/web-platform/tests/css/css-view-transitions/capture-with-visibility-hidden-child.html +++ b/testing/web-platform/tests/css/css-view-transitions/capture-with-visibility-hidden-child.html @@ -23,6 +23,23 @@ visibility: hidden; animation-duration: 500s; } + +::view-transtion-group(*), +::view-transtion-image-pair(*) { + animation-play-state: paused; +} + +::view-transition-old(*) { + animation: unset; + opacity: 1; +} + +::view-transition-new(*) { + animation: unset; + opacity: 0; +} + + ::view-transition { background: pink; } diff --git a/testing/web-platform/tests/css/css-view-transitions/capture-with-visibility-mixed-descendants.html b/testing/web-platform/tests/css/css-view-transitions/capture-with-visibility-mixed-descendants.html index 3a4811ada10..45776052491 100644 --- a/testing/web-platform/tests/css/css-view-transitions/capture-with-visibility-mixed-descendants.html +++ b/testing/web-platform/tests/css/css-view-transitions/capture-with-visibility-mixed-descendants.html @@ -34,6 +34,22 @@ visibility: hidden; animation-duration: 500s; } + +::view-transtion-group(*), +::view-transtion-image-pair(*) { + animation-play-state: paused; +} + +::view-transition-old(*) { + animation: unset; + opacity: 1; +} + +::view-transition-new(*) { + animation: unset; + opacity: 0; +} + ::view-transition { background: pink; } diff --git a/testing/web-platform/tests/css/css-view-transitions/inline-with-offset-from-containing-block.html b/testing/web-platform/tests/css/css-view-transitions/inline-with-offset-from-containing-block.html index 6bbb8b49392..31f8449ff63 100644 --- a/testing/web-platform/tests/css/css-view-transitions/inline-with-offset-from-containing-block.html +++ b/testing/web-platform/tests/css/css-view-transitions/inline-with-offset-from-containing-block.html @@ -31,6 +31,12 @@ margin: -10px 0 0 -10px; animation-play-state: paused; } + html::view-transition-image-pair(text), + html::view-transition-old(text), + html::view-transition-new(text) { + animation-play-state: paused; + } +
diff --git a/testing/web-platform/tests/css/css-view-transitions/pseudo-with-classes-match-multiple.html b/testing/web-platform/tests/css/css-view-transitions/pseudo-with-classes-match-multiple.html index 7ff42edf366..f5c060c2095 100644 --- a/testing/web-platform/tests/css/css-view-transitions/pseudo-with-classes-match-multiple.html +++ b/testing/web-platform/tests/css/css-view-transitions/pseudo-with-classes-match-multiple.html @@ -20,7 +20,10 @@ div { view-transition-name: target; } -::view-transition-group(*) { +::view-transition-group(*), +::view-transition-image-pair(*), +::view-transition-old(*), +::view-transition-new(*) { animation-play-state: paused; } diff --git a/testing/web-platform/tests/css/css-view-transitions/pseudo-with-classes-match-wildcard-no-star.html b/testing/web-platform/tests/css/css-view-transitions/pseudo-with-classes-match-wildcard-no-star.html index 96186cd8784..123168310b8 100644 --- a/testing/web-platform/tests/css/css-view-transitions/pseudo-with-classes-match-wildcard-no-star.html +++ b/testing/web-platform/tests/css/css-view-transitions/pseudo-with-classes-match-wildcard-no-star.html @@ -28,6 +28,17 @@ ::view-transition-old(.cls) { left: 100px; } + +::view-transition-old(*) { + animation: unset; + opacity: 1; +} + +::view-transition-new(*) { + animation: unset; + opacity: 0; +} +
diff --git a/testing/web-platform/tests/css/css-view-transitions/pseudo-with-classes-match-wildcard.html b/testing/web-platform/tests/css/css-view-transitions/pseudo-with-classes-match-wildcard.html index f777e245ba6..11bdcbc12cb 100644 --- a/testing/web-platform/tests/css/css-view-transitions/pseudo-with-classes-match-wildcard.html +++ b/testing/web-platform/tests/css/css-view-transitions/pseudo-with-classes-match-wildcard.html @@ -20,7 +20,10 @@ div { view-transition-class: cls; } -::view-transition-group(*) { +::view-transition-group(*), +::view-transition-image-pair(*), +::view-transition-old(*), +::view-transition-new(*) { animation-play-state: paused; } diff --git a/testing/web-platform/tests/css/css-view-transitions/pseudo-with-classes-multiple-vt-classes.html b/testing/web-platform/tests/css/css-view-transitions/pseudo-with-classes-multiple-vt-classes.html index fa7ae4eeb15..c28d6f90dfe 100644 --- a/testing/web-platform/tests/css/css-view-transitions/pseudo-with-classes-multiple-vt-classes.html +++ b/testing/web-platform/tests/css/css-view-transitions/pseudo-with-classes-multiple-vt-classes.html @@ -20,7 +20,10 @@ div { view-transition-name: target; } -::view-transition-group(*) { +::view-transition-group(*), +::view-transition-image-pair(*), +::view-transition-old(*), +::view-transition-new(*) { animation-play-state: paused; } diff --git a/testing/web-platform/tests/css/css-view-transitions/pseudo-with-classes-view-transition-image-pair.html b/testing/web-platform/tests/css/css-view-transitions/pseudo-with-classes-view-transition-image-pair.html index 0473742260c..5cc971b9446 100644 --- a/testing/web-platform/tests/css/css-view-transitions/pseudo-with-classes-view-transition-image-pair.html +++ b/testing/web-platform/tests/css/css-view-transitions/pseudo-with-classes-view-transition-image-pair.html @@ -23,8 +23,20 @@ ::view-transition-image-pair(target.cls) { transform: translateX(100px); + animation-play-state: paused; } +::view-transition-old(*) { + animation: unset; + opacity: 1; +} + +::view-transition-new(*) { + animation: unset; + opacity: 0; +} + +
diff --git a/testing/web-platform/tests/css/css-view-transitions/rotated-cat-off-top-edge.html b/testing/web-platform/tests/css/css-view-transitions/rotated-cat-off-top-edge.html index f61b916caa5..34979361429 100644 --- a/testing/web-platform/tests/css/css-view-transitions/rotated-cat-off-top-edge.html +++ b/testing/web-platform/tests/css/css-view-transitions/rotated-cat-off-top-edge.html @@ -16,7 +16,19 @@ } ::view-transition-group(root) { animation-duration: 500s; + animation-play-state: paused; } + +::view-transition-old(*) { + animation: unset; + opacity: 1; +} + +::view-transition-new(*) { + animation: unset; + opacity: 0; +} + diff --git a/testing/web-platform/tests/css/css-view-transitions/snapshot-containing-block-absolute.html b/testing/web-platform/tests/css/css-view-transitions/snapshot-containing-block-absolute.html index 91b71f7eec9..a8a03b62e19 100644 --- a/testing/web-platform/tests/css/css-view-transitions/snapshot-containing-block-absolute.html +++ b/testing/web-platform/tests/css/css-view-transitions/snapshot-containing-block-absolute.html @@ -35,6 +35,7 @@ body { ::view-transition-group(target) { animation-duration: 50s; + animation-play-state: paused; top: unset; left: unset; right: 0px; @@ -50,6 +51,16 @@ body { background-color: limegreen; } +::view-transition-old(*) { + animation: unset; + opacity: 1; +} + +::view-transition-new(*) { + animation: unset; + opacity: 0; +} +
TARGET
diff --git a/testing/web-platform/tests/css/css-view-transitions/update-callback-called-once.html b/testing/web-platform/tests/css/css-view-transitions/update-callback-called-once.html index c84368c95c4..cd27df6dafd 100644 --- a/testing/web-platform/tests/css/css-view-transitions/update-callback-called-once.html +++ b/testing/web-platform/tests/css/css-view-transitions/update-callback-called-once.html @@ -11,6 +11,13 @@ height: 100px; background-color: red; } + + ::view-transition-group(*), + ::view-transition-image-pair(*), + ::view-transition-old(*), + ::view-transition-new(*) { + animation-play-state: paused; + }

Test passes if there is a filled green square and no red.

diff --git a/testing/web-platform/tests/css/mediaqueries/WEB_FEATURES.yml b/testing/web-platform/tests/css/mediaqueries/WEB_FEATURES.yml index b090111be0a..24027360aab 100644 --- a/testing/web-platform/tests/css/mediaqueries/WEB_FEATURES.yml +++ b/testing/web-platform/tests/css/mediaqueries/WEB_FEATURES.yml @@ -22,3 +22,6 @@ features: files: - display-mode.html - display-mode.tentative.html +- name: update + files: + - update-media-feature.html diff --git a/testing/web-platform/tests/css/printing/background-image-print-ref.html b/testing/web-platform/tests/css/printing/background-image-print-ref.html index 305aa2fce2f..8fb1b3998f0 100644 --- a/testing/web-platform/tests/css/printing/background-image-print-ref.html +++ b/testing/web-platform/tests/css/printing/background-image-print-ref.html @@ -4,6 +4,9 @@ diff --git a/testing/web-platform/tests/html/rendering/non-replaced-elements/tables/table-background-print.html b/testing/web-platform/tests/html/rendering/non-replaced-elements/tables/table-background-print.html index 0cbaca60192..05fdef0a860 100644 --- a/testing/web-platform/tests/html/rendering/non-replaced-elements/tables/table-background-print.html +++ b/testing/web-platform/tests/html/rendering/non-replaced-elements/tables/table-background-print.html @@ -4,6 +4,11 @@ +
diff --git a/testing/web-platform/tests/html/rendering/non-replaced-elements/tables/table-row-pagination-001-print-ref.html b/testing/web-platform/tests/html/rendering/non-replaced-elements/tables/table-row-pagination-001-print-ref.html index 2aa94109ad6..b78f9d3bf8a 100644 --- a/testing/web-platform/tests/html/rendering/non-replaced-elements/tables/table-row-pagination-001-print-ref.html +++ b/testing/web-platform/tests/html/rendering/non-replaced-elements/tables/table-row-pagination-001-print-ref.html @@ -6,6 +6,7 @@
diff --git a/testing/web-platform/tests/html/semantics/permission-element/bounded-css-properties-reftest-ref.html b/testing/web-platform/tests/html/semantics/permission-element/bounded-css-properties-reftest-ref.html index c62ff5b24d6..2fb0c1f4e95 100644 --- a/testing/web-platform/tests/html/semantics/permission-element/bounded-css-properties-reftest-ref.html +++ b/testing/web-platform/tests/html/semantics/permission-element/bounded-css-properties-reftest-ref.html @@ -30,6 +30,6 @@ } - - + + \ No newline at end of file diff --git a/testing/web-platform/tests/html/semantics/permission-element/bounded-css-properties-reftest.tentative.html b/testing/web-platform/tests/html/semantics/permission-element/bounded-css-properties-reftest.tentative.html index ad6986f52b4..4d27b23eb51 100644 --- a/testing/web-platform/tests/html/semantics/permission-element/bounded-css-properties-reftest.tentative.html +++ b/testing/web-platform/tests/html/semantics/permission-element/bounded-css-properties-reftest.tentative.html @@ -32,6 +32,6 @@ } - - + + \ No newline at end of file diff --git a/testing/web-platform/tests/html/semantics/permission-element/bounded-css-properties.tentative.html b/testing/web-platform/tests/html/semantics/permission-element/bounded-css-properties.tentative.html index c0f0fe34542..9678286179a 100644 --- a/testing/web-platform/tests/html/semantics/permission-element/bounded-css-properties.tentative.html +++ b/testing/web-platform/tests/html/semantics/permission-element/bounded-css-properties.tentative.html @@ -33,9 +33,9 @@ - - - + + + - + + + + + + +
+ + +
+ +This is some text + + +

Foo bar baz

+ + + \ No newline at end of file diff --git a/testing/web-platform/tests/html/semantics/permission-element/no-end-tag-no-contents.tentative.html b/testing/web-platform/tests/html/semantics/permission-element/no-end-tag-no-contents.tentative.html deleted file mode 100644 index bea3d7102ca..00000000000 --- a/testing/web-platform/tests/html/semantics/permission-element/no-end-tag-no-contents.tentative.html +++ /dev/null @@ -1,28 +0,0 @@ - - - - - - - -this is some text - - - \ No newline at end of file diff --git a/testing/web-platform/tests/html/semantics/permission-element/no-focus.tentative.html b/testing/web-platform/tests/html/semantics/permission-element/no-focus.tentative.html index 6e81a20962c..96467885183 100644 --- a/testing/web-platform/tests/html/semantics/permission-element/no-focus.tentative.html +++ b/testing/web-platform/tests/html/semantics/permission-element/no-focus.tentative.html @@ -9,12 +9,12 @@ - + This is some text - + \ No newline at end of file diff --git a/testing/web-platform/tests/html/semantics/permission-element/pseudo-elements-in-div-ref.html b/testing/web-platform/tests/html/semantics/permission-element/pseudo-elements-in-div-ref.html index 38d0cc25a52..8e6267f9aa3 100644 --- a/testing/web-platform/tests/html/semantics/permission-element/pseudo-elements-in-div-ref.html +++ b/testing/web-platform/tests/html/semantics/permission-element/pseudo-elements-in-div-ref.html @@ -18,7 +18,7 @@
- +
\ No newline at end of file diff --git a/testing/web-platform/tests/html/semantics/permission-element/pseudo-elements-in-div.tentative.html b/testing/web-platform/tests/html/semantics/permission-element/pseudo-elements-in-div.tentative.html index e9080708021..24a2be07fab 100644 --- a/testing/web-platform/tests/html/semantics/permission-element/pseudo-elements-in-div.tentative.html +++ b/testing/web-platform/tests/html/semantics/permission-element/pseudo-elements-in-div.tentative.html @@ -25,7 +25,7 @@ all implementation. -->
- +
\ No newline at end of file diff --git a/testing/web-platform/tests/html/semantics/permission-element/pseudo-elements-ref.html b/testing/web-platform/tests/html/semantics/permission-element/pseudo-elements-ref.html index ca7ad2daa89..a9d8d865595 100644 --- a/testing/web-platform/tests/html/semantics/permission-element/pseudo-elements-ref.html +++ b/testing/web-platform/tests/html/semantics/permission-element/pseudo-elements-ref.html @@ -8,6 +8,6 @@

You should not see the word FAIL below.

- + \ No newline at end of file diff --git a/testing/web-platform/tests/html/semantics/permission-element/pseudo-elements.tentative.html b/testing/web-platform/tests/html/semantics/permission-element/pseudo-elements.tentative.html index e0faefeb55d..3e553e01bcf 100644 --- a/testing/web-platform/tests/html/semantics/permission-element/pseudo-elements.tentative.html +++ b/testing/web-platform/tests/html/semantics/permission-element/pseudo-elements.tentative.html @@ -13,6 +13,6 @@

You should not see the word FAIL below.

- + \ No newline at end of file diff --git a/testing/web-platform/tests/html/semantics/permission-element/quirks-mode-no-height-is-still-bounded-ref.html b/testing/web-platform/tests/html/semantics/permission-element/quirks-mode-no-height-is-still-bounded-ref.html index a97003d82f0..738dc95f8eb 100644 --- a/testing/web-platform/tests/html/semantics/permission-element/quirks-mode-no-height-is-still-bounded-ref.html +++ b/testing/web-platform/tests/html/semantics/permission-element/quirks-mode-no-height-is-still-bounded-ref.html @@ -8,7 +8,7 @@ } - + + + + + diff --git a/testing/web-platform/tests/html/semantics/permission-element/unbounded-width-with-border-reftest-ref.html b/testing/web-platform/tests/html/semantics/permission-element/unbounded-width-with-border-reftest-ref.html index 8219b9a7420..0753b313e88 100644 --- a/testing/web-platform/tests/html/semantics/permission-element/unbounded-width-with-border-reftest-ref.html +++ b/testing/web-platform/tests/html/semantics/permission-element/unbounded-width-with-border-reftest-ref.html @@ -51,11 +51,11 @@ width: fit-content; } -
-
-
-
-
+
+
+
+
+
+ + + \ No newline at end of file diff --git a/testing/web-platform/tests/service-workers/service-worker/resources/client-url-creation-url-iframe.html b/testing/web-platform/tests/service-workers/service-worker/resources/client-url-creation-url-iframe.html new file mode 100644 index 00000000000..db2b9e5bbd9 --- /dev/null +++ b/testing/web-platform/tests/service-workers/service-worker/resources/client-url-creation-url-iframe.html @@ -0,0 +1,85 @@ + +Service Worker: Client.url is Window creation URL iframe resource + \ No newline at end of file diff --git a/testing/web-platform/tests/service-workers/service-worker/resources/client-url-creation-url-sw.js b/testing/web-platform/tests/service-workers/service-worker/resources/client-url-creation-url-sw.js new file mode 100644 index 00000000000..013d9be4cae --- /dev/null +++ b/testing/web-platform/tests/service-workers/service-worker/resources/client-url-creation-url-sw.js @@ -0,0 +1,24 @@ +// This is a service worker script used by the client-url-creation-url test. +// It exists only to look up the client URL of the test iframe and send it back +// to the test page. +addEventListener('message', message_event => { + const port = message_event.data.port; + + const async_work = async () => { + try { + const clients = await self.clients.matchAll(); + + // In our test there should be exactly one client that is our test + // navigation iframe. + if (clients.length == 1) { + const client = clients[0]; + port.postMessage(client.url); + } else { + port.postMessage(`error: expected 1 client, not ${clients.length}`); + } + } catch (error) { + port.postMessage(`error: ${error.message}`); + } + }; + message_event.waitUntil(async_work()); +}); \ No newline at end of file diff --git a/testing/web-platform/tests/service-workers/service-worker/tentative/static-router/static-router-resource-timing.https.html b/testing/web-platform/tests/service-workers/service-worker/tentative/static-router/static-router-resource-timing.https.html index e32436da41b..9d6137d9c52 100644 --- a/testing/web-platform/tests/service-workers/service-worker/tentative/static-router/static-router-resource-timing.https.html +++ b/testing/web-platform/tests/service-workers/service-worker/tentative/static-router/static-router-resource-timing.https.html @@ -68,7 +68,7 @@ function test_resource_timing(options) { assert_less_than_equal(entry.workerCacheLookupStart, entry.fetchStart, description); } break; - case 'race-network-and-fetch': + case 'race-network-and-fetch-handler': assert_equals(entry.workerCacheLookupStart, 0, description); if (entry.workerFinalSourceType === 'network') { assert_equals(entry.workerStart, 0, description); @@ -261,7 +261,7 @@ promise_test(async t => { test_resource_timing({ performance: iframe.contentWindow.performance, url: url, - matched_source_type: 'race-network-and-fetch', + matched_source_type: 'race-network-and-fetch-handler', final_source_type: 'fetch-event', description: "race as source on main resource, and fetch-event wins" }); @@ -280,7 +280,7 @@ promise_test(async t => { test_resource_timing({ performance: iframe.contentWindow.performance, url: url, - matched_source_type: 'race-network-and-fetch', + matched_source_type: 'race-network-and-fetch-handler', final_source_type: 'network', description: "race as source on main resource, and network wins" }); @@ -301,7 +301,7 @@ promise_test(async t => { test_resource_timing({ performance: iframe.contentWindow.performance, url: `${RACE_ROUTE}${subresource}`, - matched_source_type: 'race-network-and-fetch', + matched_source_type: 'race-network-and-fetch-handler', final_source_type: 'fetch-event', description: "race as source on subresource and fetch wins" }); @@ -323,7 +323,7 @@ promise_test(async t => { test_resource_timing({ performance: iframe.contentWindow.performance, url: `${RACE_ROUTE}${subresource}`, - matched_source_type: 'race-network-and-fetch', + matched_source_type: 'race-network-and-fetch-handler', final_source_type: 'network', description: "race as source on subresource and network wins" }); diff --git a/testing/web-platform/tests/shadow-dom/declarative/WEB_FEATURES.yml b/testing/web-platform/tests/shadow-dom/declarative/WEB_FEATURES.yml index 4e31ed49627..e5a15eb0231 100644 --- a/testing/web-platform/tests/shadow-dom/declarative/WEB_FEATURES.yml +++ b/testing/web-platform/tests/shadow-dom/declarative/WEB_FEATURES.yml @@ -1,3 +1,6 @@ features: - name: declarative-shadow-dom files: "**" +- name: gethtml + files: + - gethtml* diff --git a/testing/web-platform/tests/shadow-dom/event-post-dispatch.html b/testing/web-platform/tests/shadow-dom/event-post-dispatch.html index 98157a69b9c..a8fe0a24b1d 100644 --- a/testing/web-platform/tests/shadow-dom/event-post-dispatch.html +++ b/testing/web-platform/tests/shadow-dom/event-post-dispatch.html @@ -138,7 +138,7 @@ document.body.removeChild(n4.test4); let n5 = createTestTree(test5); document.body.appendChild(n5.test5); test(() => { - let log = dispatchEventWithEventLog(n5, n5.target, new MouseEvent('my-event', {bubbles: true, compoesed: true, relatedTarget: n5.relatedTarget})); + let log = dispatchEventWithEventLog(n5, n5.target, new MouseEvent('my-event', {bubbles: true, composed: true, relatedTarget: n5.relatedTarget})); assert_equals(log.event.target, null); assert_equals(log.event.relatedTarget, null); assert_equals(log.event.eventPhase, 0); @@ -147,7 +147,7 @@ test(() => { }, 'Event properties post dispatch with relatedTarget in the same shadow tree. (composed: true)'); test(() => { - let log = dispatchEventWithEventLog(n5, n5.target, new MouseEvent('my-event', {bubbles: true, compoesed: false, relatedTarget: n5.relatedTarget})); + let log = dispatchEventWithEventLog(n5, n5.target, new MouseEvent('my-event', {bubbles: true, composed: false, relatedTarget: n5.relatedTarget})); assert_equals(log.event.target, null); assert_equals(log.event.relatedTarget, null); assert_equals(log.event.eventPhase, 0); diff --git a/testing/web-platform/tests/speculation-rules/prefetch/resources/speculation-tags-utils.js b/testing/web-platform/tests/speculation-rules/prefetch/resources/speculation-tags-utils.js index d6963fc4c85..9b10fab9898 100644 --- a/testing/web-platform/tests/speculation-rules/prefetch/resources/speculation-tags-utils.js +++ b/testing/web-platform/tests/speculation-rules/prefetch/resources/speculation-tags-utils.js @@ -1,17 +1,83 @@ // Utilities for speculation tags tests. -function testRulesetTag(tag, expectedTag, description) { - promise_test(async t => { - const agent = await spawnWindow(t); - const nextUrl = agent.getExecutorURL({ page: 2 }); - await agent.forceSpeculationRules({ - tag, - prefetch: [{source: "list", urls: [nextUrl]}] - }); - await agent.navigate(nextUrl); +{ + // Retrieves a tag level variant from URLSearchParams of the current window and + // returns it. Throw an Error if it doesn't have the valid tag level param. + function getTagLevel() { + const params = new URLSearchParams(window.location.search); + const level = params.get('tag-level'); + if (level === null) + throw new Error('window.location does not have a tag-level param'); + if (level !== 'ruleset' && level !== 'rule') + throw new Error('window.location does not have a valid tag-level param'); + return level; + } - const headers = await agent.getRequestHeaders(); - assert_prefetched(headers, "must be prefetched"); - assert_equals(headers.sec_speculation_tags, expectedTag, "Sec-Speculation-Tags"); - }, "Sec-Speculation-Tags: " + description); + function testRulesetTag(tag, expectedTag, description) { + promise_test(async t => { + const agent = await spawnWindow(t); + const nextUrl = agent.getExecutorURL({ page: 2 }); + await agent.forceSpeculationRules({ + tag, + prefetch: [{source: "list", urls: [nextUrl]}] + }); + await agent.navigate(nextUrl); + + const headers = await agent.getRequestHeaders(); + assert_prefetched(headers, "must be prefetched"); + assert_equals(headers.sec_speculation_tags, expectedTag, "Sec-Speculation-Tags"); + }, "Sec-Speculation-Tags [ruleset-based]: " + description); + } + + function testInvalidRulesetTag(tag, description) { + testRulesetTag(tag, 'null', description); + } + + function testRuleTag(tag, expectedTag, description) { + promise_test(async t => { + const agent = await spawnWindow(t); + const nextUrl = agent.getExecutorURL({ page: 2 }); + await agent.forceSpeculationRules({ + prefetch: [{tag, source: "list", urls: [nextUrl]}] + }); + await agent.navigate(nextUrl); + + const headers = await agent.getRequestHeaders(); + assert_prefetched(headers, "must be prefetched"); + assert_equals(headers.sec_speculation_tags, expectedTag, "Sec-Speculation-Tags"); + }, "Sec-Speculation-Tags [rule-based]: " + description); + } + + function testInvalidRuleTag(tag, description) { + promise_test(async t => { + const agent = await spawnWindow(t); + const nextUrl = agent.getExecutorURL({ page: 2 }); + await agent.forceSpeculationRules({ + prefetch: [{tag, source: "list", urls: [nextUrl]}] + }); + await agent.navigate(nextUrl); + + const headers = await agent.getRequestHeaders(); + assert_not_prefetched(headers, "must not be prefetched"); + assert_equals(headers.sec_speculation_tags, "", "Sec-Speculation-Tags"); + }, "Sec-Speculation-Tags [rule-based]: " + description); + } + + // Runs the test function for valid tag cases based on the tag level. + globalThis.testTag = (tag, expectedTag, description) => { + if (getTagLevel() === 'ruleset') { + testRulesetTag(tag, expectedTag, description); + } else { + testRuleTag(tag, expectedTag, description); + } + }; + + // Runs the test function for invalid tag cases based on the tag level. + globalThis.testInvalidTag = (tag, description) => { + if (getTagLevel() === 'ruleset') { + testInvalidRulesetTag(tag, description); + } else { + testInvalidRuleTag(tag, description); + } + }; } diff --git a/testing/web-platform/tests/speculation-rules/prefetch/speculation-tags-ruleset-invalid.https.html b/testing/web-platform/tests/speculation-rules/prefetch/speculation-tags-invalid.https.html similarity index 60% rename from testing/web-platform/tests/speculation-rules/prefetch/speculation-tags-ruleset-invalid.https.html rename to testing/web-platform/tests/speculation-rules/prefetch/speculation-tags-invalid.https.html index c890f7aae56..e35bab0d6da 100644 --- a/testing/web-platform/tests/speculation-rules/prefetch/speculation-tags-ruleset-invalid.https.html +++ b/testing/web-platform/tests/speculation-rules/prefetch/speculation-tags-invalid.https.html @@ -1,5 +1,7 @@ Prefetch request's Sec-Speculation-Tags request headers + + @@ -17,18 +19,17 @@ setup(() => assertSpeculationRulesIsSupported()); // https://wicg.github.io/nav-speculation/prefetch.html#sec-speculation-tags-header // Invalid (null) tag cases. -testRulesetTag(123, 'null', "integer tag"); -testRulesetTag({a: "1"}, 'null', "object tag"); -testRulesetTag(null, 'null', "null value tag"); -testRulesetTag(undefined, 'null', "undefined value tag"); -testRulesetTag('\n', 'null', "non-printable character tag"); -testRulesetTag('my\nrules', 'null', "string with non-printable character tag"); -testRulesetTag('マイルール', 'null', "non-ascii string tag"); +testInvalidTag(123, "integer tag"); +testInvalidTag({a: "1"}, "object tag"); +testInvalidTag(null, "null value tag"); +testInvalidTag('\n', "non-printable character tag"); +testInvalidTag('my\nrules', "string with non-printable character tag"); +testInvalidTag('マイルール', "non-ascii string tag"); // Boundary checks: the tag allows only printable ascii characters between 0x20 // and 0x7E, inclusive. -testRulesetTag('\x19', 'null', "0x19 tag"); -testRulesetTag('\x20', '" "', "0x20 tag"); -testRulesetTag('\x7E', '"~"', "0x7E tag"); -testRulesetTag('\x7F', 'null', "0x7F tag"); +testInvalidTag('\x19', "0x19 tag"); +testTag('\x20', '" "', "0x20 tag"); +testTag('\x7E', '"~"', "0x7E tag"); +testInvalidTag('\x7F', "0x7F tag"); diff --git a/testing/web-platform/tests/speculation-rules/prefetch/speculation-tags-ruleset-no-tags.https.html b/testing/web-platform/tests/speculation-rules/prefetch/speculation-tags-no-tags.https.html similarity index 100% rename from testing/web-platform/tests/speculation-rules/prefetch/speculation-tags-ruleset-no-tags.https.html rename to testing/web-platform/tests/speculation-rules/prefetch/speculation-tags-no-tags.https.html diff --git a/testing/web-platform/tests/speculation-rules/prefetch/speculation-tags-ruleset-valid.https.html b/testing/web-platform/tests/speculation-rules/prefetch/speculation-tags-valid.https.html similarity index 63% rename from testing/web-platform/tests/speculation-rules/prefetch/speculation-tags-ruleset-valid.https.html rename to testing/web-platform/tests/speculation-rules/prefetch/speculation-tags-valid.https.html index b5ce4ef730e..e5eb7050653 100644 --- a/testing/web-platform/tests/speculation-rules/prefetch/speculation-tags-ruleset-valid.https.html +++ b/testing/web-platform/tests/speculation-rules/prefetch/speculation-tags-valid.https.html @@ -1,5 +1,7 @@ Prefetch request's Sec-Speculation-Tags request headers + + @@ -19,12 +21,12 @@ setup(() => assertSpeculationRulesIsSupported()); const R = String.raw; // Valid tag cases. -testRulesetTag('my-rules', '"my-rules"', "string tag"); -testRulesetTag('null', '"null"', "'null' string tag"); -testRulesetTag('', '""', "empty string tag"); -testRulesetTag(' ', '" "', "single space tag"); -testRulesetTag('"', R`"\""`, "double quote tag"); -testRulesetTag('"""', R`"\"\"\""`, "double quotes tag"); -testRulesetTag('\\', R`"\\"`, "backslash tag"); -testRulesetTag('\\\\\\', R`"\\\\\\"`, "backslashes tag"); +testTag('my-rules', '"my-rules"', "string tag"); +testTag('null', '"null"', "'null' string tag"); +testTag('', '""', "empty string tag"); +testTag(' ', '" "', "single space tag"); +testTag('"', R`"\""`, "double quote tag"); +testTag('"""', R`"\"\"\""`, "double quotes tag"); +testTag('\\', R`"\\"`, "backslash tag"); +testTag('\\\\\\', R`"\\\\\\"`, "backslashes tag"); diff --git a/testing/web-platform/tests/speech-api/SpeechRecognition-basics.https.html b/testing/web-platform/tests/speech-api/SpeechRecognition-basics.https.html index 16c22e7c95f..827844096f6 100644 --- a/testing/web-platform/tests/speech-api/SpeechRecognition-basics.https.html +++ b/testing/web-platform/tests/speech-api/SpeechRecognition-basics.https.html @@ -12,6 +12,6 @@ test(function() { assert_false(reco.interimResults, 'SpeechRecognition.interimResults'); assert_equals(reco.maxAlternatives, 1, 'SpeechRecognition.maxAlternatives'); assert_equals(reco.mode, 'ondevice-preferred', 'SpeechRecognition.mode'); - assert_equals(reco.context, null, 'SpeechRecognition.context'); + assert_equals(reco.phrases, null, 'SpeechRecognition.phrases'); }); diff --git a/testing/web-platform/tests/speech-api/SpeechRecognition-installOnDevice.https.html b/testing/web-platform/tests/speech-api/SpeechRecognition-installOnDevice.https.html index 08d370d5dc2..1d1dd35edc2 100644 --- a/testing/web-platform/tests/speech-api/SpeechRecognition-installOnDevice.https.html +++ b/testing/web-platform/tests/speech-api/SpeechRecognition-installOnDevice.https.html @@ -29,6 +29,32 @@ promise_test(async (t) => { "installOnDevice should resolve with `true` when called with a supported language code." ); + // Verify that the newly installed language pack is available. + const availableOnDeviceResultPromise = SpeechRecognition.availableOnDevice(validLang); + assert_true( + availableOnDeviceResultPromise instanceof Promise, + "availableOnDevice should return a Promise." + ); + + const availableOnDeviceResult = await availableOnDeviceResultPromise; + assert_true( + typeof availableOnDeviceResult === "string", + "The resolved value of the availableOnDevice promise should be a string." + ); + + assert_true(availableOnDeviceResult === "available", + "The resolved value of the availableOnDevice promise should be available." + ); + + // Verify that installing an already installed language pack resolves to true. + const secondResultPromise = SpeechRecognition.installOnDevice(validLang); + const secondResult = await secondResultPromise; + assert_equals( + secondResult, + true, + "installOnDevice should resolve with `true` if the language is already installed." + ); + // Test that it returns a promise. const invalidResultPromise = SpeechRecognition.installOnDevice(invalidLang); const invalidResult = await invalidResultPromise; diff --git a/testing/web-platform/tests/streams/writable-streams/close.any.js b/testing/web-platform/tests/streams/writable-streams/close.any.js index 45261e7ca7e..3b2c07cc3bf 100644 --- a/testing/web-platform/tests/streams/writable-streams/close.any.js +++ b/testing/web-platform/tests/streams/writable-streams/close.any.js @@ -468,3 +468,14 @@ promise_test(t => { return promise_rejects_js(t, TypeError, ws.close(), 'close should reject'); }, 'close() on a stream with a pending close should reject'); + +// See https://github.com/whatwg/streams/issues/1341. +promise_test(async t => { + const ws = new WritableStream(); + const writer = ws.getWriter(); + + await writer.write(1); + await writer.close(); + + return promise_rejects_js(t, TypeError, writer.write(2), 'write should reject'); +}, 'write() on a closed stream should reject'); diff --git a/testing/web-platform/tests/tools/ci/azure/fyi_hook.yml b/testing/web-platform/tests/tools/ci/azure/fyi_hook.yml index df92ce10664..8e541e14d86 100644 --- a/testing/web-platform/tests/tools/ci/azure/fyi_hook.yml +++ b/testing/web-platform/tests/tools/ci/azure/fyi_hook.yml @@ -10,7 +10,7 @@ jobs: displayName: 'wpt.fyi hook: ${{ parameters.artifactName }}' dependsOn: ${{ parameters.dependsOn }} pool: - vmImage: 'ubuntu-20.04' + vmImage: 'ubuntu-24.04' steps: - checkout: none - script: | diff --git a/testing/web-platform/tests/tools/ci/tc/tasks/test.yml b/testing/web-platform/tests/tools/ci/tc/tasks/test.yml index 54cf522ebd8..c96082cfa03 100644 --- a/testing/web-platform/tests/tools/ci/tc/tasks/test.yml +++ b/testing/web-platform/tests/tools/ci/tc/tasks/test.yml @@ -4,7 +4,7 @@ components: workerType: ci schedulerId: taskcluster-github deadline: "24 hours" - image: ghcr.io/web-platform-tests/wpt:1 + image: ghcr.io/web-platform-tests/wpt:2 maxRunTime: 7200 artifacts: public/results: diff --git a/testing/web-platform/tests/tools/wpt/tests/test_wpt.py b/testing/web-platform/tests/tools/wpt/tests/test_wpt.py index c977b9a873f..cea4122dc9e 100644 --- a/testing/web-platform/tests/tools/wpt/tests/test_wpt.py +++ b/testing/web-platform/tests/tools/wpt/tests/test_wpt.py @@ -1,6 +1,7 @@ # mypy: allow-untyped-defs import errno +import logging import os import shutil import socket @@ -8,14 +9,14 @@ import subprocess import sys import tempfile import time - from urllib.request import urlopen from urllib.error import URLError import pytest +from tools.wpt import browser, utils, wpt + here = os.path.abspath(os.path.dirname(__file__)) -from tools.wpt import utils, wpt def is_port_8000_in_use(): @@ -57,6 +58,19 @@ def manifest_dir(): utils.rmtree(path) +@pytest.fixture(scope="module") +def download_firefox(): + try: + logger = logging.getLogger("download_firefox") + path = tempfile.mkdtemp() + firefox = browser.Firefox(logger) + bin_path = firefox.install(dest=path) + assert os.path.exists(bin_path) and os.path.isfile(bin_path) + yield bin_path + finally: + utils.rmtree(path) + + @pytest.fixture def temp_test(): os.makedirs("../../.tools-tests") @@ -120,7 +134,8 @@ def test_list_tests(manifest_dir): @pytest.mark.slow -def test_list_tests_missing_manifest(manifest_dir): +@pytest.mark.remote_network +def test_list_tests_missing_manifest(manifest_dir, download_firefox): """The `--list-tests` option should not produce an error in the absence of a test manifest file.""" @@ -135,17 +150,20 @@ def test_list_tests_missing_manifest(manifest_dir): # drastically reduces the time to execute the test. "--tests", here, "--metadata", manifest_dir, + "--log-mach", "-", "--list-tests", "--yes", # WebTransport server is not needed (web-platform-tests/wpt#41675). "--no-enable-webtransport-h3", + "--binary", download_firefox, "firefox", "/dom/nodes/Element-tagName.html"]) assert excinfo.value.code == 0 @pytest.mark.slow -def test_list_tests_invalid_manifest(manifest_dir): +@pytest.mark.remote_network +def test_list_tests_invalid_manifest(manifest_dir, download_firefox): """The `--list-tests` option should not produce an error in the presence of a malformed test manifest file.""" @@ -165,10 +183,12 @@ def test_list_tests_invalid_manifest(manifest_dir): # drastically reduces the time to execute the test. "--tests", here, "--metadata", manifest_dir, + "--log-mach", "-", "--list-tests", "--yes", # WebTransport server is not needed (web-platform-tests/wpt#41675). "--no-enable-webtransport-h3", + "--binary", download_firefox, "firefox", "/dom/nodes/Element-tagName.html"]) assert excinfo.value.code == 0 diff --git a/testing/web-platform/tests/webmessaging/WEB_FEATURES.yml b/testing/web-platform/tests/webmessaging/WEB_FEATURES.yml new file mode 100644 index 00000000000..0aacecad892 --- /dev/null +++ b/testing/web-platform/tests/webmessaging/WEB_FEATURES.yml @@ -0,0 +1,5 @@ +features: +- name: messageerror + files: + - messageerror.html + - postMessage_CryptoKey_insecure.sub.html diff --git a/testing/web-platform/tests/webnn/conformance_tests/dequantizeLinear.https.any.js b/testing/web-platform/tests/webnn/conformance_tests/dequantizeLinear.https.any.js index 310dd03fcb5..35c15055661 100644 --- a/testing/web-platform/tests/webnn/conformance_tests/dequantizeLinear.https.any.js +++ b/testing/web-platform/tests/webnn/conformance_tests/dequantizeLinear.https.any.js @@ -656,6 +656,55 @@ const dequantizeLinearTests = [ } } }, + { + 'name': 'dequantizeLinear as an intermediate node', + 'graph': { + 'inputs': { + 'dequantizeLinearInput': { + 'data': [34, 23], + 'descriptor': {shape: [2, 1], dataType: 'int32'}, + 'constant': false + }, + 'dequantizeLinearScale': { + 'data': [1.1202747821807861, 0.2800687253475189], + 'descriptor': {shape: [2], dataType: 'float32'}, + 'constant': true + }, + 'dequantizeLinearZeroPoint': { + 'data': [35, -24], + 'descriptor': {shape: [2], dataType: 'int32'}, + 'constant': true + } + }, + 'operators': [ + { + 'name': 'transpose', + 'arguments': [ + {'input': 'dequantizeLinearInput'}, { + 'options': { + 'permutation': [1, 0], + } + } + ], + 'outputs': 'transposeOutput' + }, + { + 'name': 'dequantizeLinear', + 'arguments': [ + {'input': 'transposeOutput'}, {'scale': 'dequantizeLinearScale'}, + {'zeroPoint': 'dequantizeLinearZeroPoint'} + ], + 'outputs': 'dequantizeLinearOutput' + } + ], + 'expectedOutputs': { + 'dequantizeLinearOutput': { + 'data': [-1.1202747821807861, 13.163229942321777], + 'descriptor': {shape: [1, 2], dataType: 'float32'} + } + } + } + }, ]; if (navigator.ml) { diff --git a/testing/web-platform/tests/webnn/conformance_tests/inputs-with-special-names.https.any.js b/testing/web-platform/tests/webnn/conformance_tests/inputs-with-special-names.https.any.js new file mode 100644 index 00000000000..2921cf1fe5e --- /dev/null +++ b/testing/web-platform/tests/webnn/conformance_tests/inputs-with-special-names.https.any.js @@ -0,0 +1,74 @@ +// META: title=test input with special character names +// META: global=window +// META: variant=?cpu +// META: variant=?gpu +// META: variant=?npu +// META: script=../resources/utils.js +// META: timeout=long + +'use strict'; + +// https://www.w3.org/TR/webnn/#api-mlgraphbuilder-input + +let mlContext; + +// Skip tests if WebNN is unimplemented. +promise_setup(async () => { + assert_implements(navigator.ml, 'missing navigator.ml'); + mlContext = await navigator.ml.createContext(contextOptions); +}); + +const specialNameArray = [ + '12-L#!.☺', + '🤦🏼‍♂️124DS#!F', + + // Escape Sequence + 'hello\n\t\r\b\f\v\'\"\0\\webnn', + + // Hexadecimal Escape Sequences + // '\x41'→ 'A' + '\x41\x41\x41', + + // Unicode & Hexadecimal Characters + // "\u00A9" → "©" + // "\xA9" → "©" + // "\u2665" → "♥" + // "\u2026" → "…" + // "\U0001F600" → 😀 (Grinning Face Emoji) + '\u00A9\xA9\u2665\u2026', + '\U0001F600' +]; + +specialNameArray.forEach((name) => { + promise_test(async () => { + const builder = new MLGraphBuilder(mlContext); + const inputOperand = builder.input(name, {dataType: 'float32', shape: [4]}); + const outputOperand = builder.abs(inputOperand); + + const [inputTensor, outputTensor, mlGraph] = await Promise.all([ + mlContext.createTensor({ + dataType: 'float32', + shape: [4], + readable: true, + writable: true, + }), + mlContext.createTensor({dataType: 'float32', shape: [4], readable: true}), + builder.build({'output': outputOperand}) + ]); + + const inputData = Float32Array.from([-2, -1, 1, 2]); + mlContext.writeTensor(inputTensor, inputData); + + const inputs = {}; + inputs[name] = inputTensor; + + mlContext.dispatch(mlGraph, inputs, {'output': outputTensor}); + + // Wait for graph execution to complete. + await mlContext.readTensor(outputTensor); + + assert_array_equals( + new Float32Array(await mlContext.readTensor(outputTensor)), + Float32Array.from([2, 1, 1, 2])); + }, `abs input with special character name '${name}'`); +}); diff --git a/toolkit/components/backgroundtasks/tests/xpcshell/test_backgroundtask_experiments.js b/toolkit/components/backgroundtasks/tests/xpcshell/test_backgroundtask_experiments.js index cb4f3bb9574..1313b270369 100644 --- a/toolkit/components/backgroundtasks/tests/xpcshell/test_backgroundtask_experiments.js +++ b/toolkit/components/backgroundtasks/tests/xpcshell/test_backgroundtask_experiments.js @@ -67,7 +67,7 @@ add_setup(async () => { await manager.onStartup(); await manager.store.addEnrollment(ExperimentFakes.experiment("foo")); - manager.unenroll("foo", "some-reason"); + manager.unenroll("foo"); await manager.store.addEnrollment( ExperimentFakes.experiment("bar", { active: false }) ); @@ -76,7 +76,7 @@ add_setup(async () => { ); manager.store.addEnrollment(ExperimentFakes.rollout("rol1")); - manager.unenroll("rol1", "some-reason"); + manager.unenroll("rol1"); manager.store.addEnrollment(ExperimentFakes.rollout("rol2")); }); diff --git a/toolkit/components/messaging-system/schemas/SpecialMessageActionSchemas/test/browser/browser_sma_submit_onboarding_opt_out_ping.js b/toolkit/components/messaging-system/schemas/SpecialMessageActionSchemas/test/browser/browser_sma_submit_onboarding_opt_out_ping.js index f752755ce66..484632d3980 100644 --- a/toolkit/components/messaging-system/schemas/SpecialMessageActionSchemas/test/browser/browser_sma_submit_onboarding_opt_out_ping.js +++ b/toolkit/components/messaging-system/schemas/SpecialMessageActionSchemas/test/browser/browser_sma_submit_onboarding_opt_out_ping.js @@ -18,7 +18,7 @@ add_task(async function test_SUBMIT_ONBOARDING_OPT_OUT_PING() { await manager.onStartup(); await manager.store.addEnrollment(ExperimentFakes.experiment("foo")); - manager.unenroll("foo", "some-reason"); + manager.unenroll("foo"); await manager.store.addEnrollment( ExperimentFakes.experiment("bar", { active: false }) ); @@ -27,7 +27,7 @@ add_task(async function test_SUBMIT_ONBOARDING_OPT_OUT_PING() { ); manager.store.addEnrollment(ExperimentFakes.rollout("rol1")); - manager.unenroll("rol1", "some-reason"); + manager.unenroll("rol1"); manager.store.addEnrollment(ExperimentFakes.rollout("rol2")); let { promise, resolve } = Promise.withResolvers(); diff --git a/toolkit/components/nimbus/ExperimentAPI.sys.mjs b/toolkit/components/nimbus/ExperimentAPI.sys.mjs index eeb90eaf0c7..c7e59272795 100644 --- a/toolkit/components/nimbus/ExperimentAPI.sys.mjs +++ b/toolkit/components/nimbus/ExperimentAPI.sys.mjs @@ -16,6 +16,7 @@ ChromeUtils.defineESModuleGetters(lazy, { RemoteSettings: "resource://services-settings/remote-settings.sys.mjs", RemoteSettingsExperimentLoader: "resource://nimbus/lib/RemoteSettingsExperimentLoader.sys.mjs", + UnenrollmentCause: "resource://nimbus/lib/ExperimentManager.sys.mjs", }); ChromeUtils.defineLazyGetter(lazy, "log", () => { @@ -592,7 +593,9 @@ export class _ExperimentFeature { ); if (missingIds?.size) { - throw new ExperimentLocalizationError("l10n-missing-entry"); + throw new ExperimentLocalizationError( + lazy.NimbusTelemetry.ValidationFailureReason.L10N_MISSING_ENTRY + ); } return result; @@ -645,7 +648,9 @@ export class _ExperimentFeature { missingIds.add(value.id); break; } else { - throw new ExperimentLocalizationError("l10n-missing-entry"); + throw new ExperimentLocalizationError( + lazy.NimbusTelemetry.ValidationFailureReason.L10N_MISSING_ENTRY + ); } } @@ -681,7 +686,12 @@ export class _ExperimentFeature { (typeof enrollment.localizations[locale] !== "object" || enrollment.localizations[locale] === null) ) { - ExperimentAPI._manager.unenroll(enrollment.slug, "l10n-missing-locale"); + ExperimentAPI._manager._unenroll( + enrollment, + lazy.UnenrollmentCause.fromReason( + lazy.NimbusTelemetry.UnenrollReason.L10N_MISSING_LOCALE + ) + ); return undefined; } @@ -698,7 +708,10 @@ export class _ExperimentFeature { } catch (e) { // This should never happen. if (e instanceof ExperimentLocalizationError) { - ExperimentAPI._manager.unenroll(enrollment.slug, e.reason); + ExperimentAPI._manager._unenroll( + enrollment, + lazy.UnenrollmentCause.fromReason(e.reason) + ); } else { throw e; } diff --git a/toolkit/components/nimbus/FeatureManifest.yaml b/toolkit/components/nimbus/FeatureManifest.yaml index 2e0cde00fa1..3170b45ef1e 100644 --- a/toolkit/components/nimbus/FeatureManifest.yaml +++ b/toolkit/components/nimbus/FeatureManifest.yaml @@ -1224,13 +1224,13 @@ newTabSectionsExperiment: pref: browser.newtabpage.activity-stream.discoverystream.sections.personalization.inferred.enabled description: >- Enable the inferred personalization for topics - reportContentEnabled: + reportAdsEnabled: type: boolean setPref: branch: user - pref: browser.newtabpage.activity-stream.discoverystream.reportContent.enabled + pref: browser.newtabpage.activity-stream.discoverystream.reportAds.enabled description: >- - Enable reporting for ads and content + Enable reporting for ads localeSectionstConfig: type: string setPref: diff --git a/toolkit/components/nimbus/FirefoxLabs.sys.mjs b/toolkit/components/nimbus/FirefoxLabs.sys.mjs index 83ec4b9f348..c6c8b9e3f9e 100644 --- a/toolkit/components/nimbus/FirefoxLabs.sys.mjs +++ b/toolkit/components/nimbus/FirefoxLabs.sys.mjs @@ -6,6 +6,8 @@ const lazy = {}; ChromeUtils.defineESModuleGetters(lazy, { ExperimentAPI: "resource://nimbus/ExperimentAPI.sys.mjs", + NimbusTelemetry: "resource://nimbus/lib/Telemetry.sys.mjs", + UnenrollmentCause: "resource://nimbus/lib/ExperimentManager.sys.mjs", }); ChromeUtils.defineLazyGetter(lazy, "log", () => { @@ -95,7 +97,12 @@ export class FirefoxLabs { } try { - lazy.ExperimentAPI._manager.unenroll(slug, "labs-opt-out"); + lazy.ExperimentAPI._manager.unenroll( + slug, + lazy.UnenrollmentCause.fromReason( + lazy.NimbusTelemetry.UnenrollReason.LABS_OPT_OUT + ) + ); } catch (e) { lazy.log.error(`unenroll: failed to unenroll from ${slug}`, e); } diff --git a/toolkit/components/nimbus/lib/ExperimentManager.sys.mjs b/toolkit/components/nimbus/lib/ExperimentManager.sys.mjs index 319ef9f44b9..2120930cf90 100644 --- a/toolkit/components/nimbus/lib/ExperimentManager.sys.mjs +++ b/toolkit/components/nimbus/lib/ExperimentManager.sys.mjs @@ -36,6 +36,8 @@ const STUDIES_OPT_OUT_PREF = "app.shield.optoutstudies.enabled"; const STUDIES_ENABLED_CHANGED = "nimbus:studies-enabled-changed"; +const FORCE_ENROLLMENT_SOURCE = "force-enrollment"; + function featuresCompat(branch) { if (!branch || (!branch.feature && !branch.features)) { return []; @@ -55,6 +57,68 @@ function getFeatureFromBranch(branch, featureId) { ); } +export const UnenrollmentCause = { + fromCheckRecipeResult(result) { + const { UnenrollReason } = lazy.NimbusTelemetry; + + let reason; + + if (result.ok) { + switch (result.status) { + case lazy.MatchStatus.NOT_SEEN: + reason = UnenrollReason.RECIPE_NOT_SEEN; + break; + + case lazy.MatchStatus.NO_MATCH: + reason = UnenrollReason.TARGETING_MISMATCH; + break; + + case lazy.MatchStatus.TARGETING_ONLY: + reason = UnenrollReason.BUCKETING; + break; + + // TARGETING_AND_BUCKETING cannot cause unenrollment. + } + } else { + reason = result.reason; + } + + return { reason }; + }, + + fromReason(reason) { + return { reason }; + }, + + ChangedPref(pref) { + return { + reason: lazy.NimbusTelemetry.UnenrollReason.CHANGED_PREF, + changedPref: pref, + }; + }, + + PrefFlipsConflict(conflictingSlug) { + return { + reason: lazy.NimbusTelemetry.UnenrollReason.PREF_FLIPS_CONFLICT, + conflictingSlug, + }; + }, + + PrefFlipsFailed(prefName, prefType) { + return { + reason: lazy.NimbusTelemetry.UnenrollReason.PREF_FLIPS_FAILED, + prefName, + prefType, + }; + }, + + Unknown() { + return { + reason: lazy.NimbusTelemetry.UnenrollReason.UNKNOWN, + }; + }, +}; + /** * A module for processes Experiment recipes, choosing and storing enrollment state, * and sending experiment-related Telemetry. @@ -260,19 +324,39 @@ export class _ExperimentManager { return; } - if (result.status === lazy.MatchStatus.TARGETING_AND_BUCKETING) { - const enrollment = await this.enroll(recipe, source); - if (enrollment) { + switch (result.status) { + case lazy.MatchStatus.ENROLLMENT_PAUSED: lazy.NimbusTelemetry.recordEnrollmentStatus({ - slug: enrollment.slug, - branch: enrollment.branch.slug, - status: EnrollmentStatus.ENROLLED, - reason: EnrollmentStatusReason.QUALIFIED, + slug: recipe.slug, + status: EnrollmentStatus.NOT_ENROLLED, + reason: EnrollmentStatusReason.ENROLLMENTS_PAUSED, }); - } - } + break; - // TODO(bug 1955169): Record NotEnrolled enrollment status telemetry. + case lazy.MatchStatus.NO_MATCH: + lazy.NimbusTelemetry.recordEnrollmentStatus({ + slug: recipe.slug, + status: EnrollmentStatus.NOT_ENROLLED, + reason: EnrollmentStatusReason.NOT_TARGETED, + }); + break; + + case lazy.MatchStatus.TARGETING_ONLY: + lazy.NimbusTelemetry.recordEnrollmentStatus({ + slug: recipe.slug, + status: EnrollmentStatus.NOT_ENROLLED, + reason: EnrollmentStatusReason.NOT_SELECTED, + }); + break; + + case lazy.MatchStatus.TARGETING_AND_BUCKETING: + await this.enroll(recipe, source); + break; + + // This function will not be called with MatchStatus.NOT_SEEN -- + // RemoteSettingsExperimentLoader will call updateEnrollment directly + // instead. + } } /** @@ -427,12 +511,18 @@ export class _ExperimentManager { slug, lazy.NimbusTelemetry.EnrollmentFailureReason.NAME_CONFLICT ); + lazy.NimbusTelemetry.recordEnrollmentStatus({ + slug, + status: lazy.NimbusTelemetry.EnrollmentStatus.NOT_ENROLLED, + reason: lazy.NimbusTelemetry.EnrollmentStatusReason.NAME_CONFLICT, + }); + throw new Error(`An experiment with the slug "${slug}" already exists.`); } let storeLookupByFeature = recipe.isRollout ? this.store.getRolloutForFeature.bind(this.store) - : this.store.hasExperimentForFeature.bind(this.store); + : this.store.getExperimentForFeature.bind(this.store); const userId = await this.getUserId(bucketConfig); let branch; @@ -461,8 +551,9 @@ export class _ExperimentManager { } const features = featuresCompat(branch); - for (let feature of features) { - if (storeLookupByFeature(feature?.featureId)) { + for (const feature of features) { + const existingEnrollment = storeLookupByFeature(feature?.featureId); + if (existingEnrollment) { lazy.log.debug( `Skipping enrollment for "${slug}" because there is an existing ${ recipe.isRollout ? "rollout" : "experiment" @@ -472,7 +563,12 @@ export class _ExperimentManager { slug, lazy.NimbusTelemetry.EnrollmentFailureReason.FEATURE_CONFLICT ); - // TODO (bug 1955170) Add enrollment status telemetry + lazy.NimbusTelemetry.recordEnrollmentStatus({ + slug, + status: lazy.NimbusTelemetry.EnrollmentStatus.NOT_ENROLLED, + reason: lazy.NimbusTelemetry.EnrollmentStatusReason.FEATURE_CONFLICT, + conflict_slug: existingEnrollment.slug, + }); return null; } } @@ -519,10 +615,7 @@ export class _ExperimentManager { for (const prefName of Object.keys(featureValue.prefs)) { if (prefNames.has(prefName)) { - this._unenroll(enrollment, { - reason: lazy.NimbusTelemetry.UnenrollReason.PREF_FLIPS_CONFLICT, - conflictingSlug: slug, - }); + this._unenroll(enrollment, UnenrollmentCause.PrefFlipsConflict(slug)); break; } } @@ -588,7 +681,7 @@ export class _ExperimentManager { return enrollment; } - forceEnroll(recipe, branch, source = "force-enrollment") { + forceEnroll(recipe, branch) { /** * If we happen to be enrolled in an experiment for the same feature * we need to unenroll from that experiment. @@ -608,7 +701,12 @@ export class _ExperimentManager { } found for the same feature ${feature.featureId}, unenrolling.` ); - this.unenroll(enrollment.slug, source); + this._unenroll( + enrollment, + UnenrollmentCause.fromReason( + lazy.NimbusTelemetry.UnenrollReason.FORCE_ENROLLMENT + ) + ); } } @@ -621,7 +719,7 @@ export class _ExperimentManager { slug, }, branch, - source, + FORCE_ENROLLMENT_SOURCE, { force: true } ); @@ -661,26 +759,19 @@ export class _ExperimentManager { if (enrollment.active) { if (!result.ok) { // If the recipe failed validation then we must unenroll. - this._unenroll(enrollment, { reason: result.reason }); - lazy.NimbusTelemetry.recordEnrollmentStatus({ - slug: enrollment.slug, - branch: enrollment.branch.slug, - status: EnrollmentStatus.DISQUALIFIED, - reason: EnrollmentStatusReason.ERROR, - error_string: result.reason, - }); - + this._unenroll( + enrollment, + UnenrollmentCause.fromCheckRecipeResult(result) + ); return false; } if (result.status === lazy.MatchStatus.NOT_SEEN) { // If the recipe was not present in the source we must unenroll. - this._unenroll(enrollment, { reason: UnenrollReason.RECIPE_NOT_SEEN }); - lazy.NimbusTelemetry.recordEnrollmentStatus({ - slug: enrollment.slug, - branch: enrollment.branch.slug, - status: EnrollmentStatus.WAS_ENROLLED, - }); + this._unenroll( + enrollment, + UnenrollmentCause.fromCheckRecipeResult(result) + ); return false; } @@ -688,30 +779,20 @@ export class _ExperimentManager { // Our branch has been removed so we must unenroll. // // This should not happen in practice. - this._unenroll(enrollment, { reason: UnenrollReason.BRANCH_REMOVED }); - lazy.NimbusTelemetry.recordEnrollmentStatus({ - slug: enrollment.slug, - branch: enrollment.branch.slug, - status: EnrollmentStatus.DISQUALIFIED, - reason: EnrollmentStatus.ERROR, - error_string: UnenrollReason.BRANCH_REMOVED, - }); - + this._unenroll( + enrollment, + UnenrollmentCause.fromReason(UnenrollReason.BRANCH_REMOVED) + ); return false; } if (result.status === lazy.MatchStatus.NO_MATCH) { // If we have an active enrollment and we no longer match targeting we // must unenroll. - this._unenroll(enrollment, { - reason: UnenrollReason.TARGETING_MISMATCH, - }); - lazy.NimbusTelemetry.recordEnrollmentStatus({ - slug: enrollment.slug, - branch: enrollment.branch.slug, - status: EnrollmentStatus.DISQUALIFIED, - reason: EnrollmentStatusReason.NOT_TARGETED, - }); + this._unenroll( + enrollment, + UnenrollmentCause.fromCheckRecipeResult(result) + ); return false; } @@ -721,20 +802,26 @@ export class _ExperimentManager { ) { // If we no longer fall in the bucketing allocation for this rollout we // must unenroll. - this._unenroll(enrollment, { reason: UnenrollReason.BUCKETING }); + this._unenroll( + enrollment, + UnenrollmentCause.fromCheckRecipeResult(result) + ); return false; } + if (result.status === lazy.MatchStatus.TARGETING_AND_BUCKETING) { + lazy.NimbusTelemetry.recordEnrollmentStatus({ + slug: enrollment.slug, + branch: enrollment.branch.slug, + status: EnrollmentStatus.ENROLLED, + reason: EnrollmentStatusReason.QUALIFIED, + }); + } + // Either this recipe is not a rollout or both targeting matches and we // are in the bucket allocation. For the former, we do not re-evaluate // bucketing for experiments because the bucketing cannot change. For the // latter, we are already active so we don't need to enroll. - lazy.NimbusTelemetry.recordEnrollmentStatus({ - slug: enrollment.slug, - branch: enrollment.branch.slug, - status: EnrollmentStatus.ENROLLED, - reason: EnrollmentStatusReason.QUALIFIED, - }); return true; } @@ -752,16 +839,7 @@ export class _ExperimentManager { // We only re-enroll if we match targeting and bucketing and the user did // not purposefully opt out via about:studies. lazy.log.debug(`Re-enrolling in rollout "${recipe.slug}`); - const enrollment = await this.enroll(recipe, source, { reenroll: true }); - if (enrollment) { - lazy.NimbusTelemetry.recordEnrollmentStatus({ - slug: enrollment.slug, - branch: enrollment.branch.slug, - status: EnrollmentStatus.ENROLLED, - reason: EnrollmentStatusReason.QUALIFIED, - }); - return true; - } + return !!(await this.enroll(recipe, source, { reenroll: true })); } return false; @@ -772,13 +850,13 @@ export class _ExperimentManager { * * @param {string} slug * The slug of the enrollment to stop. - * @param {string} reason - * An optional reason for the unenrollment. If not provided, "unknown" - * will be used. + * @param {object?} cause + * The cause of this unenrollment. If not provided, "unknown" will be + * used for the unenrollment reason. * - * This will be reported in telemetry. + * See `UnenrollCause` for details. */ - unenroll(slug, reason) { + unenroll(slug, cause) { const enrollment = this.store.get(slug); if (!enrollment) { lazy.NimbusTelemetry.recordUnenrollmentFailure( @@ -789,9 +867,7 @@ export class _ExperimentManager { return; } - this._unenroll(enrollment, { - reason: reason ?? lazy.NimbusTelemetry.UnenrollReason.UNKNOWN, - }); + this._unenroll(enrollment, cause ?? UnenrollmentCause.Unknown()); } /** @@ -800,33 +876,18 @@ export class _ExperimentManager { * @param {Enrollment} enrollment * The enrollment to end. * - * @param {object} options - * @param {string} options.reason - * An optional reason for the unenrollment. + * @param {object} cause + * The cause of this unenrollment. * - * This will be reported in telemetry. + * See `UnenrollmentCause` for details. * - * @param {object?} options.changedPref - * If the unenrollment was due to pref change, this will contain the - * information about the pref that changed. + * @param {object?} options * - * @param {string} options.changedPref.name - * The name of the pref that caused the unenrollment. - * - * @param {string} options.changedPref.branch - * The branch that was changed ("user" or "default"). + * @param {boolean} options.duringRestore + * If true, this indicates that this was during the call to + * `_restoreEnrollmentPrefs`. */ - _unenroll( - enrollment, - { - reason = "unknown", - changedPref = undefined, - duringRestore = false, - conflictingSlug = undefined, - prefName = undefined, - prefType = undefined, - } = {} - ) { + _unenroll(enrollment, cause, { duringRestore = false } = {}) { const { slug } = enrollment; if (!enrollment.active) { @@ -841,22 +902,12 @@ export class _ExperimentManager { this.store.updateExperiment(slug, { active: false, - unenrollReason: reason, + unenrollReason: cause.reason, }); - lazy.NimbusTelemetry.recordUnenrollment( - slug, - reason, - enrollment.branch.slug, - { - changedPref, - conflictingSlug, - prefType, - prefName, - } - ); + lazy.NimbusTelemetry.recordUnenrollment(enrollment, cause); - this._unsetEnrollmentPrefs(enrollment, { changedPref, duringRestore }); + this._unsetEnrollmentPrefs(enrollment, cause, { duringRestore }); lazy.log.debug(`Recipe unenrolled: ${slug}`); } @@ -873,11 +924,21 @@ export class _ExperimentManager { * Unenroll from all active studies if user opts out. */ _handleStudiesOptOut() { - for (const { slug } of this.store.getAllActiveExperiments()) { - this.unenroll(slug, lazy.NimbusTelemetry.UnenrollReason.STUDIES_OPT_OUT); + for (const enrollment of this.store.getAllActiveExperiments()) { + this._unenroll( + enrollment, + UnenrollmentCause.fromReason( + lazy.NimbusTelemetry.UnenrollReason.STUDIES_OPT_OUT + ) + ); } - for (const { slug } of this.store.getAllActiveRollouts()) { - this.unenroll(slug, lazy.NimbusTelemetry.UnenrollReason.STUDIES_OPT_OUT); + for (const enrollment of this.store.getAllActiveRollouts()) { + this._unenroll( + enrollment, + UnenrollmentCause.fromReason( + lazy.NimbusTelemetry.UnenrollReason.STUDIES_OPT_OUT + ) + ); } this.optInRecipes = []; @@ -1121,26 +1182,20 @@ export class _ExperimentManager { * Otherwise, it will be set to the original value from before the enrollment * began. * - * @param {Enrollment} enrollment + * @param {object} enrollment * The enrollment that has ended. * + * @param {object} cause + * The cause of the unenrollment. + * + * See `UnenrollmentCause` for details. + * * @param {object} options * - * @param {object?} options.changedPref - * If provided, a changed pref that caused the unenrollment that - * triggered unsetting these prefs. This is provided as to not - * overwrite a changed pref with an original value. - * - * @param {string} options.changedPref.name - * The name of the changed pref. - * - * @param {string} options.changedPref.branch - * The branch that was changed ("user" or "default"). - * * @param {boolean} options.duringRestore * The unenrollment was caused during restore. */ - _unsetEnrollmentPrefs(enrollment, { changedPref, duringRestore } = {}) { + _unsetEnrollmentPrefs(enrollment, cause, { duringRestore } = {}) { if (!enrollment.prefs?.length) { return; } @@ -1153,8 +1208,9 @@ export class _ExperimentManager { this._removePrefObserver(pref.name, enrollment.slug); if ( - changedPref?.name == pref.name && - changedPref.branch === pref.branch + cause.reason === lazy.NimbusTelemetry.UnenrollReason.CHANGED_PREF && + cause.changedPref.name === pref.name && + cause.changedPref.branch === pref.branch ) { // Resetting the original value would overwite the pref the user just // set. Skip it. @@ -1224,6 +1280,8 @@ export class _ExperimentManager { * enrollment has ended. */ _restoreEnrollmentPrefs(enrollment) { + const { UnenrollReason } = lazy.NimbusTelemetry; + const { branch, prefs = [], isRollout } = enrollment; if (!prefs?.length) { @@ -1237,10 +1295,11 @@ export class _ExperimentManager { for (const { name, featureId, variable } of prefs) { // If the feature no longer exists, unenroll. if (!Object.hasOwn(lazy.NimbusFeatures, featureId)) { - this._unenroll(enrollment, { - reason: lazy.NimbusTelemetry.UnenrollReason.INVALID_FEATURE, - duringRestore: true, - }); + this._unenroll( + enrollment, + UnenrollmentCause.fromReason(UnenrollReason.INVALID_FEATURE), + { duringRestore: true } + ); return false; } @@ -1248,10 +1307,11 @@ export class _ExperimentManager { // If the feature is missing a variable that set a pref, unenroll. if (!Object.hasOwn(variables, variable)) { - this._unenroll(enrollment, { - reason: lazy.NimbusTelemetry.UnenrollReason.PREF_VARIABLE_MISSING, - duringRestore: true, - }); + this._unenroll( + enrollment, + UnenrollmentCause.fromReason(UnenrollReason.PREF_VARIABLE_MISSING), + { duringRestore: true } + ); return false; } @@ -1259,10 +1319,11 @@ export class _ExperimentManager { // If the variable is no longer a pref-setting variable, unenroll. if (!Object.hasOwn(variableDef, "setPref")) { - this._unenroll(enrollment, { - reason: lazy.NimbusTelemetry.UnenrollReason.PREF_VARIABLE_NO_LONGER, - duringRestore: true, - }); + this._unenroll( + enrollment, + UnenrollmentCause.fromReason(UnenrollReason.PREF_VARIABLE_NO_LONGER), + { duringRestore: true } + ); return false; } @@ -1273,10 +1334,11 @@ export class _ExperimentManager { : variableDef.setPref; if (prefName !== name) { - this._unenroll(enrollment, { - reason: lazy.NimbusTelemetry.UnenrollReason.PREF_VARIABLE_CHANGED, - duringRestore: true, - }); + this._unenroll( + enrollment, + UnenrollmentCause.fromReason(UnenrollReason.PREF_VARIABLE_CHANGED), + { duringRestore: true } + ); return false; } } @@ -1497,10 +1559,7 @@ export class _ExperimentManager { }; for (const enrollment of enrollments) { - this._unenroll(enrollment, { - reason: lazy.NimbusTelemetry.UnenrollReason.CHANGED_PREF, - changedPref, - }); + this._unenroll(enrollment, UnenrollmentCause.ChangedPref(changedPref)); } } } diff --git a/toolkit/components/nimbus/lib/PrefFlipsFeature.sys.mjs b/toolkit/components/nimbus/lib/PrefFlipsFeature.sys.mjs index f1c77f2fccf..d85ad171b4b 100644 --- a/toolkit/components/nimbus/lib/PrefFlipsFeature.sys.mjs +++ b/toolkit/components/nimbus/lib/PrefFlipsFeature.sys.mjs @@ -6,8 +6,8 @@ const lazy = {}; ChromeUtils.defineESModuleGetters(lazy, { NimbusFeatures: "resource://nimbus/ExperimentAPI.sys.mjs", - NimbusTelemetry: "resource://nimbus/lib/Telemetry.sys.mjs", PrefUtils: "resource://normandy/lib/PrefUtils.sys.mjs", + UnenrollmentCause: "resource://nimbus/lib/ExperimentManager.sys.mjs", }); const FEATURE_ID = "prefFlips"; @@ -91,10 +91,10 @@ export class PrefFlipsFeature { } for (const enrollment of toUnenroll) { - this.manager._unenroll(enrollment, { - reason: lazy.NimbusTelemetry.UnenrollReason.PREF_FLIPS_CONFLICT, - conflictingSlug: activeEnrollment.slug, - }); + this.manager._unenroll( + enrollment, + lazy.UnenrollmentCause.PrefFlipsConflict(activeEnrollment.slug) + ); } } @@ -388,11 +388,10 @@ export class PrefFlipsFeature { break; } - this.manager._unenroll(enrollment, { - reason: lazy.NimbusTelemetry.UnenrollReason.PREF_FLIPS_FAILED, - prefName: pref, - prefType, - }); + this.manager._unenroll( + enrollment, + lazy.UnenrollmentCause.PrefFlipsFailed(pref, prefType) + ); } } diff --git a/toolkit/components/nimbus/lib/RemoteSettingsExperimentLoader.sys.mjs b/toolkit/components/nimbus/lib/RemoteSettingsExperimentLoader.sys.mjs index 06c0045df66..6042ca42c4a 100644 --- a/toolkit/components/nimbus/lib/RemoteSettingsExperimentLoader.sys.mjs +++ b/toolkit/components/nimbus/lib/RemoteSettingsExperimentLoader.sys.mjs @@ -99,6 +99,7 @@ const SCHEMAS = { }; export const MatchStatus = Object.freeze({ + ENROLLMENT_PAUSED: "ENROLLMENT_PAUSED", NOT_SEEN: "NOT_SEEN", NO_MATCH: "NO_MATCH", TARGETING_ONLY: "TARGETING_ONLY", @@ -805,6 +806,11 @@ export class EnrollmentsContext { return CheckRecipeResult.UnsupportedFeatures(unsupportedFeatureIds); } + if (recipe.isEnrollmentPaused) { + lazy.log.debug(`${recipe.slug}: enrollment paused`); + return CheckRecipeResult.Ok(MatchStatus.ENROLLMENT_PAUSED); + } + if (this.shouldCheckTargeting) { const match = await this.checkTargeting(recipe); @@ -861,11 +867,6 @@ export class EnrollmentsContext { return result; } - if (recipe.isEnrollmentPaused) { - lazy.log.debug(`${recipe.slug}: enrollment paused`); - return CheckRecipeResult.Ok(MatchStatus.TARGETING_ONLY); - } - if (!(await this.manager.isInBucketAllocation(recipe.bucketConfig))) { lazy.log.debug(`${recipe.slug} did not match bucket sampling`); return CheckRecipeResult.Ok(MatchStatus.TARGETING_ONLY); diff --git a/toolkit/components/nimbus/lib/Telemetry.sys.mjs b/toolkit/components/nimbus/lib/Telemetry.sys.mjs index d03608a8ad0..d5f48e2e068 100644 --- a/toolkit/components/nimbus/lib/Telemetry.sys.mjs +++ b/toolkit/components/nimbus/lib/Telemetry.sys.mjs @@ -29,6 +29,7 @@ const EnrollmentStatus = Object.freeze({ }); const EnrollmentStatusReason = Object.freeze({ + CHANGED_PREF: "ChangedPref", QUALIFIED: "Qualified", OPT_IN: "OptIn", OPT_OUT: "OptOut", @@ -36,6 +37,9 @@ const EnrollmentStatusReason = Object.freeze({ NOT_TARGETED: "NotTargeted", ENROLLMENTS_PAUSED: "EnrollmentsPaused", FEATURE_CONFLICT: "FeatureConflict", + FORCE_ENROLLMENT: "ForceEnrollment", + NAME_CONFLICT: "NameConflict", + PREF_FLIPS_CONFLICT: "PrefFlipsConflict", ERROR: "Error", }); @@ -62,7 +66,9 @@ const UnenrollReason = Object.freeze({ BRANCH_REMOVED: "branch-removed", BUCKETING: "bucketing", CHANGED_PREF: "changed-pref", + FORCE_ENROLLMENT: "force-enrollment", INDIVIDUAL_OPT_OUT: "individual-opt-out", + LABS_OPT_OUT: "labs-opt-out", PREF_FLIPS_CONFLICT: "prefFlips-conflict", PREF_FLIPS_FAILED: "prefFlips-failed", PREF_VARIABLE_CHANGED: "pref-variable-changed", @@ -101,6 +107,16 @@ export const NimbusTelemetry = { branch: enrollment.branch.slug, experiment_type: enrollment.experimentType, }); + + this.recordEnrollmentStatus({ + slug: enrollment.slug, + branch: enrollment.branch.slug, + status: EnrollmentStatus.ENROLLED, + reason: + enrollment.force || enrollment.isFirefoxLabsOptIn + ? EnrollmentStatusReason.OPT_IN + : EnrollmentStatusReason.QUALIFIED, + }); }, recordEnrollmentFailure(slug, reason) { @@ -161,57 +177,99 @@ export const NimbusTelemetry = { ); }, - recordUnenrollment( - slug, - reason, - branchSlug, - { changedPref, conflictingSlug, prefType, prefName } = {} - ) { - lazy.TelemetryEnvironment.setExperimentInactive(slug); - Services.fog.setExperimentInactive(slug); + recordUnenrollment(enrollment, cause) { + lazy.TelemetryEnvironment.setExperimentInactive(enrollment.slug); + Services.fog.setExperimentInactive(enrollment.slug); + + const legacyEventExtra = { + branch: enrollment.branch.slug, + reason: cause.reason, + }; + const gleanEvent = { + experiment: enrollment.slug, + branch: enrollment.branch.slug, + reason: cause.reason, + }; + + switch (cause.reason) { + case UnenrollReason.CHANGED_PREF: + legacyEventExtra.changedPref = cause.changedPref.name; + gleanEvent.changed_pref = cause.changedPref.name; + break; + + case UnenrollReason.PREF_FLIPS_CONFLICT: + legacyEventExtra.conflictingSlug = cause.conflictingSlug; + gleanEvent.conflicting_slug = cause.conflictingSlug; + break; + + case UnenrollReason.PREF_FLIPS_FAILED: + legacyEventExtra.prefType = cause.prefType; + gleanEvent.pref_type = cause.prefType; + + legacyEventExtra.prefName = cause.prefName; + gleanEvent.pref_name = cause.prefName; + break; + } lazy.TelemetryEvents.sendEvent( LegacyTelemetryEvents.UNENROLL, LEGACY_TELEMETRY_EVENT_OBJECT, - slug, - Object.assign( - { - reason, - branch: branchSlug, - }, - reason === UnenrollReason.CHANGED_PREF - ? { changedPref: changedPref.name } - : {}, - reason === UnenrollReason.PREF_FLIPS_CONFLICT - ? { conflictingSlug } - : {}, - reason === UnenrollReason.PREF_FLIPS_FAILED - ? { prefType, prefName } - : {} - ) + enrollment.slug, + legacyEventExtra ); - Glean.nimbusEvents.unenrollment.record( - Object.assign( - { - experiment: slug, - branch: branchSlug, - reason, - }, - reason === UnenrollReason.CHANGED_PREF - ? { changed_pref: changedPref.name } - : {}, - reason === UnenrollReason.PREF_FLIPS_CONFLICT - ? { conflicting_slug: conflictingSlug } - : {}, - reason === UnenrollReason.PREF_FLIPS_FAILED - ? { - pref_type: prefType, - pref_name: prefName, - } - : {} - ) - ); + Glean.nimbusEvents.unenrollment.record(gleanEvent); + + const enrollmentStatus = { + slug: enrollment.slug, + branch: enrollment.branch.slug, + }; + + switch (cause.reason) { + case UnenrollReason.BUCKETING: + enrollmentStatus.status = EnrollmentStatus.DISQUALIFIED; + enrollmentStatus.reason = EnrollmentStatusReason.NOT_SELECTED; + break; + + case UnenrollReason.RECIPE_NOT_SEEN: + enrollmentStatus.status = EnrollmentStatus.WAS_ENROLLED; + break; + + case UnenrollReason.TARGETING_MISMATCH: + enrollmentStatus.status = EnrollmentStatus.DISQUALIFIED; + enrollmentStatus.reason = EnrollmentStatusReason.NOT_TARGETED; + break; + + case UnenrollReason.INDIVIDUAL_OPT_OUT: + case UnenrollReason.LABS_OPT_OUT: + case UnenrollReason.STUDIES_OPT_OUT: + enrollmentStatus.status = EnrollmentStatus.DISQUALIFIED; + enrollmentStatus.reason = EnrollmentStatusReason.OPT_OUT; + break; + + case UnenrollReason.CHANGED_PREF: + enrollmentStatus.status = EnrollmentStatus.DISQUALIFIED; + enrollmentStatus.reason = EnrollmentStatusReason.CHANGED_PREF; + break; + + case UnenrollReason.FORCE_ENROLLMENT: + enrollmentStatus.status = EnrollmentStatus.DISQUALIFIED; + enrollmentStatus.reason = EnrollmentStatusReason.FORCE_ENROLLMENT; + break; + + case UnenrollReason.PREF_FLIPS_CONFLICT: + enrollmentStatus.status = EnrollmentStatus.DISQUALIFIED; + enrollmentStatus.reason = EnrollmentStatusReason.PREF_FLIPS_CONFLICT; + enrollmentStatus.conflict_slug = cause.conflictingSlug; + break; + + default: + enrollmentStatus.status = EnrollmentStatus.DISQUALIFIED; + enrollmentStatus.reason = EnrollmentStatusReason.ERROR; + enrollmentStatus.error_string = cause.reason; + } + + this.recordEnrollmentStatus(enrollmentStatus); }, recordUnenrollmentFailure(slug, reason) { diff --git a/toolkit/components/nimbus/metrics.yaml b/toolkit/components/nimbus/metrics.yaml index 3569cad3f7f..622115e7361 100644 --- a/toolkit/components/nimbus/metrics.yaml +++ b/toolkit/components/nimbus/metrics.yaml @@ -963,8 +963,10 @@ nimbus_events: description: If the enrollment hit a feature conflict, the slug of the conflicting experiment/rollout bugs: - https://bugzilla.mozilla.org/show_bug.cgi?id=1817481 + - https://bugzilla.mozilla.org/show_bug.cgi?id=1955169 data_reviews: - https://bugzilla.mozilla.org/show_bug.cgi?id=1817481 + - https://bugzilla.mozilla.org/show_bug.cgi?id=1955169 data_sensitivity: - technical notification_emails: diff --git a/toolkit/components/nimbus/test/NimbusTestUtils.sys.mjs b/toolkit/components/nimbus/test/NimbusTestUtils.sys.mjs index 4e64eec203b..e9106f0dc83 100644 --- a/toolkit/components/nimbus/test/NimbusTestUtils.sys.mjs +++ b/toolkit/components/nimbus/test/NimbusTestUtils.sys.mjs @@ -255,13 +255,13 @@ export const ExperimentFakes = { } return function doEnrollmentCleanup() { - manager.unenroll(enrollment.slug, "cleanup"); + manager.unenroll(enrollment.slug); manager.store._deleteForTests(enrollment.slug); }; }, async cleanupAll(slugs, { manager = lazy.ExperimentAPI._manager } = {}) { for (const slug of slugs) { - manager.unenroll(slug, "cleanup"); + manager.unenroll(slug); } if (manager.store.getAllActiveExperiments().length) { diff --git a/toolkit/components/nimbus/test/browser/browser_nimbus_telemetry.js b/toolkit/components/nimbus/test/browser/browser_nimbus_telemetry.js index 5cf00e3f3e8..c0921d55236 100644 --- a/toolkit/components/nimbus/test/browser/browser_nimbus_telemetry.js +++ b/toolkit/components/nimbus/test/browser/browser_nimbus_telemetry.js @@ -63,7 +63,7 @@ add_task(async function test_experiment_enroll_unenroll_Telemetry() { object: TELEMETRY_OBJECT, value: experiment.slug, extra: { - reason: "cleanup", + reason: "unknown", branch: experiment.branch.slug, }, }, diff --git a/toolkit/components/nimbus/test/browser/browser_remotesettings_experiment_enroll.js b/toolkit/components/nimbus/test/browser/browser_remotesettings_experiment_enroll.js index b805f937878..471c26d7dbf 100644 --- a/toolkit/components/nimbus/test/browser/browser_remotesettings_experiment_enroll.js +++ b/toolkit/components/nimbus/test/browser/browser_remotesettings_experiment_enroll.js @@ -55,7 +55,7 @@ add_task(async function test_experimentEnrollment() { Assert.ok(experiment.active, "Should be enrolled in the experiment"); - ExperimentManager.unenroll(recipe.slug, "mochitest-cleanup"); + ExperimentManager.unenroll(recipe.slug); experiment = ExperimentAPI.getExperiment({ slug: recipe.slug, diff --git a/toolkit/components/nimbus/test/unit/test_ExperimentAPI_NimbusFeatures.js b/toolkit/components/nimbus/test/unit/test_ExperimentAPI_NimbusFeatures.js index 52b4443618b..b1130ea3a02 100644 --- a/toolkit/components/nimbus/test/unit/test_ExperimentAPI_NimbusFeatures.js +++ b/toolkit/components/nimbus/test/unit/test_ExperimentAPI_NimbusFeatures.js @@ -95,7 +95,7 @@ add_task(async function readyCallAfterStore_with_remote_value() { Assert.ok(!feature.getVariable("enabled"), "Loads value from store"); - manager.unenroll(MATCHING_ROLLOUT.slug, "test-cleanup"); + manager.unenroll(MATCHING_ROLLOUT.slug); sandbox.restore(); assertEmptyStore(manager.store); @@ -146,7 +146,7 @@ add_task(async function update_remote_defaults_onUpdate() { Assert.equal(stub.callCount, 1, "Called once for remote configs"); Assert.equal(stub.firstCall.args[1], "rollout-updated", "Correct reason"); - manager.unenroll(MATCHING_ROLLOUT.slug, "test-cleanup"); + manager.unenroll(MATCHING_ROLLOUT.slug); sandbox.restore(); assertEmptyStore(manager.store); @@ -224,7 +224,7 @@ add_task(async function update_remote_defaults_readyPromise() { "Update called after enrollment processed." ); - manager.unenroll(MATCHING_ROLLOUT.slug, "test-cleanup"); + manager.unenroll(MATCHING_ROLLOUT.slug); assertEmptyStore(manager.store); sandbox.restore(); }); @@ -246,7 +246,7 @@ add_task(async function update_remote_defaults_enabled() { "Feature is disabled by remote configuration" ); - manager.unenroll(NON_MATCHING_ROLLOUT.slug, "test-cleanup"); + manager.unenroll(NON_MATCHING_ROLLOUT.slug); assertEmptyStore(manager.store); sandbox.restore(); }); @@ -305,6 +305,6 @@ add_task(async function remote_isEarlyStartup_config() { "nimbus.syncdefaultsstore.password-autocomplete" ); - manager.unenroll(rollout.slug, "test-cleanup"); + manager.unenroll(rollout.slug); assertEmptyStore(manager.store); }); diff --git a/toolkit/components/nimbus/test/unit/test_ExperimentManager_enroll.js b/toolkit/components/nimbus/test/unit/test_ExperimentManager_enroll.js index 8398b1801ec..31dd57606fb 100644 --- a/toolkit/components/nimbus/test/unit/test_ExperimentManager_enroll.js +++ b/toolkit/components/nimbus/test/unit/test_ExperimentManager_enroll.js @@ -59,7 +59,7 @@ add_task(async function test_add_to_store() { ); Assert.equal(experiment.active, true, "should set .active = true"); - manager.unenroll("foo", "test-cleanup"); + manager.unenroll("foo"); assertEmptyStore(manager.store); }); @@ -92,7 +92,7 @@ add_task(async function test_add_rollout_to_store() { ); Assert.equal(experiment.isRollout, true, "should have .isRollout"); - manager.unenroll("rollout-slug", "test-cleanup"); + manager.unenroll("rollout-slug"); assertEmptyStore(manager.store); }); @@ -219,7 +219,7 @@ add_task(async function test_setExperimentActive_recordEnrollment_called() { "Glean.nimbusEvents.enrollment recorded with correct experiment type" ); - manager.unenroll("foo", "test-cleanup"); + manager.unenroll("foo"); assertEmptyStore(manager.store); sandbox.restore(); @@ -327,7 +327,7 @@ add_task(async function test_setRolloutActive_recordEnrollment_called() { "Glean.nimbusEvents.enrollment recorded with correct experiment type" ); - manager.unenroll("rollout", "test-cleanup"); + manager.unenroll("rollout"); assertEmptyStore(manager.store); sandbox.restore(); @@ -346,14 +346,20 @@ add_task(async function test_failure_name_conflict() { // Clear any pre-existing data in Glean Services.fog.testResetFOG(); + Services.fog.applyServerKnobsConfig( + JSON.stringify({ + metrics_enabled: { + "nimbus_events.enrollment_status": true, + }, + }) + ); await manager.onStartup(); // Check that there aren't any Glean enroll_failed events yet - var failureEvents = Glean.nimbusEvents.enrollFailed.testGetValue("events"); Assert.equal( - undefined, - failureEvents, + Glean.nimbusEvents.enrollFailed.testGetValue("events"), + null, "no Glean enroll_failed events before failure" ); @@ -366,29 +372,35 @@ add_task(async function test_failure_name_conflict() { "should throw if a conflicting experiment exists" ); - Assert.equal( - NimbusTelemetry.recordEnrollmentFailure.calledWith("foo", "name-conflict"), - true, - "should send failure telemetry if a conflicting experiment exists" + // Check that the Glean events were recorded. + Assert.deepEqual( + Glean.nimbusEvents.enrollFailed.testGetValue("events").map(ev => ev.extra), + [ + { + experiment: "foo", + reason: "name-conflict", + }, + ], + "enrollFailed telemetry recorded correctly" ); - // Check that the Glean enrollment event was recorded. - failureEvents = Glean.nimbusEvents.enrollFailed.testGetValue("events"); - // We expect only one event - Assert.equal(1, failureEvents.length); - // And that one event matches the expected enrolled experiment - Assert.equal( - "foo", - failureEvents[0].extra.experiment, - "Glean.nimbusEvents.enroll_failed recorded with correct experiment slug" - ); - Assert.equal( - "name-conflict", - failureEvents[0].extra.reason, - "Glean.nimbusEvents.enroll_failed recorded with correct reason" + Assert.deepEqual( + Glean.nimbusEvents.enrollmentStatus + .testGetValue("events") + .map(ev => ev.extra), + [ + { + slug: "foo", + status: "NotEnrolled", + reason: "NameConflict", + }, + ], + "enrollmentStatus telemetry recorded correctly" ); - manager.unenroll("foo", "test-cleanup"); + Services.fog.testResetFOG(); + + manager.unenroll("foo"); assertEmptyStore(manager.store); sandbox.restore(); @@ -468,7 +480,7 @@ add_task(async function test_failure_group_conflict() { "Glean.nimbusEvents.enroll_failed recorded with correct reason" ); - manager.unenroll("foo", "test-cleanup"); + manager.unenroll("foo"); assertEmptyStore(manager.store); sandbox.restore(); @@ -535,7 +547,7 @@ add_task(async function test_rollout_failure_group_conflict() { "Glean.nimbusEvents.enroll_failed recorded with correct reason" ); - manager.unenroll("rollout-recipe", "test-cleanup"); + manager.unenroll("rollout-recipe"); assertEmptyStore(manager.store); sandbox.restore(); @@ -696,16 +708,17 @@ add_task(async function enroll_in_reference_aw_experiment() { // in prefs. Assert.ok(prefValue.length < 3498, "Make sure we don't bloat the prefs"); - manager.unenroll(recipe.slug, "enroll_in_reference_aw_experiment:cleanup"); + manager.unenroll(recipe.slug); assertEmptyStore(manager.store); }); add_task(async function test_forceEnroll_cleanup() { + Services.fog.testResetFOG(); + const manager = ExperimentFakes.manager(); const sandbox = sinon.createSandbox(); - let unenrollStub = sandbox.spy(manager, "unenroll"); - let existingRecipe = ExperimentFakes.recipe("foo", { + const existingRecipe = ExperimentFakes.recipe("foo", { branches: [ { slug: "treatment", @@ -714,7 +727,7 @@ add_task(async function test_forceEnroll_cleanup() { }, ], }); - let forcedRecipe = ExperimentFakes.recipe("bar", { + const forcedRecipe = ExperimentFakes.recipe("bar", { branches: [ { slug: "treatment", @@ -724,34 +737,61 @@ add_task(async function test_forceEnroll_cleanup() { ], }); + sandbox.spy(manager, "_unenroll"); + await manager.onStartup(); await manager.enroll(existingRecipe, "test_forceEnroll_cleanup"); + Services.fog.applyServerKnobsConfig( + JSON.stringify({ + metrics_enabled: { + "nimbus_events.enrollment_status": true, + }, + }) + ); + sandbox.spy(NimbusTelemetry, "setExperimentActive"); manager.forceEnroll(forcedRecipe, forcedRecipe.branches[0]); - Assert.ok(unenrollStub.called, "Unenrolled from existing experiment"); - Assert.equal( - unenrollStub.firstCall.args[0], - existingRecipe.slug, - "Called with existing recipe slug" + Assert.deepEqual( + Glean.nimbusEvents.enrollmentStatus + .testGetValue("events") + ?.map(ev => ev.extra), + [ + { + slug: "foo", + branch: "treatment", + status: "Disqualified", + reason: "ForceEnrollment", + }, + { + slug: "optin-bar", + branch: "treatment", + status: "Enrolled", + reason: "OptIn", + }, + ] + ); + + Assert.ok( + manager._unenroll.calledOnceWith( + sinon.match({ slug: existingRecipe.slug }), + { reason: "force-enrollment" } + ), + "Unenrolled from existing experiment" ); Assert.ok( - NimbusTelemetry.setExperimentActive.calledOnce, + NimbusTelemetry.setExperimentActive.calledOnceWith( + sinon.match({ slug: "optin-bar" }) + ), "Activated forced experiment" ); - Assert.equal( - NimbusTelemetry.setExperimentActive.firstCall.args[0].slug, - `optin-${forcedRecipe.slug}`, - "Called with forced experiment slug" - ); - Assert.equal( - manager.store.getExperimentForFeature("force-enrollment").slug, - `optin-${forcedRecipe.slug}`, + Assert.ok( + manager.store.get("optin-bar")?.active, "Enrolled in forced experiment" ); - manager.unenroll(`optin-${forcedRecipe.slug}`, "test-cleanup"); + manager.unenroll(`optin-bar`); assertEmptyStore(manager.store); @@ -761,23 +801,44 @@ add_task(async function test_forceEnroll_cleanup() { add_task(async function test_rollout_unenroll_conflict() { const manager = ExperimentFakes.manager(); const sandbox = sinon.createSandbox(); - let unenrollStub = sandbox.stub(manager, "unenroll").returns(true); - let enrollStub = sandbox.stub(manager, "_enroll").returns(true); - let rollout = ExperimentFakes.rollout("rollout_conflict"); + + const conflictingRollout = ExperimentFakes.recipe("conflicting-rollout", { + bucketConfig: { + ...ExperimentFakes.recipe.bucketConfig, + count: 1000, + }, + isRollout: true, + }); + + const rollout = ExperimentFakes.recipe("rollout", { isRollout: true }); + + await manager.onStartup(); // We want to force a conflict - sandbox.stub(manager.store, "getRolloutForFeature").returns(rollout); + await manager.enroll(conflictingRollout, "rs-loader"); - manager.forceEnroll(rollout, rollout.branch); + sandbox.spy(manager, "_unenroll"); + + manager.forceEnroll(rollout, rollout.branches[0]); - Assert.ok(unenrollStub.calledOnce, "Should unenroll the conflicting rollout"); Assert.ok( - unenrollStub.calledWith(rollout.slug, "force-enrollment"), - "Should call with expected slug" + manager._unenroll.calledOnceWith( + sinon.match({ slug: conflictingRollout.slug }), + { reason: "force-enrollment" } + ), + "Should unenroll the conflicting rollout" ); - Assert.ok(enrollStub.calledOnce, "Should call enroll as expected"); - manager.unenroll(rollout.slug, "test-cleanup"); + Assert.ok( + !manager.store.get(conflictingRollout.slug)?.active, + "Conflicting rollout should be inactive" + ); + Assert.ok( + manager.store.get(`optin-${rollout.slug}`)?.active, + "Rollout should be active" + ); + + manager.unenroll(`optin-${rollout.slug}`); assertEmptyStore(manager.store); sandbox.restore(); @@ -1115,10 +1176,10 @@ add_task(async function test_group_enrollment() { ); // Cleanup - manager1.unenroll("group_enroll", "test-cleanup"); + manager1.unenroll("group_enroll"); assertEmptyStore(manager1.store); - manager2.unenroll("group_enroll", "test-cleanup"); + manager2.unenroll("group_enroll"); assertEmptyStore(manager2.store); }); diff --git a/toolkit/components/nimbus/test/unit/test_ExperimentManager_lifecycle.js b/toolkit/components/nimbus/test/unit/test_ExperimentManager_lifecycle.js index 8012b427814..6bd5d2740b0 100644 --- a/toolkit/components/nimbus/test/unit/test_ExperimentManager_lifecycle.js +++ b/toolkit/components/nimbus/test/unit/test_ExperimentManager_lifecycle.js @@ -11,6 +11,9 @@ const { MatchStatus } = ChromeUtils.importESModule( const { NimbusTelemetry } = ChromeUtils.importESModule( "resource://nimbus/lib/Telemetry.sys.mjs" ); +const { UnenrollmentCause } = ChromeUtils.importESModule( + "resource://nimbus/lib/ExperimentManager.sys.mjs" +); async function cleanupStore(store) { Assert.deepEqual( @@ -119,16 +122,17 @@ add_task(async function test_startup_unenroll() { store.addEnrollment(recipe); const manager = ExperimentFakes.manager(store); - const unenrollSpy = sandbox.spy(manager, "unenroll"); + sandbox.spy(manager, "_unenroll"); await manager.onStartup(); Assert.ok( - unenrollSpy.calledOnce, - "Unenrolled from active experiment if user opt out is true" - ); - Assert.ok( - unenrollSpy.calledWith("startup_unenroll", "studies-opt-out"), + manager._unenroll.calledOnceWith( + sinon.match({ slug: "startup_unenroll" }), + { + reason: "studies-opt-out", + } + ), "Called unenroll for expected recipe" ); @@ -170,7 +174,7 @@ add_task(async function test_onRecipe_enroll() { "should add recipe to the store" ); - manager.unenroll(fooRecipe.slug, "test-cleanup"); + manager.unenroll(fooRecipe.slug); await cleanupStore(manager.store); }); @@ -204,7 +208,7 @@ add_task(async function test_onRecipe_update() { "should call .updateEnrollment() if the recipe has already been enrolled" ); - manager.unenroll(fooRecipe.slug, "test-cleanup"); + manager.unenroll(fooRecipe.slug); await cleanupStore(manager.store); }); @@ -423,7 +427,12 @@ add_task(async function test_experimentStore_updateEvent() { ); stub.resetHistory(); - manager.unenroll("experiment", "individual-opt-out"); + manager.unenroll( + "experiment", + UnenrollmentCause.fromReason( + NimbusTelemetry.UnenrollReason.INDIVIDUAL_OPT_OUT + ) + ); Assert.ok( stub.calledOnceWith("update", { slug: "experiment", diff --git a/toolkit/components/nimbus/test/unit/test_ExperimentManager_prefs.js b/toolkit/components/nimbus/test/unit/test_ExperimentManager_prefs.js index ba791df0a69..5d5981145b0 100644 --- a/toolkit/components/nimbus/test/unit/test_ExperimentManager_prefs.js +++ b/toolkit/components/nimbus/test/unit/test_ExperimentManager_prefs.js @@ -4,6 +4,9 @@ const { _ExperimentFeature: ExperimentFeature, NimbusFeatures } = ChromeUtils.importESModule("resource://nimbus/ExperimentAPI.sys.mjs"); +const { ObjectUtils } = ChromeUtils.importESModule( + "resource://gre/modules/ObjectUtils.sys.mjs" +); const { PrefUtils } = ChromeUtils.importESModule( "resource://normandy/lib/PrefUtils.sys.mjs" ); @@ -12,6 +15,18 @@ const { TelemetryTestUtils } = ChromeUtils.importESModule( "resource://testing-common/TelemetryTestUtils.sys.mjs" ); +function assertIncludes(array, obj, msg) { + let found = false; + for (const el of array) { + if (ObjectUtils.deepEqual(el, obj)) { + found = true; + break; + } + } + + Assert.ok(found, msg); +} + /** * Pick a single entry from an object and return a new object containing only * that entry. @@ -1721,6 +1736,13 @@ add_task(async function test_prefChange() { expectedUser = null, }) { Services.fog.testResetFOG(); + Services.fog.applyServerKnobsConfig( + JSON.stringify({ + metrics_enabled: { + "nimbus_events.enrollment_status": true, + }, + }) + ); Services.telemetry.snapshotEvents( Ci.nsITelemetry.DATASET_PRERELEASE_CHANNELS, /* clear = */ true @@ -1861,6 +1883,42 @@ add_task(async function test_prefChange() { ); } + const expectedEnrollmentStatusEvents = []; + for (const enrollmentKind of Object.keys(configs)) { + expectedEnrollmentStatusEvents.push({ + slug: slugs[enrollmentKind], + branch: "control", + status: "Enrolled", + reason: "Qualified", + }); + } + for (const ev of expectedLegacyEvents) { + expectedEnrollmentStatusEvents.push({ + slug: ev.value, + branch: "control", + status: "Disqualified", + reason: "ChangedPref", + }); + } + + const enrollmentStatusEvents = ( + Glean.nimbusEvents.enrollmentStatus.testGetValue("events") ?? [] + ).map(ev => ev.extra); + + for (const expectedEvent of expectedEnrollmentStatusEvents) { + assertIncludes( + enrollmentStatusEvents, + expectedEvent, + "Event should appear in the enrollment status telemetry" + ); + } + + Assert.equal( + enrollmentStatusEvents.length, + expectedEnrollmentStatusEvents.length, + "We should see the expected number of enrollment status events" + ); + for (const enrollmentKind of expectedEnrollments) { await cleanup[enrollmentKind](); } diff --git a/toolkit/components/nimbus/test/unit/test_ExperimentManager_unenroll.js b/toolkit/components/nimbus/test/unit/test_ExperimentManager_unenroll.js index a7c0f77ee91..82135055101 100644 --- a/toolkit/components/nimbus/test/unit/test_ExperimentManager_unenroll.js +++ b/toolkit/components/nimbus/test/unit/test_ExperimentManager_unenroll.js @@ -32,13 +32,15 @@ add_task(async function test_set_inactive() { await manager.onStartup(); await manager.store.addEnrollment(ExperimentFakes.experiment("foo")); - manager.unenroll("foo", "some-reason"); + manager.unenroll("foo"); Assert.equal( manager.store.get("foo").active, false, "should set .active to false" ); + + assertEmptyStore(manager.store); }); add_task(async function test_unenroll_opt_out() { @@ -107,6 +109,8 @@ add_task(async function test_unenroll_opt_out() { "Glean.nimbusEvents.unenrollment recorded with correct reason" ); + assertEmptyStore(manager.store); + // reset pref Services.prefs.clearUserPref(STUDIES_OPT_OUT_PREF); sandbox.restore(); @@ -178,6 +182,8 @@ add_task(async function test_unenroll_rollout_opt_out() { "Glean.nimbusEvents.unenrollment recorded with correct reason" ); + assertEmptyStore(manager.store); + // reset pref Services.prefs.clearUserPref(STUDIES_OPT_OUT_PREF); sandbox.restore(); @@ -203,6 +209,9 @@ add_task(async function test_unenroll_uploadPref() { false, "Should set .active to false" ); + + assertEmptyStore(manager.store); + Services.prefs.clearUserPref(UPLOAD_ENABLED_PREF); }); @@ -231,7 +240,7 @@ add_task(async function test_setExperimentInactive_called() { "experiment should be active before unenroll" ); - manager.unenroll("foo", "some-reason"); + manager.unenroll("foo"); Assert.ok( TelemetryEnvironment.setExperimentInactive.calledWith("foo"), @@ -245,6 +254,8 @@ add_task(async function test_setExperimentInactive_called() { "experiment should be inactive after unenroll" ); + assertEmptyStore(manager.store); + sandbox.restore(); }); @@ -270,7 +281,7 @@ add_task(async function test_send_unenroll_event() { "no Glean unenrollment events before unenrollment" ); - manager.unenroll("foo", "some-reason"); + manager.unenroll("foo", { reason: "some-reason" }); Assert.ok(TelemetryEvents.sendEvent.calledOnce); Assert.deepEqual( @@ -308,6 +319,8 @@ add_task(async function test_send_unenroll_event() { "Glean.nimbusEvents.unenrollment recorded with correct reason" ); + assertEmptyStore(manager.store); + sandbox.restore(); }); @@ -349,6 +362,8 @@ add_task(async function test_undefined_reason() { "Glean.nimbusEvents.unenrollment recorded with correct (unknown) reason" ); + assertEmptyStore(manager.store); + sandbox.restore(); }); @@ -369,7 +384,7 @@ add_task(async function test_remove_rollouts() { await manager.onStartup(); - manager.unenroll("foo", "some-reason"); + manager.unenroll("foo", { reason: "some-reason" }); Assert.ok( manager.store.updateExperiment.calledOnce, @@ -382,4 +397,48 @@ add_task(async function test_remove_rollouts() { }), "Called with expected parameters" ); + + assertEmptyStore(manager.store); +}); + +add_task(async function test_unenroll_individualOptOut_statusTelemetry() { + Services.fog.testResetFOG(); + + const manager = ExperimentFakes.manager(); + + await manager.onStartup(); + + await manager.enroll( + ExperimentFakes.recipe("foo", { + bucketConfig: { + ...ExperimentFakes.recipe.bucketConfig, + count: 1000, + }, + branches: [ExperimentFakes.recipe.branches[0]], + }) + ); + + Services.fog.applyServerKnobsConfig( + JSON.stringify({ + metrics_enabled: { + "nimbus_events.enrollment_status": true, + }, + }) + ); + + manager.unenroll("foo", { reason: "individual-opt-out" }); + + Assert.deepEqual( + Glean.nimbusEvents.enrollmentStatus + .testGetValue("events") + ?.map(ev => ev.extra), + [ + { + slug: "foo", + branch: "control", + status: "Disqualified", + reason: "OptOut", + }, + ] + ); }); diff --git a/toolkit/components/nimbus/test/unit/test_FirefoxLabs.js b/toolkit/components/nimbus/test/unit/test_FirefoxLabs.js index 85e7ebbc351..74f5c6e5188 100644 --- a/toolkit/components/nimbus/test/unit/test_FirefoxLabs.js +++ b/toolkit/components/nimbus/test/unit/test_FirefoxLabs.js @@ -35,6 +35,8 @@ function setupTest({ recipes }) { ExperimentAPI._resetForTests(); sandbox.restore(); + + Services.fog.testResetFOG(); }, }; } @@ -154,6 +156,14 @@ add_task(async function test_enroll() { const labs = await FirefoxLabs.create(); + Services.fog.applyServerKnobsConfig( + JSON.stringify({ + metrics_enabled: { + "nimbus_events.enrollment_status": true, + }, + }) + ); + await Assert.rejects( labs.enroll(), /enroll: slug and branchSlug are required/, @@ -184,6 +194,20 @@ add_task(async function test_enroll() { "ExperimentManager.enroll called" ); + Assert.deepEqual( + Glean.nimbusEvents.enrollmentStatus + .testGetValue("events") + ?.map(ev => ev.extra), + [ + { + slug: recipe.slug, + branch: "control", + status: "Enrolled", + reason: "OptIn", + }, + ] + ); + Assert.ok(manager.store.get(recipe.slug)?.active, "Active enrollment exists"); labs.unenroll(recipe.slug); @@ -314,6 +338,14 @@ add_task(async function test_unenroll() { await labs.enroll("opt-in", "control"); Assert.ok(manager.store.get("opt-in")?.active, "Enrolled in opt-in"); + Services.fog.applyServerKnobsConfig( + JSON.stringify({ + metrics_enabled: { + "nimbus_events.enrollment_status": true, + }, + }) + ); + // Should not throw. labs.unenroll("bogus"); @@ -330,6 +362,20 @@ add_task(async function test_unenroll() { // Should not throw. labs.unenroll("opt-in"); + Assert.deepEqual( + Glean.nimbusEvents.enrollmentStatus + .testGetValue("events") + ?.map(ev => ev.extra), + [ + { + slug: "opt-in", + branch: "control", + status: "Disqualified", + reason: "OptOut", + }, + ] + ); + manager.unenroll("rollout"); cleanup(); }); diff --git a/toolkit/components/nimbus/test/unit/test_RemoteSettingsExperimentLoader.js b/toolkit/components/nimbus/test/unit/test_RemoteSettingsExperimentLoader.js index 8aab111fb97..8e0e5d49c1b 100644 --- a/toolkit/components/nimbus/test/unit/test_RemoteSettingsExperimentLoader.js +++ b/toolkit/components/nimbus/test/unit/test_RemoteSettingsExperimentLoader.js @@ -330,7 +330,7 @@ add_task(async function test_experiment_optin_targeting() { "Should enroll in experiment" ); - manager.unenroll(`optin-${recipe.slug}`, "test-cleanup"); + manager.unenroll(`optin-${recipe.slug}`); sandbox.restore(); Services.prefs.clearUserPref(DEBUG_PREF); diff --git a/toolkit/components/nimbus/test/unit/test_RemoteSettingsExperimentLoader_updateRecipes.js b/toolkit/components/nimbus/test/unit/test_RemoteSettingsExperimentLoader_updateRecipes.js index 48c9283934c..88a295f8799 100644 --- a/toolkit/components/nimbus/test/unit/test_RemoteSettingsExperimentLoader_updateRecipes.js +++ b/toolkit/components/nimbus/test/unit/test_RemoteSettingsExperimentLoader_updateRecipes.js @@ -23,6 +23,9 @@ const { TelemetryEnvironment } = ChromeUtils.importESModule( const { TelemetryTestUtils } = ChromeUtils.importESModule( "resource://testing-common/TelemetryTestUtils.sys.mjs" ); +const { UnenrollmentCause } = ChromeUtils.importESModule( + "resource://nimbus/lib/ExperimentManager.sys.mjs" +); function assertEnrollments(store, expectedActive, expectedInactive) { for (const slug of expectedActive) { @@ -1299,7 +1302,12 @@ add_task(async function test_rollout_reenroll_optout() { "Should enroll in rollout" ); - manager.unenroll(rollout.slug, "individual-opt-out"); + manager.unenroll( + rollout.slug, + UnenrollmentCause.fromReason( + NimbusTelemetry.UnenrollReason.INDIVIDUAL_OPT_OUT + ) + ); await loader.updateRecipes(); @@ -1438,8 +1446,8 @@ add_task(async function test_active_and_past_experiment_targeting() { ["experiment-a", "experiment-b", "rollout-a", "rollout-b"] ); - manager.unenroll("experiment-c", "test"); - manager.unenroll("rollout-c", "test"); + manager.unenroll("experiment-c"); + manager.unenroll("rollout-c"); assertEmptyStore(manager.store); cleanupFeatures(); @@ -2316,14 +2324,144 @@ add_task(async function test_updateRecipes_enrollmentStatus_telemetry() { }, ]); - manager.unenroll("stays-enrolled", "test"); - manager.unenroll("enrolls", "test"); + manager.unenroll("stays-enrolled"); + manager.unenroll("enrolls"); assertEmptyStore(manager.store); Services.fog.testResetFOG(); cleanupFeatures(); }); +add_task(async function test_updateRecipes_enrollmentStatus_notEnrolled() { + const loader = ExperimentFakes.rsLoader(); + const manager = loader.manager; + + await manager.onStartup(); + await loader.enable(); + + const features = [ + new ExperimentFeature("test-feature-0", { variables: {} }), + new ExperimentFeature("test-feature-1", { variables: {} }), + new ExperimentFeature("test-feature-2", { variables: {} }), + new ExperimentFeature("test-feature-3", { variables: {} }), + new ExperimentFeature("test-feature-4", { variables: {} }), + new ExperimentFeature("test-feature-5", { variables: {} }), + new ExperimentFeature("test-feature-6", { variables: {} }), + new ExperimentFeature("test-feature-7", { variables: {} }), + new ExperimentFeature("test-feature-8", { variables: {} }), + ]; + + const cleanupFeatures = ExperimentTestUtils.addTestFeatures(...features); + + function recipe(slug, featureId) { + return ExperimentFakes.recipe(slug, { + bucketConfig: { + ...ExperimentFakes.recipe.bucketConfig, + count: 1000, + }, + branches: [ + { + ratio: 1, + slug: "control", + features: [ + { + featureId, + value: {}, + }, + ], + }, + ], + }); + } + + const recipes = [ + { + ...recipe("enrollment-paused", "test-feature-0"), + isEnrollmentPaused: true, + }, + { + ...recipe("no-match", "test-feature-1"), + targeting: "false", + }, + { + ...recipe("targeting-only", "test-feature-2"), + bucketConfig: { + ...ExperimentFakes.recipe.bucketConfig, + count: 0, + }, + }, + { + ...recipe("already-enrolled-rollout", "test-feature-3"), + isRollout: true, + }, + recipe("already-enrolled-experiment", "test-feature-3"), + ]; + + await manager.enroll( + { ...recipe("enrolled-rollout", "test-feature-3"), isRollout: true }, + "force-enrollment" + ); + await manager.enroll( + recipe("enrolled-experiment", "test-feature-3"), + "force-enrollment" + ); + + sinon.stub(loader.remoteSettingsClients.experiments, "get").resolves(recipes); + + Services.fog.applyServerKnobsConfig( + JSON.stringify({ + metrics_enabled: { + "nimbus_events.enrollment_status": true, + }, + }) + ); + + await loader.updateRecipes("timer"); + + Assert.deepEqual( + Glean.nimbusEvents.enrollmentStatus + .testGetValue("events") + ?.map(ev => ev.extra), + [ + { + slug: "enrollment-paused", + status: "NotEnrolled", + reason: "EnrollmentsPaused", + }, + { + slug: "no-match", + status: "NotEnrolled", + reason: "NotTargeted", + }, + { + slug: "targeting-only", + status: "NotEnrolled", + reason: "NotSelected", + }, + { + slug: "already-enrolled-rollout", + status: "NotEnrolled", + reason: "FeatureConflict", + conflict_slug: "enrolled-rollout", + }, + { + slug: "already-enrolled-experiment", + status: "NotEnrolled", + reason: "FeatureConflict", + conflict_slug: "enrolled-experiment", + }, + ] + ); + + manager.unenroll("enrolled-experiment"); + manager.unenroll("enrolled-rollout"); + + assertEmptyStore(manager.store); + Services.fog.testResetFOG(); + + cleanupFeatures(); +}); + add_task(async function test_updateRecipesWithPausedEnrollment() { const loader = ExperimentFakes.rsLoader(); const manager = loader.manager; @@ -2344,19 +2482,19 @@ add_task(async function test_updateRecipesWithPausedEnrollment() { .resolves([recipe]); sinon.spy(manager, "onRecipe"); - sinon.spy(manager, "enroll"); + sinon.spy(manager, "_enroll"); await loader.updateRecipes("test"); Assert.ok( manager.onRecipe.calledOnceWith(recipe, "rs-loader", { ok: true, - status: MatchStatus.TARGETING_ONLY, + status: MatchStatus.ENROLLMENT_PAUSED, }), - "Should call onRecipe with targeting match" + "Should call onRecipe with enrollments paused" ); Assert.ok( - manager.enroll.notCalled, + manager._enroll.notCalled, "Should not call enroll for paused recipe" ); @@ -2561,8 +2699,8 @@ add_task(async function testUnenrollsFirst() { await loader.updateRecipes("timer"); assertEnrollments(manager.store, ["e3", "r3"], ["e1", "e2", "r1", "r2"]); - manager.unenroll("e3", "test"); - manager.unenroll("r3", "test"); + manager.unenroll("e3"); + manager.unenroll("r3"); assertEmptyStore(manager.store); }); diff --git a/toolkit/components/nimbus/test/unit/test_nimbusTelemetry.js b/toolkit/components/nimbus/test/unit/test_nimbusTelemetry.js index 364bb7911cd..90b998f9382 100644 --- a/toolkit/components/nimbus/test/unit/test_nimbusTelemetry.js +++ b/toolkit/components/nimbus/test/unit/test_nimbusTelemetry.js @@ -37,6 +37,10 @@ function nimbusTargetingContextTelemetryDisabled() { add_task( { skip_if: nimbusTargetingContextTelemetryDisabled }, async function test_enrollAndUnenroll_gleanMetricConfiguration() { + info( + "Testing the interaction of gleanMetricConfiguration with submission of enrollment status and targeting context telemetry" + ); + const experiment = ExperimentFakes.recipe("experiment", { bucketConfig: { ...ExperimentFakes.recipe.bucketConfig, @@ -92,9 +96,14 @@ add_task( await manager.onStartup(); await manager.store.ready(); + // We don't call recordTargetingContext() here because we dont actually want + // to do all the work when we're just testing whether or not the metrics are + // being recorded. Glean.nimbusTargetingEnvironment.targetingContextValue.set( "nothing-active-0" ); + + // We're submitting a bogus event here -- we shouldn't see it recorded. Glean.nimbusEvents.enrollmentStatus.record({ reason: "nothing-active-0" }); Assert.equal( @@ -108,12 +117,14 @@ add_task( "enrollmentStatus not recorded by default" ); + // Because the feature listener gets triggered before we submit telemetry, + // this will actually cause it to submit its own enrollment status telemetry + // for enrollment. await manager.enroll(rollout, "rs-loader"); Glean.nimbusTargetingEnvironment.targetingContextValue.set( "rollout-active-1" ); - Glean.nimbusEvents.enrollmentStatus.record({ reason: "rollout-active-1" }); Assert.equal( Glean.nimbusTargetingEnvironment.targetingContextValue.testGetValue(), @@ -121,45 +132,63 @@ add_task( "targetingContextValue not recorded by default" ); - { - const events = Glean.nimbusEvents.enrollmentStatus.testGetValue("events"); - Assert.equal(events?.length ?? 0, 1, "enrollmentStatus recorded once"); - Assert.equal( - events[0].extra.reason, - "rollout-active-1", - "enrollmentStatus recorded once" - ); - } + Assert.deepEqual( + Glean.nimbusEvents.enrollmentStatus + .testGetValue("events") + ?.map(ev => ev.extra), + [ + { + slug: rollout.slug, + branch: rollout.branches[0].slug, + status: "Enrolled", + reason: "Qualified", + }, + ], + "Should have recorded enrollmentStatus (rollout enabled metric)" + ); + // Likewise, because the feature listener gets triggered before we submit + // telemetry, this will disable the metric before we submit it, so we + // shouldn't see another event. await manager.enroll(experiment, "rs-loader"); + Assert.ok( + manager.store.get(experiment.slug)?.active, + "Experiment enrolled and active" + ); Glean.nimbusTargetingEnvironment.targetingContextValue.set( "experiment-active-2" ); - Glean.nimbusEvents.enrollmentStatus.record({ - reason: "experiment-active-2", - }); Assert.equal( Glean.nimbusTargetingEnvironment.targetingContextValue.testGetValue(), - "experiment-active-2" + "experiment-active-2", + "Targeting context metric was recorded" ); - { - const events = Glean.nimbusEvents.enrollmentStatus.testGetValue("events"); - Assert.equal( - events?.length ?? 0, - 1, - "enrollmentStatus not recorded again" - ); - } + Assert.deepEqual( + Glean.nimbusEvents.enrollmentStatus + .testGetValue("events") + ?.map(ev => ev.extra), + [ + { + slug: rollout.slug, + branch: rollout.branches[0].slug, + status: "Enrolled", + reason: "Qualified", + }, + ], + "Should not have recorded enrollmentStatus again (experiment disabled metric)" + ); - await manager.unenroll("experiment"); + // Now the listener triggers again and the metric re-enables. We should see + // telemetry for this unenrollment but not setting the targeting context + // value. + await manager.unenroll(experiment.slug, { reason: "recipe-not-seen" }); Glean.nimbusTargetingEnvironment.targetingContextValue.set( "rollout-active-3" ); - Glean.nimbusEvents.enrollmentStatus.record({ reason: "rollout-active-3" }); Assert.equal( Glean.nimbusTargetingEnvironment.targetingContextValue.testGetValue(), @@ -167,22 +196,32 @@ add_task( "targetingContextValue was not recorded again" ); - { - const events = Glean.nimbusEvents.enrollmentStatus.testGetValue("events"); - Assert.equal(events?.length ?? 0, 2, "enrollmentStatus recorded again"); - Assert.equal( - events[1].extra.reason, - "rollout-active-3", - "enrollmentStatus recorded with correct value" - ); - } + Assert.deepEqual( + Glean.nimbusEvents.enrollmentStatus + .testGetValue("events") + .map(ev => ev.extra), + [ + { + slug: rollout.slug, + branch: rollout.branches[0].slug, + status: "Enrolled", + reason: "Qualified", + }, + { + slug: experiment.slug, + branch: experiment.branches[0].slug, + status: "WasEnrolled", + }, + ] + ); - await manager.unenroll("rollout"); + // Finally, this disables the enrollment status metric in the onUpdate + // handler. We don't see its unenrollment. + await manager.unenroll(rollout.slug, { reason: "recipe-not-seen" }); Glean.nimbusTargetingEnvironment.targetingContextValue.set( "nothing-active-0" ); - Glean.nimbusEvents.enrollmentStatus.record({ reason: "nothing-active-0" }); Assert.equal( Glean.nimbusTargetingEnvironment.targetingContextValue.testGetValue(), @@ -190,14 +229,24 @@ add_task( "targetingContextValue was not recorded again" ); - { - const events = Glean.nimbusEvents.enrollmentStatus.testGetValue("events"); - Assert.equal( - events?.length ?? 0, - 2, - "enrollmentStatus not recorded again" - ); - } + Assert.deepEqual( + Glean.nimbusEvents.enrollmentStatus + .testGetValue("events") + .map(ev => ev.extra), + [ + { + slug: rollout.slug, + branch: rollout.branches[0].slug, + status: "Enrolled", + reason: "Qualified", + }, + { + slug: experiment.slug, + branch: experiment.branches[0].slug, + status: "WasEnrolled", + }, + ] + ); Services.fog.testResetFOG(); diff --git a/toolkit/components/nimbus/test/unit/test_prefFlips.js b/toolkit/components/nimbus/test/unit/test_prefFlips.js index 690967f28f5..571a9d2b741 100644 --- a/toolkit/components/nimbus/test/unit/test_prefFlips.js +++ b/toolkit/components/nimbus/test/unit/test_prefFlips.js @@ -1587,7 +1587,7 @@ add_task(async function test_prefFlips_unenrollment() { for (const { slug, isRollout = false } of expectedEnrollments) { const computedSlug = `${slug}-${isRollout ? "rollout" : "experiment"}`; info(`Unenrolling from ${computedSlug}\n`); - manager.unenroll(computedSlug, "cleanup"); + manager.unenroll(computedSlug); } assertEmptyStore(manager.store); assertNoObservers(manager); @@ -1963,6 +1963,13 @@ add_task(async function test_prefFlip_setPref_restore() { for (const [i, { name, ...testCase }] of TEST_CASES.entries()) { Services.fog.testResetFOG(); + Services.fog.applyServerKnobsConfig( + JSON.stringify({ + metrics_enabled: { + "nimbus_events.enrollment_status": true, + }, + }) + ); Services.telemetry.snapshotEvents( Ci.nsITelemetry.DATASET_PRERELEASE_CHANNELS, /* clear = */ true @@ -2039,9 +2046,35 @@ add_task(async function test_prefFlip_setPref_restore() { }, ] ); + Assert.deepEqual( + Glean.nimbusEvents.enrollmentStatus + .testGetValue("events") + ?.map(ev => ev.extra), + [ + { + slug: enrollmentOrder[0], + branch: "control", + status: "Enrolled", + reason: "Qualified", + }, + { + slug: enrollmentOrder[0], + branch: "control", + status: "Disqualified", + reason: "PrefFlipsConflict", + conflict_slug: enrollmentOrder[1], + }, + { + slug: enrollmentOrder[1], + branch: "control", + status: "Enrolled", + reason: "Qualified", + }, + ] + ); info("Unenrolling..."); - manager.unenroll(enrollmentOrder[1], "test-cleanup"); + manager.unenroll(enrollmentOrder[1]); info("Checking expected prefs..."); checkExpectedPrefBranches(expectedPrefs); @@ -2120,7 +2153,7 @@ add_task(async function test_prefFlips_cacheOriginalValues() { "originalValues cached on serialized enrollment" ); - manager.unenroll(recipe.slug, "test"); + manager.unenroll(recipe.slug); Assert.ok( !Services.prefs.prefHasUserValue("test.pref.please.ignore"), "pref unset after unenrollment" @@ -2207,7 +2240,7 @@ add_task(async function test_prefFlips_restore_unenroll() { null ); - manager.unenroll(recipe.slug, "test"); + manager.unenroll(recipe.slug); Assert.ok( !Services.prefs.prefHasUserValue("test.pref.please.ignore"), "pref unset after unenrollment" @@ -2506,10 +2539,10 @@ add_task(async function test_prefFlips_failed_experiment_and_rollout() { info("Unenrolling..."); if (expectedEnrollments.includes(ROLLOUT)) { - manager.unenroll(ROLLOUT, "test-cleanup"); + manager.unenroll(ROLLOUT); } if (expectedEnrollments.includes(EXPERIMENT)) { - manager.unenroll(EXPERIMENT, "test-cleanup"); + manager.unenroll(EXPERIMENT); } info("Cleaning up..."); diff --git a/toolkit/components/normandy/content/AboutPages.sys.mjs b/toolkit/components/normandy/content/AboutPages.sys.mjs index 5fc13c2724d..005caba86a6 100644 --- a/toolkit/components/normandy/content/AboutPages.sys.mjs +++ b/toolkit/components/normandy/content/AboutPages.sys.mjs @@ -11,11 +11,13 @@ ChromeUtils.defineESModuleGetters(lazy, { BranchedAddonStudyAction: "resource://normandy/actions/BranchedAddonStudyAction.sys.mjs", ExperimentManager: "resource://nimbus/lib/ExperimentManager.sys.mjs", + NimbusTelemetry: "resource://nimbus/lib/Telemetry.sys.mjs", PreferenceExperiments: "resource://normandy/lib/PreferenceExperiments.sys.mjs", RecipeRunner: "resource://normandy/lib/RecipeRunner.sys.mjs", RemoteSettingsExperimentLoader: "resource://nimbus/lib/RemoteSettingsExperimentLoader.sys.mjs", + UnenrollmentCause: "resource://nimbus/lib/ExperimentManager.sys.mjs", }); const SHIELD_LEARN_MORE_URL_PREF = "app.normandy.shieldLearnMoreUrl"; @@ -209,8 +211,13 @@ ChromeUtils.defineLazyGetter(AboutPages, "aboutStudies", () => { } }, - async removeMessagingSystemExperiment(slug, reason) { - lazy.ExperimentManager.unenroll(slug, reason); + async removeMessagingSystemExperiment(slug) { + lazy.ExperimentManager.unenroll( + slug, + lazy.UnenrollmentCause.fromReason( + lazy.NimbusTelemetry.UnenrollReason.INDIVIDUAL_OPT_OUT + ) + ); this._sendToAll( "Shield:UpdateMessagingSystemExperimentList", lazy.ExperimentManager.store.getAll() diff --git a/toolkit/components/normandy/content/ShieldFrameParent.sys.mjs b/toolkit/components/normandy/content/ShieldFrameParent.sys.mjs index 73aa6204741..0684a86d4e7 100644 --- a/toolkit/components/normandy/content/ShieldFrameParent.sys.mjs +++ b/toolkit/components/normandy/content/ShieldFrameParent.sys.mjs @@ -34,10 +34,7 @@ export class ShieldFrameParent extends JSWindowActorParent { ); break; case "Shield:RemoveMessagingSystemExperiment": - aboutStudies.removeMessagingSystemExperiment( - msg.data.slug, - msg.data.reason - ); + aboutStudies.removeMessagingSystemExperiment(msg.data.slug); break; case "Shield:OpenDataPreferences": aboutStudies.openDataPreferences(); diff --git a/toolkit/components/normandy/content/about-studies/about-studies.js b/toolkit/components/normandy/content/about-studies/about-studies.js index 9f13a6c399a..dfd4f52f7fe 100644 --- a/toolkit/components/normandy/content/about-studies/about-studies.js +++ b/toolkit/components/normandy/content/about-studies/about-studies.js @@ -319,7 +319,6 @@ class MessagingSystemListItem extends React.Component { handleClickRemove() { sendPageEvent("RemoveMessagingSystemExperiment", { slug: this.props.study.slug, - reason: "individual-opt-out", }); } diff --git a/toolkit/content/xul.css b/toolkit/content/xul.css index bd80261aee2..191748cd53b 100644 --- a/toolkit/content/xul.css +++ b/toolkit/content/xul.css @@ -369,48 +369,34 @@ tooltip { max-width: 40em; overflow: clip; pointer-events: none; -} -/** - * It's important that these styles are in a UA sheet, because the default - * tooltip is native anonymous content - */ -@media (-moz-platform: linux) { - tooltip { + /** + * It's important that these styles are in a UA sheet, because the default + * tooltip is native anonymous content + */ + @media (-moz-platform: linux) { padding: 6px 10px; /* Matches Adwaita. */ line-height: 1.4; /* For Noto Sans; note that 1.2 may clip descenders. */ + border: .5px solid color-mix(in srgb, currentColor 60%, transparent); + @media (-moz-gtk-csd-transparency-available) { + border-radius: env(-moz-gtk-csd-tooltip-radius); + } } -} - -@media (-moz-platform: macos) { - tooltip { + @media (-moz-platform: macos) { padding: 2px 6px; /* Matches native metrics. */ } -} - -@media (-moz-platform: windows) { - tooltip { + @media (-moz-platform: windows) { appearance: none; border: 1px solid; - } - /* TODO(emilio): Probably make InfoText/InfoBackground do the right thing and - * remove this? */ - @media not (prefers-contrast) { - tooltip { - background-color: #f9f9fb; - color: black; - border-color: #67676c; + /* TODO(emilio): Probably make InfoText/InfoBackground do the right thing and + * remove this? */ + @media not (prefers-contrast) { + background-color: light-dark(#f9f9fb, #2b2a33); + color: light-dark(black, white); + border-color: light-dark(#67676c, #f9f9fb); border-radius: 4px; } - - @media (prefers-color-scheme: dark) { - tooltip { - background-color: #2b2a33; - color: white; - border-color: #f9f9fb; - } - } } } diff --git a/toolkit/mozapps/update/tests/unit_background_update/test_backgroundupdate_glean.js b/toolkit/mozapps/update/tests/unit_background_update/test_backgroundupdate_glean.js index 65ef8a6409d..99d5114f729 100644 --- a/toolkit/mozapps/update/tests/unit_background_update/test_backgroundupdate_glean.js +++ b/toolkit/mozapps/update/tests/unit_background_update/test_backgroundupdate_glean.js @@ -177,7 +177,7 @@ add_task(async function test_targeting_exists() { await manager.onStartup(); await manager.store.addEnrollment(ExperimentFakes.experiment("foo")); - manager.unenroll("foo", "some-reason"); + manager.unenroll("foo"); await manager.store.addEnrollment( ExperimentFakes.experiment("bar", { active: false }) ); @@ -186,7 +186,7 @@ add_task(async function test_targeting_exists() { ); manager.store.addEnrollment(ExperimentFakes.rollout("rol1")); - manager.unenroll("rol1", "some-reason"); + manager.unenroll("rol1"); manager.store.addEnrollment(ExperimentFakes.rollout("rol2")); let targetSnapshot = await ASRouterTargeting.getEnvironmentSnapshot({ diff --git a/widget/LookAndFeel.h b/widget/LookAndFeel.h index ed0bfd9ab14..36e2b7ac7b9 100644 --- a/widget/LookAndFeel.h +++ b/widget/LookAndFeel.h @@ -305,6 +305,9 @@ class LookAndFeel { /** GTK button-to-button spacing in the inline axis */ TitlebarButtonSpacing, + /** GTK tooltip radius */ + TooltipRadius, + /** * Corresponding to dynamic-range. * https://drafts.csswg.org/mediaqueries-5/#dynamic-range diff --git a/widget/gtk/gtk3drawing.cpp b/widget/gtk/gtk3drawing.cpp index cfe6c187fea..c0b3fe185d4 100644 --- a/widget/gtk/gtk3drawing.cpp +++ b/widget/gtk/gtk3drawing.cpp @@ -56,20 +56,6 @@ static gint moz_gtk_get_tab_thickness(GtkStyleContext* style); static void Inset(GdkRectangle*, const GtkBorder&); -static void InsetByMargin(GdkRectangle*, GtkStyleContext* style); - -static void moz_gtk_add_style_margin(GtkStyleContext* style, gint* left, - gint* top, gint* right, gint* bottom) { - GtkBorder margin; - - gtk_style_context_get_margin(style, gtk_style_context_get_state(style), - &margin); - *left += margin.left; - *right += margin.right; - *top += margin.top; - *bottom += margin.bottom; -} - static void moz_gtk_add_style_border(GtkStyleContext* style, gint* left, gint* top, gint* right, gint* bottom) { GtkBorder border; @@ -96,14 +82,6 @@ static void moz_gtk_add_style_padding(GtkStyleContext* style, gint* left, *bottom += padding.bottom; } -static void moz_gtk_add_margin_border_padding(GtkStyleContext* style, - gint* left, gint* top, - gint* right, gint* bottom) { - moz_gtk_add_style_margin(style, left, top, right, bottom); - moz_gtk_add_style_border(style, left, top, right, bottom); - moz_gtk_add_style_padding(style, left, top, right, bottom); -} - static void moz_gtk_add_border_padding(GtkStyleContext* style, gint* left, gint* top, gint* right, gint* bottom) { moz_gtk_add_style_border(style, left, top, right, bottom); @@ -598,14 +576,6 @@ static void Inset(GdkRectangle* rect, const GtkBorder& aBorder) { rect->height -= aBorder.top + aBorder.bottom; } -// Inset a rectangle by the margins specified in a style context. -static void InsetByMargin(GdkRectangle* rect, GtkStyleContext* style) { - GtkBorder margin; - gtk_style_context_get_margin(style, gtk_style_context_get_state(style), - &margin); - Inset(rect, margin); -} - // Inset a rectangle by the border and padding specified in a style context. static void InsetByBorderPadding(GdkRectangle* rect, GtkStyleContext* style) { GtkStateFlags state = gtk_style_context_get_state(style); @@ -617,20 +587,6 @@ static void InsetByBorderPadding(GdkRectangle* rect, GtkStyleContext* style) { Inset(rect, border); } -static void moz_gtk_draw_styled_frame(GtkStyleContext* style, cairo_t* cr, - const GdkRectangle* aRect, - bool drawFocus) { - GdkRectangle rect = *aRect; - - InsetByMargin(&rect, style); - - gtk_render_background(style, cr, rect.x, rect.y, rect.width, rect.height); - gtk_render_frame(style, cr, rect.x, rect.y, rect.width, rect.height); - if (drawFocus) { - gtk_render_focus(style, cr, rect.x, rect.y, rect.width, rect.height); - } -} - /* See gtk_range_draw() for reference. */ static gint moz_gtk_scale_paint(cairo_t* cr, GdkRectangle* rect, @@ -916,54 +872,6 @@ static gint moz_gtk_arrow_paint(cairo_t* cr, GdkRectangle* rect, return MOZ_GTK_SUCCESS; } -static gint moz_gtk_tooltip_paint(cairo_t* cr, const GdkRectangle* aRect, - GtkWidgetState* state, - GtkTextDirection direction) { - // Tooltip widget is made in GTK3 as following tree: - // Tooltip window - // Horizontal Box - // Icon (not supported by Firefox) - // Label - // Each element can be fully styled by CSS of GTK theme. - // We have to draw all elements with appropriate offset and right dimensions. - - // Tooltip drawing - GtkStyleContext* style = - GetStyleContext(MOZ_GTK_TOOLTIP, state->image_scale, direction); - GdkRectangle rect = *aRect; - gtk_render_background(style, cr, rect.x, rect.y, rect.width, rect.height); - gtk_render_frame(style, cr, rect.x, rect.y, rect.width, rect.height); - - // Horizontal Box drawing - // - // The box element has hard-coded 6px margin-* GtkWidget properties, which - // are added between the window dimensions and the CSS margin box of the - // horizontal box. The frame of the tooltip window is drawn in the - // 6px margin. - // For drawing Horizontal Box we have to inset drawing area by that 6px - // plus its CSS margin. - GtkStyleContext* boxStyle = - GetStyleContext(MOZ_GTK_TOOLTIP_BOX, state->image_scale, direction); - - rect.x += 6; - rect.y += 6; - rect.width -= 12; - rect.height -= 12; - - InsetByMargin(&rect, boxStyle); - gtk_render_background(boxStyle, cr, rect.x, rect.y, rect.width, rect.height); - gtk_render_frame(boxStyle, cr, rect.x, rect.y, rect.width, rect.height); - - // Label drawing - InsetByBorderPadding(&rect, boxStyle); - - GtkStyleContext* labelStyle = - GetStyleContext(MOZ_GTK_TOOLTIP_BOX_LABEL, state->image_scale, direction); - moz_gtk_draw_styled_frame(labelStyle, cr, &rect, false); - - return MOZ_GTK_SUCCESS; -} - static gint moz_gtk_resizer_paint(cairo_t* cr, GdkRectangle* rect, GtkWidgetState* state, GtkTextDirection direction) { @@ -1409,23 +1317,6 @@ gint moz_gtk_get_widget_border(WidgetNodeType widget, gint* left, gint* top, case MOZ_GTK_FRAME: w = GetWidget(MOZ_GTK_FRAME); break; - case MOZ_GTK_TOOLTIP: { - // In GTK 3 there are 6 pixels of additional margin around the box. - // See details there: - // https://github.com/GNOME/gtk/blob/5ea69a136bd7e4970b3a800390e20314665aaed2/gtk/ui/gtktooltipwindow.ui#L11 - *left = *right = *top = *bottom = 6; - - // We also need to add margin/padding/borders from Tooltip content. - // Tooltip contains horizontal box, where icon and label is put. - // We ignore icon as long as we don't have support for it. - GtkStyleContext* boxStyle = GetStyleContext(MOZ_GTK_TOOLTIP_BOX); - moz_gtk_add_margin_border_padding(boxStyle, left, top, right, bottom); - - GtkStyleContext* labelStyle = GetStyleContext(MOZ_GTK_TOOLTIP_BOX_LABEL); - moz_gtk_add_margin_border_padding(labelStyle, left, top, right, bottom); - - return MOZ_GTK_SUCCESS; - } /* These widgets have no borders, since they are not containers. */ case MOZ_GTK_SPLITTER_HORIZONTAL: case MOZ_GTK_SPLITTER_VERTICAL: @@ -1670,8 +1561,6 @@ gint moz_gtk_widget_paint(WidgetNodeType widget, cairo_t* cr, return moz_gtk_text_view_paint(cr, rect, state, direction); case MOZ_GTK_DROPDOWN: return moz_gtk_combo_box_paint(cr, rect, state, direction); - case MOZ_GTK_TOOLTIP: - return moz_gtk_tooltip_paint(cr, rect, state, direction); case MOZ_GTK_FRAME: return moz_gtk_frame_paint(cr, rect, state, direction); case MOZ_GTK_RESIZER: diff --git a/widget/gtk/nsLookAndFeel.cpp b/widget/gtk/nsLookAndFeel.cpp index 9fce9aae65a..ff3076e4dfb 100644 --- a/widget/gtk/nsLookAndFeel.cpp +++ b/widget/gtk/nsLookAndFeel.cpp @@ -1176,6 +1176,11 @@ nsresult nsLookAndFeel::NativeGetInt(IntID aID, int32_t& aResult) { // No GTK API for checking if inverted colors is enabled aResult = 0; break; + case IntID::TooltipRadius: { + EnsureInit(); + aResult = EffectiveTheme().mTooltipRadius; + break; + } case IntID::TitlebarRadius: { EnsureInit(); aResult = EffectiveTheme().mTitlebarRadius; @@ -1800,8 +1805,6 @@ void nsLookAndFeel::InitializeGlobalSettings() { } void nsLookAndFeel::ConfigureFinalEffectiveTheme() { - MOZ_ASSERT(mSystemThemeOverridden, - "By this point, the alt theme should be configured"); const bool shouldUseSystemTheme = [&] { using ChromeSetting = PreferenceSheet::ChromeColorSchemeSetting; // NOTE: We can't call ColorSchemeForChrome directly because this might run @@ -1825,6 +1828,10 @@ void nsLookAndFeel::ConfigureFinalEffectiveTheme() { LOGLNF("OverrideSystemThemeIfNeeded(matchesSystem=%d, usingSystem=%d)\n", shouldUseSystemTheme, usingSystem); + if (shouldUseSystemTheme == usingSystem) { + return; + } + if (shouldUseSystemTheme) { RestoreSystemTheme(); } else if (usingSystem) { @@ -2100,6 +2107,7 @@ void nsLookAndFeel::PerThemeData::Init() { mInfo.mFg = GetTextColor(style); style = GetStyleContext(MOZ_GTK_TOOLTIP); mInfo.mBg = GetBackgroundColor(style, mInfo.mFg); + mTooltipRadius = GetBorderRadius(style); style = GetStyleContext(MOZ_GTK_MENUITEM); { diff --git a/widget/gtk/nsLookAndFeel.h b/widget/gtk/nsLookAndFeel.h index d9281284484..8b2c097341d 100644 --- a/widget/gtk/nsLookAndFeel.h +++ b/widget/gtk/nsLookAndFeel.h @@ -58,6 +58,12 @@ class nsLookAndFeel final : public nsXPLookAndFeel { char16_t GetPasswordCharacterImpl() override; bool GetEchoPasswordImpl() override; + void RefreshImpl() override { + // When calling Refresh(), we don't need to reload all our GTK theme info, + // but we might need to change our effective theme. + RecordChange(NativeChangeKind::OtherSettings); + } + bool GetDefaultDrawInTitlebar() override; nsXPLookAndFeel::TitlebarAction GetTitlebarAction( @@ -167,6 +173,7 @@ class nsLookAndFeel final : public nsXPLookAndFeel { float mCaretRatio = 0.0f; int32_t mTitlebarRadius = 0; + int32_t mTooltipRadius = 0; int32_t mTitlebarButtonSpacing = 0; char16_t mInvisibleCharacter = 0; bool mMenuSupportsDrag = false; diff --git a/widget/gtk/nsNativeThemeGTK.cpp b/widget/gtk/nsNativeThemeGTK.cpp index 08e2966d02b..c354843da68 100644 --- a/widget/gtk/nsNativeThemeGTK.cpp +++ b/widget/gtk/nsNativeThemeGTK.cpp @@ -291,9 +291,6 @@ bool nsNativeThemeGTK::GetGtkWidgetAndState(StyleAppearance aAppearance, *aWidgetFlags = GTK_ARROW_LEFT; } break; - case StyleAppearance::Tooltip: - aGtkWidgetType = MOZ_GTK_TOOLTIP; - break; case StyleAppearance::ProgressBar: aGtkWidgetType = MOZ_GTK_PROGRESSBAR; break; @@ -868,7 +865,6 @@ bool nsNativeThemeGTK::GetWidgetPadding(nsDeviceContext* aContext, } switch (aAppearance) { case StyleAppearance::Toolbarbutton: - case StyleAppearance::Tooltip: case StyleAppearance::MozWindowButtonClose: case StyleAppearance::MozWindowButtonMinimize: case StyleAppearance::MozWindowButtonMaximize: @@ -919,21 +915,6 @@ auto nsNativeThemeGTK::IsWidgetNonNative(nsIFrame* aFrame, return NonNative::No; } - // As an special-case, for tooltips, we check if the tooltip color is the - // same between the light and dark themes. If so we can get away with drawing - // the native widget, see bug 1817396. - if (aAppearance == StyleAppearance::Tooltip) { - auto darkColor = - LookAndFeel::Color(StyleSystemColor::Infotext, ColorScheme::Dark, - LookAndFeel::UseStandins::No); - auto lightColor = - LookAndFeel::Color(StyleSystemColor::Infotext, ColorScheme::Light, - LookAndFeel::UseStandins::No); - if (darkColor == lightColor) { - return NonNative::No; - } - } - // If the non-native theme doesn't support the widget then oh well... if (!Theme::ThemeSupportsWidget(aFrame->PresContext(), aFrame, aAppearance)) { return NonNative::No; @@ -1068,7 +1049,6 @@ bool nsNativeThemeGTK::WidgetAttributeChangeRequiresRepaint( // Some widget types just never change state. if (aAppearance == StyleAppearance::Progresschunk || aAppearance == StyleAppearance::ProgressBar || - aAppearance == StyleAppearance::Tooltip || aAppearance == StyleAppearance::MozWindowDecorations) { return false; } @@ -1117,7 +1097,6 @@ nsNativeThemeGTK::ThemeSupportsWidget(nsPresContext* aPresContext, case StyleAppearance::Tab: // case StyleAppearance::Tabpanel: case StyleAppearance::Tabpanels: - case StyleAppearance::Tooltip: case StyleAppearance::NumberInput: case StyleAppearance::PasswordInput: case StyleAppearance::Textfield: @@ -1178,14 +1157,7 @@ nsITheme::Transparency nsNativeThemeGTK::GetWidgetTransparency( return Theme::GetWidgetTransparency(aFrame, aAppearance); } - switch (aAppearance) { - // Tooltips use gtk_paint_flat_box() on Gtk2 - // but are shaped on Gtk3 - case StyleAppearance::Tooltip: - return eTransparent; - default: - return eUnknownTransparency; - } + return eUnknownTransparency; } already_AddRefed do_CreateNativeThemeDoNotUseDirectly() { diff --git a/widget/nsXPLookAndFeel.cpp b/widget/nsXPLookAndFeel.cpp index d9116c8e83d..7ed9d7d4a06 100644 --- a/widget/nsXPLookAndFeel.cpp +++ b/widget/nsXPLookAndFeel.cpp @@ -158,6 +158,7 @@ static const char sIntPrefs[][45] = { "ui.touchDeviceSupportPresent", "ui.titlebarRadius", "ui.titlebarButtonSpacing", + "ui.tooltipRadius", "ui.dynamicRange", "ui.panelAnimations", "ui.hideCursorWhileTyping", diff --git a/xpcom/ds/StaticAtoms.py b/xpcom/ds/StaticAtoms.py index 6cf910227b5..564ba8ff271 100644 --- a/xpcom/ds/StaticAtoms.py +++ b/xpcom/ds/StaticAtoms.py @@ -2293,6 +2293,7 @@ STATIC_ATOMS = [ Atom("_moz_gtk_csd_available", "-moz-gtk-csd-available"), Atom("_moz_gtk_csd_transparency_available", "-moz-gtk-csd-transparency-available"), Atom("_moz_gtk_csd_titlebar_radius", "-moz-gtk-csd-titlebar-radius"), + Atom("_moz_gtk_csd_tooltip_radius", "-moz-gtk-csd-tooltip-radius"), Atom("_moz_gtk_csd_titlebar_button_spacing", "-moz-gtk-csd-titlebar-button-spacing"), Atom("_moz_gtk_csd_minimize_button", "-moz-gtk-csd-minimize-button"), Atom("_moz_gtk_csd_minimize_button_position", "-moz-gtk-csd-minimize-button-position"),