diff --git a/Cargo.lock b/Cargo.lock index 18711e84c2948..4920d73538d3f 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1160,6 +1160,7 @@ version = "0.1.0" dependencies = [ "anyhow", "crash_helper_common", + "mach2", "minidump-writer", "nix 0.30.1", "num-derive", @@ -1173,6 +1174,8 @@ name = "crash_helper_common" version = "0.1.0" dependencies = [ "getrandom 0.3.3", + "log", + "mach2", "minidump-writer", "nix 0.30.1", "num-derive", diff --git a/browser/base/content/browser-sync.js b/browser/base/content/browser-sync.js index 06811ddce9856..958c0cb40600c 100644 --- a/browser/base/content/browser-sync.js +++ b/browser/base/content/browser-sync.js @@ -19,6 +19,8 @@ const { UIState } = ChromeUtils.importESModule( ); ChromeUtils.defineESModuleGetters(this, { + AIWindow: + "moz-src:///browser/components/aiwindow/ui/modules/AIWindow.sys.mjs", ASRouter: "resource:///modules/asrouter/ASRouter.sys.mjs", EnsureFxAccountsWebChannel: "resource://gre/modules/FxAccountsWebChannel.sys.mjs", @@ -518,6 +520,7 @@ var gSync = { "browser/sync.ftl", "browser/syncedTabs.ftl", "browser/newtab/asrouter.ftl", + "preview/aiWindow.ftl", ], true )); @@ -2384,9 +2387,13 @@ var gSync = { deleteLocalData: false, }; + const bodyId = AIWindow.hasActiveAIWindows() + ? "fxa-signout-dialog-body-aiwindow" + : "fxa-signout-dialog-body"; + let [title, body, button, checkbox] = await document.l10n.formatValues([ { id: "fxa-signout-dialog-title2" }, - { id: "fxa-signout-dialog-body" }, + { id: bodyId }, { id: "fxa-signout-dialog2-button" }, { id: "fxa-signout-dialog2-checkbox" }, ]); diff --git a/browser/base/content/browser-trustPanel.js b/browser/base/content/browser-trustPanel.js index 3d133d2e89b23..a537b03b35a09 100644 --- a/browser/base/content/browser-trustPanel.js +++ b/browser/base/content/browser-trustPanel.js @@ -189,20 +189,19 @@ class TrustPanel { async onContentBlockingEvent( event, - webProgress, + _webProgress, _isSimulated, _previousState ) { // Only accept contentblocking events for uris that we initialised `updateIdentity` // with, this can go wrong if trustpanel is enabled mid page load. - if (!this.#enabled || webProgress.browsingContext.currentURI != this.#uri) { + if (!this.#enabled || !this.#uri) { return; } // First update all our internal state based on the allowlist and the // different blockers: this.anyDetected = false; - this.anyBlocking = false; this.#lastEvent = event; // Check whether the user has added an exception for this site. @@ -219,7 +218,6 @@ class TrustPanel { // the data with the document directly. blocker.activated = blocker.isBlocking(event); this.anyDetected = this.anyDetected || blocker.isDetected(event); - this.anyBlocking = this.anyBlocking || blocker.activated; } if (this.#popup) { diff --git a/browser/base/content/test/performance/browser_startup_flicker.js b/browser/base/content/test/performance/browser_startup_flicker.js index 1eded47c4b003..ea0c5307762fc 100644 --- a/browser/base/content/test/performance/browser_startup_flicker.js +++ b/browser/base/content/test/performance/browser_startup_flicker.js @@ -27,17 +27,10 @@ add_task(async function () { let frame = frames[i], previousFrame = frames[i - 1]; let rects = compareFrames(frame, previousFrame); - if (!alreadyFocused && isLikelyFocusChange(rects, frame)) { - todo( - false, - "bug 1445161 - the window should be focused at first paint, " + - rects.toSource() - ); - continue; - } - alreadyFocused = true; let expectedRects = []; + let focusRects = []; + rects = rects.filter(rect => { let width = frame.width; @@ -83,14 +76,32 @@ add_task(async function () { } } - ok(false, "unexpected changed rect: " + rectText); + if (!alreadyFocused) { + focusRects.push(rect); + } return true; }); + + if (!alreadyFocused && isLikelyFocusChange(focusRects, frame)) { + todo( + false, + "bug 1445161 - the window should be focused at first paint, " + + focusRects.toSource() + ); + continue; + } + alreadyFocused = true; + if (!rects.length) { info("ignoring identical frame"); continue; } + for (let rect of rects) { + let rectText = `${rect.toSource()}, window width: ${frame.width}`; + ok(false, "unexpected changed rect: " + rectText); + } + await reportFlickerWithAPNG(previousFrame, frame, i, expectedRects); unexpectedRects += rects.length; } diff --git a/browser/components/BrowserComponents.manifest b/browser/components/BrowserComponents.manifest index d80e3ff90acea..cb214b44ab96f 100644 --- a/browser/components/BrowserComponents.manifest +++ b/browser/components/BrowserComponents.manifest @@ -105,6 +105,7 @@ category browser-quit-application-granted resource://gre/modules/UpdateListener. #endif category browser-quit-application-granted moz-src:///browser/components/urlbar/UrlbarSearchTermsPersistence.sys.mjs UrlbarSearchTermsPersistence.uninit category browser-quit-application-granted moz-src:///browser/components/ipprotection/IPProtectionService.sys.mjs IPProtectionService.uninit +category browser-quit-application-granted moz-src:///browser/components/aiwindow/ui/modules/AIWindow.sys.mjs AIWindow.uninit category search-service-notification moz-src:///browser/components/search/SearchUIUtils.sys.mjs SearchUIUtils.showSearchServiceNotification diff --git a/browser/components/aiwindow/ui/modules/AIWindow.sys.mjs b/browser/components/aiwindow/ui/modules/AIWindow.sys.mjs index 2a2d0898a963e..5a3f31ad051ab 100644 --- a/browser/components/aiwindow/ui/modules/AIWindow.sys.mjs +++ b/browser/components/aiwindow/ui/modules/AIWindow.sys.mjs @@ -17,15 +17,16 @@ ChromeUtils.defineESModuleGetters(lazy, { AIWindowMenu: "moz-src:///browser/components/aiwindow/ui/modules/AIWindowMenu.sys.mjs", BrowserWindowTracker: "resource:///modules/BrowserWindowTracker.sys.mjs", - SearchService: "moz-src:///toolkit/components/search/SearchService.sys.mjs", - SearchUIUtils: "moz-src:///browser/components/search/SearchUIUtils.sys.mjs", ChatStore: "moz-src:///browser/components/aiwindow/ui/modules/ChatStore.sys.mjs", NewTabPagePreloading: "moz-src:///browser/components/tabbrowser/NewTabPagePreloading.sys.mjs", + ONLOGOUT_NOTIFICATION: "resource://gre/modules/FxAccountsCommon.sys.mjs", PanelMultiView: "moz-src:///browser/components/customizableui/PanelMultiView.sys.mjs", PrivateBrowsingUtils: "resource://gre/modules/PrivateBrowsingUtils.sys.mjs", + SearchService: "moz-src:///toolkit/components/search/SearchService.sys.mjs", + SearchUIUtils: "moz-src:///browser/components/search/SearchUIUtils.sys.mjs", MemoriesSchedulers: "moz-src:///browser/components/aiwindow/models/memories/MemoriesSchedulers.sys.mjs", }); @@ -62,6 +63,7 @@ export const AIWindow = { } ChromeUtils.defineLazyGetter(AIWindow, "chatStore", () => lazy.ChatStore); + Services.obs.addObserver(this, lazy.ONLOGOUT_NOTIFICATION); this._initialized = true; // On startup/restart, if the first window initialized is an @@ -71,6 +73,42 @@ export const AIWindow = { } }, + uninit() { + if (!this._initialized) { + return; + } + Services.obs.removeObserver(this, lazy.ONLOGOUT_NOTIFICATION); + this._initialized = false; + }, + + observe(_subject, topic) { + if (topic === lazy.ONLOGOUT_NOTIFICATION) { + this._onAccountLogout(); + } + }, + + // Switches all active AI Windows back to classic mode when the user signs out + // of their Firefox Account. + _onAccountLogout() { + for (const win of Services.wm.getEnumerator("navigator:browser")) { + if (!win.closed && this.isAIWindowActive(win)) { + this.toggleAIWindow(win, false); + } + } + }, + + // Checks if there are any open AI Windows. It's used to determine if certain + // operations (like Account sign-out warnings) need to account for active AI + // Window sessions. + hasActiveAIWindows() { + for (const win of Services.wm.getEnumerator("navigator:browser")) { + if (!win.closed && this.isAIWindowActiveAndEnabled(win)) { + return true; + } + } + return false; + }, + _reconcileNewTabPages(win, previousNewTabURL) { const newTabURI = Services.io.newURI(win.BROWSER_NEW_TAB_URL); const oldTabURI = Services.io.newURI(previousNewTabURL); @@ -194,13 +232,27 @@ export const AIWindow = { } aiWindowURI.data = initialURL; args.appendElement(aiWindowURI); + } - const aiOption = Cc["@mozilla.org/hash-property-bag;1"].createInstance( + let propBag; + try { + propBag = args.length > 1 && args.queryElementAt(1, Ci.nsIPropertyBag2); + } catch (e) { + console.error( + new Error( + "Tried to create AI window but property bag argument is wrong" + ), + propBag + ); + return args; + } + if (!propBag) { + propBag = Cc["@mozilla.org/hash-property-bag;1"].createInstance( Ci.nsIWritablePropertyBag2 ); - aiOption.setPropertyAsBool("ai-window", aiWindow); - args.appendElement(aiOption); + args.appendElement(propBag); } + propBag.setPropertyAsBool("ai-window", true); return args; }, diff --git a/browser/components/aiwindow/ui/test/browser/browser.toml b/browser/components/aiwindow/ui/test/browser/browser.toml index 4fd888dc63bf3..e82ad38254cdf 100644 --- a/browser/components/aiwindow/ui/test/browser/browser.toml +++ b/browser/components/aiwindow/ui/test/browser/browser.toml @@ -61,3 +61,5 @@ support-files = [ ["browser_sidebar_aiwindow.js"] ["browser_smartwindow_init.js"] + +["browser_smartwindow_tab_move.js"] diff --git a/browser/components/aiwindow/ui/test/browser/browser_aiwindow_memories.js b/browser/components/aiwindow/ui/test/browser/browser_aiwindow_memories.js index 0a1cbcdf1e701..21df96658cd91 100644 --- a/browser/components/aiwindow/ui/test/browser/browser_aiwindow_memories.js +++ b/browser/components/aiwindow/ui/test/browser/browser_aiwindow_memories.js @@ -68,7 +68,8 @@ add_task(async function test_memories_scheduler_on_init_with_aiwindow() { testWin.document.documentElement.setAttribute("ai-window", ""); - AIWindow._initialized = false; + // Use uninit() to properly clean up observer before re-initializing + AIWindow.uninit(); AIWindow.init(testWin); Assert.ok( @@ -89,7 +90,8 @@ add_task( private: false, }); - AIWindow._initialized = false; + // Use uninit() to properly clean up observer before re-initializing + AIWindow.uninit(); AIWindow.init(testWin); Assert.ok( diff --git a/browser/components/aiwindow/ui/test/browser/browser_aiwindow_switcher.js b/browser/components/aiwindow/ui/test/browser/browser_aiwindow_switcher.js index 38a7cc4c64661..5361e1f828e1a 100644 --- a/browser/components/aiwindow/ui/test/browser/browser_aiwindow_switcher.js +++ b/browser/components/aiwindow/ui/test/browser/browser_aiwindow_switcher.js @@ -235,3 +235,29 @@ add_task(async function test_switcher_repositions_on_pref_change() { await SpecialPowers.flushPrefEnv(); }); + +// Test that _onAccountLogout switches AI windows to classic mode +add_task(async function test_onAccountLogout_switches_windows() { + const { AIWindow } = ChromeUtils.importESModule( + "moz-src:///browser/components/aiwindow/ui/modules/AIWindow.sys.mjs" + ); + + await SpecialPowers.pushPrefEnv({ + set: [["browser.aiwindow.enabled", true]], + }); + + document.documentElement.setAttribute("ai-window", ""); + Assert.ok( + AIWindow.isAIWindowActive(window), + "Window should start in AI mode" + ); + + AIWindow._onAccountLogout(); + + Assert.ok( + !AIWindow.isAIWindowActive(window), + "Window should switch to classic mode after logout" + ); + + await SpecialPowers.popPrefEnv(); +}); diff --git a/browser/components/aiwindow/ui/test/browser/browser_smartwindow_tab_move.js b/browser/components/aiwindow/ui/test/browser/browser_smartwindow_tab_move.js new file mode 100644 index 0000000000000..869c70c310163 --- /dev/null +++ b/browser/components/aiwindow/ui/test/browser/browser_smartwindow_tab_move.js @@ -0,0 +1,47 @@ +/* Any copyright is dedicated to the Public Domain. + * http://creativecommons.org/publicdomain/zero/1.0/ */ + +"use strict"; + +const { TabStateFlusher } = ChromeUtils.importESModule( + "resource:///modules/sessionstore/TabStateFlusher.sys.mjs" +); + +// Move a tab to a new window, check the new window is also an AI window. +add_task(async function test() { + let tab1 = await BrowserTestUtils.addTab(gBrowser, "about:blank"); + let tab2 = await BrowserTestUtils.addTab(gBrowser, "about:blank"); + + is(gBrowser.multiSelectedTabsCount, 0, "Zero multiselected tabs"); + + AIWindow.toggleAIWindow(window, true); + registerCleanupFunction(() => { + AIWindow.toggleAIWindow(window, false); + }); + await BrowserTestUtils.switchTab(gBrowser, tab1); + let prevBrowser = tab1.linkedBrowser; + + let delayedStartupPromise = BrowserTestUtils.waitForNewWindow(); + let newWindow = gBrowser.replaceTabsWithWindow(tab1); + await delayedStartupPromise; + + ok( + !prevBrowser.frameLoader, + "the swapped-from browser's frameloader has been destroyed" + ); + + let gBrowser2 = newWindow.gBrowser; + ok(AIWindow.isAIWindowActive(newWindow), "The new window is an AI window"); + + is(gBrowser.visibleTabs.length, 2, "Two tabs now in the old window"); + is(gBrowser2.visibleTabs.length, 1, "One tab in the new window"); + + tab1 = gBrowser2.visibleTabs[0]; + ok(tab1, "Got a tab1"); + await tab1.focus(); + + await TabStateFlusher.flush(tab1.linkedBrowser); + + await BrowserTestUtils.closeWindow(newWindow); + await BrowserTestUtils.removeTab(tab2); +}); diff --git a/browser/components/preferences/home.js b/browser/components/preferences/home.js index 1d6e2e154da37..0167a4d795c24 100644 --- a/browser/components/preferences/home.js +++ b/browser/components/preferences/home.js @@ -81,8 +81,8 @@ if (Services.prefs.getBoolPref("browser.settings-redesign.enabled")) { }) ); - // Set up `browser.startup.homepage` again to help make a pretty list of domains - // for displaying in the "Choose a specific website" link to the Custom Homepage subpage. + // Set up `browser.startup.homepage` again to update and display its value + // on the Homepage and Custom Homepage settings panes. Preferences.addSetting({ id: "homepageDisplayPref", pref: "browser.startup.homepage", @@ -99,22 +99,22 @@ if (Services.prefs.getBoolPref("browser.settings-redesign.enabled")) { gotoPref("customHomepage"); }, - getControlConfig(config, deps) { - const servicePages = [ - "about:home", - "chrome://browser/content/blanktab.html", - ]; + getControlConfig(config, { homepageDisplayPref }) { let customURLsDescription; - if (servicePages.includes(deps.homepageDisplayPref.value.trim())) { - // Make sure we only show user-provided values for custom URLs rather than - // values we set in `browser.startup.homepage` for "Firefox Home" - // and "Blank Page". + // Make sure we only show user-provided values for custom URLs rather than + // values we set in `browser.startup.homepage` for "Firefox Home" + // and "Blank Page". + if ( + [DEFAULT_HOMEPAGE_URL, BLANK_HOMEPAGE_URL].includes( + homepageDisplayPref.value.trim() + ) + ) { customURLsDescription = null; } else { // Add a comma-separated list of Custom URLs the user set for their homepage // to the description part of the "Choose a specific site" box button. - customURLsDescription = deps.homepageDisplayPref.value + customURLsDescription = homepageDisplayPref.value .split("|") .map(uri => BrowserUtils.formatURIStringForDisplay(uri, { @@ -135,30 +135,175 @@ if (Services.prefs.getBoolPref("browser.settings-redesign.enabled")) { }, }); - // Custom Homepage Subpage: layout items + /** + * Helper function to convert a pipe-delimited string of custom homepage URLs + * into an array of trimmed URLs. + * + * @param urls {string} + * @returns {string[]} + */ + const getURLs = urls => { + return urls + .split("|") + .map(u => u.trim()) + .filter(Boolean); + }; + + Preferences.addSetting( + /** @type {{ _inputValue: string } & SettingConfig } */ ({ + id: "customHomepageAddUrlInput", + _inputValue: "", + get() { + return this._inputValue; + }, + + set(val, _, setting) { + this._inputValue = val.trim() ?? ""; + setting.onChange(); + }, + }) + ); + Preferences.addSetting({ - id: "customHomepageCard", + id: "customHomepageAddAddressButton", + deps: ["homepageDisplayPref", "customHomepageAddUrlInput"], + onUserClick(e, { homepageDisplayPref, customHomepageAddUrlInput }) { + // Focus is being stolen by a parent component here (moz-fieldset). + // Focus on the button to get the input value. + e.target.focus(); + + let inputVal = customHomepageAddUrlInput.value; + + // Don't do anything for empty strings + if (!inputVal) { + return; + } + + if ( + [DEFAULT_HOMEPAGE_URL, BLANK_HOMEPAGE_URL].includes( + homepageDisplayPref.value.trim() + ) + ) { + // Replace the default homepage value with the new Custom URL. + homepageDisplayPref.value = inputVal; + } else { + // Append this URL to the list of Custom URLs saved in prefs. + let urls = getURLs(homepageDisplayPref.value); + urls.push(inputVal); + homepageDisplayPref.value = urls.join("|"); + } + + // Reset the field to empty string + customHomepageAddUrlInput.value = ""; + }, }); Preferences.addSetting({ id: "customHomepageBoxGroup", - }); + deps: ["homepageDisplayPref"], + getControlConfig(config, { homepageDisplayPref }) { + const urls = getURLs(homepageDisplayPref.value); + let listItems = []; + let type = "list"; + + // Show a reorderable list of Custom URLs if the user has provided any. + // Make sure to exclude "Firefox Home" and "Blank Page" values that are also + // stored in the homepage pref. + if ( + [DEFAULT_HOMEPAGE_URL, BLANK_HOMEPAGE_URL].includes( + homepageDisplayPref.value.trim() + ) === false + ) { + type = "reorderable-list"; + listItems = urls.map((url, index) => ({ + id: `customHomepageUrl-${index}`, + key: `url-${index}-${url}`, + control: "moz-box-item", + controlAttrs: { label: url, "data-url": url }, + options: [ + { + control: "moz-button", + iconSrc: "chrome://global/skin/icons/delete.svg", + l10nId: "home-custom-homepage-delete-address-button", + slot: "actions-start", + controlAttrs: { + "data-action": "delete", + "data-index": index, + }, + }, + ], + })); + } else { + // If no custom URLs have been set, show the "no results" string instead. + listItems = [ + { + control: "moz-box-item", + l10nId: "home-custom-homepage-no-results", + controlAttrs: { + class: "description-deemphasized", + }, + }, + ]; + } - Preferences.addSetting({ - id: "customHomepageBoxForm", - }); + return { + ...config, + controlAttrs: { + ...config.controlAttrs, + type, + }, + options: [ + { + id: "customHomepageBoxForm", + control: "moz-box-item", + slot: "header", + items: [ + { + id: "customHomepageAddUrlInput", + l10nId: "home-custom-homepage-address", + control: "moz-input-text", + }, + { + id: "customHomepageAddAddressButton", + l10nId: "home-custom-homepage-address-button", + control: "moz-button", + slot: "actions", + }, + ], + }, + ...listItems, + { + id: "customHomepageBoxActions", + control: "moz-box-item", + slot: "footer", + items: [], // "replace with" buttons + }, + ], + }; + }, + onUserReorder(e, { homepageDisplayPref }) { + let urls = getURLs(homepageDisplayPref.value); - Preferences.addSetting({ - id: "customHomepageBoxUrlList", - }); + let { draggedIndex, targetIndex } = e.detail; + let [moved] = urls.splice(draggedIndex, 1); + urls.splice(targetIndex, 0, moved); - Preferences.addSetting({ - id: "customHomepageBoxActions", - }); + homepageDisplayPref.value = urls.join("|"); + }, + onUserClick(e, { homepageDisplayPref }) { + let urls = getURLs(homepageDisplayPref.value); - // temp filler id for Custom URL subpage setup - to be removed in follow-up patches - Preferences.addSetting({ - id: "customHomepagePlaceholderButton", + if ( + e.target.localName === "moz-button" && + e.target.getAttribute("data-action") === "delete" + ) { + let index = Number(e.target.dataset.index); + if (Number.isInteger(index) && index >= 0 && index < urls.length) { + urls.splice(index, 1); + homepageDisplayPref.value = urls.join("|"); + } + } + }, }); // Homepage / New Tabs diff --git a/browser/components/preferences/main.js b/browser/components/preferences/main.js index ef8d15e9c241b..c09759c6d7e0e 100644 --- a/browser/components/preferences/main.js +++ b/browser/components/preferences/main.js @@ -2556,53 +2556,15 @@ SettingGroupManager.registerGroups({ customHomepage: { inProgress: true, headingLevel: 2, + l10nId: "home-custom-homepage-card-header", + iconSrc: "chrome://global/skin/icons/link.svg", items: [ { - id: "customHomepageCard", - control: "moz-card", - l10nId: "home-custom-homepage-card", - iconSrc: "chrome://global/skin/icons/link.svg", - items: [ - { - id: "customHomepageBoxGroup", - control: "moz-box-group", - controlAttrs: { - type: "list", - }, - items: [ - { - id: "customHomepageBoxForm", - control: "moz-box-item", - items: [ - { - id: "customHomepagePlaceholderButton", - control: "moz-button", - }, - ], - }, - { - id: "customHomepageBoxUrlList", - control: "moz-box-item", - items: [ - { - id: "customHomepagePlaceholderButton", - control: "moz-button", - }, - ], - }, - { - id: "customHomepageBoxActions", - control: "moz-box-item", - items: [ - { - id: "customHomepagePlaceholderButton", - control: "moz-button", - }, - ], - }, - ], - }, - ], + id: "customHomepageBoxGroup", + control: "moz-box-group", + controlAttrs: { + type: "list", + }, }, ], }, diff --git a/browser/components/preferences/preferences.js b/browser/components/preferences/preferences.js index 9e6ec0c0b4acc..41840d7e51a31 100644 --- a/browser/components/preferences/preferences.js +++ b/browser/components/preferences/preferences.js @@ -262,7 +262,7 @@ const CONFIG_PANES = Object.freeze({ etpCustomize: { parent: "etp", l10nId: "preferences-etp-customize-header", - groupIds: ["etpReset", "etpCustomize"], + groupIds: ["etpCustomize", "etpReset"], }, manageAddresses: { parent: "privacy", diff --git a/browser/components/tabbrowser/content/tabbrowser.js b/browser/components/tabbrowser/content/tabbrowser.js index 545ee3629d88d..c3a3e0e56c5cb 100644 --- a/browser/components/tabbrowser/content/tabbrowser.js +++ b/browser/components/tabbrowser/content/tabbrowser.js @@ -6556,23 +6556,16 @@ * in the current window, in which case this will do nothing. * * @param {MozTabbrowserTab|MozTabbrowserTabGroup|MozTabbrowserTabGroup.labelElement} aTab + * @param {object} [aOptions={}] + * Key-value pairs that will be serialized into the features string. */ - replaceTabWithWindow(aTab, aOptions) { + replaceTabWithWindow(aTab, aOptions = {}) { if (this.tabs.length == 1) { return null; } // TODO bug 1967925: Consider handling the case where aTab is a tab group // and also the only tab group in its window. - var options = "chrome,dialog=no,all"; - for (var name in aOptions) { - options += "," + name + "=" + aOptions[name]; - } - - if (PrivateBrowsingUtils.isWindowPrivate(window)) { - options += ",private=1"; - } - // Play the tab closing animation to give immediate feedback while // waiting for the new window to appear. if (!gReduceMotion && this.isTab(aTab)) { @@ -6581,12 +6574,16 @@ } // tell a new window to take the "dropped" tab - return window.openDialog( - AppConstants.BROWSER_CHROME_URL, - "_blank", - options, - aTab - ); + let args = Cc["@mozilla.org/array;1"].createInstance(Ci.nsIMutableArray); + args.appendElement(aTab); + return BrowserWindowTracker.openWindow({ + private: PrivateBrowsingUtils.isWindowPrivate(window), + features: Object.entries(aOptions) + .map(([key, value]) => `${key}=${value}`) + .join(","), + openerWindow: window, + args, + }); } /** diff --git a/browser/components/urlbar/ActionsProviderContextualSearch.sys.mjs b/browser/components/urlbar/ActionsProviderContextualSearch.sys.mjs index f2fa594174eb1..ce7dfdcaab736 100644 --- a/browser/components/urlbar/ActionsProviderContextualSearch.sys.mjs +++ b/browser/components/urlbar/ActionsProviderContextualSearch.sys.mjs @@ -228,8 +228,15 @@ class ProviderContextualSearch extends ActionsProvider { let searchStr = queryContext.trimmedSearchString.toLocaleLowerCase(); for (let engine of await lazy.SearchService.getVisibleEngines()) { + let engineName = engine.name.toLocaleLowerCase(); + let engineAliases = engine.aliases.map(a => a.toLocaleLowerCase()); + + const matches = (search, name) => + search.length < 3 ? name.startsWith(search) : name.includes(search); + if ( - engine.name.toLocaleLowerCase().startsWith(searchStr) && + (matches(searchStr, engineName) || + engineAliases.some(alias => matches(searchStr, alias))) && ((await this.#shouldskipRecentVisitCheck(searchStr)) || (await this.#engineDomainHasRecentVisits(engine.searchUrlDomain))) ) { diff --git a/browser/components/urlbar/UrlbarController.sys.mjs b/browser/components/urlbar/UrlbarController.sys.mjs index 8752b701bc460..a751bd0118491 100644 --- a/browser/components/urlbar/UrlbarController.sys.mjs +++ b/browser/components/urlbar/UrlbarController.sys.mjs @@ -1636,42 +1636,42 @@ class TelemetryEvent { this.#previousSearchWordsSet = null; } + /** + * Prefs to record in telemetry. + * + * If a pref is a `fallbackPref` for a Nimbus variable, list the variable + * instead of the pref. That way, the metric will record the variable value + * when the variable is defined and the pref value otherwise. + */ #PING_PREFS = { maxRichResults: Glean.urlbar.prefMaxResults, - "quicksuggest.online.available": Glean.urlbar.prefSuggestOnlineAvailable, + quickSuggestOnlineAvailable: Glean.urlbar.prefSuggestOnlineAvailable, "quicksuggest.online.enabled": Glean.urlbar.prefSuggestOnlineEnabled, "suggest.quicksuggest.all": Glean.urlbar.prefSuggestAll, "suggest.quicksuggest.sponsored": Glean.urlbar.prefSuggestSponsored, "suggest.topsites": Glean.urlbar.prefSuggestTopsites, }; - // Used to record telemetry for prefs that are fallbacks for Nimbus variables. - // `onNimbusChanged` is called for these variables rather than `onPrefChanged` - // but we want to record telemetry as if the prefs themselves changed. This - // object maps Nimbus variable names to their fallback prefs. - #PING_NIMBUS_VARIABLES = { - quickSuggestOnlineAvailable: "quicksuggest.online.available", - }; - #readPingPrefs() { for (const p of Object.keys(this.#PING_PREFS)) { this.#recordPref(p); } } - #recordPref(pref, newValue = undefined) { + #recordPref(pref) { const metric = this.#PING_PREFS[pref]; - const prefValue = newValue ?? lazy.UrlbarPrefs.get(pref); if (metric) { - metric.set(prefValue); + metric.set(lazy.UrlbarPrefs.get(pref)); } + switch (pref) { case "suggest.quicksuggest.all": case "suggest.quicksuggest.sponsored": case "quicksuggest.enabled": - if (!prefValue) { + if (!lazy.UrlbarPrefs.get(pref)) { this.handleDisableSuggest(); } + break; } } @@ -1679,10 +1679,8 @@ class TelemetryEvent { this.#recordPref(pref); } - onNimbusChanged(name, newValue) { - if (this.#PING_NIMBUS_VARIABLES.hasOwnProperty(name)) { - this.#recordPref(this.#PING_NIMBUS_VARIABLES[name], newValue); - } + onNimbusChanged(variable) { + this.#recordPref(variable); } // Used to avoid re-entering `record()`. diff --git a/browser/components/urlbar/metrics.yaml b/browser/components/urlbar/metrics.yaml index 8e30002486d51..791490f3fcc6b 100644 --- a/browser/components/urlbar/metrics.yaml +++ b/browser/components/urlbar/metrics.yaml @@ -800,6 +800,7 @@ urlbar: Corresponds to the value of the `browser.urlbar.maxRichResults` pref. bugs: - https://bugzilla.mozilla.org/show_bug.cgi?id=1817196 + - https://bugzilla.mozilla.org/show_bug.cgi?id=2014318 data_reviews: - https://bugzilla.mozilla.org/show_bug.cgi?id=1817196 data_sensitivity: @@ -809,6 +810,7 @@ urlbar: expires: never send_in_pings: - events + - metrics pref_suggest_all: lifetime: application @@ -819,6 +821,7 @@ urlbar: `browser.urlbar.suggest.quicksuggest.all` pref. bugs: - https://bugzilla.mozilla.org/show_bug.cgi?id=1995574 + - https://bugzilla.mozilla.org/show_bug.cgi?id=2014318 data_reviews: - https://bugzilla.mozilla.org/show_bug.cgi?id=1995574 data_sensitivity: @@ -828,6 +831,7 @@ urlbar: expires: never send_in_pings: - events + - metrics pref_suggest_online_available: lifetime: application @@ -840,6 +844,7 @@ urlbar: Suggest introduced in 146 with Terms of Use and OHTTP. bugs: - https://bugzilla.mozilla.org/show_bug.cgi?id=1995362 + - https://bugzilla.mozilla.org/show_bug.cgi?id=2014318 data_reviews: - https://bugzilla.mozilla.org/show_bug.cgi?id=1995362 data_sensitivity: @@ -849,6 +854,7 @@ urlbar: expires: never send_in_pings: - events + - metrics pref_suggest_online_enabled: lifetime: application @@ -862,6 +868,7 @@ urlbar: Suggest introduced in 146 with Terms of Use and OHTTP. bugs: - https://bugzilla.mozilla.org/show_bug.cgi?id=1995362 + - https://bugzilla.mozilla.org/show_bug.cgi?id=2014318 data_reviews: - https://bugzilla.mozilla.org/show_bug.cgi?id=1995362 data_sensitivity: @@ -871,6 +878,7 @@ urlbar: expires: never send_in_pings: - events + - metrics pref_suggest_sponsored: lifetime: application @@ -882,6 +890,7 @@ urlbar: bugs: - https://bugzilla.mozilla.org/show_bug.cgi?id=1847855 - https://bugzilla.mozilla.org/show_bug.cgi?id=1849726 + - https://bugzilla.mozilla.org/show_bug.cgi?id=2014318 data_reviews: - https://bugzilla.mozilla.org/show_bug.cgi?id=1847855 data_sensitivity: @@ -891,6 +900,7 @@ urlbar: expires: never send_in_pings: - events + - metrics pref_suggest_topsites: lifetime: application @@ -900,6 +910,7 @@ urlbar: Corresponds to the value of the `browser.urlbar.suggest.topsites` pref. bugs: - https://bugzilla.mozilla.org/show_bug.cgi?id=1817196 + - https://bugzilla.mozilla.org/show_bug.cgi?id=2014318 data_reviews: - https://bugzilla.mozilla.org/show_bug.cgi?id=1817196 data_sensitivity: @@ -909,6 +920,7 @@ urlbar: expires: never send_in_pings: - events + - metrics autofill_deletion: type: counter diff --git a/browser/components/urlbar/tests/browser/browser_contextualsearch.js b/browser/components/urlbar/tests/browser/browser_contextualsearch.js index 075777b19b3cc..67e6c8cb35410 100644 --- a/browser/components/urlbar/tests/browser/browser_contextualsearch.js +++ b/browser/components/urlbar/tests/browser/browser_contextualsearch.js @@ -32,7 +32,6 @@ const CONFIG = [ }, }, }, - { identifier: "config-engine", base: { @@ -141,6 +140,47 @@ add_task(async function test_engine_match() { await onLoad; }); +add_task(async function test_alias_match() { + let newConfig = [CONFIG[0]].concat([ + { + identifier: "alias-engine", + base: { + urls: { + search: { base: "https://example.net", searchTermParamName: "q" }, + }, + aliases: ["test"], + }, + }, + ]); + await SearchTestUtils.updateRemoteSettingsConfig(newConfig); + + await UrlbarTestUtils.promiseAutocompleteResultPopup({ + window, + value: "test", + }); + + let onLoad = BrowserTestUtils.browserLoaded( + gBrowser.selectedBrowser, + false, + "https://example.net/?q=test" + ); + + EventUtils.synthesizeKey("KEY_Tab"); + await UrlbarTestUtils.assertSearchMode(window, { + engineName: "alias-engine", + entry: "keywordoffer", + isPreview: true, + source: 3, + }); + + await UrlbarTestUtils.promisePopupClose(window, () => { + EventUtils.sendString("test"); + EventUtils.synthesizeKey("KEY_Enter"); + }); + + await onLoad; +}); + add_task(async function test_actions() { let testActionCalled = 0; await loadUri("https://example.net/"); diff --git a/browser/components/urlbar/tests/engagementTelemetry/browser/browser_glean_telemetry_record_preferences.js b/browser/components/urlbar/tests/engagementTelemetry/browser/browser_glean_telemetry_record_preferences.js index 5fd100a73fbe6..24aed5ca85adb 100644 --- a/browser/components/urlbar/tests/engagementTelemetry/browser/browser_glean_telemetry_record_preferences.js +++ b/browser/components/urlbar/tests/engagementTelemetry/browser/browser_glean_telemetry_record_preferences.js @@ -91,7 +91,7 @@ add_task(async function boolNimbusVariable() { Assert.equal( Glean.urlbar[glean].testGetValue(), initialValue, - `Record ${glean} when UrlbarController is initialized` + "Metric value should be correct initially: " + glean ); let nimbusCleanup = await UrlbarTestUtils.initNimbusFeature({ @@ -101,7 +101,28 @@ add_task(async function boolNimbusVariable() { Assert.equal( Glean.urlbar[glean].testGetValue(), !initialValue, - `Record ${glean} when the ${variable} variable is set` + "Metric value should be correct after installing experiment: " + glean + ); + + // Open a new window and make sure the metric is still correct. These pref + // metrics are currently recorded by `TelemetryEvent`, an instance of which + // is created per browser window. + let win = await BrowserTestUtils.openNewBrowserWindow(); + await TestUtils.waitForTick(); + + Assert.equal( + Glean.urlbar[glean].testGetValue(), + !initialValue, + "Metric value should remain correct after opening new window: " + glean + ); + + await BrowserTestUtils.closeWindow(win); + await TestUtils.waitForTick(); + + Assert.equal( + Glean.urlbar[glean].testGetValue(), + !initialValue, + "Metric value should remain correct after closing new window: " + glean ); await nimbusCleanup(); @@ -109,7 +130,7 @@ add_task(async function boolNimbusVariable() { Assert.equal( Glean.urlbar[glean].testGetValue(), initialValue, - `Record ${glean} when the ${variable} variable is unset` + "Metric value should be reset after uninstalling experiment: " + glean ); } }); diff --git a/browser/locales-preview/aiWindow.ftl b/browser/locales-preview/aiWindow.ftl index 07347269c16ac..b4fe2c11554ea 100644 --- a/browser/locales-preview/aiWindow.ftl +++ b/browser/locales-preview/aiWindow.ftl @@ -113,3 +113,7 @@ aiwindow-memories-off = aiwindow-new-chat = .tooltiptext = New chat .aria-label = New chat + +## Sign out dialog + +fxa-signout-dialog-body-aiwindow = Synced data will remain in your account. Your open Smart Windows will switch to standard windows. diff --git a/browser/locales/en-US/browser/preferences/preferences.ftl b/browser/locales/en-US/browser/preferences/preferences.ftl index 36088350e1743..7c4755e0daf68 100644 --- a/browser/locales/en-US/browser/preferences/preferences.ftl +++ b/browser/locales/en-US/browser/preferences/preferences.ftl @@ -892,11 +892,8 @@ home-custom-homepage-subpage = .heading = Custom Homepage # Subheader on the Custom Homepage subpage. Followed by a form to enter URLs and a list of URLs already saved, if any. -home-custom-homepage-subheader = Website address(es) - -# Subheader on the Custom Homepage subpage. Followed by a form to enter URLs and a list of URLs already saved, if any. -home-custom-homepage-card = - .heading = Website address(es) +home-custom-homepage-card-header = + .label = Website address(es) home-custom-homepage-address = .placeholder = Enter address @@ -904,7 +901,12 @@ home-custom-homepage-address-button = .label = Add address # Shown when no custom websites/URLs to use as a homepage have been added yet -home-custom-homepage-no-websites-yet = No websites added yet. +home-custom-homepage-no-results = + .label = No websites added yet. + +home-custom-homepage-delete-address-button = + .aria-label = Delete address + .title = Delete address # Further options to use when setting the home page. Two action buttons are placed in line with this prompt # to replace the current home page with a currently open page or bookmark. diff --git a/browser/locales/l10n-changesets.json b/browser/locales/l10n-changesets.json index 0e4da36585229..0c2d27fce36ff 100644 --- a/browser/locales/l10n-changesets.json +++ b/browser/locales/l10n-changesets.json @@ -15,7 +15,7 @@ "win64-aarch64-devedition", "win64-devedition" ], - "revision": "ecdfdca74a53222f4937e831829de0ab6e1ee5f8" + "revision": "a7420bb8974bc65bbc7efed4fe2c97056ff3ec0e" }, "af": { "pin": false, @@ -33,7 +33,7 @@ "win64-aarch64-devedition", "win64-devedition" ], - "revision": "ecdfdca74a53222f4937e831829de0ab6e1ee5f8" + "revision": "a7420bb8974bc65bbc7efed4fe2c97056ff3ec0e" }, "an": { "pin": false, @@ -51,7 +51,7 @@ "win64-aarch64-devedition", "win64-devedition" ], - "revision": "ecdfdca74a53222f4937e831829de0ab6e1ee5f8" + "revision": "a7420bb8974bc65bbc7efed4fe2c97056ff3ec0e" }, "ar": { "pin": false, @@ -69,7 +69,7 @@ "win64-aarch64-devedition", "win64-devedition" ], - "revision": "ecdfdca74a53222f4937e831829de0ab6e1ee5f8" + "revision": "a7420bb8974bc65bbc7efed4fe2c97056ff3ec0e" }, "ast": { "pin": false, @@ -87,7 +87,7 @@ "win64-aarch64-devedition", "win64-devedition" ], - "revision": "ecdfdca74a53222f4937e831829de0ab6e1ee5f8" + "revision": "a7420bb8974bc65bbc7efed4fe2c97056ff3ec0e" }, "az": { "pin": false, @@ -105,7 +105,7 @@ "win64-aarch64-devedition", "win64-devedition" ], - "revision": "ecdfdca74a53222f4937e831829de0ab6e1ee5f8" + "revision": "a7420bb8974bc65bbc7efed4fe2c97056ff3ec0e" }, "be": { "pin": false, @@ -123,7 +123,7 @@ "win64-aarch64-devedition", "win64-devedition" ], - "revision": "ecdfdca74a53222f4937e831829de0ab6e1ee5f8" + "revision": "a7420bb8974bc65bbc7efed4fe2c97056ff3ec0e" }, "bg": { "pin": false, @@ -141,7 +141,7 @@ "win64-aarch64-devedition", "win64-devedition" ], - "revision": "ecdfdca74a53222f4937e831829de0ab6e1ee5f8" + "revision": "a7420bb8974bc65bbc7efed4fe2c97056ff3ec0e" }, "bn": { "pin": false, @@ -159,7 +159,7 @@ "win64-aarch64-devedition", "win64-devedition" ], - "revision": "ecdfdca74a53222f4937e831829de0ab6e1ee5f8" + "revision": "a7420bb8974bc65bbc7efed4fe2c97056ff3ec0e" }, "bo": { "pin": false, @@ -177,7 +177,7 @@ "win64-aarch64-devedition", "win64-devedition" ], - "revision": "ecdfdca74a53222f4937e831829de0ab6e1ee5f8" + "revision": "a7420bb8974bc65bbc7efed4fe2c97056ff3ec0e" }, "br": { "pin": false, @@ -195,7 +195,7 @@ "win64-aarch64-devedition", "win64-devedition" ], - "revision": "ecdfdca74a53222f4937e831829de0ab6e1ee5f8" + "revision": "a7420bb8974bc65bbc7efed4fe2c97056ff3ec0e" }, "brx": { "pin": false, @@ -213,7 +213,7 @@ "win64-aarch64-devedition", "win64-devedition" ], - "revision": "ecdfdca74a53222f4937e831829de0ab6e1ee5f8" + "revision": "a7420bb8974bc65bbc7efed4fe2c97056ff3ec0e" }, "bs": { "pin": false, @@ -231,7 +231,7 @@ "win64-aarch64-devedition", "win64-devedition" ], - "revision": "ecdfdca74a53222f4937e831829de0ab6e1ee5f8" + "revision": "a7420bb8974bc65bbc7efed4fe2c97056ff3ec0e" }, "ca": { "pin": false, @@ -249,7 +249,7 @@ "win64-aarch64-devedition", "win64-devedition" ], - "revision": "ecdfdca74a53222f4937e831829de0ab6e1ee5f8" + "revision": "a7420bb8974bc65bbc7efed4fe2c97056ff3ec0e" }, "ca-valencia": { "pin": false, @@ -267,7 +267,7 @@ "win64-aarch64-devedition", "win64-devedition" ], - "revision": "ecdfdca74a53222f4937e831829de0ab6e1ee5f8" + "revision": "a7420bb8974bc65bbc7efed4fe2c97056ff3ec0e" }, "cak": { "pin": false, @@ -285,7 +285,7 @@ "win64-aarch64-devedition", "win64-devedition" ], - "revision": "ecdfdca74a53222f4937e831829de0ab6e1ee5f8" + "revision": "a7420bb8974bc65bbc7efed4fe2c97056ff3ec0e" }, "ckb": { "pin": false, @@ -303,7 +303,7 @@ "win64-aarch64-devedition", "win64-devedition" ], - "revision": "ecdfdca74a53222f4937e831829de0ab6e1ee5f8" + "revision": "a7420bb8974bc65bbc7efed4fe2c97056ff3ec0e" }, "cs": { "pin": false, @@ -321,7 +321,7 @@ "win64-aarch64-devedition", "win64-devedition" ], - "revision": "ecdfdca74a53222f4937e831829de0ab6e1ee5f8" + "revision": "a7420bb8974bc65bbc7efed4fe2c97056ff3ec0e" }, "cy": { "pin": false, @@ -339,7 +339,7 @@ "win64-aarch64-devedition", "win64-devedition" ], - "revision": "ecdfdca74a53222f4937e831829de0ab6e1ee5f8" + "revision": "a7420bb8974bc65bbc7efed4fe2c97056ff3ec0e" }, "da": { "pin": false, @@ -357,7 +357,7 @@ "win64-aarch64-devedition", "win64-devedition" ], - "revision": "ecdfdca74a53222f4937e831829de0ab6e1ee5f8" + "revision": "a7420bb8974bc65bbc7efed4fe2c97056ff3ec0e" }, "de": { "pin": false, @@ -375,7 +375,7 @@ "win64-aarch64-devedition", "win64-devedition" ], - "revision": "ecdfdca74a53222f4937e831829de0ab6e1ee5f8" + "revision": "a7420bb8974bc65bbc7efed4fe2c97056ff3ec0e" }, "dsb": { "pin": false, @@ -393,7 +393,7 @@ "win64-aarch64-devedition", "win64-devedition" ], - "revision": "ecdfdca74a53222f4937e831829de0ab6e1ee5f8" + "revision": "a7420bb8974bc65bbc7efed4fe2c97056ff3ec0e" }, "el": { "pin": false, @@ -411,7 +411,7 @@ "win64-aarch64-devedition", "win64-devedition" ], - "revision": "ecdfdca74a53222f4937e831829de0ab6e1ee5f8" + "revision": "a7420bb8974bc65bbc7efed4fe2c97056ff3ec0e" }, "en-CA": { "pin": false, @@ -429,7 +429,7 @@ "win64-aarch64-devedition", "win64-devedition" ], - "revision": "ecdfdca74a53222f4937e831829de0ab6e1ee5f8" + "revision": "a7420bb8974bc65bbc7efed4fe2c97056ff3ec0e" }, "en-GB": { "pin": false, @@ -447,7 +447,7 @@ "win64-aarch64-devedition", "win64-devedition" ], - "revision": "ecdfdca74a53222f4937e831829de0ab6e1ee5f8" + "revision": "a7420bb8974bc65bbc7efed4fe2c97056ff3ec0e" }, "eo": { "pin": false, @@ -465,7 +465,7 @@ "win64-aarch64-devedition", "win64-devedition" ], - "revision": "ecdfdca74a53222f4937e831829de0ab6e1ee5f8" + "revision": "a7420bb8974bc65bbc7efed4fe2c97056ff3ec0e" }, "es-AR": { "pin": false, @@ -483,7 +483,7 @@ "win64-aarch64-devedition", "win64-devedition" ], - "revision": "ecdfdca74a53222f4937e831829de0ab6e1ee5f8" + "revision": "a7420bb8974bc65bbc7efed4fe2c97056ff3ec0e" }, "es-CL": { "pin": false, @@ -501,7 +501,7 @@ "win64-aarch64-devedition", "win64-devedition" ], - "revision": "ecdfdca74a53222f4937e831829de0ab6e1ee5f8" + "revision": "a7420bb8974bc65bbc7efed4fe2c97056ff3ec0e" }, "es-ES": { "pin": false, @@ -519,7 +519,7 @@ "win64-aarch64-devedition", "win64-devedition" ], - "revision": "ecdfdca74a53222f4937e831829de0ab6e1ee5f8" + "revision": "a7420bb8974bc65bbc7efed4fe2c97056ff3ec0e" }, "es-MX": { "pin": false, @@ -537,7 +537,7 @@ "win64-aarch64-devedition", "win64-devedition" ], - "revision": "ecdfdca74a53222f4937e831829de0ab6e1ee5f8" + "revision": "a7420bb8974bc65bbc7efed4fe2c97056ff3ec0e" }, "et": { "pin": false, @@ -555,7 +555,7 @@ "win64-aarch64-devedition", "win64-devedition" ], - "revision": "ecdfdca74a53222f4937e831829de0ab6e1ee5f8" + "revision": "a7420bb8974bc65bbc7efed4fe2c97056ff3ec0e" }, "eu": { "pin": false, @@ -573,7 +573,7 @@ "win64-aarch64-devedition", "win64-devedition" ], - "revision": "ecdfdca74a53222f4937e831829de0ab6e1ee5f8" + "revision": "a7420bb8974bc65bbc7efed4fe2c97056ff3ec0e" }, "fa": { "pin": false, @@ -591,7 +591,7 @@ "win64-aarch64-devedition", "win64-devedition" ], - "revision": "ecdfdca74a53222f4937e831829de0ab6e1ee5f8" + "revision": "a7420bb8974bc65bbc7efed4fe2c97056ff3ec0e" }, "ff": { "pin": false, @@ -609,7 +609,7 @@ "win64-aarch64-devedition", "win64-devedition" ], - "revision": "ecdfdca74a53222f4937e831829de0ab6e1ee5f8" + "revision": "a7420bb8974bc65bbc7efed4fe2c97056ff3ec0e" }, "fi": { "pin": false, @@ -627,7 +627,7 @@ "win64-aarch64-devedition", "win64-devedition" ], - "revision": "ecdfdca74a53222f4937e831829de0ab6e1ee5f8" + "revision": "a7420bb8974bc65bbc7efed4fe2c97056ff3ec0e" }, "fr": { "pin": false, @@ -645,7 +645,7 @@ "win64-aarch64-devedition", "win64-devedition" ], - "revision": "ecdfdca74a53222f4937e831829de0ab6e1ee5f8" + "revision": "a7420bb8974bc65bbc7efed4fe2c97056ff3ec0e" }, "fur": { "pin": false, @@ -663,7 +663,7 @@ "win64-aarch64-devedition", "win64-devedition" ], - "revision": "ecdfdca74a53222f4937e831829de0ab6e1ee5f8" + "revision": "a7420bb8974bc65bbc7efed4fe2c97056ff3ec0e" }, "fy-NL": { "pin": false, @@ -681,7 +681,7 @@ "win64-aarch64-devedition", "win64-devedition" ], - "revision": "ecdfdca74a53222f4937e831829de0ab6e1ee5f8" + "revision": "a7420bb8974bc65bbc7efed4fe2c97056ff3ec0e" }, "ga-IE": { "pin": false, @@ -699,7 +699,7 @@ "win64-aarch64-devedition", "win64-devedition" ], - "revision": "ecdfdca74a53222f4937e831829de0ab6e1ee5f8" + "revision": "a7420bb8974bc65bbc7efed4fe2c97056ff3ec0e" }, "gd": { "pin": false, @@ -717,7 +717,7 @@ "win64-aarch64-devedition", "win64-devedition" ], - "revision": "ecdfdca74a53222f4937e831829de0ab6e1ee5f8" + "revision": "a7420bb8974bc65bbc7efed4fe2c97056ff3ec0e" }, "gl": { "pin": false, @@ -735,7 +735,7 @@ "win64-aarch64-devedition", "win64-devedition" ], - "revision": "ecdfdca74a53222f4937e831829de0ab6e1ee5f8" + "revision": "a7420bb8974bc65bbc7efed4fe2c97056ff3ec0e" }, "gn": { "pin": false, @@ -753,7 +753,7 @@ "win64-aarch64-devedition", "win64-devedition" ], - "revision": "ecdfdca74a53222f4937e831829de0ab6e1ee5f8" + "revision": "a7420bb8974bc65bbc7efed4fe2c97056ff3ec0e" }, "gu-IN": { "pin": false, @@ -771,7 +771,7 @@ "win64-aarch64-devedition", "win64-devedition" ], - "revision": "ecdfdca74a53222f4937e831829de0ab6e1ee5f8" + "revision": "a7420bb8974bc65bbc7efed4fe2c97056ff3ec0e" }, "he": { "pin": false, @@ -789,7 +789,7 @@ "win64-aarch64-devedition", "win64-devedition" ], - "revision": "ecdfdca74a53222f4937e831829de0ab6e1ee5f8" + "revision": "a7420bb8974bc65bbc7efed4fe2c97056ff3ec0e" }, "hi-IN": { "pin": false, @@ -807,7 +807,7 @@ "win64-aarch64-devedition", "win64-devedition" ], - "revision": "ecdfdca74a53222f4937e831829de0ab6e1ee5f8" + "revision": "a7420bb8974bc65bbc7efed4fe2c97056ff3ec0e" }, "hr": { "pin": false, @@ -825,7 +825,7 @@ "win64-aarch64-devedition", "win64-devedition" ], - "revision": "ecdfdca74a53222f4937e831829de0ab6e1ee5f8" + "revision": "a7420bb8974bc65bbc7efed4fe2c97056ff3ec0e" }, "hsb": { "pin": false, @@ -843,7 +843,7 @@ "win64-aarch64-devedition", "win64-devedition" ], - "revision": "ecdfdca74a53222f4937e831829de0ab6e1ee5f8" + "revision": "a7420bb8974bc65bbc7efed4fe2c97056ff3ec0e" }, "hu": { "pin": false, @@ -861,7 +861,7 @@ "win64-aarch64-devedition", "win64-devedition" ], - "revision": "ecdfdca74a53222f4937e831829de0ab6e1ee5f8" + "revision": "a7420bb8974bc65bbc7efed4fe2c97056ff3ec0e" }, "hy-AM": { "pin": false, @@ -879,7 +879,7 @@ "win64-aarch64-devedition", "win64-devedition" ], - "revision": "ecdfdca74a53222f4937e831829de0ab6e1ee5f8" + "revision": "a7420bb8974bc65bbc7efed4fe2c97056ff3ec0e" }, "hye": { "pin": false, @@ -897,7 +897,7 @@ "win64-aarch64-devedition", "win64-devedition" ], - "revision": "ecdfdca74a53222f4937e831829de0ab6e1ee5f8" + "revision": "a7420bb8974bc65bbc7efed4fe2c97056ff3ec0e" }, "ia": { "pin": false, @@ -915,7 +915,7 @@ "win64-aarch64-devedition", "win64-devedition" ], - "revision": "ecdfdca74a53222f4937e831829de0ab6e1ee5f8" + "revision": "a7420bb8974bc65bbc7efed4fe2c97056ff3ec0e" }, "id": { "pin": false, @@ -933,7 +933,7 @@ "win64-aarch64-devedition", "win64-devedition" ], - "revision": "ecdfdca74a53222f4937e831829de0ab6e1ee5f8" + "revision": "a7420bb8974bc65bbc7efed4fe2c97056ff3ec0e" }, "is": { "pin": false, @@ -951,7 +951,7 @@ "win64-aarch64-devedition", "win64-devedition" ], - "revision": "ecdfdca74a53222f4937e831829de0ab6e1ee5f8" + "revision": "a7420bb8974bc65bbc7efed4fe2c97056ff3ec0e" }, "it": { "pin": false, @@ -969,7 +969,7 @@ "win64-aarch64-devedition", "win64-devedition" ], - "revision": "ecdfdca74a53222f4937e831829de0ab6e1ee5f8" + "revision": "a7420bb8974bc65bbc7efed4fe2c97056ff3ec0e" }, "ja": { "pin": false, @@ -985,7 +985,7 @@ "win64-aarch64-devedition", "win64-devedition" ], - "revision": "ecdfdca74a53222f4937e831829de0ab6e1ee5f8" + "revision": "a7420bb8974bc65bbc7efed4fe2c97056ff3ec0e" }, "ja-JP-mac": { "pin": false, @@ -993,7 +993,7 @@ "macosx64", "macosx64-devedition" ], - "revision": "ecdfdca74a53222f4937e831829de0ab6e1ee5f8" + "revision": "a7420bb8974bc65bbc7efed4fe2c97056ff3ec0e" }, "ka": { "pin": false, @@ -1011,7 +1011,7 @@ "win64-aarch64-devedition", "win64-devedition" ], - "revision": "ecdfdca74a53222f4937e831829de0ab6e1ee5f8" + "revision": "a7420bb8974bc65bbc7efed4fe2c97056ff3ec0e" }, "kab": { "pin": false, @@ -1029,7 +1029,7 @@ "win64-aarch64-devedition", "win64-devedition" ], - "revision": "ecdfdca74a53222f4937e831829de0ab6e1ee5f8" + "revision": "a7420bb8974bc65bbc7efed4fe2c97056ff3ec0e" }, "kk": { "pin": false, @@ -1047,7 +1047,7 @@ "win64-aarch64-devedition", "win64-devedition" ], - "revision": "ecdfdca74a53222f4937e831829de0ab6e1ee5f8" + "revision": "a7420bb8974bc65bbc7efed4fe2c97056ff3ec0e" }, "km": { "pin": false, @@ -1065,7 +1065,7 @@ "win64-aarch64-devedition", "win64-devedition" ], - "revision": "ecdfdca74a53222f4937e831829de0ab6e1ee5f8" + "revision": "a7420bb8974bc65bbc7efed4fe2c97056ff3ec0e" }, "kn": { "pin": false, @@ -1083,7 +1083,7 @@ "win64-aarch64-devedition", "win64-devedition" ], - "revision": "ecdfdca74a53222f4937e831829de0ab6e1ee5f8" + "revision": "a7420bb8974bc65bbc7efed4fe2c97056ff3ec0e" }, "ko": { "pin": false, @@ -1101,7 +1101,7 @@ "win64-aarch64-devedition", "win64-devedition" ], - "revision": "ecdfdca74a53222f4937e831829de0ab6e1ee5f8" + "revision": "a7420bb8974bc65bbc7efed4fe2c97056ff3ec0e" }, "lij": { "pin": false, @@ -1119,7 +1119,7 @@ "win64-aarch64-devedition", "win64-devedition" ], - "revision": "ecdfdca74a53222f4937e831829de0ab6e1ee5f8" + "revision": "a7420bb8974bc65bbc7efed4fe2c97056ff3ec0e" }, "lo": { "pin": false, @@ -1137,7 +1137,7 @@ "win64-aarch64-devedition", "win64-devedition" ], - "revision": "ecdfdca74a53222f4937e831829de0ab6e1ee5f8" + "revision": "a7420bb8974bc65bbc7efed4fe2c97056ff3ec0e" }, "lt": { "pin": false, @@ -1155,7 +1155,7 @@ "win64-aarch64-devedition", "win64-devedition" ], - "revision": "ecdfdca74a53222f4937e831829de0ab6e1ee5f8" + "revision": "a7420bb8974bc65bbc7efed4fe2c97056ff3ec0e" }, "ltg": { "pin": false, @@ -1173,7 +1173,7 @@ "win64-aarch64-devedition", "win64-devedition" ], - "revision": "ecdfdca74a53222f4937e831829de0ab6e1ee5f8" + "revision": "a7420bb8974bc65bbc7efed4fe2c97056ff3ec0e" }, "lv": { "pin": false, @@ -1191,7 +1191,7 @@ "win64-aarch64-devedition", "win64-devedition" ], - "revision": "ecdfdca74a53222f4937e831829de0ab6e1ee5f8" + "revision": "a7420bb8974bc65bbc7efed4fe2c97056ff3ec0e" }, "meh": { "pin": false, @@ -1209,7 +1209,7 @@ "win64-aarch64-devedition", "win64-devedition" ], - "revision": "ecdfdca74a53222f4937e831829de0ab6e1ee5f8" + "revision": "a7420bb8974bc65bbc7efed4fe2c97056ff3ec0e" }, "mk": { "pin": false, @@ -1227,7 +1227,7 @@ "win64-aarch64-devedition", "win64-devedition" ], - "revision": "ecdfdca74a53222f4937e831829de0ab6e1ee5f8" + "revision": "a7420bb8974bc65bbc7efed4fe2c97056ff3ec0e" }, "ml": { "pin": false, @@ -1245,7 +1245,7 @@ "win64-aarch64-devedition", "win64-devedition" ], - "revision": "ecdfdca74a53222f4937e831829de0ab6e1ee5f8" + "revision": "a7420bb8974bc65bbc7efed4fe2c97056ff3ec0e" }, "mr": { "pin": false, @@ -1263,7 +1263,7 @@ "win64-aarch64-devedition", "win64-devedition" ], - "revision": "ecdfdca74a53222f4937e831829de0ab6e1ee5f8" + "revision": "a7420bb8974bc65bbc7efed4fe2c97056ff3ec0e" }, "ms": { "pin": false, @@ -1281,7 +1281,7 @@ "win64-aarch64-devedition", "win64-devedition" ], - "revision": "ecdfdca74a53222f4937e831829de0ab6e1ee5f8" + "revision": "a7420bb8974bc65bbc7efed4fe2c97056ff3ec0e" }, "my": { "pin": false, @@ -1299,7 +1299,7 @@ "win64-aarch64-devedition", "win64-devedition" ], - "revision": "ecdfdca74a53222f4937e831829de0ab6e1ee5f8" + "revision": "a7420bb8974bc65bbc7efed4fe2c97056ff3ec0e" }, "nb-NO": { "pin": false, @@ -1317,7 +1317,7 @@ "win64-aarch64-devedition", "win64-devedition" ], - "revision": "ecdfdca74a53222f4937e831829de0ab6e1ee5f8" + "revision": "a7420bb8974bc65bbc7efed4fe2c97056ff3ec0e" }, "ne-NP": { "pin": false, @@ -1335,7 +1335,7 @@ "win64-aarch64-devedition", "win64-devedition" ], - "revision": "ecdfdca74a53222f4937e831829de0ab6e1ee5f8" + "revision": "a7420bb8974bc65bbc7efed4fe2c97056ff3ec0e" }, "nl": { "pin": false, @@ -1353,7 +1353,7 @@ "win64-aarch64-devedition", "win64-devedition" ], - "revision": "ecdfdca74a53222f4937e831829de0ab6e1ee5f8" + "revision": "a7420bb8974bc65bbc7efed4fe2c97056ff3ec0e" }, "nn-NO": { "pin": false, @@ -1371,7 +1371,7 @@ "win64-aarch64-devedition", "win64-devedition" ], - "revision": "ecdfdca74a53222f4937e831829de0ab6e1ee5f8" + "revision": "a7420bb8974bc65bbc7efed4fe2c97056ff3ec0e" }, "oc": { "pin": false, @@ -1389,7 +1389,7 @@ "win64-aarch64-devedition", "win64-devedition" ], - "revision": "ecdfdca74a53222f4937e831829de0ab6e1ee5f8" + "revision": "a7420bb8974bc65bbc7efed4fe2c97056ff3ec0e" }, "pa-IN": { "pin": false, @@ -1407,7 +1407,7 @@ "win64-aarch64-devedition", "win64-devedition" ], - "revision": "ecdfdca74a53222f4937e831829de0ab6e1ee5f8" + "revision": "a7420bb8974bc65bbc7efed4fe2c97056ff3ec0e" }, "pl": { "pin": false, @@ -1425,7 +1425,7 @@ "win64-aarch64-devedition", "win64-devedition" ], - "revision": "ecdfdca74a53222f4937e831829de0ab6e1ee5f8" + "revision": "a7420bb8974bc65bbc7efed4fe2c97056ff3ec0e" }, "pt-BR": { "pin": false, @@ -1443,7 +1443,7 @@ "win64-aarch64-devedition", "win64-devedition" ], - "revision": "ecdfdca74a53222f4937e831829de0ab6e1ee5f8" + "revision": "a7420bb8974bc65bbc7efed4fe2c97056ff3ec0e" }, "pt-PT": { "pin": false, @@ -1461,7 +1461,7 @@ "win64-aarch64-devedition", "win64-devedition" ], - "revision": "ecdfdca74a53222f4937e831829de0ab6e1ee5f8" + "revision": "a7420bb8974bc65bbc7efed4fe2c97056ff3ec0e" }, "rm": { "pin": false, @@ -1479,7 +1479,7 @@ "win64-aarch64-devedition", "win64-devedition" ], - "revision": "ecdfdca74a53222f4937e831829de0ab6e1ee5f8" + "revision": "a7420bb8974bc65bbc7efed4fe2c97056ff3ec0e" }, "ro": { "pin": false, @@ -1497,7 +1497,7 @@ "win64-aarch64-devedition", "win64-devedition" ], - "revision": "ecdfdca74a53222f4937e831829de0ab6e1ee5f8" + "revision": "a7420bb8974bc65bbc7efed4fe2c97056ff3ec0e" }, "ru": { "pin": false, @@ -1515,7 +1515,7 @@ "win64-aarch64-devedition", "win64-devedition" ], - "revision": "ecdfdca74a53222f4937e831829de0ab6e1ee5f8" + "revision": "a7420bb8974bc65bbc7efed4fe2c97056ff3ec0e" }, "sat": { "pin": false, @@ -1533,7 +1533,7 @@ "win64-aarch64-devedition", "win64-devedition" ], - "revision": "ecdfdca74a53222f4937e831829de0ab6e1ee5f8" + "revision": "a7420bb8974bc65bbc7efed4fe2c97056ff3ec0e" }, "sc": { "pin": false, @@ -1551,7 +1551,7 @@ "win64-aarch64-devedition", "win64-devedition" ], - "revision": "ecdfdca74a53222f4937e831829de0ab6e1ee5f8" + "revision": "a7420bb8974bc65bbc7efed4fe2c97056ff3ec0e" }, "scn": { "pin": false, @@ -1569,7 +1569,7 @@ "win64-aarch64-devedition", "win64-devedition" ], - "revision": "ecdfdca74a53222f4937e831829de0ab6e1ee5f8" + "revision": "a7420bb8974bc65bbc7efed4fe2c97056ff3ec0e" }, "sco": { "pin": false, @@ -1587,7 +1587,7 @@ "win64-aarch64-devedition", "win64-devedition" ], - "revision": "ecdfdca74a53222f4937e831829de0ab6e1ee5f8" + "revision": "a7420bb8974bc65bbc7efed4fe2c97056ff3ec0e" }, "si": { "pin": false, @@ -1605,7 +1605,7 @@ "win64-aarch64-devedition", "win64-devedition" ], - "revision": "ecdfdca74a53222f4937e831829de0ab6e1ee5f8" + "revision": "a7420bb8974bc65bbc7efed4fe2c97056ff3ec0e" }, "sk": { "pin": false, @@ -1623,7 +1623,7 @@ "win64-aarch64-devedition", "win64-devedition" ], - "revision": "ecdfdca74a53222f4937e831829de0ab6e1ee5f8" + "revision": "a7420bb8974bc65bbc7efed4fe2c97056ff3ec0e" }, "skr": { "pin": false, @@ -1641,7 +1641,7 @@ "win64-aarch64-devedition", "win64-devedition" ], - "revision": "ecdfdca74a53222f4937e831829de0ab6e1ee5f8" + "revision": "a7420bb8974bc65bbc7efed4fe2c97056ff3ec0e" }, "sl": { "pin": false, @@ -1659,7 +1659,7 @@ "win64-aarch64-devedition", "win64-devedition" ], - "revision": "ecdfdca74a53222f4937e831829de0ab6e1ee5f8" + "revision": "a7420bb8974bc65bbc7efed4fe2c97056ff3ec0e" }, "son": { "pin": false, @@ -1677,7 +1677,7 @@ "win64-aarch64-devedition", "win64-devedition" ], - "revision": "ecdfdca74a53222f4937e831829de0ab6e1ee5f8" + "revision": "a7420bb8974bc65bbc7efed4fe2c97056ff3ec0e" }, "sq": { "pin": false, @@ -1695,7 +1695,7 @@ "win64-aarch64-devedition", "win64-devedition" ], - "revision": "ecdfdca74a53222f4937e831829de0ab6e1ee5f8" + "revision": "a7420bb8974bc65bbc7efed4fe2c97056ff3ec0e" }, "sr": { "pin": false, @@ -1713,7 +1713,7 @@ "win64-aarch64-devedition", "win64-devedition" ], - "revision": "ecdfdca74a53222f4937e831829de0ab6e1ee5f8" + "revision": "a7420bb8974bc65bbc7efed4fe2c97056ff3ec0e" }, "sv-SE": { "pin": false, @@ -1731,7 +1731,7 @@ "win64-aarch64-devedition", "win64-devedition" ], - "revision": "ecdfdca74a53222f4937e831829de0ab6e1ee5f8" + "revision": "a7420bb8974bc65bbc7efed4fe2c97056ff3ec0e" }, "szl": { "pin": false, @@ -1749,7 +1749,7 @@ "win64-aarch64-devedition", "win64-devedition" ], - "revision": "ecdfdca74a53222f4937e831829de0ab6e1ee5f8" + "revision": "a7420bb8974bc65bbc7efed4fe2c97056ff3ec0e" }, "ta": { "pin": false, @@ -1767,7 +1767,7 @@ "win64-aarch64-devedition", "win64-devedition" ], - "revision": "ecdfdca74a53222f4937e831829de0ab6e1ee5f8" + "revision": "a7420bb8974bc65bbc7efed4fe2c97056ff3ec0e" }, "te": { "pin": false, @@ -1785,7 +1785,7 @@ "win64-aarch64-devedition", "win64-devedition" ], - "revision": "ecdfdca74a53222f4937e831829de0ab6e1ee5f8" + "revision": "a7420bb8974bc65bbc7efed4fe2c97056ff3ec0e" }, "tg": { "pin": false, @@ -1803,7 +1803,7 @@ "win64-aarch64-devedition", "win64-devedition" ], - "revision": "ecdfdca74a53222f4937e831829de0ab6e1ee5f8" + "revision": "a7420bb8974bc65bbc7efed4fe2c97056ff3ec0e" }, "th": { "pin": false, @@ -1821,7 +1821,7 @@ "win64-aarch64-devedition", "win64-devedition" ], - "revision": "ecdfdca74a53222f4937e831829de0ab6e1ee5f8" + "revision": "a7420bb8974bc65bbc7efed4fe2c97056ff3ec0e" }, "tl": { "pin": false, @@ -1839,7 +1839,7 @@ "win64-aarch64-devedition", "win64-devedition" ], - "revision": "ecdfdca74a53222f4937e831829de0ab6e1ee5f8" + "revision": "a7420bb8974bc65bbc7efed4fe2c97056ff3ec0e" }, "tr": { "pin": false, @@ -1857,7 +1857,7 @@ "win64-aarch64-devedition", "win64-devedition" ], - "revision": "ecdfdca74a53222f4937e831829de0ab6e1ee5f8" + "revision": "a7420bb8974bc65bbc7efed4fe2c97056ff3ec0e" }, "trs": { "pin": false, @@ -1875,7 +1875,7 @@ "win64-aarch64-devedition", "win64-devedition" ], - "revision": "ecdfdca74a53222f4937e831829de0ab6e1ee5f8" + "revision": "a7420bb8974bc65bbc7efed4fe2c97056ff3ec0e" }, "uk": { "pin": false, @@ -1893,7 +1893,7 @@ "win64-aarch64-devedition", "win64-devedition" ], - "revision": "ecdfdca74a53222f4937e831829de0ab6e1ee5f8" + "revision": "a7420bb8974bc65bbc7efed4fe2c97056ff3ec0e" }, "ur": { "pin": false, @@ -1911,7 +1911,7 @@ "win64-aarch64-devedition", "win64-devedition" ], - "revision": "ecdfdca74a53222f4937e831829de0ab6e1ee5f8" + "revision": "a7420bb8974bc65bbc7efed4fe2c97056ff3ec0e" }, "uz": { "pin": false, @@ -1929,7 +1929,7 @@ "win64-aarch64-devedition", "win64-devedition" ], - "revision": "ecdfdca74a53222f4937e831829de0ab6e1ee5f8" + "revision": "a7420bb8974bc65bbc7efed4fe2c97056ff3ec0e" }, "vi": { "pin": false, @@ -1947,7 +1947,7 @@ "win64-aarch64-devedition", "win64-devedition" ], - "revision": "ecdfdca74a53222f4937e831829de0ab6e1ee5f8" + "revision": "a7420bb8974bc65bbc7efed4fe2c97056ff3ec0e" }, "wo": { "pin": false, @@ -1965,7 +1965,7 @@ "win64-aarch64-devedition", "win64-devedition" ], - "revision": "ecdfdca74a53222f4937e831829de0ab6e1ee5f8" + "revision": "a7420bb8974bc65bbc7efed4fe2c97056ff3ec0e" }, "xh": { "pin": false, @@ -1983,7 +1983,7 @@ "win64-aarch64-devedition", "win64-devedition" ], - "revision": "ecdfdca74a53222f4937e831829de0ab6e1ee5f8" + "revision": "a7420bb8974bc65bbc7efed4fe2c97056ff3ec0e" }, "zh-CN": { "pin": false, @@ -2001,7 +2001,7 @@ "win64-aarch64-devedition", "win64-devedition" ], - "revision": "ecdfdca74a53222f4937e831829de0ab6e1ee5f8" + "revision": "a7420bb8974bc65bbc7efed4fe2c97056ff3ec0e" }, "zh-TW": { "pin": false, @@ -2019,6 +2019,6 @@ "win64-aarch64-devedition", "win64-devedition" ], - "revision": "ecdfdca74a53222f4937e831829de0ab6e1ee5f8" + "revision": "a7420bb8974bc65bbc7efed4fe2c97056ff3ec0e" } } \ No newline at end of file diff --git a/browser/modules/BrowserWindowTracker.sys.mjs b/browser/modules/BrowserWindowTracker.sys.mjs index 0f2f66e4bbc27..9aecab66d8f23 100644 --- a/browser/modules/BrowserWindowTracker.sys.mjs +++ b/browser/modules/BrowserWindowTracker.sys.mjs @@ -325,7 +325,6 @@ export const BrowserWindowTracker = { let { openerWindow = undefined, private: isPrivate = false, - aiWindow = false, features = undefined, all = true, args = null, @@ -353,9 +352,6 @@ export const BrowserWindowTracker = { } else { windowFeatures += ",non-private"; } - if (aiWindow) { - windowFeatures += ",ai-window"; - } if (!args) { loadURIString ??= lazy.BrowserHandler.defaultArgs; args = Cc["@mozilla.org/supports-string;1"].createInstance( diff --git a/devtools/client/debugger/test/mochitest/browser_aj.toml b/devtools/client/debugger/test/mochitest/browser_aj.toml index ec6662d6ba999..7974cbc10e391 100644 --- a/devtools/client/debugger/test/mochitest/browser_aj.toml +++ b/devtools/client/debugger/test/mochitest/browser_aj.toml @@ -26,7 +26,8 @@ prefs = [ ["browser_dbg-backgroundtask-debugging.js"] skip-if = [ - "os == 'linux' && os_version == '24.04' && arch == 'x86_64' && display == 'x11' && asan", # Bug 1937229 + "os == 'linux' && os_version == '24.04' && arch == 'x86_64' && display == 'x11' && asan", # Bug 1767707 + "os == 'linux' && os_version == '24.04' && arch == 'x86_64' && display == 'x11' && debug", # Bug 1767707 "os == 'mac' && os_version == '14.70' && arch == 'x86_64'", # Bug 1937229 ] diff --git a/docshell/base/nsDocShell.cpp b/docshell/base/nsDocShell.cpp index d9ca09eed5ec0..db49c39ec7144 100644 --- a/docshell/base/nsDocShell.cpp +++ b/docshell/base/nsDocShell.cpp @@ -10692,7 +10692,8 @@ bool nsDocShell::ShouldDoInitialAboutBlankSyncLoad( return false; } - if (mHasStartedLoadingOtherThanInitialBlankURI || + if (mHasStartedLoadingOtherThanInitialBlankURI || !mDocumentViewer || + !mDocumentViewer->GetDocument() || !mDocumentViewer->GetDocument()->IsUncommittedInitialDocument()) { return false; } diff --git a/dom/base/nsContentUtils.cpp b/dom/base/nsContentUtils.cpp index be53d54487d58..35f77abd53679 100644 --- a/dom/base/nsContentUtils.cpp +++ b/dom/base/nsContentUtils.cpp @@ -12097,11 +12097,12 @@ void nsContentUtils::ExtractErrorValues( // Try to process as an Error object. Use the file/line/column values // from the Error as they will be more specific to the root cause of // the problem. - if (JSErrorReport* err = JS_ErrorFromException(aCx, obj)) { + JS::BorrowedErrorReport err(aCx); + if (JS_ErrorFromException(aCx, obj, err)) { // Use xpc to extract the error message only. We don't actually send // this report anywhere. RefPtr report = new xpc::ErrorReport(); - report->Init(err, + report->Init(err.get(), nullptr, // toString result false, // chrome 0); // window ID diff --git a/dom/canvas/CanvasRenderingContext2D.cpp b/dom/canvas/CanvasRenderingContext2D.cpp index 7aaa3a6f82a99..4405898cad8e1 100644 --- a/dom/canvas/CanvasRenderingContext2D.cpp +++ b/dom/canvas/CanvasRenderingContext2D.cpp @@ -5238,7 +5238,7 @@ UniquePtr CanvasRenderingContext2D::DrawOrMeasureText( baselineAnchor = font->GetBaselines(fontOrientation).mAlphabetic; break; case CanvasTextBaseline::Ideographic: - baselineAnchor = font->GetBaselines(fontOrientation).mIdeographic; + baselineAnchor = font->GetBaselines(fontOrientation).mIdeographicUnder; break; case CanvasTextBaseline::Bottom: baselineAnchor = -fontMetrics.emDescent; @@ -5281,7 +5281,7 @@ UniquePtr CanvasRenderingContext2D::DrawOrMeasureText( fontMetrics.emDescent + baselineAnchor, // emHeightDescent baselines.mHanging - baselineAnchor, baselines.mAlphabetic - baselineAnchor, - baselines.mIdeographic - baselineAnchor); + baselines.mIdeographicUnder - baselineAnchor); } // If we did not actually calculate bounds, set up a simple bounding box diff --git a/dom/chrome-webidl/InspectorUtils.webidl b/dom/chrome-webidl/InspectorUtils.webidl index fbf015959bdfa..3d7df4e3b0ad2 100644 --- a/dom/chrome-webidl/InspectorUtils.webidl +++ b/dom/chrome-webidl/InspectorUtils.webidl @@ -147,6 +147,21 @@ namespace InspectorUtils { // Returns a set of GRID_* flags based on whether the element is a grid // container or not. unsigned short getGridContainerType(Element aElement); + + // Given a DOM element, return the anchor named `anchorName`, or the default + // anchor otherwise. + InspectorAnchorElement? getAnchorFor(Element element, optional DOMString? anchorName = null); +}; + +enum InspectorAnchorType { + "explicit", + "popover", + "pseudo-element", +}; + +dictionary InspectorAnchorElement { + required Element element; + required InspectorAnchorType type; }; enum DeclarationOrigin { diff --git a/dom/file/FileReader.cpp b/dom/file/FileReader.cpp index bbe07d065b823..f2e962ca4ba68 100644 --- a/dom/file/FileReader.cpp +++ b/dom/file/FileReader.cpp @@ -224,8 +224,8 @@ void FileReader::OnLoadEndArrayBuffer() { JS_ClearPendingException(jsapi.cx()); JS::Rooted exceptionObject(cx, &exceptionValue.toObject()); - JSErrorReport* er = JS_ErrorFromException(cx, exceptionObject); - if (!er || er->message()) { + JS::BorrowedErrorReport er(cx); + if (!JS_ErrorFromException(cx, exceptionObject, er) || er->message()) { FreeDataAndDispatchError(NS_ERROR_OUT_OF_MEMORY); return; } diff --git a/dom/html/HTMLSelectElement.cpp b/dom/html/HTMLSelectElement.cpp index b8399338bf371..e78090b7f08fd 100644 --- a/dom/html/HTMLSelectElement.cpp +++ b/dom/html/HTMLSelectElement.cpp @@ -27,7 +27,6 @@ #include "nsError.h" #include "nsGkAtoms.h" #include "nsIFrame.h" -#include "nsISelectControlFrame.h" #include "nsLayoutUtils.h" #include "nsListControlFrame.h" #include "nsTextNode.h" @@ -349,21 +348,21 @@ void HTMLSelectElement::InsertOptionsIntoList(nsIContent* aOptions, // Get the frame stuff for notification. No need to flush here // since if there's no frame for the select yet the select will // get into the right state once it's created. - nsISelectControlFrame* selectFrame = nullptr; + nsListControlFrame* listBoxFrame = nullptr; AutoWeakFrame weakSelectFrame; bool didGetFrame = false; // Actually select the options if the added options warrant it for (int32_t i = aListIndex; i < insertIndex; i++) { // Notify the frame that the option is added - if (!didGetFrame || (selectFrame && !weakSelectFrame.IsAlive())) { - selectFrame = GetSelectFrame(); - weakSelectFrame = do_QueryFrame(selectFrame); + if (!didGetFrame || (listBoxFrame && !weakSelectFrame.IsAlive())) { + listBoxFrame = GetListBoxFrame(); + weakSelectFrame = do_QueryFrame(listBoxFrame); didGetFrame = true; } - if (selectFrame) { - selectFrame->AddOption(i); + if (listBoxFrame) { + listBoxFrame->AddOption(i); } RefPtr option = Item(i); @@ -379,7 +378,7 @@ void HTMLSelectElement::InsertOptionsIntoList(nsIContent* aOptions, // This is sort of a hack ... we need to notify that the option was // set and change selectedIndex even though we didn't really change // its value. - OnOptionSelected(selectFrame, i, true, false, aNotify); + OnOptionSelected(listBoxFrame, i, true, false, aNotify); } } @@ -427,10 +426,10 @@ nsresult HTMLSelectElement::RemoveOptionsFromList(nsIContent* aOptions, if (numRemoved) { // Tell the widget we removed the options - if (nsISelectControlFrame* selectFrame = GetSelectFrame()) { + if (nsListControlFrame* listBoxFrame = GetListBoxFrame()) { nsAutoScriptBlocker scriptBlocker; for (int32_t i = aListIndex; i < aListIndex + numRemoved; ++i) { - selectFrame->RemoveOption(i); + listBoxFrame->RemoveOption(i); } } @@ -607,7 +606,7 @@ int32_t HTMLSelectElement::GetFirstChildOptionIndex(nsIContent* aOptions, return retval; } -nsISelectControlFrame* HTMLSelectElement::GetSelectFrame() { +nsListControlFrame* HTMLSelectElement::GetListBoxFrame() { return do_QueryFrame(GetPrimaryFrame()); } @@ -748,8 +747,8 @@ void HTMLSelectElement::SetSelectedIndexInternal(int32_t aIndex, bool aNotify) { SetOptionsSelectedByIndex(aIndex, aIndex, mask); - if (nsISelectControlFrame* selectFrame = GetSelectFrame()) { - selectFrame->OnSetSelectedIndex(oldSelectedIndex, mSelectedIndex); + if (nsListControlFrame* listBoxFrame = GetListBoxFrame()) { + listBoxFrame->OnSetSelectedIndex(oldSelectedIndex, mSelectedIndex); } OnSelectionChanged(); } @@ -759,7 +758,7 @@ bool HTMLSelectElement::IsOptionSelectedByIndex(int32_t aIndex) const { return option && option->Selected(); } -void HTMLSelectElement::OnOptionSelected(nsISelectControlFrame* aSelectFrame, +void HTMLSelectElement::OnOptionSelected(nsListControlFrame* aSelectFrame, int32_t aIndex, bool aSelected, bool aChangeOptionState, bool aNotify) { @@ -858,7 +857,7 @@ bool HTMLSelectElement::SetOptionsSelectedByIndex(int32_t aStartIndex, bool optionsSelected = false; bool optionsDeselected = false; - nsISelectControlFrame* selectFrame = nullptr; + nsListControlFrame* listBoxFrame = nullptr; bool didGetFrame = false; AutoWeakFrame weakSelectFrame; @@ -915,11 +914,11 @@ bool HTMLSelectElement::SetOptionsSelectedByIndex(int32_t aStartIndex, // to flush here, if there's no frame yet we don't need to // force it to be created just to notify it about a change // in the select. - selectFrame = GetSelectFrame(); - weakSelectFrame = do_QueryFrame(selectFrame); + listBoxFrame = GetListBoxFrame(); + weakSelectFrame = do_QueryFrame(listBoxFrame); didGetFrame = true; - OnOptionSelected(selectFrame, optIndex, true, !option->Selected(), + OnOptionSelected(listBoxFrame, optIndex, true, !option->Selected(), aOptionsMask.contains(OptionFlag::Notify)); optionsSelected = true; } @@ -939,17 +938,17 @@ bool HTMLSelectElement::SetOptionsSelectedByIndex(int32_t aStartIndex, HTMLOptionElement* option = Item(optIndex); // If the index is already deselected, ignore it. if (option && option->Selected()) { - if (!didGetFrame || (selectFrame && !weakSelectFrame.IsAlive())) { + if (!didGetFrame || (listBoxFrame && !weakSelectFrame.IsAlive())) { // To notify the frame if anything gets changed, don't // flush, if the frame doesn't exist we don't need to // create it just to tell it about this change. - selectFrame = GetSelectFrame(); - weakSelectFrame = do_QueryFrame(selectFrame); + listBoxFrame = GetListBoxFrame(); + weakSelectFrame = do_QueryFrame(listBoxFrame); didGetFrame = true; } - OnOptionSelected(selectFrame, optIndex, false, true, + OnOptionSelected(listBoxFrame, optIndex, false, true, aOptionsMask.contains(OptionFlag::Notify)); optionsDeselected = true; @@ -973,17 +972,17 @@ bool HTMLSelectElement::SetOptionsSelectedByIndex(int32_t aStartIndex, // If the index is already selected, ignore it. if (option && option->Selected()) { - if (!didGetFrame || (selectFrame && !weakSelectFrame.IsAlive())) { + if (!didGetFrame || (listBoxFrame && !weakSelectFrame.IsAlive())) { // To notify the frame if anything gets changed, don't // flush, if the frame doesn't exist we don't need to // create it just to tell it about this change. - selectFrame = GetSelectFrame(); - weakSelectFrame = do_QueryFrame(selectFrame); + listBoxFrame = GetListBoxFrame(); + weakSelectFrame = do_QueryFrame(listBoxFrame); didGetFrame = true; } - OnOptionSelected(selectFrame, optIndex, false, true, + OnOptionSelected(listBoxFrame, optIndex, false, true, aOptionsMask.contains(OptionFlag::Notify)); optionsDeselected = true; } @@ -1234,8 +1233,8 @@ void HTMLSelectElement::DoneAddingChildren(bool aHaveNotified) { } // Notify the frame - if (nsISelectControlFrame* selectFrame = GetSelectFrame()) { - selectFrame->DoneAddingChildren(); + if (auto* listBoxFrame = GetListBoxFrame()) { + listBoxFrame->DoneAddingChildren(); } if (!mInhibitStateRestoration) { @@ -1520,7 +1519,7 @@ HTMLSelectElement::SubmitNamesValues(FormData* aFormData) { } void HTMLSelectElement::DispatchContentReset() { - if (nsListControlFrame* listFrame = do_QueryFrame(GetPrimaryFrame())) { + if (nsListControlFrame* listFrame = GetListBoxFrame()) { listFrame->OnContentReset(); } } diff --git a/dom/html/HTMLSelectElement.h b/dom/html/HTMLSelectElement.h index 70f5a1b86dc6b..e8d1bafe06da4 100644 --- a/dom/html/HTMLSelectElement.h +++ b/dom/html/HTMLSelectElement.h @@ -22,7 +22,7 @@ class nsContentList; class nsIDOMHTMLOptionElement; class nsIHTMLCollection; -class nsISelectControlFrame; +class nsListControlFrame; namespace mozilla { @@ -396,7 +396,7 @@ class HTMLSelectElement final : public nsGenericHTMLFormControlElementWithState, * its selected state to aSelected. * @param aNotify whether to notify the style system and such */ - void OnOptionSelected(nsISelectControlFrame* aSelectFrame, int32_t aIndex, + void OnOptionSelected(nsListControlFrame* aSelectFrame, int32_t aIndex, bool aSelected, bool aChangeOptionState, bool aNotify); /** * Restore state to a particular state string (representing the options) @@ -458,11 +458,8 @@ class HTMLSelectElement final : public nsGenericHTMLFormControlElementWithState, int32_t GetFirstChildOptionIndex(nsIContent* aOptions, int32_t aStartIndex, int32_t aEndIndex); - /** - * Get the frame as an nsISelectControlFrame (MAY RETURN nullptr) - * @return the select frame, or null - */ - nsISelectControlFrame* GetSelectFrame(); + /** Get the frame as an nsListControlFrame (MAY RETURN nullptr) */ + nsListControlFrame* GetListBoxFrame(); /** * Helper method for dispatching ContentReset notifications to list box diff --git a/dom/ipc/ClonedErrorHolder.cpp b/dom/ipc/ClonedErrorHolder.cpp index 72c8bfbaf7f2f..54b37302e73f6 100644 --- a/dom/ipc/ClonedErrorHolder.cpp +++ b/dom/ipc/ClonedErrorHolder.cpp @@ -50,7 +50,8 @@ void ClonedErrorHolder::Init(JSContext* aCx, JS::Handle aError, ErrorResult& aRv) { JS::Rooted stack(aCx); - if (JSErrorReport* err = JS_ErrorFromException(aCx, aError)) { + JS::BorrowedErrorReport err(aCx); + if (JS_ErrorFromException(aCx, aError, err)) { mType = Type::JSError; if (err->message()) { mMessage = err->message().c_str(); @@ -309,7 +310,8 @@ bool ClonedErrorHolder::ToErrorValue(JSContext* aCx, if (!mSourceLine.IsVoid()) { JS::Rooted errObj(aCx, &aResult.toObject()); - if (JSErrorReport* err = JS_ErrorFromException(aCx, errObj)) { + JS::BorrowedErrorReport err(aCx); + if (JS_ErrorFromException(aCx, errObj, err)) { NS_ConvertUTF8toUTF16 sourceLine(mSourceLine); // Because this string ends up being consumed as an nsDependentString // in nsXPCComponents_Utils::ReportError, this needs to be a null @@ -320,8 +322,8 @@ bool ClonedErrorHolder::ToErrorValue(JSContext* aCx, // Corrupt data, leave linebuf unset. } else if (JS::UniqueTwoByteChars buffer = ToNullTerminatedJSStringBuffer(aCx, sourceLine)) { - err->initOwnedLinebuf(buffer.release(), sourceLine.Length(), - mTokenOffset); + err.get()->initOwnedLinebuf(buffer.release(), sourceLine.Length(), + mTokenOffset); } else { // Just ignore OOM and continue if the string copy failed. JS_ClearPendingException(aCx); diff --git a/dom/media/webrtc/transport/ipc/WebrtcTCPSocket.cpp b/dom/media/webrtc/transport/ipc/WebrtcTCPSocket.cpp index 4f31f98e1ee4a..8927f6ea21d21 100644 --- a/dom/media/webrtc/transport/ipc/WebrtcTCPSocket.cpp +++ b/dom/media/webrtc/transport/ipc/WebrtcTCPSocket.cpp @@ -20,6 +20,7 @@ #include "nsIIOService.h" #include "nsILoadInfo.h" #include "nsIProtocolProxyService.h" +#include "nsISocketTransport.h" #include "nsISocketTransportService.h" #include "nsIURIMutator.h" #include "nsProxyRelease.h" diff --git a/gfx/gl/ScopedGLHelpers.cpp b/gfx/gl/ScopedGLHelpers.cpp index be20cdee5a0d8..558384535b1f4 100644 --- a/gfx/gl/ScopedGLHelpers.cpp +++ b/gfx/gl/ScopedGLHelpers.cpp @@ -4,8 +4,13 @@ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ #include "GLContext.h" +#include "GLContextEGL.h" #include "ScopedGLHelpers.h" +#ifdef MOZ_WIDGET_ANDROID +# include "mozilla/layers/AndroidHardwareBuffer.h" +#endif + namespace mozilla { namespace gl { @@ -111,6 +116,30 @@ ScopedRenderbuffer::~ScopedRenderbuffer() { mGL->fDeleteRenderbuffers(1, &mRB); } +/* ScopedEGLImageForAndroidHardwareBuffer + * **************************************************************/ + +ScopedEGLImageForAndroidHardwareBuffer::ScopedEGLImageForAndroidHardwareBuffer( + GLContextEGL* aGL, layers::AndroidHardwareBuffer* aHardwareBuffer) + : mGL(aGL), mImage(EGL_NO_IMAGE) { +#ifdef MOZ_WIDGET_ANDROID + const EGLClientBuffer clientBuffer = + mGL->mEgl->mLib->fGetNativeClientBufferANDROID( + aHardwareBuffer->GetNativeBuffer()); + if (clientBuffer) { + mImage = mGL->mEgl->fCreateImage( + EGL_NO_CONTEXT, LOCAL_EGL_NATIVE_BUFFER_ANDROID, clientBuffer, nullptr); + } +#endif +} + +ScopedEGLImageForAndroidHardwareBuffer:: + ~ScopedEGLImageForAndroidHardwareBuffer() { + if (mImage) { + mGL->mEgl->fDestroyImage(mImage); + } +} + /* ScopedBindTexture **********************************************************/ static GLuint GetBoundTexture(GLContext* gl, GLenum texTarget) { diff --git a/gfx/gl/ScopedGLHelpers.h b/gfx/gl/ScopedGLHelpers.h index 0e73b71136a53..dbeda0b586e87 100644 --- a/gfx/gl/ScopedGLHelpers.h +++ b/gfx/gl/ScopedGLHelpers.h @@ -9,9 +9,14 @@ #include "GLDefs.h" namespace mozilla { +namespace layers { +class AndroidHardwareBuffer; +} + namespace gl { class GLContext; +class GLContextEGL; #ifdef DEBUG bool IsContextCurrent(GLContext* gl); @@ -97,6 +102,20 @@ struct ScopedRenderbuffer final { operator GLuint() const { return mRB; } }; +struct ScopedEGLImageForAndroidHardwareBuffer final { + private: + GLContextEGL* const mGL; + EGLImage mImage; + + public: + explicit ScopedEGLImageForAndroidHardwareBuffer( + GLContextEGL* aGL, layers::AndroidHardwareBuffer* aHardwareBuffer); + ~ScopedEGLImageForAndroidHardwareBuffer(); + + EGLImage Image() const { return mImage; } + operator EGLImage() const { return mImage; } +}; + struct ScopedBindTexture final { private: GLContext* const mGL; diff --git a/gfx/layers/ipc/PUiCompositorController.ipdl b/gfx/layers/ipc/PUiCompositorController.ipdl index b4c9d9370ea9c..2e67a5a176371 100644 --- a/gfx/layers/ipc/PUiCompositorController.ipdl +++ b/gfx/layers/ipc/PUiCompositorController.ipdl @@ -5,10 +5,8 @@ * 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/. */ -using mozilla::CSSRect from "Units.h"; -using mozilla::CSSToScreenScale from "Units.h"; -using mozilla::ScreenIntSize from "Units.h"; -using mozilla::ScreenPoint from "Units.h"; +using mozilla::gfx::IntRect from "mozilla/gfx/Rect.h"; +using mozilla::gfx::IntSize from "mozilla/gfx/Point.h"; using mozilla::layers::CompositorScrollUpdate from "mozilla/layers/CompositorScrollUpdate.h"; include "mozilla/GfxMessageUtils.h"; @@ -38,12 +36,12 @@ parent: async MaxToolbarHeight(int32_t aHeight); async FixedBottomOffset(int32_t aOffset); async DefaultClearColor(uint32_t aColor); - async RequestScreenPixels(); + async RequestScreenPixels(IntRect aSourceRect, IntSize aDestSize); async EnableLayerUpdateNotifications(bool aEnable); child: async ToolbarAnimatorMessageFromCompositor(int32_t aMessage); async NotifyCompositorScrollUpdate(CompositorScrollUpdate aUpdate); - async ScreenPixels(Shmem aMem, ScreenIntSize aSize, bool aNeedsYFlip); + async ScreenPixels(FileDescriptor? aHardwareBuffer, FileDescriptor? aAcquireFence); }; } // layers diff --git a/gfx/layers/ipc/UiCompositorControllerChild.cpp b/gfx/layers/ipc/UiCompositorControllerChild.cpp index 27793d9d50acd..0225f6a1b7c7d 100644 --- a/gfx/layers/ipc/UiCompositorControllerChild.cpp +++ b/gfx/layers/ipc/UiCompositorControllerChild.cpp @@ -20,6 +20,7 @@ #include "nsThreadUtils.h" #if defined(MOZ_WIDGET_ANDROID) +# include "mozilla/layers/AndroidHardwareBuffer.h" # include "mozilla/widget/AndroidUiThread.h" static RefPtr GetUiThread() { return mozilla::GetAndroidUiThread(); } @@ -138,12 +139,13 @@ bool UiCompositorControllerChild::SetDefaultClearColor(const uint32_t& aColor) { return SendDefaultClearColor(aColor); } -bool UiCompositorControllerChild::RequestScreenPixels() { +bool UiCompositorControllerChild::RequestScreenPixels(gfx::IntRect aSourceRect, + gfx::IntSize aDestSize) { if (!mIsOpen) { return false; } - return SendRequestScreenPixels(); + return SendRequestScreenPixels(aSourceRect, aDestSize); } bool UiCompositorControllerChild::EnableLayerUpdateNotifications( @@ -188,10 +190,6 @@ void UiCompositorControllerChild::Destroy() { task.Wait(); } -bool UiCompositorControllerChild::DeallocPixelBuffer(Shmem& aMem) { - return DeallocShmem(aMem); -} - // protected: void UiCompositorControllerChild::ActorDestroy(ActorDestroyReason aWhy) { mIsOpen = false; @@ -238,10 +236,20 @@ UiCompositorControllerChild::RecvNotifyCompositorScrollUpdate( } mozilla::ipc::IPCResult UiCompositorControllerChild::RecvScreenPixels( - ipc::Shmem&& aMem, const ScreenIntSize& aSize, bool aNeedsYFlip) { + Maybe&& aHardwareBuffer, + Maybe&& aAcquireFence) { #if defined(MOZ_WIDGET_ANDROID) + RefPtr hardwareBuffer; + if (aHardwareBuffer) { + hardwareBuffer = + layers::AndroidHardwareBuffer::DeserializeFromFileDescriptor( + aHardwareBuffer->TakePlatformHandle()); + } + if (hardwareBuffer && aAcquireFence) { + hardwareBuffer->SetAcquireFence(aAcquireFence->TakePlatformHandle()); + } if (mWidget) { - mWidget->RecvScreenPixels(std::move(aMem), aSize, aNeedsYFlip); + mWidget->RecvScreenPixels(hardwareBuffer); } #endif // defined(MOZ_WIDGET_ANDROID) diff --git a/gfx/layers/ipc/UiCompositorControllerChild.h b/gfx/layers/ipc/UiCompositorControllerChild.h index c8df8e2a6647b..d40f89f6b2bbf 100644 --- a/gfx/layers/ipc/UiCompositorControllerChild.h +++ b/gfx/layers/ipc/UiCompositorControllerChild.h @@ -10,7 +10,6 @@ #include "mozilla/gfx/2D.h" #include "mozilla/Maybe.h" -#include "mozilla/ipc/Shmem.h" #include "mozilla/layers/UiCompositorControllerParent.h" #include "mozilla/RefPtr.h" #include "nsThread.h" @@ -46,13 +45,11 @@ class UiCompositorControllerChild final bool SetFixedBottomOffset(int32_t aOffset); bool ToolbarAnimatorMessageFromUI(const int32_t& aMessage); bool SetDefaultClearColor(const uint32_t& aColor); - bool RequestScreenPixels(); + bool RequestScreenPixels(gfx::IntRect aSourceRect, gfx::IntSize aDestSize); bool EnableLayerUpdateNotifications(const bool& aEnable); void Destroy(); - bool DeallocPixelBuffer(Shmem& aMem); - #ifdef MOZ_WIDGET_ANDROID // Set mCompositorSurfaceManager. Must be called straight after initialization // for GPU process controllers. Do not call for in-process controllers. This @@ -81,9 +78,9 @@ class UiCompositorControllerChild final const int32_t& aMessage); mozilla::ipc::IPCResult RecvNotifyCompositorScrollUpdate( const CompositorScrollUpdate& aUpdate); - mozilla::ipc::IPCResult RecvScreenPixels(Shmem&& aMem, - const ScreenIntSize& aSize, - bool aNeedsYFlip); + mozilla::ipc::IPCResult RecvScreenPixels( + Maybe&& aHardwareBuffer, + Maybe&& aAcquireFence); private: explicit UiCompositorControllerChild(const uint64_t& aProcessToken, diff --git a/gfx/layers/ipc/UiCompositorControllerParent.cpp b/gfx/layers/ipc/UiCompositorControllerParent.cpp index 97729b216b55c..006c209add5be 100644 --- a/gfx/layers/ipc/UiCompositorControllerParent.cpp +++ b/gfx/layers/ipc/UiCompositorControllerParent.cpp @@ -7,6 +7,7 @@ #if defined(MOZ_WIDGET_ANDROID) # include "apz/src/APZCTreeManager.h" +# include "mozilla/layers/AndroidHardwareBuffer.h" # include "mozilla/widget/AndroidCompositorWidget.h" #endif #include @@ -139,14 +140,33 @@ mozilla::ipc::IPCResult UiCompositorControllerParent::RecvDefaultClearColor( return IPC_OK(); } -mozilla::ipc::IPCResult -UiCompositorControllerParent::RecvRequestScreenPixels() { +mozilla::ipc::IPCResult UiCompositorControllerParent::RecvRequestScreenPixels( + gfx::IntRect aSourceRect, gfx::IntSize aDestSize) { #if defined(MOZ_WIDGET_ANDROID) LayerTreeState* state = CompositorBridgeParent::GetIndirectShadowTree(mRootLayerTreeId); if (state && state->mWrBridge) { - state->mWrBridge->RequestScreenPixels(this); + state->mWrBridge->RequestScreenPixels(aSourceRect, aDestSize) + ->Then( + GetCurrentSerialEventTarget(), __func__, + [target = + RefPtr{this}](RefPtr aHardwareBuffer) { + UniqueFileHandle bufferFd = + aHardwareBuffer->SerializeToFileDescriptor(); + UniqueFileHandle fenceFd = + aHardwareBuffer->GetAndResetAcquireFence(); + (void)target->SendScreenPixels( + aHardwareBuffer + ? Some(ipc::FileDescriptor(std::move(bufferFd))) + : Nothing(), + fenceFd ? Some(ipc::FileDescriptor(std::move(fenceFd))) + : Nothing()); + }, + [target = RefPtr{this}](nsresult aError) { + (void)target->SendScreenPixels(Nothing(), Nothing()); + }); + ; state->mWrBridge->ScheduleForcedGenerateFrame(wr::RenderReasons::OTHER); } #endif // defined(MOZ_WIDGET_ANDROID) @@ -186,12 +206,6 @@ void UiCompositorControllerParent::ToolbarAnimatorMessageFromCompositor( (void)SendToolbarAnimatorMessageFromCompositor(aMessage); } -bool UiCompositorControllerParent::AllocPixelBuffer(const int32_t aSize, - ipc::Shmem* aMem) { - MOZ_ASSERT(aSize > 0); - return AllocShmem(aSize, aMem); -} - void UiCompositorControllerParent::NotifyLayersUpdated() { #ifdef MOZ_WIDGET_ANDROID if (mCompositorLayersUpdateEnabled) { diff --git a/gfx/layers/ipc/UiCompositorControllerParent.h b/gfx/layers/ipc/UiCompositorControllerParent.h index 88ce66dc81247..98b7f6dbeffc1 100644 --- a/gfx/layers/ipc/UiCompositorControllerParent.h +++ b/gfx/layers/ipc/UiCompositorControllerParent.h @@ -9,7 +9,6 @@ #include "mozilla/layers/PUiCompositorControllerParent.h" #include "mozilla/layers/CompositorScrollUpdate.h" #include "mozilla/layers/LayersTypes.h" -#include "mozilla/ipc/Shmem.h" #include "mozilla/RefPtr.h" namespace mozilla { @@ -44,14 +43,14 @@ class UiCompositorControllerParent final mozilla::ipc::IPCResult RecvMaxToolbarHeight(const int32_t& aHeight); mozilla::ipc::IPCResult RecvFixedBottomOffset(const int32_t& aOffset); mozilla::ipc::IPCResult RecvDefaultClearColor(const uint32_t& aColor); - mozilla::ipc::IPCResult RecvRequestScreenPixels(); + mozilla::ipc::IPCResult RecvRequestScreenPixels(gfx::IntRect aSourceRect, + gfx::IntSize aDestSize); mozilla::ipc::IPCResult RecvEnableLayerUpdateNotifications( const bool& aEnable); void ActorDestroy(ActorDestroyReason aWhy) override; // Class specific functions void ToolbarAnimatorMessageFromCompositor(int32_t aMessage); - bool AllocPixelBuffer(const int32_t aSize, Shmem* aMem); // Called when a layer has been updated so the UI thread may be notified if // necessary. diff --git a/gfx/layers/wr/WebRenderBridgeParent.cpp b/gfx/layers/wr/WebRenderBridgeParent.cpp index 181196f7d5cd3..d7ea4c3a7a265 100644 --- a/gfx/layers/wr/WebRenderBridgeParent.cpp +++ b/gfx/layers/wr/WebRenderBridgeParent.cpp @@ -57,6 +57,9 @@ #if defined(MOZ_WIDGET_GTK) # include "mozilla/widget/GtkCompositorWidget.h" #endif +#ifdef MOZ_WIDGET_ANDROID +# include "mozilla/layers/AndroidHardwareBuffer.h" +#endif bool is_in_main_thread() { return NS_IsMainThread(); } @@ -1824,54 +1827,38 @@ void WebRenderBridgeParent::UpdateBoolParameters() { } #if defined(MOZ_WIDGET_ANDROID) -void WebRenderBridgeParent::RequestScreenPixels( - UiCompositorControllerParent* aController) { - mScreenPixelsTarget = aController; +RefPtr +WebRenderBridgeParent::RequestScreenPixels(gfx::IntRect aSourceRect, + gfx::IntSize aDestSize) { + if (!IsRootWebRenderBridgeParent()) { + return ScreenPixelsPromise::CreateAndReject(NS_ERROR_ILLEGAL_VALUE, + __func__); + } + + mScreenPixelsRequest.emplace(ScreenPixelsRequest{ + .mSourceRect = aSourceRect, + .mDestSize = aDestSize, + .mPromise = new ScreenPixelsPromise::Private(__func__), + }); + return mScreenPixelsRequest->mPromise; } void WebRenderBridgeParent::MaybeCaptureScreenPixels() { - if (!mScreenPixelsTarget) { - return; - } + // This function should only get called in the root WRBP. + MOZ_ASSERT(IsRootWebRenderBridgeParent()); - if (mDestroyed) { + if (!mScreenPixelsRequest) { return; } + auto request = mScreenPixelsRequest.extract(); - if (auto* cbp = GetRootCompositorBridgeParent()) { - cbp->FlushPendingWrTransactionEventsWithWait(); - } - - // This function should only get called in the root WRBP. - MOZ_ASSERT(IsRootWebRenderBridgeParent()); # ifdef DEBUG CompositorBridgeParent* cbp = GetRootCompositorBridgeParent(); MOZ_ASSERT(cbp && !cbp->IsPaused()); # endif - SurfaceFormat format = SurfaceFormat::R8G8B8A8; // On android we use RGBA8 - auto client_size = mWidget->GetClientSize(); - size_t buffer_size = - client_size.width * client_size.height * BytesPerPixel(format); - - ipc::Shmem mem; - if (!mScreenPixelsTarget->AllocPixelBuffer(buffer_size, &mem)) { - // Failed to alloc shmem, Just bail out. - return; - } - - IntSize size(client_size.width, client_size.height); - - bool needsYFlip = false; - mLateInit->mApi->Readback(TimeStamp::Now(), size, format, - Range(mem.get(), buffer_size), - &needsYFlip); - - (void)mScreenPixelsTarget->SendScreenPixels( - std::move(mem), ScreenIntSize(client_size.width, client_size.height), - needsYFlip); - - mScreenPixelsTarget = nullptr; + mLateInit->mApi->RequestScreenPixels(request.mSourceRect, request.mDestSize) + ->ChainTo(request.mPromise.forget(), __func__); } #endif @@ -2556,6 +2543,12 @@ void WebRenderBridgeParent::MaybeGenerateFrame(VsyncId aId, } } +#if defined(MOZ_WIDGET_ANDROID) + // Ensure the rendered pixels get captured for the frame we are about to + // generate. Must be called before we generate the frame. + MaybeCaptureScreenPixels(); +#endif + SetOMTASampleTime(); SetAPZSampleTime(); @@ -2574,10 +2567,6 @@ void WebRenderBridgeParent::MaybeGenerateFrame(VsyncId aId, mLateInit->mApi->SendTransaction(fastTxn); -#if defined(MOZ_WIDGET_ANDROID) - MaybeCaptureScreenPixels(); -#endif - mMostRecentComposite = TimeStamp::Now(); // During disabling native compositor, webrender needs to render twice. diff --git a/gfx/layers/wr/WebRenderBridgeParent.h b/gfx/layers/wr/WebRenderBridgeParent.h index e52d5426d1d4d..81fb83921bf15 100644 --- a/gfx/layers/wr/WebRenderBridgeParent.h +++ b/gfx/layers/wr/WebRenderBridgeParent.h @@ -309,12 +309,15 @@ class WebRenderBridgeParent final : public PWebRenderBridgeParent, void BeginRecording(const TimeStamp& aRecordingStart); #if defined(MOZ_WIDGET_ANDROID) + using ScreenPixelsPromise = + MozPromise, nsresult, true>; /** * Request a screengrab for android */ - void RequestScreenPixels(UiCompositorControllerParent* aController); - void MaybeCaptureScreenPixels(); + RefPtr RequestScreenPixels(gfx::IntRect aSourceRect, + gfx::IntSize aDestSize); #endif + /** * Stop recording and the frames collected since the call to BeginRecording */ @@ -428,6 +431,13 @@ class WebRenderBridgeParent final : public PWebRenderBridgeParent, void MaybeGenerateFrame(VsyncId aId, bool aForceGenerateFrame, wr::RenderReasons aReasons); +#if defined(MOZ_WIDGET_ANDROID) + // If screen pixels have been requested via RequestScreenPixels(), sends the + // request to the renderer. Should be called immediately prior to generating + // the frame for which we want the pixels to be captured. + void MaybeCaptureScreenPixels(); +#endif + VsyncId GetVsyncIdForEpoch(const wr::Epoch& aEpoch) { for (auto& id : mPendingTransactionIds) { if (id.mEpoch.mHandle == aEpoch.mHandle) { @@ -513,8 +523,14 @@ class WebRenderBridgeParent final : public PWebRenderBridgeParent, RefPtr mWebRenderBridgeRef; #if defined(MOZ_WIDGET_ANDROID) - UiCompositorControllerParent* mScreenPixelsTarget = nullptr; + struct ScreenPixelsRequest { + gfx::IntRect mSourceRect; + gfx::IntSize mDestSize; + RefPtr mPromise; + }; + Maybe mScreenPixelsRequest; #endif + uint32_t mBoolParameterBits; uint16_t mBlobTileSize = 256; wr::RenderReasons mSkippedCompositeReasons = wr::RenderReasons::NONE; diff --git a/gfx/src/nsFontMetrics.cpp b/gfx/src/nsFontMetrics.cpp index ef081ebe40a44..3eceb6bde276f 100644 --- a/gfx/src/nsFontMetrics.cpp +++ b/gfx/src/nsFontMetrics.cpp @@ -165,6 +165,55 @@ void nsFontMetrics::Destroy() { mPresContext = nullptr; } #define ROUND_TO_TWIPS(x) (nscoord) floor(((x) * mP2A) + 0.5) #define CEIL_TO_TWIPS(x) (nscoord) ceil((x) * mP2A) +static const gfxFont::Baselines& GetBaselines( + const nsFontMetrics* aFontMetrics, + nsFontMetrics::FontOrientation aOrientation) { + RefPtr font = + aFontMetrics->GetThebesFontGroup()->GetFirstValidFont(); + return font->GetBaselines(aOrientation); +} + +static const gfxFont::Baselines& GetBaselines( + const nsFontMetrics* aFontMetrics) { + return GetBaselines(aFontMetrics, aFontMetrics->Orientation()); +} + +nscoord nsFontMetrics::AlphabeticBaseline() const { + return ROUND_TO_TWIPS(GetBaselines(this).mAlphabetic); +} + +nscoord nsFontMetrics::CentralBaseline() const { + return ROUND_TO_TWIPS(GetBaselines(this).mCentral); +} + +nscoord nsFontMetrics::XMiddleBaseline() const { + return (AlphabeticBaseline() + XHeight()) / 2; +} + +nscoord nsFontMetrics::IdeographicUnderBaseline() const { + return ROUND_TO_TWIPS(GetBaselines(this).mIdeographicUnder); +} + +nscoord nsFontMetrics::IdeographicOverBaseline() const { + return ROUND_TO_TWIPS(GetBaselines(this).mIdeographicOver); +} + +nscoord nsFontMetrics::IdeographicInkUnderBaseline() const { + return ROUND_TO_TWIPS(GetBaselines(this).mIdeographicInkUnder); +} + +nscoord nsFontMetrics::IdeographicInkOverBaseline() const { + return ROUND_TO_TWIPS(GetBaselines(this).mIdeographicInkOver); +} + +nscoord nsFontMetrics::HangingBaseline() const { + return ROUND_TO_TWIPS(GetBaselines(this).mHanging); +} + +nscoord nsFontMetrics::MathBaseline() const { + return ROUND_TO_TWIPS(GetBaselines(this).mMath); +} + static const gfxFont::Metrics& GetMetrics( const nsFontMetrics* aFontMetrics, nsFontMetrics::FontOrientation aOrientation) { diff --git a/gfx/src/nsFontMetrics.h b/gfx/src/nsFontMetrics.h index bf5a7974c847f..a7b37824e4d99 100644 --- a/gfx/src/nsFontMetrics.h +++ b/gfx/src/nsFontMetrics.h @@ -79,6 +79,51 @@ class nsFontMetrics final { */ void Destroy(); + /** + * Return the font's alphabetic baseline. + */ + nscoord AlphabeticBaseline() const; + + /** + * Return the font's central baseline. + */ + nscoord CentralBaseline() const; + + /** + * Return the font's x-middle baseline. + */ + nscoord XMiddleBaseline() const; + + /** + * Return the font's ideographic-under baseline. + */ + nscoord IdeographicUnderBaseline() const; + + /** + * Return the font's ideographic-over baseline. + */ + nscoord IdeographicOverBaseline() const; + + /** + * Return the font's ideographic-ink-under baseline. + */ + nscoord IdeographicInkUnderBaseline() const; + + /** + * Return the font's ideographic-ink-over baseline. + */ + nscoord IdeographicInkOverBaseline() const; + + /** + * Return the font's hanging baseline. + */ + nscoord HangingBaseline() const; + + /** + * Return the font's math baseline. + */ + nscoord MathBaseline() const; + /** * Return the font's x-height. */ diff --git a/gfx/thebes/gfxFont.cpp b/gfx/thebes/gfxFont.cpp index 2852c11c6f759..67becd06d78e1 100644 --- a/gfx/thebes/gfxFont.cpp +++ b/gfx/thebes/gfxFont.cpp @@ -997,6 +997,8 @@ gfxFont::gfxFont(const RefPtr& aUnscaledFont, mAntialiasOption = kAntialiasNone; } + InitBaselines(mHorizontalBaselines, nsFontMetrics::eHorizontal); + mKerningSet = HasFeatureSet(HB_TAG('k', 'e', 'r', 'n'), mKerningEnabled); // Ensure the gfxFontEntry's unitsPerEm and extents fields are initialized, @@ -1009,6 +1011,7 @@ gfxFont::~gfxFont() { // Delete objects owned through atomic pointers. (Some of these may be null, // but that's OK.) + delete mVerticalBaselines.exchange(nullptr); delete mVerticalMetrics.exchange(nullptr); delete mHarfBuzzShaper.exchange(nullptr); delete mGraphiteShaper.exchange(nullptr); @@ -4424,69 +4427,58 @@ void gfxFont::SanitizeMetrics(gfxFont::Metrics* aMetrics, } } -gfxFont::Baselines gfxFont::GetBaselines(Orientation aOrientation) { - // Approximated baselines for fonts lacking actual baseline data. These are - // fractions of the em ascent/descent from the alphabetic baseline. - const double kHangingBaselineDefault = 0.8; // fraction of ascent - const double kIdeographicBaselineDefault = -0.5; // fraction of descent - - // If no BASE table is present, just return synthetic values immediately. - if (!mFontEntry->HasFontTable(TRUETYPE_TAG('B', 'A', 'S', 'E'))) { - // No baseline table; just synthesize them immediately. - const Metrics& metrics = GetMetrics(aOrientation); - return Baselines{ - 0.0, // alphabetic - kHangingBaselineDefault * metrics.emAscent, // hanging - kIdeographicBaselineDefault * metrics.emDescent // ideographic - }; - } - - // Use harfbuzz to try to read the font's baseline metrics. - Baselines result{NAN, NAN, NAN}; +void gfxFont::InitBaselines(Baselines& aBaselines, Orientation aOrientation) { + // Use harfbuzz to try to read the font's baseline metrics. For + // missing baselines, harfbuzz will synthesize fallbacks according + // to the CSS Inline Layout Module Level 3 specification. hb_font_t* hbFont = gfxHarfBuzzShaper::CreateHBFont(this); hb_direction_t hbDir = aOrientation == nsFontMetrics::eHorizontal ? HB_DIRECTION_LTR : HB_DIRECTION_TTB; hb_position_t position; - unsigned count = 0; auto Fix2Float = [](hb_position_t f) -> gfxFloat { return f / 65536.0; }; - if (hb_ot_layout_get_baseline(hbFont, HB_OT_LAYOUT_BASELINE_TAG_ROMAN, hbDir, - HB_OT_TAG_DEFAULT_SCRIPT, - HB_OT_TAG_DEFAULT_LANGUAGE, &position)) { - result.mAlphabetic = Fix2Float(position); - count++; - } - if (hb_ot_layout_get_baseline(hbFont, HB_OT_LAYOUT_BASELINE_TAG_HANGING, - hbDir, HB_OT_TAG_DEFAULT_SCRIPT, - HB_OT_TAG_DEFAULT_LANGUAGE, &position)) { - result.mHanging = Fix2Float(position); - count++; - } - if (hb_ot_layout_get_baseline( - hbFont, HB_OT_LAYOUT_BASELINE_TAG_IDEO_EMBOX_BOTTOM_OR_LEFT, hbDir, - HB_OT_TAG_DEFAULT_SCRIPT, HB_OT_TAG_DEFAULT_LANGUAGE, &position)) { - result.mIdeographic = Fix2Float(position); - count++; - } - hb_font_destroy(hbFont); - // If we successfully read all three, we can return now. - if (count == 3) { - return result; - } - // Synthesize the baselines that we didn't find in the font. - const Metrics& metrics = GetMetrics(aOrientation); - if (std::isnan(result.mAlphabetic)) { - result.mAlphabetic = 0.0; - } - if (std::isnan(result.mHanging)) { - result.mHanging = kHangingBaselineDefault * metrics.emAscent; - } - if (std::isnan(result.mIdeographic)) { - result.mIdeographic = kIdeographicBaselineDefault * metrics.emDescent; - } + hb_ot_layout_get_baseline_with_fallback( + hbFont, HB_OT_LAYOUT_BASELINE_TAG_ROMAN, hbDir, HB_OT_TAG_DEFAULT_SCRIPT, + HB_OT_TAG_DEFAULT_LANGUAGE, &position); + aBaselines.mAlphabetic = Fix2Float(position); + + hb_ot_layout_get_baseline_with_fallback( + hbFont, HB_OT_LAYOUT_BASELINE_TAG_HANGING, hbDir, + HB_OT_TAG_DEFAULT_SCRIPT, HB_OT_TAG_DEFAULT_LANGUAGE, &position); + aBaselines.mHanging = Fix2Float(position); + + hb_ot_layout_get_baseline_with_fallback( + hbFont, HB_OT_LAYOUT_BASELINE_TAG_IDEO_EMBOX_BOTTOM_OR_LEFT, hbDir, + HB_OT_TAG_DEFAULT_SCRIPT, HB_OT_TAG_DEFAULT_LANGUAGE, &position); + aBaselines.mIdeographicUnder = Fix2Float(position); + + hb_ot_layout_get_baseline_with_fallback( + hbFont, HB_OT_LAYOUT_BASELINE_TAG_IDEO_EMBOX_TOP_OR_RIGHT, hbDir, + HB_OT_TAG_DEFAULT_SCRIPT, HB_OT_TAG_DEFAULT_LANGUAGE, &position); + aBaselines.mIdeographicOver = Fix2Float(position); + + hb_ot_layout_get_baseline_with_fallback( + hbFont, HB_OT_LAYOUT_BASELINE_TAG_IDEO_FACE_BOTTOM_OR_LEFT, hbDir, + HB_OT_TAG_DEFAULT_SCRIPT, HB_OT_TAG_DEFAULT_LANGUAGE, &position); + aBaselines.mIdeographicInkUnder = Fix2Float(position); + + hb_ot_layout_get_baseline_with_fallback( + hbFont, HB_OT_LAYOUT_BASELINE_TAG_IDEO_FACE_TOP_OR_RIGHT, hbDir, + HB_OT_TAG_DEFAULT_SCRIPT, HB_OT_TAG_DEFAULT_LANGUAGE, &position); + aBaselines.mIdeographicInkOver = Fix2Float(position); + + hb_ot_layout_get_baseline_with_fallback( + hbFont, HB_OT_LAYOUT_BASELINE_TAG_IDEO_EMBOX_CENTRAL, hbDir, + HB_OT_TAG_DEFAULT_SCRIPT, HB_OT_TAG_DEFAULT_LANGUAGE, &position); + aBaselines.mCentral = Fix2Float(position); + + hb_ot_layout_get_baseline_with_fallback( + hbFont, HB_OT_LAYOUT_BASELINE_TAG_MATH, hbDir, HB_OT_TAG_DEFAULT_SCRIPT, + HB_OT_TAG_DEFAULT_LANGUAGE, &position); + aBaselines.mMath = Fix2Float(position); - return result; + hb_font_destroy(hbFont); } // Create a Metrics record to be used for vertical layout. This should never @@ -4686,6 +4678,15 @@ void gfxFont::CreateVerticalMetrics() { } } +void gfxFont::CreateVerticalBaselines() { + auto* baselines = new Baselines(); + InitBaselines(*baselines, nsFontMetrics::eVertical); + + if (!mVerticalBaselines.compareExchange(nullptr, baselines)) { + delete baselines; + } +} + gfxFloat gfxFont::SynthesizeSpaceWidth(uint32_t aCh) { // return an appropriate width for various Unicode space characters // that we "fake" if they're not actually present in the font; diff --git a/gfx/thebes/gfxFont.h b/gfx/thebes/gfxFont.h index 2932931d71543..86c9a3fbd248a 100644 --- a/gfx/thebes/gfxFont.h +++ b/gfx/thebes/gfxFont.h @@ -1642,9 +1642,22 @@ class gfxFont { struct Baselines { gfxFloat mAlphabetic; gfxFloat mHanging; - gfxFloat mIdeographic; + gfxFloat mIdeographicUnder; + gfxFloat mIdeographicOver; + gfxFloat mIdeographicInkUnder; + gfxFloat mIdeographicInkOver; + gfxFloat mCentral; + gfxFloat mMath; }; - Baselines GetBaselines(Orientation aOrientation); + const Baselines& GetBaselines(Orientation aOrientation) { + if (aOrientation == nsFontMetrics::eHorizontal) { + return mHorizontalBaselines; + } + if (!mVerticalBaselines) { + CreateVerticalBaselines(); + } + return *mVerticalBaselines; + } /** * We let layout specify spacing on either side of any @@ -1950,6 +1963,8 @@ class gfxFont { virtual const Metrics& GetHorizontalMetrics() const = 0; void CreateVerticalMetrics(); + void CreateVerticalBaselines(); + void InitBaselines(Baselines& aBaselines, Orientation aOrientation); bool MeasureGlyphs(const gfxTextRun* aTextRun, uint32_t aStart, uint32_t aEnd, BoundingBoxType aBoundingBoxType, @@ -2214,8 +2229,11 @@ class gfxFont { mozilla::Atomic mAzureScaledFont; + Baselines mHorizontalBaselines; + // For vertical metrics, created on demand. mozilla::Atomic mVerticalMetrics; + mozilla::Atomic mVerticalBaselines; // Table used for MathML layout. mozilla::Atomic mMathTable; diff --git a/gfx/webrender_bindings/RenderCompositor.h b/gfx/webrender_bindings/RenderCompositor.h index 4097c5f002ef8..73b94485666b9 100644 --- a/gfx/webrender_bindings/RenderCompositor.h +++ b/gfx/webrender_bindings/RenderCompositor.h @@ -22,6 +22,7 @@ class GLContext; } namespace layers { +class AndroidHardwareBuffer; class CompositionRecorder; class SyncObjectHost; } // namespace layers @@ -219,6 +220,13 @@ class RenderCompositor { return false; } virtual bool MaybeProcessScreenshotQueue() { return false; } +#ifdef MOZ_WIDGET_ANDROID + virtual bool MaybeCaptureScreenPixels( + const gfx::IntRect& aSourceRect, + RefPtr aHardwareBuffer) { + return false; + } +#endif virtual RefPtr GetAndResetReleaseFence() { return nullptr; } diff --git a/gfx/webrender_bindings/RenderCompositorOGLSWGL.cpp b/gfx/webrender_bindings/RenderCompositorOGLSWGL.cpp index 4acd77c2422b1..868298bc942ea 100644 --- a/gfx/webrender_bindings/RenderCompositorOGLSWGL.cpp +++ b/gfx/webrender_bindings/RenderCompositorOGLSWGL.cpp @@ -10,6 +10,7 @@ #include "GLContext.h" #include "GLContextEGL.h" +#include "ScopedGLHelpers.h" #include "mozilla/layers/BuildConstants.h" #include "mozilla/layers/CompositorOGL.h" #include "mozilla/layers/Effects.h" @@ -313,6 +314,43 @@ bool RenderCompositorOGLSWGL::MaybeReadback( return true; } +#ifdef MOZ_WIDGET_ANDROID +bool RenderCompositorOGLSWGL::MaybeCaptureScreenPixels( + const gfx::IntRect& aSourceRect, + RefPtr aHardwareBuffer) { + auto* const gl = GetGLContext(); + auto* const gle = gl::GLContextEGL::Cast(gl); + const auto& egl = gle->mEgl; + gl::ScopedEGLImageForAndroidHardwareBuffer eglImage(gle, aHardwareBuffer); + gl::ScopedBindFramebuffer scopedBind(gl); + gl::ScopedRenderbuffer rb(gl); + gl->fBindRenderbuffer(LOCAL_GL_RENDERBUFFER, rb); + gl->fEGLImageTargetRenderbufferStorage(LOCAL_GL_RENDERBUFFER, eglImage); + gl::ScopedFramebufferForRenderbuffer fb(gl, rb); + + const auto srcRect = + gfx::IntRect(aSourceRect.x, GetBufferSize().height - aSourceRect.y, + aSourceRect.width, -aSourceRect.height); + const auto destRect = gfx::IntRect({}, aHardwareBuffer->mSize); + gl->BindReadFB(0); + gl->BindDrawFB(fb.FB()); + gl->fBlitFramebuffer(srcRect.x, srcRect.y, srcRect.XMost(), srcRect.YMost(), + destRect.x, destRect.y, destRect.XMost(), + destRect.YMost(), LOCAL_GL_COLOR_BUFFER_BIT, + LOCAL_GL_LINEAR); + + if (EGLSync sync = + egl->fCreateSync(LOCAL_EGL_SYNC_NATIVE_FENCE_ANDROID, nullptr)) { + auto fence = UniqueFileHandle(egl->fDupNativeFenceFDANDROID(sync)); + if (fence) { + aHardwareBuffer->SetAcquireFence(std::move(fence)); + } + egl->fDestroySync(sync); + } + return true; +} +#endif + // This is a DataSourceSurface that represents a 0-based PBO for GLTextureImage. class PBOUnpackSurface : public gfx::DataSourceSurface { public: diff --git a/gfx/webrender_bindings/RenderCompositorOGLSWGL.h b/gfx/webrender_bindings/RenderCompositorOGLSWGL.h index 7752574311bf0..78d4cee312474 100644 --- a/gfx/webrender_bindings/RenderCompositorOGLSWGL.h +++ b/gfx/webrender_bindings/RenderCompositorOGLSWGL.h @@ -58,6 +58,11 @@ class RenderCompositorOGLSWGL : public RenderCompositorLayersSWGL { const wr::ImageFormat& aReadbackFormat, const Range& aReadbackBuffer, bool* aNeedsYFlip) override; +#ifdef MOZ_WIDGET_ANDROID + bool MaybeCaptureScreenPixels( + const gfx::IntRect& aSourceRect, + RefPtr aHardwareBuffer) override; +#endif private: void HandleExternalImage(RenderTextureHost* aExternalImage, diff --git a/gfx/webrender_bindings/RendererOGL.cpp b/gfx/webrender_bindings/RendererOGL.cpp index 020405bd6d183..f6fd574019b5a 100644 --- a/gfx/webrender_bindings/RendererOGL.cpp +++ b/gfx/webrender_bindings/RendererOGL.cpp @@ -22,6 +22,12 @@ #include "mozilla/webrender/RenderTextureHost.h" #include "mozilla/widget/CompositorWidget.h" +#ifdef MOZ_WIDGET_ANDROID +# include "GLContextEGL.h" +# include "mozilla/layers/AndroidHardwareBuffer.h" +# include "ScopedGLHelpers.h" +#endif + namespace mozilla { namespace wr { @@ -129,6 +135,11 @@ RendererOGL::RendererOGL(RefPtr&& aThread, RendererOGL::~RendererOGL() { MOZ_COUNT_DTOR(RendererOGL); +#ifdef MOZ_WIDGET_ANDROID + if (mPendingScreenPixelsRequest) { + mPendingScreenPixelsRequest->mPromise->Reject(NS_ERROR_ABORT, __func__); + } +#endif if (!mCompositor->MakeCurrent()) { gfxCriticalNote << "Failed to make render context current during destroying."; @@ -269,6 +280,10 @@ RenderedFrameId RendererOGL::UpdateAndRender( } } +#ifdef MOZ_WIDGET_ANDROID + MaybeCaptureScreenPixels(); +#endif + if (size.Width() != 0 && size.Height() != 0) { if (!mCompositor->MaybeGrabScreenshot(size.ToUnknownSize())) { mScreenshotGrabber.MaybeGrabScreenshot(this, size.ToUnknownSize()); @@ -461,6 +476,71 @@ Maybe RendererOGL::EndRecording() { return maybeRecording; } +#ifdef MOZ_WIDGET_ANDROID +RefPtr RendererOGL::RequestScreenPixels( + gfx::IntRect aSourceRect, gfx::IntSize aDestSize) { + mPendingScreenPixelsRequest.emplace(ScreenPixelsRequest{ + .mSourceRect = aSourceRect, + .mDestSize = aDestSize, + .mPromise = new ScreenPixelsPromise::Private(__func__), + }); + return mPendingScreenPixelsRequest->mPromise; +} + +void RendererOGL::MaybeCaptureScreenPixels() { + if (!mPendingScreenPixelsRequest || !EnsureAsyncScreenshot()) { + return; + } + + auto request = mPendingScreenPixelsRequest.extract(); + + const RefPtr hardwareBuffer = + layers::AndroidHardwareBuffer::Create(request.mDestSize, + gfx::SurfaceFormat::R8G8B8A8); + + if (mCompositor->MaybeCaptureScreenPixels(request.mSourceRect, + hardwareBuffer)) { + request.mPromise->Resolve(hardwareBuffer, __func__); + return; + } + + auto* const gle = gl::GLContextEGL::Cast(gl()); + const auto& egl = gle->mEgl; + gl::ScopedEGLImageForAndroidHardwareBuffer eglImage(gle, hardwareBuffer); + gl::ScopedBindFramebuffer scopedBind(gl()); + gl::ScopedRenderbuffer rb(gl()); + gl()->fBindRenderbuffer(LOCAL_GL_RENDERBUFFER, rb); + gl()->fEGLImageTargetRenderbufferStorage(LOCAL_GL_RENDERBUFFER, eglImage); + gl::ScopedFramebufferForRenderbuffer fb(gl(), rb); + + const auto srcRect = + mCompositor->SurfaceOriginIsTopLeft() + ? request.mSourceRect + : gfx::IntRect( + request.mSourceRect.x, + mCompositor->GetBufferSize().height - request.mSourceRect.y, + request.mSourceRect.width, -request.mSourceRect.height); + const auto destRect = gfx::IntRect({}, hardwareBuffer->mSize); + gl()->BindReadFB(0); + gl()->BindDrawFB(fb.FB()); + gl()->fBlitFramebuffer(srcRect.x, srcRect.y, srcRect.XMost(), srcRect.YMost(), + destRect.x, destRect.y, destRect.XMost(), + destRect.YMost(), LOCAL_GL_COLOR_BUFFER_BIT, + LOCAL_GL_LINEAR); + + if (EGLSync sync = + egl->fCreateSync(LOCAL_EGL_SYNC_NATIVE_FENCE_ANDROID, nullptr)) { + auto fence = UniqueFileHandle(egl->fDupNativeFenceFDANDROID(sync)); + if (fence) { + hardwareBuffer->SetAcquireFence(std::move(fence)); + } + egl->fDestroySync(sync); + } + + request.mPromise->Resolve(hardwareBuffer, __func__); +} +#endif + void RendererOGL::FlushPipelineInfo() { RefPtr info = new WebRenderPipelineInfo; wr_renderer_flush_pipeline_info(mRenderer, &info->Raw()); diff --git a/gfx/webrender_bindings/RendererOGL.h b/gfx/webrender_bindings/RendererOGL.h index bc7ace6d7844e..5f0c3139ae17a 100644 --- a/gfx/webrender_bindings/RendererOGL.h +++ b/gfx/webrender_bindings/RendererOGL.h @@ -25,6 +25,7 @@ class GLContext; } namespace layers { +class AndroidHardwareBuffer; class CompositorBridgeParent; class Fence; class SyncObjectHost; @@ -93,6 +94,15 @@ class RendererOGL { Maybe EndRecording(); +#ifdef MOZ_WIDGET_ANDROID + using ScreenPixelsPromise = + MozPromise, nsresult, true>; + // Captures the pixels for the next rendered frame. Returns a promise that + // resolves once the pixels are captured. + RefPtr RequestScreenPixels(gfx::IntRect aSourceRect, + gfx::IntSize aDestSize); +#endif + /// This can be called on the render thread only. ~RendererOGL(); @@ -147,6 +157,14 @@ class RendererOGL { */ bool DidPaintContent(const wr::WebRenderPipelineInfo* aFrameEpochs); +#ifdef MOZ_WIDGET_ANDROID + // If mPendingScreenPixelsRequest is set, captures the pixels of the frame + // that has just been rendered and resolves the request. Must be called after + // the frame has been rendered but before RenderCompositor::EndFrame() (which + // swaps buffers). + void MaybeCaptureScreenPixels(); +#endif + RefPtr mThread; UniquePtr mCompositor; UniquePtr mCompositionRecorder; // can be null @@ -157,6 +175,15 @@ class RendererOGL { bool mDisableNativeCompositor; +#ifdef MOZ_WIDGET_ANDROID + struct ScreenPixelsRequest { + gfx::IntRect mSourceRect; + gfx::IntSize mDestSize; + RefPtr mPromise; + }; + Maybe mPendingScreenPixelsRequest; +#endif + RendererScreenshotGrabber mScreenshotGrabber; // The id of the root WebRender pipeline. diff --git a/gfx/webrender_bindings/WebRenderAPI.cpp b/gfx/webrender_bindings/WebRenderAPI.cpp index 7513992e01a5a..56e35e87ba5ff 100644 --- a/gfx/webrender_bindings/WebRenderAPI.cpp +++ b/gfx/webrender_bindings/WebRenderAPI.cpp @@ -27,6 +27,10 @@ #include "source-repo.h" +#ifdef MOZ_WIDGET_ANDROID +# include "mozilla/layers/AndroidHardwareBuffer.h" +#endif + static mozilla::LazyLogModule sWrDLLog("wr.dl"); #define WRDL_LOG(...) \ MOZ_LOG(sWrDLLog, LogLevel::Debug, ("WRDL(%p): " __VA_ARGS__)) @@ -967,6 +971,44 @@ RefPtr WebRenderAPI::EndRecording() { return promise; } +#ifdef MOZ_WIDGET_ANDROID +RefPtr WebRenderAPI::RequestScreenPixels( + gfx::IntRect aSourceRect, gfx::IntSize aDestSize) { + class ScreenshotEvent final : public RendererEvent { + public: + explicit ScreenshotEvent(gfx::IntRect aSourceRect, gfx::IntSize aDestSize, + RefPtr aPromise) + : mSourceRect(aSourceRect), mDestSize(aDestSize), mPromise(aPromise) { + MOZ_COUNT_CTOR(ScreenshotEvent); + } + + MOZ_COUNTED_DTOR(ScreenshotEvent); + + void Run(RenderThread& aRenderThread, WindowId aWindowId) override { + RendererOGL* const renderer = aRenderThread.GetRenderer(aWindowId); + if (!renderer) { + mPromise->Reject(NS_ERROR_FAILURE, __func__); + } + renderer->RequestScreenPixels(mSourceRect, mDestSize) + ->ChainTo(mPromise.forget(), __func__); + } + + const char* Name() override { return "ScreenshotEvent"; } + + private: + const gfx::IntRect mSourceRect; + const gfx::IntSize mDestSize; + RefPtr mPromise; + }; + + auto promise = MakeRefPtr(__func__); + auto event = MakeUnique(aSourceRect, aDestSize, promise); + + RenderThread::Get()->PostEvent(mId, std::move(event)); + return promise; +} +#endif + void TransactionBuilder::Clear() { wr_resource_updates_clear(mTxn); } Transaction* TransactionBuilder::Take() { diff --git a/gfx/webrender_bindings/WebRenderAPI.h b/gfx/webrender_bindings/WebRenderAPI.h index 854cfbd6b0155..ba5d4d41aac34 100644 --- a/gfx/webrender_bindings/WebRenderAPI.h +++ b/gfx/webrender_bindings/WebRenderAPI.h @@ -326,6 +326,16 @@ class WebRenderAPI final { RefPtr EndRecording(); +#ifdef MOZ_WIDGET_ANDROID + using ScreenPixelsPromise = + MozPromise, nsresult, true>; + // Queues a task to the render thread to capture screen pixels for the next + // rendered frame. Returns a promise that resolves once the pixels are + // captured. + RefPtr RequestScreenPixels(gfx::IntRect aSourceRect, + gfx::IntSize aDestSize); +#endif + layers::RemoteTextureInfoList* GetPendingRemoteTextureInfoList(); layers::AsyncImagePipelineOps* GetPendingAsyncImagePipelineOps( TransactionBuilder& aTxn); diff --git a/image/DecoderFactory.cpp b/image/DecoderFactory.cpp index 81ac3dff64e9a..07e8475bcc349 100644 --- a/image/DecoderFactory.cpp +++ b/image/DecoderFactory.cpp @@ -101,24 +101,6 @@ DecoderType DecoderFactory::GetDecoderType(const char* aMimeType) { return type; } -/* static */ -DecoderFlags DecoderFactory::GetDefaultDecoderFlagsForType(DecoderType aType) { - auto flags = DefaultDecoderFlags(); - -#ifdef MOZ_AV1 - if (aType == DecoderType::AVIF) { - if (StaticPrefs::image_avif_sequence_enabled()) { - flags |= DecoderFlags::AVIF_SEQUENCES_ENABLED; - } - if (StaticPrefs::image_avif_sequence_animate_avif_major_branded_images()) { - flags |= DecoderFlags::AVIF_ANIMATE_AVIF_MAJOR; - } - } -#endif - - return flags; -} - /* static */ already_AddRefed DecoderFactory::GetDecoder(DecoderType aType, RasterImage* aImage, diff --git a/image/DecoderFactory.h b/image/DecoderFactory.h index 5927889876767..11bb2387043ec 100644 --- a/image/DecoderFactory.h +++ b/image/DecoderFactory.h @@ -30,9 +30,6 @@ class DecoderFactory { /// @return the type of decoder which is appropriate for @aMimeType. static DecoderType GetDecoderType(const char* aMimeType); - /// @return the default flags to use when creating a decoder of @aType. - static DecoderFlags GetDefaultDecoderFlagsForType(DecoderType aType); - /** * Creates and initializes a decoder for non-animated images of type @aType. * (If the image *is* animated, only the first frame will be decoded.) The diff --git a/image/DecoderFlags.h b/image/DecoderFlags.h index a6cd1e18790be..afd7461c11a6d 100644 --- a/image/DecoderFlags.h +++ b/image/DecoderFlags.h @@ -32,24 +32,13 @@ enum class DecoderFlags : uint8_t { */ CANNOT_SUBSTITUTE = 1 << 4, -#ifdef MOZ_AV1 - // The flags below are stored in RasterImage to allow a decoded image to - // remain consistent in whether it is animated or not. - - // Set according to the "image.avif.sequence.enabled" preference. - AVIF_SEQUENCES_ENABLED = 1 << 5, - // Set according to the - // "image.avif.sequence.animate_avif_major_branded_images" preference. - AVIF_ANIMATE_AVIF_MAJOR = 1 << 6, -#endif - /** * By default, we don't count how many animated frames there are in an image, * as that would require us to iterate over the entire buffer for some image * formats. If the caller requires a full accounting of how many frames there * are. */ - COUNT_FRAMES = 1 << 7, + COUNT_FRAMES = 1 << 5, }; MOZ_MAKE_ENUM_CLASS_BITWISE_OPERATORS(DecoderFlags) diff --git a/image/ImageOps.cpp b/image/ImageOps.cpp index 60839da5635f5..13914d9e38b43 100644 --- a/image/ImageOps.cpp +++ b/image/ImageOps.cpp @@ -171,8 +171,7 @@ nsresult ImageOps::DecodeMetadata(ImageBuffer* aBuffer, // Create a decoder. DecoderType decoderType = DecoderFactory::GetDecoderType(PromiseFlatCString(aMimeType).get()); - DecoderFlags decoderFlags = - DecoderFactory::GetDefaultDecoderFlagsForType(decoderType); + DecoderFlags decoderFlags = DefaultDecoderFlags(); decoderFlags |= DecoderFlags::FIRST_FRAME_ONLY; RefPtr decoder = DecoderFactory::CreateAnonymousMetadataDecoder( decoderType, WrapNotNull(sourceBuffer), decoderFlags); diff --git a/image/RasterImage.cpp b/image/RasterImage.cpp index 8c4d34f53a69b..c333e20783519 100644 --- a/image/RasterImage.cpp +++ b/image/RasterImage.cpp @@ -126,11 +126,6 @@ nsresult RasterImage::Init(const char* aMimeType, uint32_t aFlags) { SurfaceCache::LockImage(ImageKey(this)); } - // Set the default flags according to the decoder type to allow preferences to - // be stored if necessary. - mDefaultDecoderFlags = - DecoderFactory::GetDefaultDecoderFlagsForType(mDecoderType); - // Mark us as initialized mInitialized = true; @@ -1185,7 +1180,7 @@ void RasterImage::Decode(const OrientedIntSize& aSize, uint32_t aFlags, SurfaceCache::UnlockEntries(ImageKey(this)); // Determine which flags we need to decode this image with. - DecoderFlags decoderFlags = mDefaultDecoderFlags; + DecoderFlags decoderFlags = DefaultDecoderFlags(); if (aFlags & FLAG_ASYNC_NOTIFY) { decoderFlags |= DecoderFlags::ASYNC_NOTIFY; } @@ -1269,7 +1264,7 @@ RasterImage::DecodeMetadata(uint32_t aFlags) { // Create a decoder. RefPtr task = DecoderFactory::CreateMetadataDecoder( - mDecoderType, WrapNotNull(this), mDefaultDecoderFlags, mSourceBuffer); + mDecoderType, WrapNotNull(this), DefaultDecoderFlags(), mSourceBuffer); // Make sure DecoderFactory was able to create a decoder successfully. if (!task) { diff --git a/image/RasterImage.h b/image/RasterImage.h index 5adb700482bed..6d90d0653ca23 100644 --- a/image/RasterImage.h +++ b/image/RasterImage.h @@ -414,12 +414,6 @@ class RasterImage final : public ImageResource, TimeStamp mDrawStartTime; - // This field is set according to the DecoderType of this image once when - // initialized so that a decoder's flags can be set according to any - // preferences that affect its behavior in a way that would otherwise cause - // errors, such as enabling or disabling animation. - DecoderFlags mDefaultDecoderFlags = DefaultDecoderFlags(); - ////////////////////////////////////////////////////////////////////////////// // Scaling. ////////////////////////////////////////////////////////////////////////////// diff --git a/image/decoders/nsAVIFDecoder.cpp b/image/decoders/nsAVIFDecoder.cpp index e97662bee6f3f..564dee75d01ee 100644 --- a/image/decoders/nsAVIFDecoder.cpp +++ b/image/decoders/nsAVIFDecoder.cpp @@ -179,14 +179,12 @@ AVIFParser::~AVIFParser() { } Mp4parseStatus AVIFParser::Create(const Mp4parseIo* aIo, ByteStream* aBuffer, - UniquePtr& aParserOut, - bool aAllowSequences, - bool aAnimateAVIFMajor) { + UniquePtr& aParserOut) { MOZ_ASSERT(aIo); MOZ_ASSERT(!aParserOut); UniquePtr p(new AVIFParser(aIo)); - Mp4parseStatus status = p->Init(aBuffer, aAllowSequences, aAnimateAVIFMajor); + Mp4parseStatus status = p->Init(aBuffer); if (status == MP4PARSE_STATUS_OK) { MOZ_ASSERT(p->mParser); @@ -327,8 +325,7 @@ static Mp4parseStatus CreateSampleIterator( return MP4PARSE_STATUS_OK; } -Mp4parseStatus AVIFParser::Init(ByteStream* aBuffer, bool aAllowSequences, - bool aAnimateAVIFMajor) { +Mp4parseStatus AVIFParser::Init(ByteStream* aBuffer) { #define CHECK_MP4PARSE_STATUS(v) \ do { \ if ((v) != MP4PARSE_STATUS_OK) { \ @@ -355,12 +352,14 @@ Mp4parseStatus AVIFParser::Init(ByteStream* aBuffer, bool aAllowSequences, bool useSequence = mInfo.has_sequence; if (useSequence) { - if (!aAllowSequences) { + if (!StaticPrefs::image_avif_sequence_enabled_AtStartup()) { MOZ_LOG(sAVIFLog, LogLevel::Debug, ("[this=%p] AVIF sequences disabled", this)); useSequence = false; - } else if (!aAnimateAVIFMajor && - !!memcmp(mInfo.major_brand, "avis", sizeof(mInfo.major_brand))) { + } else if ( + !StaticPrefs:: + image_avif_sequence_animate_avif_major_branded_images_AtStartup() && + !!memcmp(mInfo.major_brand, "avis", sizeof(mInfo.major_brand))) { useSequence = false; MOZ_LOG(sAVIFLog, LogLevel::Debug, ("[this=%p] AVIF prefers still image", this)); @@ -1258,10 +1257,8 @@ Mp4parseStatus nsAVIFDecoder::CreateParser() { Mp4parseIo io = {nsAVIFDecoder::ReadSource, this}; mBufferStream = new AVIFDecoderStream(&mBufferedData); - Mp4parseStatus status = AVIFParser::Create( - &io, mBufferStream.get(), mParser, - bool(GetDecoderFlags() & DecoderFlags::AVIF_SEQUENCES_ENABLED), - bool(GetDecoderFlags() & DecoderFlags::AVIF_ANIMATE_AVIF_MAJOR)); + Mp4parseStatus status = + AVIFParser::Create(&io, mBufferStream.get(), mParser); if (status != MP4PARSE_STATUS_OK) { return status; diff --git a/image/decoders/nsAVIFDecoder.h b/image/decoders/nsAVIFDecoder.h index 1f846291ab060..87e2acbc802ad 100644 --- a/image/decoders/nsAVIFDecoder.h +++ b/image/decoders/nsAVIFDecoder.h @@ -119,8 +119,7 @@ struct AVIFImage { class AVIFParser { public: static Mp4parseStatus Create(const Mp4parseIo* aIo, ByteStream* aBuffer, - UniquePtr& aParserOut, - bool aAllowSequences, bool aAnimateAVIFMajor); + UniquePtr& aParserOut); ~AVIFParser(); @@ -135,8 +134,7 @@ class AVIFParser { private: explicit AVIFParser(const Mp4parseIo* aIo); - Mp4parseStatus Init(ByteStream* aBuffer, bool aAllowSequences, - bool aAnimateAVIFMajor); + Mp4parseStatus Init(ByteStream* aBuffer); struct FreeAvifParser { void operator()(Mp4parseAvifParser* aPtr) { mp4parse_avif_free(aPtr); } diff --git a/image/test/fuzzing/TestDecoders.cpp b/image/test/fuzzing/TestDecoders.cpp index 20bc79330d0b8..fb0f149aafe7d 100644 --- a/image/test/fuzzing/TestDecoders.cpp +++ b/image/test/fuzzing/TestDecoders.cpp @@ -138,7 +138,6 @@ static int RunDecodeToSurfaceFuzzingJXL(nsCOMPtr inputStream) { #endif int FuzzingInitImage(int* argc, char*** argv) { - Preferences::SetBool("image.avif.sequence.enabled", true); Preferences::SetInt("image.mem.max_legal_imgframe_size_kb", 65536); #ifdef MOZ_JXL Preferences::SetBool("image.jxl.enabled", true); diff --git a/image/test/gtest/Common.cpp b/image/test/gtest/Common.cpp index 9a4ea88f93066..b401f6d803724 100644 --- a/image/test/gtest/Common.cpp +++ b/image/test/gtest/Common.cpp @@ -39,13 +39,9 @@ AutoInitializeImageLib::AutoInitializeImageLib() { EXPECT_TRUE(NS_IsMainThread()); sImageLibInitialized = true; - // Ensure AVIF sequence is enabled to run decoder tests. - nsresult rv = Preferences::SetBool("image.avif.sequence.enabled", true); - EXPECT_TRUE(rv == NS_OK); - #ifdef MOZ_JXL // Ensure JXL is enabled to run decoder tests. - rv = Preferences::SetBool("image.jxl.enabled", true); + nsresult rv = Preferences::SetBool("image.jxl.enabled", true); EXPECT_TRUE(rv == NS_OK); #endif diff --git a/image/test/gtest/TestDecoders.cpp b/image/test/gtest/TestDecoders.cpp index da6ffa6dd7967..0c4dca8fa532a 100644 --- a/image/test/gtest/TestDecoders.cpp +++ b/image/test/gtest/TestDecoders.cpp @@ -206,8 +206,7 @@ void WithSingleChunkDecode(const ImageTestCase& aTestCase, // Create a decoder. DecoderType decoderType = DecoderFactory::GetDecoderType(aTestCase.mMimeType); DecoderFlags decoderFlags = - DecoderFactory::GetDefaultDecoderFlagsForType(decoderType) | - DecoderFlags::FIRST_FRAME_ONLY; + DefaultDecoderFlags() | DecoderFlags::FIRST_FRAME_ONLY; RefPtr decoder = DecoderFactory::CreateAnonymousDecoder( decoderType, sourceBuffer, aOutputSize, decoderFlags, aTestCase.mSurfaceFlags); @@ -297,8 +296,7 @@ static void CheckDecoderMultiChunk(const ImageTestCase& aTestCase, sourceBuffer->ExpectLength(length); DecoderType decoderType = DecoderFactory::GetDecoderType(aTestCase.mMimeType); DecoderFlags decoderFlags = - DecoderFactory::GetDefaultDecoderFlagsForType(decoderType) | - DecoderFlags::FIRST_FRAME_ONLY; + DefaultDecoderFlags() | DecoderFlags::FIRST_FRAME_ONLY; RefPtr decoder = DecoderFactory::CreateAnonymousDecoder( decoderType, sourceBuffer, Nothing(), decoderFlags, aTestCase.mSurfaceFlags); @@ -447,8 +445,7 @@ static void WithSingleChunkAnimationDecode(const ImageTestCase& aTestCase, // Create a metadata decoder first, because otherwise RasterImage will get // unhappy about finding out the image is animated during a full decode. DecoderType decoderType = DecoderFactory::GetDecoderType(aTestCase.mMimeType); - DecoderFlags decoderFlags = - DecoderFactory::GetDefaultDecoderFlagsForType(decoderType); + DecoderFlags decoderFlags = DefaultDecoderFlags(); RefPtr task = DecoderFactory::CreateMetadataDecoder( decoderType, rasterImage, decoderFlags, sourceBuffer); ASSERT_TRUE(task != nullptr); diff --git a/image/test/gtest/TestFrameAnimator.cpp b/image/test/gtest/TestFrameAnimator.cpp index 7269366554b03..0ecec9d27caf6 100644 --- a/image/test/gtest/TestFrameAnimator.cpp +++ b/image/test/gtest/TestFrameAnimator.cpp @@ -75,8 +75,7 @@ static void WithFrameAnimatorDecode(const ImageTestCase& aTestCase, // Create a metadata decoder first, because otherwise RasterImage will get // unhappy about finding out the image is animated during a full decode. DecoderType decoderType = DecoderFactory::GetDecoderType(aTestCase.mMimeType); - DecoderFlags decoderFlags = - DecoderFactory::GetDefaultDecoderFlagsForType(decoderType); + DecoderFlags decoderFlags = DefaultDecoderFlags(); RefPtr task = DecoderFactory::CreateMetadataDecoder( decoderType, rasterImage, decoderFlags, sourceBuffer); ASSERT_TRUE(task != nullptr); diff --git a/image/test/gtest/TestMetadata.cpp b/image/test/gtest/TestMetadata.cpp index f484e19f71420..c681e647b5b70 100644 --- a/image/test/gtest/TestMetadata.cpp +++ b/image/test/gtest/TestMetadata.cpp @@ -33,8 +33,7 @@ static void CheckMetadataFrameCount( NotNull>& aSourceBuffer, BMPWithinICO aBMPWithinICO) { // Create a metadata decoder. DecoderType decoderType = DecoderFactory::GetDecoderType(aTestCase.mMimeType); - DecoderFlags decoderFlags = - DecoderFactory::GetDefaultDecoderFlagsForType(decoderType); + DecoderFlags decoderFlags = DefaultDecoderFlags(); decoderFlags |= DecoderFlags::COUNT_FRAMES; RefPtr decoder = DecoderFactory::CreateAnonymousMetadataDecoder(decoderType, aSourceBuffer, @@ -98,8 +97,7 @@ static void CheckMetadataCommon(const ImageTestCase& aTestCase, BMPWithinICO aBMPWithinICO) { // Create a metadata decoder. DecoderType decoderType = DecoderFactory::GetDecoderType(aTestCase.mMimeType); - DecoderFlags decoderFlags = - DecoderFactory::GetDefaultDecoderFlagsForType(decoderType); + DecoderFlags decoderFlags = DefaultDecoderFlags(); decoderFlags |= DecoderFlags::FIRST_FRAME_ONLY; RefPtr decoder = DecoderFactory::CreateAnonymousMetadataDecoder(decoderType, aSourceBuffer, diff --git a/image/test/mochitest/test_discardAnimatedImage.html b/image/test/mochitest/test_discardAnimatedImage.html index 3b34cda3a5d32..eea66fbf522cd 100644 --- a/image/test/mochitest/test_discardAnimatedImage.html +++ b/image/test/mochitest/test_discardAnimatedImage.html @@ -52,8 +52,7 @@ window.onload = function() { // Enable discarding for the test. SpecialPowers.pushPrefEnv({ - 'set':[['image.mem.discardable',true], - ['image.avif.sequence.enabled',true]] + 'set':[['image.mem.discardable',true]] }, runTest); } diff --git a/ipc/chromium/src/chrome/common/mach_ipc_mac.cc b/ipc/chromium/src/chrome/common/mach_ipc_mac.cc index 7f2be34421371..78d902a48a916 100644 --- a/ipc/chromium/src/chrome/common/mach_ipc_mac.cc +++ b/ipc/chromium/src/chrome/common/mach_ipc_mac.cc @@ -113,7 +113,8 @@ static std::string FormatMachError(kern_return_t kr) { //============================================================================== bool MachChildProcessCheckIn( const char* bootstrap_service_name, mach_msg_timeout_t timeout, - std::vector& send_rights) { + std::vector& send_rights, + std::vector& receive_rights) { mozilla::UniqueMachSendRight task_sender; kern_return_t kr = bootstrap_look_up(bootstrap_port, bootstrap_service_name, mozilla::getter_Transfers(task_sender)); @@ -133,10 +134,11 @@ bool MachChildProcessCheckIn( } // The buffer must be big enough to store a full reply including - // kMaxPassedMachSendRights port descriptors. + // kMaxPassedMachSendRights and kMaxPassedMachReceiveRights port descriptors. size_t buffer_size = sizeof(mach_msg_base_t) + sizeof(mach_msg_port_descriptor_t) * - mozilla::geckoargs::kMaxPassedMachSendRights + + (mozilla::geckoargs::kMaxPassedMachSendRights + + mozilla::geckoargs::kMaxPassedMachReceiveRights) + sizeof(mach_msg_trailer_t); mozilla::UniquePtr buffer = mozilla::MakeUnique(buffer_size); @@ -168,14 +170,19 @@ bool MachChildProcessCheckIn( mach_msg_base_t* reply = reinterpret_cast(buffer.get()); MOZ_RELEASE_ASSERT(reply->header.msgh_bits & MACH_MSGH_BITS_COMPLEX); MOZ_RELEASE_ASSERT(reply->body.msgh_descriptor_count <= - mozilla::geckoargs::kMaxPassedMachSendRights); + (mozilla::geckoargs::kMaxPassedMachSendRights + + mozilla::geckoargs::kMaxPassedMachReceiveRights)); mach_msg_port_descriptor_t* descrs = reinterpret_cast(reply + 1); for (size_t i = 0; i < reply->body.msgh_descriptor_count; ++i) { MOZ_RELEASE_ASSERT(descrs[i].type == MACH_MSG_PORT_DESCRIPTOR); - MOZ_RELEASE_ASSERT(descrs[i].disposition == MACH_MSG_TYPE_MOVE_SEND); - send_rights.emplace_back(descrs[i].name); + if (descrs[i].disposition == MACH_MSG_TYPE_MOVE_RECEIVE) { + receive_rights.emplace_back(descrs[i].name); + } else { + MOZ_RELEASE_ASSERT(descrs[i].disposition == MACH_MSG_TYPE_MOVE_SEND); + send_rights.emplace_back(descrs[i].name); + } } return true; @@ -188,6 +195,7 @@ mozilla::Result MachHandleProcessCheckInSync( mach_port_t endpoint, pid_t child_pid, mach_msg_timeout_t timeout, const std::vector& send_rights, + std::vector& receive_rights, task_t* child_task) { using mozilla::Err; using mozilla::Ok; @@ -197,6 +205,10 @@ MachHandleProcessCheckInSync( MOZ_ASSERT(send_rights.size() <= mozilla::geckoargs::kMaxPassedMachSendRights, "Child process cannot receive more than kMaxPassedMachSendRights " "during check-in!"); + MOZ_ASSERT( + receive_rights.size() <= mozilla::geckoargs::kMaxPassedMachReceiveRights, + "Child process cannot receive more than kMaxPassedMachReceiveRights " + "during check-in!"); // Receive the check-in message from content. This will contain its 'task_t' // data, and a reply port which can be used to send the reply message. @@ -252,7 +264,8 @@ MachHandleProcessCheckInSync( // with any send rights over to that child process which they should have on // startup. size_t reply_size = sizeof(mach_msg_base_t) + - sizeof(mach_msg_port_descriptor_t) * send_rights.size(); + sizeof(mach_msg_port_descriptor_t) * + (send_rights.size() + receive_rights.size()); mozilla::UniquePtr buffer = mozilla::MakeUnique(reply_size); mach_msg_base_t* reply = reinterpret_cast(buffer.get()); @@ -260,7 +273,8 @@ MachHandleProcessCheckInSync( MACH_MSGH_BITS(MACH_MSG_TYPE_MOVE_SEND_ONCE, 0) | MACH_MSGH_BITS_COMPLEX; reply->header.msgh_size = reply_size; reply->header.msgh_remote_port = request.header.msgh_remote_port; - reply->body.msgh_descriptor_count = send_rights.size(); + reply->body.msgh_descriptor_count = + send_rights.size() + receive_rights.size(); // Fill the descriptors from our mChildArgs. mach_msg_port_descriptor_t* descrs = @@ -270,6 +284,12 @@ MachHandleProcessCheckInSync( descrs[i].disposition = MACH_MSG_TYPE_COPY_SEND; descrs[i].name = send_rights[i].get(); } + for (size_t i = 0; i < receive_rights.size(); ++i) { + size_t j = i + send_rights.size(); + descrs[j].type = MACH_MSG_PORT_DESCRIPTOR; + descrs[j].disposition = MACH_MSG_TYPE_MOVE_RECEIVE; + descrs[j].name = receive_rights[i].release(); + } // Send the reply. kr = mach_msg(&reply->header, MACH_SEND_MSG | MACH_SEND_TIMEOUT, @@ -298,13 +318,16 @@ MachHandleProcessCheckInSync( class MachCheckInListener : public MessageLoopForIO::MachPortWatcher { public: - MachCheckInListener(MachHandleProcessCheckInPromise::Private* promise, - mozilla::UniqueMachReceiveRight endpoint, pid_t child_pid, - std::vector send_rights) + MachCheckInListener( + MachHandleProcessCheckInPromise::Private* promise, + mozilla::UniqueMachReceiveRight endpoint, pid_t child_pid, + std::vector send_rights, + std::vector receive_rights) : promise_(promise), child_pid_(child_pid), endpoint_(std::move(endpoint)), - send_rights_(std::move(send_rights)) {} + send_rights_(std::move(send_rights)), + receive_rights_(std::move(receive_rights)) {} // Start listening for a check-in - can |delete this|. void Start(mozilla::TimeDuration timeout); @@ -326,6 +349,7 @@ class MachCheckInListener : public MessageLoopForIO::MachPortWatcher { MessageLoopForIO::MachPortWatchController watch_controller_; nsCOMPtr timeout_timer_; std::vector send_rights_; + std::vector receive_rights_; }; void MachCheckInListener::Start(mozilla::TimeDuration timeout) { @@ -356,9 +380,9 @@ void MachCheckInListener::OnMachMessageReceived(mach_port_t port) { MOZ_ASSERT(endpoint_.get() == port); task_t task = MACH_PORT_NULL; - auto result = - MachHandleProcessCheckInSync(endpoint_.get(), child_pid_, - /* timeout */ 0, send_rights_, &task); + auto result = MachHandleProcessCheckInSync(endpoint_.get(), child_pid_, + /* timeout */ 0, send_rights_, + receive_rights_, &task); CompleteAndDelete(result.map([&](const mozilla::Ok&) { return task; })); } @@ -388,13 +412,14 @@ void MachCheckInListener::CompleteAndDelete( RefPtr MachHandleProcessCheckIn( mozilla::UniqueMachReceiveRight endpoint, pid_t child_pid, mozilla::TimeDuration timeout, - std::vector send_rights) { + std::vector send_rights, + std::vector receive_rights) { mozilla::ipc::AssertIOThread(); auto promise = mozilla::MakeRefPtr(__func__); (new MachCheckInListener(promise, std::move(endpoint), child_pid, - std::move(send_rights))) + std::move(send_rights), std::move(receive_rights))) ->Start(timeout); return promise; } diff --git a/ipc/chromium/src/chrome/common/mach_ipc_mac.h b/ipc/chromium/src/chrome/common/mach_ipc_mac.h index d2d62fdc1b97f..30b0f0b034bd6 100644 --- a/ipc/chromium/src/chrome/common/mach_ipc_mac.h +++ b/ipc/chromium/src/chrome/common/mach_ipc_mac.h @@ -44,7 +44,8 @@ kern_return_t MachReceivePortSendRight( // the parent process. bool MachChildProcessCheckIn( const char* bootstrap_service_name, mach_msg_timeout_t timeout, - std::vector& send_rights); + std::vector& send_rights, + std::vector& receive_rights); //============================================================================== // Called by MacProcessLauncher to transfer ports to the child process, and @@ -54,7 +55,8 @@ using MachHandleProcessCheckInPromise = RefPtr MachHandleProcessCheckIn( mozilla::UniqueMachReceiveRight endpoint, pid_t child_pid, mozilla::TimeDuration timeout, - std::vector send_rights); + std::vector send_rights, + std::vector receive_rights); #endif #endif // BASE_MACH_IPC_MAC_H_ diff --git a/ipc/glue/GeckoChildProcessHost.cpp b/ipc/glue/GeckoChildProcessHost.cpp index 732eefff44fbb..ef75406acf6bf 100644 --- a/ipc/glue/GeckoChildProcessHost.cpp +++ b/ipc/glue/GeckoChildProcessHost.cpp @@ -1137,11 +1137,7 @@ Result BaseProcessLauncher::DoSetup() { geckoargs::sCrashReporter.Put(std::move(childCrashFd), mChildArgs); #endif // XP_UNIX && !XP_IOS - UniqueFileHandle crashHelperClientFd = - CrashReporter::RegisterChildIPCChannel(); - if (crashHelperClientFd) { - geckoargs::sCrashHelper.Put(std::move(crashHelperClientFd), mChildArgs); - } else { + if (!CrashReporter::RegisterChildIPCChannel(mChildArgs)) { NS_WARNING("Could not create an IPC channel to the crash helper"); } } @@ -1548,10 +1544,12 @@ RefPtr MacProcessLauncher::DoLaunch() { // Wait for the child process to send us its 'task_t' data, then // send it the mach send/receive rights which are being passed on // the commandline. - return MachHandleProcessCheckIn(std::move(self->mParentRecvPort), - base::GetProcId(aResults.mHandle), - mozilla::TimeDuration::FromSeconds(10), - std::move(self->mChildArgs.mSendRights)) + return MachHandleProcessCheckIn( + std::move(self->mParentRecvPort), + base::GetProcId(aResults.mHandle), + mozilla::TimeDuration::FromSeconds(10), + std::move(self->mChildArgs.mSendRights), + std::move(self->mChildArgs.mReceiveRights)) ->Then( XRE_GetAsyncIOEventTarget(), __func__, [self, results = std::move(aResults)](task_t aTask) mutable { diff --git a/js/loader/ImportMap.cpp b/js/loader/ImportMap.cpp index a5b6a213875b9..6a11e1365ddca 100644 --- a/js/loader/ImportMap.cpp +++ b/js/loader/ImportMap.cpp @@ -408,7 +408,8 @@ UniquePtr ImportMap::ParseString( } MOZ_ASSERT(exn.isObject()); Rooted obj(aCx, &exn.toObject()); - JSErrorReport* err = JS_ErrorFromException(aCx, obj); + JS::BorrowedErrorReport err(aCx); + MOZ_ALWAYS_TRUE(JS_ErrorFromException(aCx, obj, err)); if (err->exnType == JSEXN_SYNTAXERR) { JS_ClearPendingException(aCx); JS_ReportErrorNumberASCII(aCx, js::GetErrorMessage, nullptr, diff --git a/js/public/ErrorReport.h b/js/public/ErrorReport.h index 18852c245509e..6b711f051dde5 100644 --- a/js/public/ErrorReport.h +++ b/js/public/ErrorReport.h @@ -32,6 +32,7 @@ #include "js/AllocPolicy.h" #include "js/CharacterEncoding.h" // JS::ConstUTF8CharsZ #include "js/ColumnNumber.h" // JS::ColumnNumberOneOrigin +#include "js/Exception.h" // JS::BorrowedErrorReport #include "js/RootingAPI.h" // JS::HandleObject, JS::RootedObject #include "js/UniquePtr.h" // js::UniquePtr #include "js/Value.h" // JS::Value @@ -414,14 +415,14 @@ struct MOZ_STACK_CLASS JS_PUBLIC_API ErrorReportBuilder { JSString* maybeCreateReportFromDOMException(JS::HandleObject obj, JSContext* cx); - // We may have a provided JSErrorReport, so need a way to represent that. + // If non-nullptr, this is either |&ownedReport| or |borrowedReport.report_|. JSErrorReport* reportp; // Or we may need to synthesize a JSErrorReport one of our own. JSErrorReport ownedReport; - // Root our exception value to keep a possibly borrowed |reportp| alive. - JS::RootedObject exnObject; + // Used to keep a possibly borrowed |reportp| alive. + JS::BorrowedErrorReport borrowedReport; // And for our filename. JS::UniqueChars filename; diff --git a/js/public/Exception.h b/js/public/Exception.h index a2e0c14613639..4f62cd380aa36 100644 --- a/js/public/Exception.h +++ b/js/public/Exception.h @@ -27,6 +27,37 @@ enum class ExceptionStackBehavior : bool { // retrieved by JS::GetPendingExceptionStack. Capture }; + +// Represents a |JSErrorReport*| borrowed from an ErrorObject. The object root +// ensures the error report won't be freed in the scope of this class. +// +// Typical usage: +// +// BorrowedErrorReport report(cx); +// if (JS_ErrorFromException(cx, obj, report)) { +// // ... Use report->exnType, report.get(), etc. +// } +class MOZ_RAII BorrowedErrorReport { + Rooted owner_; + JSErrorReport* report_ = nullptr; + + public: + explicit BorrowedErrorReport(JSContext* cx) : owner_(cx) {} + + void init(JSObject* owner, JSErrorReport* report) { + MOZ_ASSERT(owner); + MOZ_ASSERT(report); + owner_ = owner; + report_ = report; + } + + JSErrorReport* get() const { + MOZ_ASSERT(report_); + return report_; + } + const JSErrorReport* operator->() const { return get(); } +}; + } // namespace JS extern JS_PUBLIC_API bool JS_IsExceptionPending(JSContext* cx); @@ -78,13 +109,13 @@ extern JS_PUBLIC_API void JS_ClearPendingException(JSContext* cx); /** * If the given object is an exception object, the exception will have (or be - * able to lazily create) an error report struct, and this function will return - * the address of that struct. Otherwise, it returns nullptr. The lifetime - * of the error report struct that might be returned is the same as the - * lifetime of the exception object. + * able to lazily create) an error report struct, and this function will + * populate |errorReport| with it and return true. Otherwise, returns false. + * + * See |BorrowedErrorReport| for a usage example. */ -extern JS_PUBLIC_API JSErrorReport* JS_ErrorFromException(JSContext* cx, - JS::HandleObject obj); +extern JS_PUBLIC_API bool JS_ErrorFromException( + JSContext* cx, JS::HandleObject obj, JS::BorrowedErrorReport& errorReport); namespace JS { diff --git a/js/public/JitCodeAPI.h b/js/public/JitCodeAPI.h new file mode 100644 index 0000000000000..3d969b7ca70ce --- /dev/null +++ b/js/public/JitCodeAPI.h @@ -0,0 +1,42 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 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/. */ + +/* SpiderMonkey API for obtaining JitCode information. */ + +#ifndef js_JitCodeAPI_h +#define js_JitCodeAPI_h + +#include "js/AllocPolicy.h" +#include "js/ColumnNumber.h" // JS::LimitedColumnNumberOneOrigin +#include "js/Vector.h" + +namespace JS { + +struct JitCodeSourceInfo { + uint32_t offset = 0; + + // Line number (1-origin). + uint32_t lineno = 0; + // Column number in UTF-16 code units. + JS::LimitedColumnNumberOneOrigin colno; +}; + +using SourceInfoVector = + js::Vector; + +struct JitCodeRecord { + uint64_t code_addr = 0; + uint32_t instructionSize = 0; + + SourceInfoVector sourceInfo; +}; + +// Lookup a JitCodeRecord by code address +// Returns nullptr if not found +JitCodeRecord* LookupJitCodeRecord(uint64_t addr); + +} // namespace JS + +#endif /* js_JitCodeAPI_h */ diff --git a/js/public/JitPerfSpewer.h b/js/public/JitPerfSpewer.h new file mode 100644 index 0000000000000..c1b27f1144f15 --- /dev/null +++ b/js/public/JitPerfSpewer.h @@ -0,0 +1,22 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 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/. */ + +/* SpiderMonkey JIT perf spewer control API. */ + +#ifndef js_JitPerfSpewer_h +#define js_JitPerfSpewer_h + +namespace js { +namespace jit { + +// Reset the perf spewer state and enable/disable gecko profiling mode. +// When enabled, JIT code generation will collect accurate native-to-bytecode +// mappings for use by the Gecko Profiler. +void ResetPerfSpewer(bool enabled); + +} // namespace jit +} // namespace js + +#endif /* js_JitPerfSpewer_h */ diff --git a/js/public/ProfilingFrameIterator.h b/js/public/ProfilingFrameIterator.h index fb001b0a9057b..ef0b495bef581 100644 --- a/js/public/ProfilingFrameIterator.h +++ b/js/public/ProfilingFrameIterator.h @@ -32,6 +32,10 @@ struct CallStackFrameInfo { // The script source ID for this frame. Used to identify which script source // this frame belongs to. uint32_t sourceId; + // Line number (1-origin, 0 means no line info available) + uint32_t line; + // Column number (1-origin, 0 means no column info available) + uint32_t column; }; } // namespace jit @@ -173,6 +177,8 @@ class MOZ_NON_PARAM JS_PUBLIC_API ProfilingFrameIterator { JSScript* interpreterScript; uint64_t realmID; uint32_t sourceId; + uint32_t line; + uint32_t column; public: void* returnAddress() const { @@ -248,16 +254,15 @@ class MOZ_STACK_CLASS ProfiledFrameHandle { js::jit::JitcodeGlobalEntry& entry_; void* addr_; void* canonicalAddr_; - const char* label_; - uint32_t sourceId_; + js::jit::CallStackFrameInfo frameInfo_; uint32_t depth_; ProfiledFrameHandle(JSRuntime* rt, js::jit::JitcodeGlobalEntry& entry, - void* addr, const char* label, uint32_t sourceId, + void* addr, const js::jit::CallStackFrameInfo& frameInfo, uint32_t depth); public: - const char* label() const { return label_; } + const char* label() const { return frameInfo_.label; } uint32_t depth() const { return depth_; } void* canonicalAddress() const { return canonicalAddr_; } @@ -265,7 +270,11 @@ class MOZ_STACK_CLASS ProfiledFrameHandle { JS_PUBLIC_API uint64_t realmID() const; - JS_PUBLIC_API uint32_t sourceId() const; + JS_PUBLIC_API uint32_t sourceId() const { return frameInfo_.sourceId; } + + JS_PUBLIC_API uint32_t line() const { return frameInfo_.line; } + + JS_PUBLIC_API uint32_t column() const { return frameInfo_.column; } }; class ProfiledFrameRange { diff --git a/js/public/friend/ErrorNumbers.msg b/js/public/friend/ErrorNumbers.msg index 34cbfd39664c3..2214d9d18820a 100644 --- a/js/public/friend/ErrorNumbers.msg +++ b/js/public/friend/ErrorNumbers.msg @@ -908,6 +908,7 @@ MSG_DEF(JSMSG_TEMPORAL_PLAIN_TIME_INVALID_VALUE, 4, JSEXN_RANGEERR, "time val MSG_DEF(JSMSG_TEMPORAL_PLAIN_TIME_MISSING_UNIT, 0, JSEXN_TYPEERR, "Time-like objects must have at least one time unit") MSG_DEF(JSMSG_TEMPORAL_PLAIN_MONTH_DAY_INVALID, 0, JSEXN_RANGEERR, "month-day must be valid ISO date values") MSG_DEF(JSMSG_TEMPORAL_PLAIN_YEAR_MONTH_INVALID, 0, JSEXN_RANGEERR, "year-month must be valid ISO date values") +MSG_DEF(JSMSG_TEMPORAL_PLAIN_YEAR_MONTH_BAD_DURATION, 1, JSEXN_RANGEERR, "duration \"{0}\" property must be zero") MSG_DEF(JSMSG_TEMPORAL_ZONED_DATE_TIME_NO_TIME_FOUND, 0, JSEXN_RANGEERR, "date-time can't be represented in the given time zone") MSG_DEF(JSMSG_TEMPORAL_ZONED_DATE_TIME_INCONSISTENT_INSTANT, 0, JSEXN_RANGEERR, "time zone computed inconsistent instant values") MSG_DEF(JSMSG_TEMPORAL_PARSER_NEGATIVE_ZERO_YEAR, 1, JSEXN_RANGEERR, "can't parse {0}: year 0 must not start with \"-\"") diff --git a/js/src/builtin/TestingFunctions.cpp b/js/src/builtin/TestingFunctions.cpp index e2ad748a7c5e9..eb4e72fc44917 100644 --- a/js/src/builtin/TestingFunctions.cpp +++ b/js/src/builtin/TestingFunctions.cpp @@ -5128,10 +5128,13 @@ static bool ReadGeckoProfilingStack(JSContext* cx, unsigned argc, Value* vp) { } struct InlineFrameInfo { - InlineFrameInfo(const char* kind, UniqueChars label) - : kind(kind), label(std::move(label)) {} + InlineFrameInfo(const char* kind, UniqueChars label, uint32_t line, + uint32_t column) + : kind(kind), label(std::move(label)), line(line), column(column) {} const char* kind; UniqueChars label; + uint32_t line; + uint32_t column; }; Vector, 0, TempAllocPolicy> @@ -5176,7 +5179,8 @@ static bool ReadGeckoProfilingStack(JSContext* cx, unsigned argc, Value* vp) { return false; } - if (!frameInfo.back().emplaceBack(frameKindStr, std::move(label))) { + if (!frameInfo.back().emplaceBack(frameKindStr, std::move(label), + frames[i].line, frames[i].column)) { return false; } } @@ -5225,6 +5229,16 @@ static bool ReadGeckoProfilingStack(JSContext* cx, unsigned argc, Value* vp) { return false; } + if (!JS_DefineProperty(cx, inlineFrameInfo, "line", inlineFrame.line, + propAttrs)) { + return false; + } + + if (!JS_DefineProperty(cx, inlineFrameInfo, "column", inlineFrame.column, + propAttrs)) { + return false; + } + idx = PropertyKey::Int(inlineFrameNo); if (!JS_DefinePropertyById(cx, inlineStack, idx, inlineFrameInfo, 0)) { return false; diff --git a/js/src/builtin/temporal/Calendar.cpp b/js/src/builtin/temporal/Calendar.cpp index 73516f6bde54e..38fb48a4d09d3 100644 --- a/js/src/builtin/temporal/Calendar.cpp +++ b/js/src/builtin/temporal/Calendar.cpp @@ -1899,6 +1899,123 @@ static bool CalendarDateToISO(JSContext* cx, CalendarId calendar, return NonISOCalendarDateToISO(cx, calendar, fields, overflow, result); } +/** + * NonISOMonthDayToISOReferenceDate ( calendar, fields, overflow ) + * + * Return the reference ISO year for "chinese" and "dangi" calendars. Return + * zero if no reference ISO year can be determined. + */ +static int32_t EastAsianCalendarReferenceISOYear(CalendarId calendar, + MonthCode monthCode, + int32_t day) { + MOZ_ASSERT(calendar == CalendarId::Chinese || calendar == CalendarId::Dangi); + MOZ_ASSERT(day > 0); + + if (day < 30) { + switch (monthCode.code()) { + case MonthCode::Code::M01: + case MonthCode::Code::M02: + case MonthCode::Code::M03: + case MonthCode::Code::M04: + case MonthCode::Code::M05: + case MonthCode::Code::M06: + case MonthCode::Code::M07: + case MonthCode::Code::M08: + case MonthCode::Code::M09: + case MonthCode::Code::M10: + case MonthCode::Code::M11: + case MonthCode::Code::M12: + return 1972; + + case MonthCode::Code::M01L: + return 0; + case MonthCode::Code::M02L: + return 1947; + case MonthCode::Code::M03L: + return 1966; + case MonthCode::Code::M04L: + return 1963; + case MonthCode::Code::M05L: + return 1971; + case MonthCode::Code::M06L: + return 1960; + case MonthCode::Code::M07L: + return 1968; + case MonthCode::Code::M08L: + return 1957; + case MonthCode::Code::M09L: + return 2014; + case MonthCode::Code::M10L: + return 1984; + case MonthCode::Code::M11L: + return day <= 10 ? 2033 : 2034; + case MonthCode::Code::M12L: + return 0; + + case MonthCode::Code::Invalid: + case MonthCode::Code::M13: + break; + } + } else { + switch (monthCode.code()) { + case MonthCode::Code::M01: + return 1970; + case MonthCode::Code::M02: + return 1972; + case MonthCode::Code::M03: + return calendar == CalendarId::Chinese ? 1966 : 1968; + case MonthCode::Code::M04: + return 1970; + case MonthCode::Code::M05: + return 1972; + case MonthCode::Code::M06: + return 1971; + case MonthCode::Code::M07: + return 1972; + case MonthCode::Code::M08: + return 1971; + case MonthCode::Code::M09: + return 1972; + case MonthCode::Code::M10: + return 1972; + case MonthCode::Code::M11: + return 1970; + case MonthCode::Code::M12: + return 1972; + + case MonthCode::Code::M01L: + return 0; + case MonthCode::Code::M02L: + return 0; + case MonthCode::Code::M03L: + return 1955; + case MonthCode::Code::M04L: + return 1944; + case MonthCode::Code::M05L: + return 1952; + case MonthCode::Code::M06L: + return 1941; + case MonthCode::Code::M07L: + return 1938; + case MonthCode::Code::M08L: + return 0; + case MonthCode::Code::M09L: + return 0; + case MonthCode::Code::M10L: + return 0; + case MonthCode::Code::M11L: + return 0; + case MonthCode::Code::M12L: + return 0; + + case MonthCode::Code::Invalid: + case MonthCode::Code::M13: + break; + } + } + MOZ_CRASH("unexpected month code"); +} + /** * CalendarMonthDayToISOReferenceDate ( calendar, fields, overflow ) */ @@ -1907,7 +2024,7 @@ static bool NonISOMonthDayToISOReferenceDate(JSContext* cx, CalendarId calendar, ISODate startISODate, ISODate endISODate, MonthCode monthCode, int32_t day, - UniqueICU4XDate& resultDate) { + UniqueICU4XDate* resultDate) { MOZ_ASSERT(startISODate != endISODate); int32_t direction = startISODate > endISODate ? -1 : 1; @@ -1948,11 +2065,11 @@ static bool NonISOMonthDayToISOReferenceDate(JSContext* cx, CalendarId calendar, // Stop searching if |endISODate| was reached. if (direction < 0 ? isoDate < endISODate : isoDate > endISODate) { - resultDate = nullptr; + *resultDate = nullptr; return true; } - resultDate = result.unwrap(); + *resultDate = result.unwrap(); return true; } @@ -1996,7 +2113,7 @@ static bool NonISOMonthDayToISOReferenceDate(JSContext* cx, CalendarId calendar, return false; } - resultDate = nullptr; + *resultDate = nullptr; return true; } @@ -2064,6 +2181,18 @@ static bool NonISOMonthDayToISOReferenceDate(JSContext* cx, CalendarId calendar, } else { MOZ_ASSERT(monthCode != MonthCode{}); + if (calendar == CalendarId::Chinese || calendar == CalendarId::Dangi) { + int32_t referenceYear = + EastAsianCalendarReferenceISOYear(calendar, monthCode, day); + if (referenceYear == 0) { + if (overflow == TemporalOverflow::Reject) { + ReportCalendarFieldOverflow(cx, "day", day); + return false; + } + monthCode = MonthCode{monthCode.ordinal()}; + } + } + // Constrain `day` to maximum possible day of the input month. int32_t maxDaysInMonth = CalendarDaysInMonth(calendar, monthCode).second; if (overflow == TemporalOverflow::Constrain) { @@ -2092,15 +2221,15 @@ static bool NonISOMonthDayToISOReferenceDate(JSContext* cx, CalendarId calendar, // If there is still no such date, it is the latest ISO 8601 date // corresponding to the calendar date on or before December 31, 1899. // - // Year -8000 is sufficient to find all possible month-days, even for - // rare cases like `{calendar: "chinese", monthCode: "M09L", day: 30}`. - {ISODate{1899, 12, 31}, ISODate{-8000, 1, 1}}, + // Year 1600 is sufficient to find all possible month-days, even for + // rare cases like `{calendar: "chinese", monthCode: "M08L", day: 30}`. + {ISODate{1899, 12, 31}, ISODate{1600, 1, 1}}, }; UniqueICU4XDate date; for (auto& [start, end] : candidates) { if (!NonISOMonthDayToISOReferenceDate(cx, calendar, cal.get(), start, end, - monthCode, day, date)) { + monthCode, day, &date)) { return false; } if (date) { @@ -2108,14 +2237,24 @@ static bool NonISOMonthDayToISOReferenceDate(JSContext* cx, CalendarId calendar, } } - // We shouldn't end up here with |maxIterations == 10'000|, but just in case - // still handle this case and report an error. + // We shouldn't end up here, but just in case still handle a missing date and + // report an error. if (!date) { ReportCalendarFieldOverflow(cx, "day", day); return false; } *result = ToISODate(date.get()); + + // FIXME: spec bug - missing handling for reference years when input fields + // have a year component: + // https://github.com/tc39/proposal-intl-era-monthcode/issues/113 + MOZ_ASSERT_IF( + (calendar == CalendarId::Chinese || calendar == CalendarId::Dangi) && + !(fields.has(CalendarField::Year) || + fields.has(CalendarField::EraYear)), + result->year == + EastAsianCalendarReferenceISOYear(calendar, monthCode, day)); return true; } @@ -2281,6 +2420,12 @@ bool js::temporal::CalendarEra(JSContext* cx, Handle calendar, return true; } + // TODO: Remove when we update to the next ICU4X release. + // See: https://github.com/unicode-org/icu4x/pull/7503 + if (calendarId == CalendarId::Japanese && date.year <= 1872) { + calendarId = CalendarId::Gregorian; + } + auto era = EraCode::Standard; // Call into ICU4X if the calendar has more than one era. @@ -2341,6 +2486,12 @@ bool js::temporal::CalendarEraYear(JSContext* cx, } MOZ_ASSERT(eras.size() > 1); + // TODO: Remove when we update to the next ICU4X release. + // See: https://github.com/unicode-org/icu4x/pull/7503 + if (calendarId == CalendarId::Japanese && date.year <= 1872) { + calendarId = CalendarId::Gregorian; + } + auto cal = CreateICU4XCalendar(calendarId); auto dt = CreateICU4XDate(cx, date, calendarId, cal.get()); if (!dt) { @@ -3169,6 +3320,31 @@ static int32_t CompareCalendarDate(const CalendarDateWithOrdinalMonth& one, ISODate{two.year, two.month, two.day}); } +/** + * ISODateSurpasses ( sign, baseDate, isoDate2, years, months, weeks, days ) + */ +static inline bool ISODateSurpasses(int32_t sign, const ISODate& one, + const ISODate& two) { + return CompareISODate(one, two) * sign > 0; +} + +/** + * CompareSurpasses ( sign, year, monthOrCode, day, target ) + */ +static inline bool CompareSurpasses(int32_t sign, const CalendarDate& one, + const CalendarDate& two) { + return CompareCalendarDate(one, two) * sign > 0; +} + +/** + * CompareSurpasses ( sign, year, monthOrCode, day, target ) + */ +static inline bool CompareSurpasses(int32_t sign, + const CalendarDateWithOrdinalMonth& one, + const CalendarDateWithOrdinalMonth& two) { + return CompareCalendarDate(one, two) * sign > 0; +} + static CalendarDate ToCalendarDate(CalendarId calendarId, const icu4x::capi::Date* dt) { int32_t year = CalendarDateYear(calendarId, dt); @@ -3219,6 +3395,7 @@ static bool AddYearMonthDuration(JSContext* cx, CalendarId calendarId, const icu4x::capi::Calendar* calendar, const CalendarDate& calendarDate, const DateDuration& duration, + TemporalOverflow overflow, CalendarDate* result) { MOZ_ASSERT(CalendarHasLeapMonths(calendarId)); MOZ_ASSERT(IsValidDuration(duration)); @@ -3234,17 +3411,17 @@ static bool AddYearMonthDuration(JSContext* cx, CalendarId calendarId, } year = durationYear.value(); + // Regulate according to |overflow|. + auto firstDayOfMonth = CreateDateFromCodes(cx, calendarId, calendar, year, + monthCode, 1, overflow); + if (!firstDayOfMonth) { + return false; + } + // Months per year are variable, so we have construct a new date for each // year to balance the years and months. int64_t months = duration.months; if (months != 0) { - auto firstDayOfMonth = - CreateDateFromCodes(cx, calendarId, calendar, year, monthCode, 1, - TemporalOverflow::Constrain); - if (!firstDayOfMonth) { - return false; - } - if (months > 0) { while (true) { // Check if adding |months| is still in the current year. @@ -3326,7 +3503,7 @@ static bool AddNonISODate(JSContext* cx, CalendarId calendarId, } else { auto date = ToCalendarDate(calendarId, dt.get()); if (!AddYearMonthDuration(cx, calendarId, cal.get(), date, duration, - &calendarDate)) { + overflow, &calendarDate)) { return false; } } @@ -3445,37 +3622,30 @@ bool js::temporal::CalendarDateAdd(JSContext* cx, */ static DateDuration DifferenceISODate(const ISODate& one, const ISODate& two, TemporalUnit largestUnit) { - MOZ_ASSERT(IsValidISODate(one)); - MOZ_ASSERT(IsValidISODate(two)); - - // Both inputs are also within the date limits. + MOZ_ASSERT(one != two); MOZ_ASSERT(ISODateWithinLimits(one)); MOZ_ASSERT(ISODateWithinLimits(two)); MOZ_ASSERT(TemporalUnit::Year <= largestUnit && largestUnit <= TemporalUnit::Day); - // Step 1.a. + // Step 3.a. int32_t sign = -CompareISODate(one, two); + MOZ_ASSERT(sign != 0); - // Step 1.b. - if (sign == 0) { - return {}; - } - - // Step 1.c. + // Step 3.b. int32_t years = 0; - // Step 1.e. (Reordered) + // Step 3.d. (Reordered) int32_t months = 0; - // Steps 1.d and 1.f. + // Steps 3.c and 3.e. if (largestUnit == TemporalUnit::Year || largestUnit == TemporalUnit::Month) { years = two.year - one.year; months = two.month - one.month; auto intermediate = ISODate{one.year + years, one.month, one.day}; - if (CompareISODate(intermediate, two) * sign > 0) { + if (ISODateSurpasses(sign, intermediate, two)) { years -= sign; months += 12 * sign; } @@ -3488,7 +3658,7 @@ static DateDuration DifferenceISODate(const ISODate& one, const ISODate& two, intermediate.month += 12; intermediate.year -= 1; } - if (CompareISODate(intermediate, two) * sign > 0) { + if (ISODateSurpasses(sign, intermediate, two)) { months -= sign; } @@ -3503,19 +3673,19 @@ static DateDuration DifferenceISODate(const ISODate& one, const ISODate& two, auto constrained = ConstrainISODate( ISODate{int32_t(intermediate.year), intermediate.month, one.day}); - // Step 1.g. + // Step 3.f. int64_t weeks = 0; - // Steps 1.i-k. + // Steps 3.h-j. int64_t days = MakeDay(two) - MakeDay(constrained); - // Step 1.h. (Weeks computed from days.) + // Step 3.g. (Weeks computed from days.) if (largestUnit == TemporalUnit::Week) { weeks = days / 7; days %= 7; } - // Step 1.l. + // Step 3.k. auto result = DateDuration{ int64_t(years), int64_t(months), @@ -3526,21 +3696,24 @@ static DateDuration DifferenceISODate(const ISODate& one, const ISODate& two, return result; } +/** + * NonISODateUntil ( calendar, one, two, largestUnit ) + */ static bool DifferenceNonISODate(JSContext* cx, CalendarId calendarId, const ISODate& one, const ISODate& two, TemporalUnit largestUnit, DateDuration* result) { - // Both inputs are also within the date limits. + MOZ_ASSERT(!CalendarHasLeapMonths(calendarId)); + MOZ_ASSERT(one != two); MOZ_ASSERT(ISODateWithinLimits(one)); MOZ_ASSERT(ISODateWithinLimits(two)); MOZ_ASSERT(TemporalUnit::Year <= largestUnit && largestUnit <= TemporalUnit::Month); - if (one == two) { - *result = {}; - return true; - } + // If the months per year are fixed, we can use a modified DifferenceISODate + // implementation to compute the date duration. + const int32_t monthsPerYear = CalendarMonthsPerYear(calendarId); auto cal = CreateICU4XCalendar(calendarId); @@ -3554,171 +3727,213 @@ static bool DifferenceNonISODate(JSContext* cx, CalendarId calendarId, return false; } - int32_t years = 0; - int32_t months = 0; + auto oneDate = ToCalendarDateWithOrdinalMonth(calendarId, dtOne.get()); + auto twoDate = ToCalendarDateWithOrdinalMonth(calendarId, dtTwo.get()); - ISODate constrainedIso; - if (!CalendarHasLeapMonths(calendarId)) { - // If the months per year are fixed, we can use a modified DifferenceISODate - // implementation to compute the date duration. - int32_t monthsPerYear = CalendarMonthsPerYear(calendarId); + int32_t sign = -CompareCalendarDate(oneDate, twoDate); + MOZ_ASSERT(sign != 0); - auto oneDate = ToCalendarDateWithOrdinalMonth(calendarId, dtOne.get()); - auto twoDate = ToCalendarDateWithOrdinalMonth(calendarId, dtTwo.get()); + int32_t years = twoDate.year - oneDate.year; + int32_t months = twoDate.month - oneDate.month; - int32_t sign = -CompareCalendarDate(oneDate, twoDate); - MOZ_ASSERT(sign != 0); + // If |oneDate + years| surpasses |twoDate|, reduce |years| by one and add + // |monthsPerYear| to |months|. The next step will balance the intermediate + // result. + auto intermediate = CalendarDateWithOrdinalMonth{oneDate.year + years, + oneDate.month, oneDate.day}; + if (CompareSurpasses(sign, intermediate, twoDate)) { + years -= sign; + months += monthsPerYear * sign; + } - years = twoDate.year - oneDate.year; - months = twoDate.month - oneDate.month; + // Add both |years| and |months| and then balance the intermediate result to + // ensure its month is within the valid bounds. + intermediate = CalendarDateWithOrdinalMonth{ + oneDate.year + years, oneDate.month + months, oneDate.day}; + if (intermediate.month > monthsPerYear) { + intermediate.month -= monthsPerYear; + intermediate.year += 1; + } else if (intermediate.month < 1) { + intermediate.month += monthsPerYear; + intermediate.year -= 1; + } - // If |oneDate + years| surpasses |twoDate|, reduce |years| by one and add - // |monthsPerYear| to |months|. The next step will balance the intermediate - // result. - auto intermediate = CalendarDateWithOrdinalMonth{ - oneDate.year + years, oneDate.month, oneDate.day}; - if (CompareCalendarDate(intermediate, twoDate) * sign > 0) { - years -= sign; - months += monthsPerYear * sign; - } + // If |intermediate| surpasses |twoDate|, reduce |month| by one. + if (CompareSurpasses(sign, intermediate, twoDate)) { + months -= sign; + } - // Add both |years| and |months| and then balance the intermediate result to - // ensure its month is within the valid bounds. - intermediate = CalendarDateWithOrdinalMonth{ - oneDate.year + years, oneDate.month + months, oneDate.day}; - if (intermediate.month > monthsPerYear) { - intermediate.month -= monthsPerYear; - intermediate.year += 1; - } else if (intermediate.month < 1) { - intermediate.month += monthsPerYear; - intermediate.year -= 1; - } + // Convert years to months if necessary. + if (largestUnit == TemporalUnit::Month) { + months += years * monthsPerYear; + years = 0; + } - // If |intermediate| surpasses |twoDate|, reduce |month| by one. - if (CompareCalendarDate(intermediate, twoDate) * sign > 0) { - months -= sign; - } + // Constrain to a proper calendar date. + auto balanced = BalanceYearMonth(oneDate.year + years, oneDate.month + months, + monthsPerYear); - // Convert years to months if necessary. - if (largestUnit == TemporalUnit::Month) { - months += years * monthsPerYear; - years = 0; - } + auto constrained = + CreateDateFrom(cx, calendarId, cal.get(), balanced.year, balanced.month, + oneDate.day, TemporalOverflow::Constrain); + if (!constrained) { + return false; + } - // Constrain to a proper date. - auto balanced = BalanceYearMonth(oneDate.year + years, - oneDate.month + months, monthsPerYear); + auto constrainedIso = ToISODate(constrained.get()); + MOZ_ASSERT(!ISODateSurpasses(sign, constrainedIso, two), + "constrained doesn't surpass two"); - auto constrained = - CreateDateFrom(cx, calendarId, cal.get(), balanced.year, balanced.month, - oneDate.day, TemporalOverflow::Constrain); - if (!constrained) { - return false; - } - constrainedIso = ToISODate(constrained.get()); + int64_t days = MakeDay(two) - MakeDay(constrainedIso); - MOZ_ASSERT(CompareISODate(constrainedIso, two) * sign <= 0, - "constrained doesn't surpass two"); - } else { - auto oneDate = ToCalendarDate(calendarId, dtOne.get()); - auto twoDate = ToCalendarDate(calendarId, dtTwo.get()); + *result = DateDuration{ + int64_t(years), + int64_t(months), + 0, + int64_t(days), + }; + MOZ_ASSERT(IsValidDuration(*result)); + return true; +} - int32_t sign = -CompareCalendarDate(oneDate, twoDate); - MOZ_ASSERT(sign != 0); +/** + * NonISODateUntil ( calendar, one, two, largestUnit ) + */ +static bool DifferenceNonISODateWithLeapMonth( + JSContext* cx, CalendarId calendarId, const ISODate& one, + const ISODate& two, TemporalUnit largestUnit, DateDuration* result) { + MOZ_ASSERT(CalendarHasLeapMonths(calendarId)); + MOZ_ASSERT(one != two); + MOZ_ASSERT(ISODateWithinLimits(one)); + MOZ_ASSERT(ISODateWithinLimits(two)); - years = twoDate.year - oneDate.year; + MOZ_ASSERT(TemporalUnit::Year <= largestUnit && + largestUnit <= TemporalUnit::Month); - // If |oneDate + years| surpasses |twoDate|, reduce |years| by one and add - // |monthsPerYear| to |months|. The next step will balance the intermediate - // result. - auto constrained = CreateDateFromCodes( - cx, calendarId, cal.get(), oneDate.year + years, oneDate.monthCode, - oneDate.day, TemporalOverflow::Constrain); - if (!constrained) { - return false; - } + auto cal = CreateICU4XCalendar(calendarId); - auto constrainedDate = ToCalendarDate(calendarId, constrained.get()); - if (CompareCalendarDate(constrainedDate, twoDate) * sign > 0) { - years -= sign; - } + auto dtOne = CreateICU4XDate(cx, one, calendarId, cal.get()); + if (!dtOne) { + return false; + } - // Add as many months as possible without surpassing |twoDate|. - while (true) { - CalendarDate intermediateDate; - if (!AddYearMonthDuration(cx, calendarId, cal.get(), oneDate, - {years, months + sign}, &intermediateDate)) { - return false; - } - if (CompareCalendarDate(intermediateDate, twoDate) * sign > 0) { - break; - } - months += sign; - constrainedDate = intermediateDate; - } - MOZ_ASSERT(std::abs(months) < CalendarMonthsPerYear(calendarId)); + auto dtTwo = CreateICU4XDate(cx, two, calendarId, cal.get()); + if (!dtTwo) { + return false; + } - // Convert years to months if necessary. - if (largestUnit == TemporalUnit::Month && years != 0) { - auto monthsUntilEndOfYear = [](const icu4x::capi::Date* date) { - int32_t month = OrdinalMonth(date); - int32_t monthsInYear = MonthsInYear(date); - MOZ_ASSERT(1 <= month && month <= monthsInYear); + auto oneDate = ToCalendarDate(calendarId, dtOne.get()); + auto twoDate = ToCalendarDate(calendarId, dtTwo.get()); - return monthsInYear - month + 1; - }; + int32_t sign = -CompareCalendarDate(oneDate, twoDate); + MOZ_ASSERT(sign != 0); - auto monthsSinceStartOfYear = [](const icu4x::capi::Date* date) { - return OrdinalMonth(date) - 1; - }; + int32_t years = twoDate.year - oneDate.year; - // Add months until end of year resp. since start of year. - if (sign > 0) { - months += monthsUntilEndOfYear(dtOne.get()); - } else { - months -= monthsSinceStartOfYear(dtOne.get()); - } + // If |oneDate + years| surpasses |twoDate|, reduce |years| by one. The next + // step will balance the intermediate result. + auto unconstrainedDate = + CalendarDate{oneDate.year + years, oneDate.monthCode, oneDate.day}; + if (CompareSurpasses(sign, unconstrainedDate, twoDate)) { + years -= sign; + } - // Months in full year. - for (int32_t y = sign; y != years; y += sign) { - auto dt = - CreateDateFromCodes(cx, calendarId, cal.get(), oneDate.year + y, - MonthCode{1}, 1, TemporalOverflow::Constrain); - if (!dt) { - return false; - } - months += MonthsInYear(dt.get()) * sign; - } + auto constrainedStartOfMonth = + CreateDateFromCodes(cx, calendarId, cal.get(), oneDate.year + years, + oneDate.monthCode, 1, TemporalOverflow::Constrain); + if (!constrainedStartOfMonth) { + return false; + } - // Add months since start of year resp. until end of year. - auto dt = CreateDateFromCodes(cx, calendarId, cal.get(), - oneDate.year + years, oneDate.monthCode, 1, - TemporalOverflow::Constrain); + auto constrainedDateStartOfMonth = + ToCalendarDate(calendarId, constrainedStartOfMonth.get()); + + auto constrainedDate = CalendarDate{ + .year = constrainedDateStartOfMonth.year, + .monthCode = constrainedDateStartOfMonth.monthCode, + .day = oneDate.day, + }; + if (CompareSurpasses(sign, constrainedDate, twoDate)) { + years -= sign; + } + + // Add as many months as possible without surpassing |twoDate|. + int32_t months = 0; + while (true) { + CalendarDate intermediateDate; + if (!AddYearMonthDuration(cx, calendarId, cal.get(), oneDate, + {years, months + sign}, + TemporalOverflow::Constrain, &intermediateDate)) { + return false; + } + if (CompareSurpasses(sign, intermediateDate, twoDate)) { + break; + } + months += sign; + constrainedDate = intermediateDate; + } + MOZ_ASSERT(std::abs(months) < CalendarMonthsPerYear(calendarId)); + + // Convert years to months if necessary. + if (largestUnit == TemporalUnit::Month && years != 0) { + auto monthsUntilEndOfYear = [](const icu4x::capi::Date* date) { + int32_t month = OrdinalMonth(date); + int32_t monthsInYear = MonthsInYear(date); + MOZ_ASSERT(1 <= month && month <= monthsInYear); + + return monthsInYear - month + 1; + }; + + auto monthsSinceStartOfYear = [](const icu4x::capi::Date* date) { + return OrdinalMonth(date) - 1; + }; + + // Add months until end of year resp. since start of year. + if (sign > 0) { + months += monthsUntilEndOfYear(dtOne.get()); + } else { + months -= monthsSinceStartOfYear(dtOne.get()); + } + + // Months in full year. + for (int32_t y = sign; y != years; y += sign) { + auto dt = + CreateDateFromCodes(cx, calendarId, cal.get(), oneDate.year + y, + MonthCode{1}, 1, TemporalOverflow::Constrain); if (!dt) { return false; } - if (sign > 0) { - months += monthsSinceStartOfYear(dt.get()); - } else { - months -= monthsUntilEndOfYear(dt.get()); - } - - years = 0; + months += MonthsInYear(dt.get()) * sign; } - constrained = - CreateDateFromCodes(cx, calendarId, cal.get(), constrainedDate.year, - constrainedDate.monthCode, constrainedDate.day, - TemporalOverflow::Constrain); - if (!constrained) { + // Add months since start of year resp. until end of year. + auto dt = + CreateDateFromCodes(cx, calendarId, cal.get(), oneDate.year + years, + oneDate.monthCode, 1, TemporalOverflow::Constrain); + if (!dt) { return false; } - constrainedIso = ToISODate(constrained.get()); + if (sign > 0) { + months += monthsSinceStartOfYear(dt.get()); + } else { + months -= monthsUntilEndOfYear(dt.get()); + } - MOZ_ASSERT(CompareISODate(constrainedIso, two) * sign <= 0, - "constrained doesn't surpass two"); + years = 0; + } + + auto constrained = + CreateDateFromCodes(cx, calendarId, cal.get(), constrainedDate.year, + constrainedDate.monthCode, constrainedDate.day, + TemporalOverflow::Constrain); + if (!constrained) { + return false; } + auto constrainedIso = ToISODate(constrained.get()); + MOZ_ASSERT(!ISODateSurpasses(sign, constrainedIso, two), + "constrained doesn't surpass two"); + int64_t days = MakeDay(two) - MakeDay(constrainedIso); *result = DateDuration{ @@ -3760,12 +3975,9 @@ static bool NonISODateUntil(JSContext* cx, CalendarId calendarId, *result = DifferenceISODate(one, two, largestUnit); return true; - case CalendarId::Chinese: case CalendarId::Coptic: - case CalendarId::Dangi: case CalendarId::Ethiopian: case CalendarId::EthiopianAmeteAlem: - case CalendarId::Hebrew: case CalendarId::Indian: case CalendarId::IslamicCivil: case CalendarId::IslamicTabular: @@ -3773,6 +3985,12 @@ static bool NonISODateUntil(JSContext* cx, CalendarId calendarId, case CalendarId::Persian: return DifferenceNonISODate(cx, calendarId, one, two, largestUnit, result); + + case CalendarId::Chinese: + case CalendarId::Dangi: + case CalendarId::Hebrew: + return DifferenceNonISODateWithLeapMonth(cx, calendarId, one, two, + largestUnit, result); } MOZ_CRASH("invalid calendar id"); } @@ -3785,16 +4003,23 @@ bool js::temporal::CalendarDateUntil(JSContext* cx, const ISODate& one, const ISODate& two, TemporalUnit largestUnit, DateDuration* result) { + MOZ_ASSERT(ISODateWithinLimits(one)); + MOZ_ASSERT(ISODateWithinLimits(two)); MOZ_ASSERT(largestUnit <= TemporalUnit::Day); - auto calendarId = calendar.identifier(); + // Steps 1-2. + if (one == two) { + *result = {}; + return true; + } - // Step 1. + // Step 3. + auto calendarId = calendar.identifier(); if (calendarId == CalendarId::ISO8601) { *result = DifferenceISODate(one, two, largestUnit); return true; } - // Step 2. + // Step 4. return NonISODateUntil(cx, calendarId, one, two, largestUnit, result); } diff --git a/js/src/builtin/temporal/PlainYearMonth.cpp b/js/src/builtin/temporal/PlainYearMonth.cpp index fd1a3d2273172..a624047ebbf3b 100644 --- a/js/src/builtin/temporal/PlainYearMonth.cpp +++ b/js/src/builtin/temporal/PlainYearMonth.cpp @@ -483,6 +483,34 @@ static bool DifferenceTemporalPlainYearMonth(JSContext* cx, return true; } +const char* NonZeroDurationPartAfterMonths(const Duration& duration) { + if (duration.weeks != 0) { + return "weeks"; + } + if (duration.days != 0) { + return "days"; + } + if (duration.hours != 0) { + return "hours"; + } + if (duration.minutes != 0) { + return "minutes"; + } + if (duration.seconds != 0) { + return "seconds"; + } + if (duration.milliseconds != 0) { + return "milliseconds"; + } + if (duration.microseconds != 0) { + return "microseconds"; + } + if (duration.nanoseconds != 0) { + return "nanoseconds"; + } + return nullptr; +} + /** * AddDurationToYearMonth ( operation, yearMonth, temporalDurationLike, options * ) @@ -503,86 +531,58 @@ static bool AddDurationToYearMonth(JSContext* cx, TemporalAddDuration operation, duration = duration.negate(); } - // Steps 3-4. + // Step 3. + auto internalDuration = ToInternalDurationRecord(duration); + + // Steps 4-5. auto overflow = TemporalOverflow::Constrain; if (args.hasDefined(1)) { - // Step 3. + // Step 4. Rooted options( cx, RequireObjectArg(cx, "options", ToName(operation), args[1])); if (!options) { return false; } - // Step 4. + // Step 5. if (!GetTemporalOverflowOption(cx, options, &overflow)) { return false; } } - // Step 5. - int32_t sign = DurationSign(duration); - // Step 6. - auto calendar = yearMonth.calendar(); + const auto& durationToAdd = internalDuration.date; // Step 7. - Rooted fields(cx); - if (!ISODateToFields(cx, yearMonth, &fields)) { + if (durationToAdd.weeks != 0 || durationToAdd.days != 0 || + internalDuration.time != TimeDuration{}) { + JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, + JSMSG_TEMPORAL_PLAIN_YEAR_MONTH_BAD_DURATION, + NonZeroDurationPartAfterMonths(duration)); return false; } // Step 8. - MOZ_ASSERT(!fields.has(CalendarField::Day)); - fields.setDay(1); + auto calendar = yearMonth.calendar(); // Step 9. - Rooted intermediateDate(cx); - if (!CalendarDateFromFields(cx, calendar, fields, TemporalOverflow::Constrain, - &intermediateDate)) { + Rooted fields(cx); + if (!ISODateToFields(cx, yearMonth, &fields)) { return false; } - // Steps 10-11. - ISODate date; - if (sign < 0) { - // |intermediateDate| is initialized to the first day of |yearMonth|'s - // month. Compute the last day of |yearMonth|'s month by first adding one - // month and then subtracting one day. - // - // This is roughly equivalent to these calls: - // - // js> var ym = new Temporal.PlainYearMonth(2023, 1); - // js> ym.toPlainDate({day: 1}).add({months: 1}).subtract({days: 1}).day - // 31 - // - // For many calendars this is equivalent to `ym.daysInMonth`, except when - // some days are skipped, for example consider the Julian-to-Gregorian - // calendar transition. - - // Step 10.a. - auto oneMonthDuration = DateDuration{0, 1}; - - // Step 10.b. - ISODate nextMonth; - if (!CalendarDateAdd(cx, calendar, intermediateDate, oneMonthDuration, - TemporalOverflow::Constrain, &nextMonth)) { - return false; - } - - // Step 10.c. - date = BalanceISODate(nextMonth, -1); + // Step 10. + MOZ_ASSERT(!fields.has(CalendarField::Day)); + fields.setDay(1); - // Step 10.d. - MOZ_ASSERT(ISODateWithinLimits(date)); - } else { - // Step 11.a. - date = intermediateDate; + // Step 11. + Rooted date(cx); + if (!CalendarDateFromFields(cx, calendar, fields, TemporalOverflow::Constrain, + &date)) { + return false; } - // Steps 12. - auto durationToAdd = ToDateDurationRecordWithoutTime(duration); - - // Step 13. + // Step 12. ISODate addedDate; if (!CalendarDateAdd(cx, calendar, date, durationToAdd, overflow, &addedDate)) { @@ -593,21 +593,21 @@ static bool AddDurationToYearMonth(JSContext* cx, TemporalAddDuration operation, Rooted addedYearMonth(cx, PlainYearMonth{addedDate, calendar}); - // Step 14. + // Step 13. Rooted addedDateFields(cx); if (!ISODateToFields(cx, addedYearMonth, &addedDateFields)) { return false; } - // Step 15. - Rooted result(cx); + // Step 14. + Rooted isoDate(cx); if (!CalendarYearMonthFromFields(cx, calendar, addedDateFields, overflow, - &result)) { + &isoDate)) { return false; } - // Step 16. - auto* obj = CreateTemporalYearMonth(cx, result); + // Step 15. + auto* obj = CreateTemporalYearMonth(cx, isoDate); if (!obj) { return false; } diff --git a/js/src/builtin/temporal/ZonedDateTime.cpp b/js/src/builtin/temporal/ZonedDateTime.cpp index cf4be87e23de9..b00e53d6fb5f0 100644 --- a/js/src/builtin/temporal/ZonedDateTime.cpp +++ b/js/src/builtin/temporal/ZonedDateTime.cpp @@ -682,7 +682,7 @@ static bool DifferenceZonedDateTime(JSContext* cx, const EpochNanoseconds& ns1, } // Step 4. - if (CompareISODate(startDateTime.date, endDateTime.date) == 0) { + if (startDateTime.date == endDateTime.date) { // Step 4.a. auto timeDuration = TimeDurationFromEpochNanosecondsDifference(ns2, ns1); diff --git a/js/src/gc/GC.h b/js/src/gc/GC.h index 014099b8668df..9593824ddb3c4 100644 --- a/js/src/gc/GC.h +++ b/js/src/gc/GC.h @@ -93,7 +93,8 @@ class ArenaChunk; _("generateMissingAllocSites", JSGC_GENERATE_MISSING_ALLOC_SITES, true) \ _("highFrequencyMode", JSGC_HIGH_FREQUENCY_MODE, false) \ _("storeBufferEntries", JSGC_STORE_BUFFER_ENTRIES, true) \ - _("storeBufferScaling", JSGC_STORE_BUFFER_SCALING, true) + _("storeBufferScaling", JSGC_STORE_BUFFER_SCALING, true) \ + _("incrementalWeakMapMarkingEnabled", JSGC_INCREMENTAL_WEAKMAP_ENABLED, true) // Get the key and writability give a GC parameter name. extern bool GetGCParameterInfo(const char* name, JSGCParamKey* keyOut, diff --git a/js/src/jit-test/jit_test.py b/js/src/jit-test/jit_test.py index 2381ec8672988..e27233d33efa2 100755 --- a/js/src/jit-test/jit_test.py +++ b/js/src/jit-test/jit_test.py @@ -11,6 +11,7 @@ import subprocess import sys import traceback +from copy import deepcopy read_input = input @@ -485,17 +486,7 @@ def main(argv): job_count = len(test_list) if options.repeat: - - def repeat_copy(job_list_generator, repeat): - job_list = list(job_list_generator) - for i in range(repeat): - for test in job_list: - if i == 0: - yield test - else: - yield test.copy() - - job_list = repeat_copy(job_list, options.repeat) + job_list = (deepcopy(test) for test in job_list for _ in range(options.repeat)) job_count *= options.repeat if options.ignore_timeouts: diff --git a/js/src/jit-test/tests/errors/nuke-error-wrapper.js b/js/src/jit-test/tests/errors/nuke-error-wrapper.js new file mode 100644 index 0000000000000..0250a1e748b42 --- /dev/null +++ b/js/src/jit-test/tests/errors/nuke-error-wrapper.js @@ -0,0 +1,35 @@ +// |jit-test| error:finished +var g = newGlobal({newCompartment: true}); +g.evaluate(` + // Override Error.prototype.name with a getter that nukes CCWs + Object.defineProperty(Error.prototype, 'name', { + get: function() { + // Nuke all cross-compartment wrappers pointing into this realm. + // This makes the CCW in the main compartment (that roots our ErrorObject) + // become a dead proxy, removing the only reference to our ErrorObject. + nukeAllCCWs(); + + // Force a full GC to tenure the ErrorObject and compact heap. + // ErrorObject survives this GC because it's 'this' (on the C++ stack). + gc(); + + // Set maxBytes to current gcBytes so the NEXT allocation triggers GC. + // The next GC will collect the ErrorObject, freeing its JSErrorReport. + gcparam('maxBytes', gcparam('gcBytes')); + + // Return undefined (not a string) to force fallback to reportp->exnType + // and then reportp->newMessageString (both UAF after GC collects ErrorObject) + return undefined; + } + }); + + // Create the Error object in this compartment + this.err = new Error("finished"); +`); + +// Get a CCW (cross-compartment wrapper) to the ErrorObject. +// Then clear the reference in compartment A so the only reference is through our CCW. +// Then throw the foreign error at top level. +var foreignError = g.err; +g.err = null; +throw foreignError; diff --git a/js/src/jit-test/tests/gc/gcparam.js b/js/src/jit-test/tests/gc/gcparam.js index cdacc2c2cef6e..46db3a836a3fb 100644 --- a/js/src/jit-test/tests/gc/gcparam.js +++ b/js/src/jit-test/tests/gc/gcparam.js @@ -63,3 +63,4 @@ testChangeParam("semispaceNurseryEnabled"); testChangeParam("nurseryMaxTimeGoalMS"); testChangeParam("storeBufferEntries"); testChangeParam("storeBufferScaling"); +testChangeParam("incrementalWeakMapMarkingEnabled"); diff --git a/js/src/jit-test/tests/profiler/frame-line-column-numbers.js b/js/src/jit-test/tests/profiler/frame-line-column-numbers.js new file mode 100644 index 0000000000000..d477a57524dbe --- /dev/null +++ b/js/src/jit-test/tests/profiler/frame-line-column-numbers.js @@ -0,0 +1,69 @@ +// |jit-test| skip-if: !getJitCompilerOptions()['baseline.enable'] && !getJitCompilerOptions()['ion.enable'] +// Test that JIT frames (both Baseline and Ion) report accurate line and column +// numbers when captured via the Gecko profiler. This verifies that the profiler +// correctly attributes stack samples to the source line where execution occurs. + +setJitCompilerOption("baseline.warmup.trigger", 0); +setJitCompilerOption("ion.warmup.trigger", 10); + +// Update these values if the line and column numbers of readGeckoProfilingStack +// call inside testLoop changes. +const EXPECTED_LINE = 22; +const EXPECTED_COLUMN = 17; +// Run enough iterations to ensure JIT compilation occurs. +const ITERATION_COUNT = 100; +const stacks = []; + +function testLoop() { + const items = Array.from({ length: ITERATION_COUNT }, (_, i) => i); + + for (const item of items) { + Math.abs(item); + stacks.push(readGeckoProfilingStack()); + Math.cos(item); + } +} + +enableGeckoProfiling(); +testLoop(); +disableGeckoProfiling(); + +assertEq(stacks.length, ITERATION_COUNT, "Should have captured the expected number of stacks"); + +let stacksWithCorrectFrame = 0; +let stacksWithIncorrectFrame = 0; + +// Now let's check that the `testLoop` frame line/column numbers are attributed +// to the correct ones. We need to skip the baseline-interpreter frames because +// the line/column number collection of them happens in a different location. +for (const stack of stacks) { + for (const physicalFrame of stack) { + const testLoopFrame = physicalFrame.find( + frame => + frame.label && + frame.label.includes("testLoop") && + frame.kind !== "baseline-interpreter" + ); + + if (testLoopFrame) { + if ( + testLoopFrame.line === EXPECTED_LINE && + testLoopFrame.column === EXPECTED_COLUMN + ) { + stacksWithCorrectFrame++; + } else { + print( + `ERROR: testLoop line ${testLoopFrame.line} and column ${testLoopFrame.column} is outside expected line ${EXPECTED_LINE} and column ${EXPECTED_COLUMN} (kind: ${testLoopFrame.kind})` + ); + stacksWithIncorrectFrame++; + } + } + } +} + +print(`Total stacks captured: ${stacks.length}`); +print(`Stacks with correct testLoop frame: ${stacksWithCorrectFrame}`); +print(`Stacks with incorrect testLoop frame: ${stacksWithIncorrectFrame}`); + +assertEq(stacksWithCorrectFrame > 0, true, "Should have captured some correct JIT frames"); +assertEq(stacksWithIncorrectFrame, 0, "All JIT frames should have correct line numbers"); diff --git a/js/src/jit/BaselineCodeGen.cpp b/js/src/jit/BaselineCodeGen.cpp index 44473a3f7e83c..70f444188bf6a 100644 --- a/js/src/jit/BaselineCodeGen.cpp +++ b/js/src/jit/BaselineCodeGen.cpp @@ -265,6 +265,9 @@ MethodStatus BaselineCompiler::compile(JSContext* cx) { JitSpew(JitSpew_Codegen, "# Emitting baseline code for script %s:%u:%u", script->filename(), script->lineno(), script->column().oneOriginValue()); + if (runtime->geckoProfiler().enabled()) { + masm.enableProfilingInstrumentation(); + } MOZ_ASSERT(!script->hasBaselineScript()); @@ -892,8 +895,7 @@ bool BaselineCodeGen::callVMInternal(VMFunctionId id, masm.push(FrameDescriptor(FrameType::BaselineJS)); } // Perform the call. - masm.call(code); - uint32_t callOffset = masm.currentOffset(); + uint32_t callOffset = masm.callJit(code); // Pop arguments from framePushed. masm.implicitPop(argSize); @@ -1996,6 +1998,15 @@ void BaselineCodeGen::emitProfilerExitFrame() { profilerExitFrameToggleOffset_ = toggleOffset; } +template +void BaselineCodeGen::emitProfilerCallSiteInstrumentation() { + if (!handler.needsProfilerCallSiteInstrumentation()) { + return; + } + + masm.instrumentProfilerCallSite(); +} + template bool BaselineCodeGen::emit_Nop() { return true; @@ -4732,6 +4743,10 @@ template <> bool BaselineCompilerCodeGen::emitCall(JSOp op) { MOZ_ASSERT(IsInvokeOp(op)); + // Record call site for profiler sampling. IC stub calls use raw masm.call() + // which doesn't automatically instrument, unlike callJit/callWithABI. + emitProfilerCallSiteInstrumentation(); + frame.syncStack(0); uint32_t argc = GET_ARGC(handler.pc()); @@ -4775,6 +4790,10 @@ template bool BaselineCodeGen::emitSpreadCall(JSOp op) { MOZ_ASSERT(IsInvokeOp(op)); + // Record call site for profiler sampling. IC stub calls use raw masm.call() + // which doesn't automatically instrument, unlike callJit/callWithABI. + emitProfilerCallSiteInstrumentation(); + frame.syncStack(0); masm.move32(Imm32(1), R0.scratchReg()); diff --git a/js/src/jit/BaselineCodeGen.h b/js/src/jit/BaselineCodeGen.h index 1f1e9e23a63c8..a59d2dcabdf04 100644 --- a/js/src/jit/BaselineCodeGen.h +++ b/js/src/jit/BaselineCodeGen.h @@ -289,6 +289,7 @@ class BaselineCodeGen { void emitProfilerEnterFrame(); void emitProfilerExitFrame(); + void emitProfilerCallSiteInstrumentation(); void emitOutOfLinePostBarrierSlot(); }; @@ -440,6 +441,8 @@ class BaselineCompilerHandler { return JS::Prefs::experimental_self_hosted_cache() && script()->selfHosted(); } + + bool needsProfilerCallSiteInstrumentation() const { return true; } }; using BaselineCompilerCodeGen = BaselineCodeGen; @@ -572,6 +575,8 @@ class BaselineInterpreterHandler { bool addEnvAllocSite() { return false; } // Not supported. bool realmIndependentJitcode() const { return true; } + + bool needsProfilerCallSiteInstrumentation() const { return false; } }; using BaselineInterpreterCodeGen = BaselineCodeGen; diff --git a/js/src/jit/BaselineIC.h b/js/src/jit/BaselineIC.h index 5b469b300acfc..88de0e06ac73c 100644 --- a/js/src/jit/BaselineIC.h +++ b/js/src/jit/BaselineIC.h @@ -299,7 +299,9 @@ class ICCacheIRStub final : public ICStub { void trace(JSTracer* trc); bool traceWeak(JSTracer* trc); - ICCacheIRStub* clone(JSRuntime* rt, ICStubSpace& newSpace); + enum class ICScriptHandling { MarkActive, AssertActive }; + ICCacheIRStub* clone(JSRuntime* rt, ICStubSpace& newSpace, + ICScriptHandling icScriptHandling); // Returns true if this stub can call JS or VM code that can trigger a GC. bool makesGCCalls() const; diff --git a/js/src/jit/CacheIR.cpp b/js/src/jit/CacheIR.cpp index 3ae32cdc03c56..1946de3347629 100644 --- a/js/src/jit/CacheIR.cpp +++ b/js/src/jit/CacheIR.cpp @@ -242,6 +242,9 @@ uint32_t CacheIRCloner::getRawInt32Field(uint32_t stubOffset) { const void* CacheIRCloner::getRawPointerField(uint32_t stubOffset) { return reinterpret_cast(readStubWord(stubOffset)); } +const ICScript* CacheIRCloner::getICScriptField(uint32_t stubOffset) { + return reinterpret_cast(readStubWord(stubOffset)); +} uint64_t CacheIRCloner::getRawInt64Field(uint32_t stubOffset) { return static_cast(readStubInt64(stubOffset)); } diff --git a/js/src/jit/CacheIR.h b/js/src/jit/CacheIR.h index 544e3cb76059a..893cc2c1801f2 100644 --- a/js/src/jit/CacheIR.h +++ b/js/src/jit/CacheIR.h @@ -241,6 +241,7 @@ class StubField { // These fields take up a single word. RawInt32, RawPointer, + ICScript, Shape, WeakShape, JSObject, @@ -314,6 +315,8 @@ inline const char* StubFieldTypeName(StubField::Type ty) { return "RawInt32"; case StubField::Type::RawPointer: return "RawPointer"; + case StubField::Type::ICScript: + return "ICScript"; case StubField::Type::Shape: return "Shape"; case StubField::Type::WeakShape: diff --git a/js/src/jit/CacheIRCloner.h b/js/src/jit/CacheIRCloner.h index 2ecf37cc2f451..6ccb58619fde8 100644 --- a/js/src/jit/CacheIRCloner.h +++ b/js/src/jit/CacheIRCloner.h @@ -70,6 +70,7 @@ class MOZ_RAII CacheIRCloner { JitCode* getJitCodeField(uint32_t stubOffset); uint32_t getRawInt32Field(uint32_t stubOffset); const void* getRawPointerField(uint32_t stubOffset); + const ICScript* getICScriptField(uint32_t stubOffset); jsid getIdField(uint32_t stubOffset); Value getValueField(uint32_t stubOffset); Value getWeakValueField(uint32_t stubOffset); diff --git a/js/src/jit/CacheIRCompiler.cpp b/js/src/jit/CacheIRCompiler.cpp index 3a3825c4d9e93..0a4edd05f6200 100644 --- a/js/src/jit/CacheIRCompiler.cpp +++ b/js/src/jit/CacheIRCompiler.cpp @@ -1127,6 +1127,7 @@ static void InitWordStubField(StubField::Type type, void* dest, switch (type) { case StubField::Type::RawInt32: case StubField::Type::RawPointer: + case StubField::Type::ICScript: case StubField::Type::AllocSite: *static_cast(dest) = value; break; @@ -1188,6 +1189,7 @@ static void InitInt64StubField(StubField::Type type, void* dest, break; case StubField::Type::RawInt32: case StubField::Type::RawPointer: + case StubField::Type::ICScript: case StubField::Type::AllocSite: case StubField::Type::Shape: case StubField::Type::WeakShape: @@ -1217,7 +1219,8 @@ void CacheIRWriter::copyStubData(uint8_t* dest) const { } } -ICCacheIRStub* ICCacheIRStub::clone(JSRuntime* rt, ICStubSpace& newSpace) { +ICCacheIRStub* ICCacheIRStub::clone(JSRuntime* rt, ICStubSpace& newSpace, + ICScriptHandling icScriptHandling) { const CacheIRStubInfo* info = stubInfo(); MOZ_ASSERT(info->makesGCCalls()); @@ -1250,6 +1253,15 @@ ICCacheIRStub* ICCacheIRStub::clone(JSRuntime* rt, ICStubSpace& newSpace) { InitWordStubField(type, dest, *srcField); src += sizeof(uintptr_t); dest += sizeof(uintptr_t); + if (type == StubField::Type::ICScript) { + auto* icScript = reinterpret_cast(*srcField); + if (icScriptHandling == ICScriptHandling::MarkActive) { + icScript->setActive(); + } else { + MOZ_ASSERT(icScriptHandling == ICScriptHandling::AssertActive); + MOZ_RELEASE_ASSERT(icScript->active()); + } + } } else { const uint64_t* srcField = reinterpret_cast(src); InitInt64StubField(type, dest, *srcField); @@ -1286,6 +1298,7 @@ void jit::TraceCacheIRStub(JSTracer* trc, T* stub, switch (fieldType) { case Type::RawInt32: case Type::RawPointer: + case Type::ICScript: case Type::RawInt64: case Type::Double: break; @@ -1431,6 +1444,7 @@ bool jit::TraceWeakCacheIRStub(JSTracer* trc, T* stub, return !isDead; case Type::RawInt32: case Type::RawPointer: + case Type::ICScript: case Type::Shape: case Type::JSObject: case Type::Symbol: diff --git a/js/src/jit/CacheIROps.yaml b/js/src/jit/CacheIROps.yaml index 2c9ace661579b..310b08360a73b 100644 --- a/js/src/jit/CacheIROps.yaml +++ b/js/src/jit/CacheIROps.yaml @@ -2064,7 +2064,7 @@ receiver: ObjId callee: ObjId rhs: ValId - icScript: RawPointerField + icScript: ICScriptField sameRealm: BoolImm nargsAndFlags: RawInt32Field @@ -2257,7 +2257,7 @@ args: callee: ObjId argc: Int32Id - icScript: RawPointerField + icScript: ICScriptField flags: CallFlagsImm argcFixed: UInt32Imm @@ -2704,7 +2704,7 @@ args: receiver: ValId callee: ObjId - icScript: RawPointerField + icScript: ICScriptField sameRealm: BoolImm nargsAndFlags: RawInt32Field diff --git a/js/src/jit/CacheIRWriter.h b/js/src/jit/CacheIRWriter.h index d0b9cc4c0645e..9d52f04dab816 100644 --- a/js/src/jit/CacheIRWriter.h +++ b/js/src/jit/CacheIRWriter.h @@ -237,6 +237,9 @@ class MOZ_RAII CacheIRWriter : public JS::CustomAutoRooter { void writeRawPointerField(const void* ptr) { addStubField(uintptr_t(ptr), StubField::Type::RawPointer); } + void writeICScriptField(const ICScript* icScript) { + addStubField(uintptr_t(icScript), StubField::Type::ICScript); + } void writeIdField(jsid id) { addStubField(id.asRawBits(), StubField::Type::Id); } diff --git a/js/src/jit/GenerateCacheIRFiles.py b/js/src/jit/GenerateCacheIRFiles.py index 40c3b504530dd..6ed6a20aae504 100644 --- a/js/src/jit/GenerateCacheIRFiles.py +++ b/js/src/jit/GenerateCacheIRFiles.py @@ -75,6 +75,7 @@ def load_yaml(yaml_path): "JitCodeField": ("JitCode*", "writeJitCodeField"), "RawInt32Field": ("uint32_t", "writeRawInt32Field"), "RawPointerField": ("const void*", "writeRawPointerField"), + "ICScriptField": ("const ICScript*", "writeICScriptField"), "IdField": ("jsid", "writeIdField"), "ValueField": ("const Value&", "writeValueField"), "WeakValueField": ("const Value&", "writeWeakValueField"), @@ -181,6 +182,7 @@ def gen_writer_method(name, args, custom_writer): "JitCodeField": ("uint32_t", "Offset", "reader.stubOffset()"), "RawInt32Field": ("uint32_t", "Offset", "reader.stubOffset()"), "RawPointerField": ("uint32_t", "Offset", "reader.stubOffset()"), + "ICScriptField": ("uint32_t", "Offset", "reader.stubOffset()"), "IdField": ("uint32_t", "Offset", "reader.stubOffset()"), "ValueField": ("uint32_t", "Offset", "reader.stubOffset()"), "WeakValueField": ("uint32_t", "Offset", "reader.stubOffset()"), @@ -326,6 +328,7 @@ def gen_reader_method(name, args): "JitCodeField": "spewField", "RawInt32Field": "spewField", "RawPointerField": "spewField", + "ICScriptField": "spewField", "IdField": "spewField", "ValueField": "spewField", "WeakValueField": "spewField", @@ -469,6 +472,7 @@ def gen_clone_method(name, args): "JitCodeField": 1, "RawInt32Field": 1, "RawPointerField": 1, + "ICScriptField": 1, "RawInt64Field": 1, "DoubleField": 1, "IdField": 1, diff --git a/js/src/jit/Ion.cpp b/js/src/jit/Ion.cpp index bbde1d3e7e256..2078fd70e657f 100644 --- a/js/src/jit/Ion.cpp +++ b/js/src/jit/Ion.cpp @@ -1815,7 +1815,7 @@ static AbortReason IonCompile(JSContext* cx, HandleScript script, } auto clearDependencies = - mozilla::MakeScopeExit([mirGen]() { mirGen->tracker.reset(); }); + mozilla::MakeScopeExit([mirGen]() { mirGen->cleanup(); }); MOZ_ASSERT(!script->baselineScript()->hasPendingIonCompileTask()); MOZ_ASSERT(!script->hasIonScript()); diff --git a/js/src/jit/IonCompileTask.cpp b/js/src/jit/IonCompileTask.cpp index f42a7183b6b84..4f09701c494c6 100644 --- a/js/src/jit/IonCompileTask.cpp +++ b/js/src/jit/IonCompileTask.cpp @@ -156,7 +156,7 @@ void jit::AttachFinishedCompilations(JSContext* cx) { static UniquePtr FreeIonCompileTask(IonCompileTask* task) { // To correctly free compilation dependencies, which may have virtual // destructors we need to explicitly empty the MIRGenerator's list here. - task->mirGen().tracker.reset(); + task->mirGen().cleanup(); // The task is allocated into its LifoAlloc, so destroying that will // destroy the task and all other data accumulated during compilation, diff --git a/js/src/jit/JitScript.cpp b/js/src/jit/JitScript.cpp index dc4151e258130..55b3e0c08c27e 100644 --- a/js/src/jit/JitScript.cpp +++ b/js/src/jit/JitScript.cpp @@ -552,7 +552,8 @@ void ICScript::purgeStubs(Zone* zone, ICStubSpace& newStubSpace) { ICCacheIRStub* prev = nullptr; ICStub* stub = entry.firstStub(); while (stub != fallback) { - ICCacheIRStub* clone = stub->toCacheIRStub()->clone(rt, newStubSpace); + ICCacheIRStub* clone = stub->toCacheIRStub()->clone( + rt, newStubSpace, ICCacheIRStub::ICScriptHandling::AssertActive); if (prev) { prev->setNext(clone); } else { @@ -771,26 +772,26 @@ static void MarkActiveICScriptsAndCopyStubs( ICCacheIRStub* stub = layout->maybeStubPtr()->toCacheIRStub(); auto lookup = alreadyClonedStubs.lookupForAdd(stub); if (!lookup) { - ICCacheIRStub* newStub = stub->clone(cx->runtime(), newStubSpace); + ICCacheIRStub* newStub = + stub->clone(cx->runtime(), newStubSpace, + ICCacheIRStub::ICScriptHandling::MarkActive); AutoEnterOOMUnsafeRegion oomUnsafe; if (!alreadyClonedStubs.add(lookup, stub, newStub)) { oomUnsafe.crash("MarkActiveICScriptsAndCopyStubs"); } } layout->setStubPtr(lookup->value()); - - // If this is a trial-inlining call site, also preserve the callee - // ICScript. Inlined constructor calls invoke CreateThisFromIC (which - // can trigger GC) before using the inlined ICScript. +#ifdef DEBUG JSJitFrameIter parentFrame(frame); ++parentFrame; BaselineFrame* blFrame = parentFrame.baselineFrame(); jsbytecode* pc; parentFrame.baselineScriptAndPc(nullptr, &pc); uint32_t pcOffset = blFrame->script()->pcToOffset(pc); - if (blFrame->icScript()->hasInlinedChild(pcOffset)) { - blFrame->icScript()->findInlinedChild(pcOffset)->setActive(); - } + ICScript* icScript = blFrame->icScript(); + MOZ_ASSERT_IF(icScript->hasInlinedChild(pcOffset), + icScript->findInlinedChild(pcOffset)->active()); +#endif } break; } diff --git a/js/src/jit/JitcodeMap.cpp b/js/src/jit/JitcodeMap.cpp index 6d4bce2efd33d..c421597cb8391 100644 --- a/js/src/jit/JitcodeMap.cpp +++ b/js/src/jit/JitcodeMap.cpp @@ -15,6 +15,8 @@ #include "jit/InlineScriptTree.h" #include "jit/JitRuntime.h" #include "jit/JitSpewer.h" +#include "js/JitCodeAPI.h" +#include "js/ProfilingFrameIterator.h" #include "js/Vector.h" #include "vm/BytecodeLocation.h" // for BytecodeLocation #include "vm/GeckoProfiler.h" @@ -27,6 +29,35 @@ using mozilla::Maybe; namespace js { namespace jit { +static void GetLineInfoFromJitCodeRecord(uint64_t addr, uint32_t* line, + uint32_t* column) { + JS::JitCodeRecord* record = JS::LookupJitCodeRecord(addr); + if (!record || record->sourceInfo.empty()) { + *line = 0; + *column = 0; + return; + } + + // Calculate offset from the base address + uint32_t codeOffset = addr - record->code_addr; + + // Binary search for the largest offset <= codeOffset + // We know for sure that sourceInfo is sorted by offset. + auto* it = std::upper_bound( + record->sourceInfo.begin(), record->sourceInfo.end(), codeOffset, + [](uint32_t offset, const JS::JitCodeSourceInfo& info) { + return offset < info.offset; + }); + + // Upper_bound returns first element > codeOffset, so go back one. + if (it != record->sourceInfo.begin()) { + --it; + } + + *line = it->lineno; + *column = it->colno.oneOriginValue(); +} + static inline JitcodeRegionEntry RegionAtAddr(const IonEntry& entry, void* ptr, uint32_t* ptrOffset) { MOZ_ASSERT(entry.containsPointer(ptr)); @@ -63,6 +94,29 @@ uint32_t IonEntry::callStackAtAddr(void* ptr, CallStackFrameInfo* results, results[count].label = getStr(scriptIdx); results[count].sourceId = getScriptSource(scriptIdx).scriptSource->id(); + + // Calculate line numbers during sampling + // For the first entry (innermost frame), use precise PC offset from + // delta-run + if (count == 0) { + pcOffset = region.findPcOffset(ptrOffset, pcOffset); + } + + const IonScriptData& scriptData = getScriptData(scriptIdx); + ImmutableScriptData* isd = scriptData.sharedData->get(); + jsbytecode* code = isd->code(); + jsbytecode* pc = code + pcOffset; + MOZ_ASSERT(pcOffset < isd->codeLength()); + + SrcNote* notes = isd->notes(); + SrcNote* notesEnd = notes + isd->noteLength(); + + JS::LimitedColumnNumberOneOrigin col; + uint32_t line = PCToLineNumber(scriptData.lineno, scriptData.column, notes, + notesEnd, code, pc, &col); + results[count].line = line; + results[count].column = col.oneOriginValue(); + count++; if (count >= maxResults) { break; @@ -119,6 +173,10 @@ uint32_t BaselineEntry::callStackAtAddr(void* ptr, CallStackFrameInfo* results, results[0].label = str(); results[0].sourceId = scriptSource().scriptSource->id(); + uint64_t addr = reinterpret_cast(ptr); + + GetLineInfoFromJitCodeRecord(addr, &results[0].line, &results[0].column); + return 1; } @@ -156,6 +214,8 @@ uint32_t RealmIndependentSharedEntry::callStackAtAddr( results[0].label = str(); results[0].sourceId = 0; + results[0].line = 0; + results[0].column = 0; return 1; } @@ -690,7 +750,8 @@ bool JitcodeRegionEntry::WriteRun(CompactBufferWriter& writer, // NB: scriptList is guaranteed to contain curTree->script() uint32_t scriptIdx = 0; for (; scriptIdx < scriptList.length(); scriptIdx++) { - if (scriptList[scriptIdx].sourceAndExtent.matches(curTree->script())) { + if (scriptList[scriptIdx].scriptData.sourceAndExtent.matches( + curTree->script())) { break; } } @@ -870,18 +931,18 @@ bool JitcodeIonTable::WriteIonTable(CompactBufferWriter& writer, JitSpew(JitSpew_Profiling, "Writing native to bytecode map for %s (offset %u-%u) (%zu entries)", - scriptList[0].sourceAndExtent.scriptSource->filename(), - scriptList[0].sourceAndExtent.toStringStart, - scriptList[0].sourceAndExtent.toStringEnd, + scriptList[0].scriptData.sourceAndExtent.scriptSource->filename(), + scriptList[0].scriptData.sourceAndExtent.toStringStart, + scriptList[0].scriptData.sourceAndExtent.toStringEnd, mozilla::PointerRangeSize(start, end)); JitSpew(JitSpew_Profiling, " ScriptList of size %u", unsigned(scriptList.length())); for (uint32_t i = 0; i < scriptList.length(); i++) { JitSpew(JitSpew_Profiling, " Script %u - %s (offset %u-%u)", i, - scriptList[i].sourceAndExtent.scriptSource->filename(), - scriptList[i].sourceAndExtent.toStringStart, - scriptList[i].sourceAndExtent.toStringEnd); + scriptList[i].scriptData.sourceAndExtent.scriptSource->filename(), + scriptList[i].scriptData.sourceAndExtent.toStringStart, + scriptList[i].scriptData.sourceAndExtent.toStringEnd); } // Write out runs first. Keep a vector tracking the positive offsets from @@ -956,16 +1017,14 @@ bool JitcodeIonTable::WriteIonTable(CompactBufferWriter& writer, } // namespace jit } // namespace js -JS::ProfiledFrameHandle::ProfiledFrameHandle(JSRuntime* rt, - js::jit::JitcodeGlobalEntry& entry, - void* addr, const char* label, - uint32_t sourceId, uint32_t depth) +JS::ProfiledFrameHandle::ProfiledFrameHandle( + JSRuntime* rt, js::jit::JitcodeGlobalEntry& entry, void* addr, + const js::jit::CallStackFrameInfo& frameInfo, uint32_t depth) : rt_(rt), entry_(entry), addr_(addr), canonicalAddr_(nullptr), - label_(label), - sourceId_(sourceId), + frameInfo_(frameInfo), depth_(depth) { if (!canonicalAddr_) { canonicalAddr_ = entry_.canonicalNativeAddrFor(rt_, addr_); @@ -990,10 +1049,6 @@ JS_PUBLIC_API uint64_t JS::ProfiledFrameHandle::realmID() const { return entry_.realmID(rt_); } -JS_PUBLIC_API uint32_t JS::ProfiledFrameHandle::sourceId() const { - return sourceId_; -} - JS_PUBLIC_API JS::ProfiledFrameRange JS::GetProfiledFrames(JSContext* cx, void* addr) { // Ensure ProfiledFrameRange::MaxInliningDepth matches @@ -1022,6 +1077,5 @@ JS::ProfiledFrameHandle JS::ProfiledFrameRange::Iter::operator*() const { // and the depth we need to pass to ProfiledFrameHandle goes down. uint32_t depth = range_.depth_ - 1 - index_; return ProfiledFrameHandle(range_.rt_, *range_.entry_, range_.addr_, - range_.frames_[depth].label, - range_.frames_[depth].sourceId, depth); + range_.frames_[depth], depth); } diff --git a/js/src/jit/JitcodeMap.h b/js/src/jit/JitcodeMap.h index 1d686a80740c5..87553289b101a 100644 --- a/js/src/jit/JitcodeMap.h +++ b/js/src/jit/JitcodeMap.h @@ -233,13 +233,26 @@ struct ScriptSourceAndExtent { } }; +struct IonScriptData { + ScriptSourceAndExtent sourceAndExtent; + RefPtr sharedData; + uint32_t lineno; + JS::LimitedColumnNumberOneOrigin column; + + explicit IonScriptData(JSScript* script) + : sourceAndExtent(script), + sharedData(script->sharedData()), + lineno(script->lineno()), + column(script->column()) {} +}; + class IonEntry : public JitcodeGlobalEntry { public: struct ScriptListEntry { - ScriptSourceAndExtent sourceAndExtent; + IonScriptData scriptData; UniqueChars str; ScriptListEntry(JSScript* script, UniqueChars str) - : sourceAndExtent(script), str(std::move(str)) {} + : scriptData(script), str(std::move(str)) {} }; using ScriptList = Vector; @@ -275,7 +288,12 @@ class IonEntry : public JitcodeGlobalEntry { const ScriptSourceAndExtent& getScriptSource(unsigned idx) const { MOZ_ASSERT(idx < numScripts()); - return scriptList_[idx].sourceAndExtent; + return scriptList_[idx].scriptData.sourceAndExtent; + } + + const IonScriptData& getScriptData(unsigned idx) const { + MOZ_ASSERT(idx < numScripts()); + return scriptList_[idx].scriptData; } const char* getStr(unsigned idx) const { @@ -291,8 +309,6 @@ class IonEntry : public JitcodeGlobalEntry { uint32_t maxResults) const; uint64_t realmID() const { return realmId_; } - - bool trace(JSTracer* trc); }; class IonICEntry : public JitcodeGlobalEntry { @@ -317,8 +333,6 @@ class IonICEntry : public JitcodeGlobalEntry { uint32_t maxResults) const; uint64_t realmID(JSRuntime* rt) const; - - bool trace(JSTracer* trc); }; class BaselineEntry : public JitcodeGlobalEntry { @@ -347,8 +361,6 @@ class BaselineEntry : public JitcodeGlobalEntry { uint32_t maxResults) const; uint64_t realmID() const { return realmId_; } - - bool trace(JSTracer* trc); }; class RealmIndependentSharedEntry : public JitcodeGlobalEntry { diff --git a/js/src/jit/MIRGenerator.h b/js/src/jit/MIRGenerator.h index 3bc1c406f48f1..ad5118bd17ef0 100644 --- a/js/src/jit/MIRGenerator.h +++ b/js/src/jit/MIRGenerator.h @@ -189,6 +189,14 @@ class MIRGenerator final { void spewPass(const char* name, BacktrackingAllocator* ra = nullptr); void spewEndFunction(); + // Explicitly reset compilation dependencies and perf spewer debug info. + // This must be called to correctly free compilation dependencies, which may + // have virtual destructors. + void cleanup() { + tracker.reset(); + perfSpewer().reset(); + } + CompilationDependencyTracker tracker; }; diff --git a/js/src/jit/MacroAssembler.cpp b/js/src/jit/MacroAssembler.cpp index 8fbadd64d412f..6bc0d3c45d344 100644 --- a/js/src/jit/MacroAssembler.cpp +++ b/js/src/jit/MacroAssembler.cpp @@ -4469,6 +4469,10 @@ void MacroAssembler::link(JitCode* code) { linkProfilerCallSites(code); } +void MacroAssembler::instrumentProfilerCallSite() { + AutoProfilerCallInstrumentation profiler(*this); +} + MacroAssembler::AutoProfilerCallInstrumentation:: AutoProfilerCallInstrumentation(MacroAssembler& masm) { if (!masm.emitProfilingInstrumentation_) { diff --git a/js/src/jit/MacroAssembler.h b/js/src/jit/MacroAssembler.h index 3ac00b8528dbb..a25bc26553fd2 100644 --- a/js/src/jit/MacroAssembler.h +++ b/js/src/jit/MacroAssembler.h @@ -5964,6 +5964,8 @@ class MacroAssembler : public MacroAssemblerSpecific { emitProfilingInstrumentation_ = true; } + void instrumentProfilerCallSite(); + private: // This class is used to surround call sites throughout the assembler. This // is used by callWithABI, and callJit functions, except if suffixed by diff --git a/js/src/jit/PerfSpewer.cpp b/js/src/jit/PerfSpewer.cpp index 715b7f95e514b..a66fa38278321 100644 --- a/js/src/jit/PerfSpewer.cpp +++ b/js/src/jit/PerfSpewer.cpp @@ -72,6 +72,7 @@ pid_t gettid_pthread() { #include "jit/MIR-wasm.h" #include "jit/MIR.h" #include "js/ColumnNumber.h" // JS::LimitedColumnNumberOneOrigin, JS::ColumnNumberOffset +#include "js/JitCodeAPI.h" #include "js/Printf.h" #include "vm/BytecodeUtil.h" #include "vm/MutexIDs.h" @@ -100,12 +101,19 @@ using namespace js::jit; enum class PerfModeType { None, Function, Source, IR, IROperands, IRGraph }; +static std::atomic geckoProfiling = false; static std::atomic PerfMode = PerfModeType::None; // Mutex to guard access to the profiler vectors and jitdump file if perf // profiling is enabled. MOZ_RUNINIT static js::Mutex PerfMutex(mutexid::PerfSpewer); +MOZ_RUNINIT static PersistentRooted< + GCVector> + jitCodeVector; +MOZ_RUNINIT static ProfilerJitCodeVector profilerData; + +static bool IsGeckoProfiling() { return geckoProfiling; } #ifdef JS_ION_PERF MOZ_RUNINIT static UniqueChars spew_dir; static FILE* JitDumpFilePtr = nullptr; @@ -352,12 +360,27 @@ void PerfSpewer::Init() { #endif } +static void ResetPerfSpewer(AutoLockPerfSpewer& lock, bool enabled) { + profilerData.clear(); + jitCodeVector.clear(); + geckoProfiling = enabled; +} + +void js::jit::ResetPerfSpewer(bool enabled) { + AutoLockPerfSpewer lock; + ::ResetPerfSpewer(lock, enabled); +} + static void DisablePerfSpewer(AutoLockPerfSpewer& lock) { fprintf(stderr, "Warning: Disabling PerfSpewer."); #ifdef XP_WIN etwCollection = false; #endif + ResetPerfSpewer(lock, false); + if (PerfMode == PerfModeType::None) { + return; + } PerfMode = PerfModeType::None; #ifdef JS_ION_PERF long page_size = sysconf(_SC_PAGESIZE); @@ -374,7 +397,55 @@ static void DisablePerfSpewer() { DisablePerfSpewer(lock); } -static bool PerfSrcEnabled() { return PerfMode == PerfModeType::Source; } +static bool MaybeCreateProfilerEntry(AutoLockPerfSpewer& lock, + JS::JitCodeRecord*& outProfilerRecord) { + outProfilerRecord = nullptr; + if (!IsGeckoProfiling()) { + return true; + } + + if (!profilerData.growBy(1)) { + DisablePerfSpewer(lock); + return false; + } + + outProfilerRecord = &profilerData.back(); + return true; +} + +static JS::JitCodeSourceInfo* CreateProfilerSourceEntry( + JS::JitCodeRecord* record, AutoLockPerfSpewer& lock) { + MOZ_ASSERT(record); + + if (!record->sourceInfo.growBy(1)) { + DisablePerfSpewer(lock); + return nullptr; + } + return &record->sourceInfo.back(); +} + +// API to lookup JitCode data for the Gecko Profiler. +JS::JitCodeRecord* JS::LookupJitCodeRecord(uint64_t addr) { + if (!JS_IsInitialized()) { + return nullptr; + } + + AutoLockPerfSpewer lock; + + // Search through profilerData for a record that contains this address + for (auto& record : profilerData) { + if (addr >= record.code_addr && + addr < record.code_addr + record.instructionSize) { + return &record; + } + } + + return nullptr; +} + +static bool PerfSrcEnabled() { + return PerfMode == PerfModeType::Source || IsGeckoProfiling(); +} #ifdef JS_JITSPEW static bool PerfIROpsEnabled() { return PerfMode == PerfModeType::IROperands; } @@ -387,7 +458,9 @@ static bool PerfIREnabled() { (PerfMode == PerfModeType::IR); } -bool js::jit::PerfEnabled() { return PerfMode != PerfModeType::None; } +bool js::jit::PerfEnabled() { + return PerfMode != PerfModeType::None || IsGeckoProfiling(); +} void InlineCachePerfSpewer::recordInstruction(MacroAssembler& masm, const CacheOp& op) { @@ -672,13 +745,23 @@ const char* InlineCachePerfSpewer::CodeName(uint32_t op) { } void PerfSpewer::CollectJitCodeInfo(UniqueChars& function_name, JitCode* code, + JS::JitCodeRecord* maybeProfilerRecord, AutoLockPerfSpewer& lock) { + // Hold the JitCode objects here so they are not GC'd during profiling. + if (IsGeckoProfiling()) { + if (!jitCodeVector.append(code)) { + DisablePerfSpewer(lock); + return; + } + } + CollectJitCodeInfo(function_name, reinterpret_cast(code->raw()), - code->instructionsSize(), lock); + code->instructionsSize(), maybeProfilerRecord, lock); } void PerfSpewer::CollectJitCodeInfo(UniqueChars& function_name, void* code_addr, uint64_t code_size, + JS::JitCodeRecord* maybeProfilerRecord, AutoLockPerfSpewer& lock) { #ifdef JS_ION_PERF static uint64_t codeIndex = 1; @@ -747,6 +830,12 @@ void PerfSpewer::CollectJitCodeInfo(UniqueChars& function_name, void* code_addr, } } #endif + + if (IsGeckoProfiling()) { + MOZ_ASSERT(maybeProfilerRecord); + maybeProfilerRecord->instructionSize = code_size; + maybeProfilerRecord->code_addr = uint64_t(code_addr); + } } void PerfSpewer::recordOffset(MacroAssembler& masm, const char* msg) { @@ -792,30 +881,65 @@ void PerfSpewer::recordOpcode(uint32_t offset, JS::UniqueChars&& str) { } void PerfSpewer::saveDebugInfo(const char* filename, uintptr_t base, + JS::JitCodeRecord* maybeProfilerRecord, AutoLockPerfSpewer& lock) { #ifdef JS_ION_PERF - if (!IsPerfProfiling()) { - return; + if (IsPerfProfiling()) { + JitDumpDebugRecord debug_record = {}; + + uint64_t n_records = debugInfo_.length(); + + debug_record.header.id = JIT_CODE_DEBUG_INFO; + debug_record.header.total_size = + sizeof(debug_record) + + n_records * (sizeof(JitDumpDebugEntry) + strlen(filename) + 1); + debug_record.header.timestamp = GetMonotonicTimestamp(); + debug_record.code_addr = uint64_t(base); + debug_record.nr_entry = n_records; + + WriteToJitDumpFile(&debug_record, sizeof(debug_record), lock); + for (DebugEntry& entry : debugInfo_) { + WriteJitDumpDebugEntry(uint64_t(base) + entry.offset, filename, + entry.line, entry.column, lock); + } } +#endif - JitDumpDebugRecord debug_record = {}; + // Populate profiler record with source info + if (maybeProfilerRecord) { +#ifdef DEBUG + uint32_t lastOffset = 0; +#endif + uint32_t lastLine = 0; + uint32_t lastColumn = 0; + + for (DebugEntry& entry : debugInfo_) { + MOZ_ASSERT(entry.offset >= lastOffset, + "debugInfo_ must be sorted by offset"); +#ifdef DEBUG + lastOffset = entry.offset; +#endif - uint64_t n_records = debugInfo_.length(); + // Skip consecutive entries with the same line/column to reduce memory + // usage and improve binary search performance. + if (entry.line == lastLine && entry.column == lastColumn) { + continue; + } - debug_record.header.id = JIT_CODE_DEBUG_INFO; - debug_record.header.total_size = - sizeof(debug_record) + - n_records * (sizeof(JitDumpDebugEntry) + strlen(filename) + 1); - debug_record.header.timestamp = GetMonotonicTimestamp(); - debug_record.code_addr = uint64_t(base); - debug_record.nr_entry = n_records; + JS::JitCodeSourceInfo* srcInfo = + CreateProfilerSourceEntry(maybeProfilerRecord, lock); + if (!srcInfo) { + return; + } + srcInfo->offset = entry.offset; + srcInfo->lineno = entry.line; + srcInfo->colno = JS::LimitedColumnNumberOneOrigin::fromUnlimited( + entry.column == 0 ? 1 : entry.column); - WriteToJitDumpFile(&debug_record, sizeof(debug_record), lock); - for (DebugEntry& entry : debugInfo_) { - WriteJitDumpDebugEntry(uint64_t(base) + entry.offset, filename, entry.line, - entry.column, lock); + lastLine = entry.line; + lastColumn = entry.column; + } } -#endif } static UniqueChars GetFunctionDesc(const char* tierName, JSContext* cx, @@ -839,6 +963,7 @@ static UniqueChars GetFunctionDesc(const char* tierName, JSContext* cx, } void PerfSpewer::saveJitCodeDebugInfo(JSScript* script, JitCode* code, + JS::JitCodeRecord* maybeProfilerRecord, AutoLockPerfSpewer& lock) { MOZ_ASSERT(code); @@ -848,17 +973,20 @@ void PerfSpewer::saveJitCodeDebugInfo(JSScript* script, JitCode* code, if (PerfIREnabled()) { // We should have generated a debug file to use here. MOZ_ASSERT(irFileName_.get()); - saveDebugInfo(irFileName_.get(), uintptr_t(code->raw()), lock); + saveDebugInfo(irFileName_.get(), uintptr_t(code->raw()), + maybeProfilerRecord, lock); return; } if (!PerfSrcEnabled() || !script || !script->filename()) { return; } - saveDebugInfo(script->filename(), uintptr_t(code->raw()), lock); + saveDebugInfo(script->filename(), uintptr_t(code->raw()), maybeProfilerRecord, + lock); } void PerfSpewer::saveWasmCodeDebugInfo(uintptr_t base, + JS::JitCodeRecord* maybeProfilerRecord, AutoLockPerfSpewer& lock) { // We should be done with the temp IR file, if we were using it. MOZ_ASSERT(!irFile_); @@ -866,7 +994,7 @@ void PerfSpewer::saveWasmCodeDebugInfo(uintptr_t base, if (!PerfIREnabled()) { return; } - saveDebugInfo(irFileName_.get(), base, lock); + saveDebugInfo(irFileName_.get(), base, maybeProfilerRecord, lock); } void PerfSpewer::saveJSProfile(JitCode* code, UniqueChars& desc, @@ -874,9 +1002,13 @@ void PerfSpewer::saveJSProfile(JitCode* code, UniqueChars& desc, MOZ_ASSERT(PerfEnabled()); MOZ_ASSERT(code && desc); AutoLockPerfSpewer lock; + JS::JitCodeRecord* maybeProfilerRecord = nullptr; + if (!MaybeCreateProfilerEntry(lock, maybeProfilerRecord)) { + return; // Allocation failure. + } - saveJitCodeDebugInfo(script, code, lock); - CollectJitCodeInfo(desc, code, lock); + saveJitCodeDebugInfo(script, code, maybeProfilerRecord, lock); + CollectJitCodeInfo(desc, code, maybeProfilerRecord, lock); } void PerfSpewer::saveWasmProfile(uintptr_t base, size_t size, @@ -884,16 +1016,18 @@ void PerfSpewer::saveWasmProfile(uintptr_t base, size_t size, MOZ_ASSERT(PerfEnabled()); MOZ_ASSERT(desc); AutoLockPerfSpewer lock; + JS::JitCodeRecord* maybeProfilerRecord = nullptr; + if (!MaybeCreateProfilerEntry(lock, maybeProfilerRecord)) { + return; // Allocation failure. + } - saveWasmCodeDebugInfo(base, lock); + saveWasmCodeDebugInfo(base, maybeProfilerRecord, lock); PerfSpewer::CollectJitCodeInfo(desc, reinterpret_cast(base), - uint64_t(size), lock); + uint64_t(size), maybeProfilerRecord, lock); } void PerfSpewer::disable(AutoLockPerfSpewer& lock) { - endRecording(); - debugInfo_.clear(); - irFileName_ = UniqueChars(); + reset(); DisablePerfSpewer(lock); } @@ -937,8 +1071,8 @@ void PerfSpewer::endRecording() { } PerfSpewer::~PerfSpewer() { - // Close the file, if it hasn't yet. - endRecording(); + // Free the allocated resources if they haven’t been freed yet. + reset(); } PerfSpewer::PerfSpewer(PerfSpewer&& other) { @@ -978,6 +1112,10 @@ void IonICPerfSpewer::saveProfile(JSContext* cx, JSScript* script, return; } UniqueChars desc = GetFunctionDesc("IonIC", cx, script, stubName); + if (!desc) { + disable(); + return; + } PerfSpewer::saveJSProfile(code, desc, script); } @@ -986,6 +1124,10 @@ void BaselineICPerfSpewer::saveProfile(JitCode* code, const char* stubName) { return; } UniqueChars desc = JS_smprintf("BaselineIC: %s", stubName); + if (!desc) { + disable(); + return; + } PerfSpewer::saveJSProfile(code, desc, nullptr); } @@ -995,6 +1137,10 @@ void BaselinePerfSpewer::saveProfile(JSContext* cx, JSScript* script, return; } UniqueChars desc = GetFunctionDesc("Baseline", cx, script); + if (!desc) { + disable(); + return; + } PerfSpewer::saveJSProfile(code, desc, script); } @@ -1023,6 +1169,9 @@ void BaselineInterpreterPerfSpewer::saveProfile(JitCode* code) { } ops_.clear(); UniqueChars desc = DuplicateString("BaselineInterpreter"); + if (!desc) { + return; + } PerfSpewer::saveJSProfile(code, desc, nullptr); return; } @@ -1086,6 +1235,10 @@ void IonPerfSpewer::saveJSProfile(JSContext* cx, JSScript* script, return; } UniqueChars desc = GetFunctionDesc("Ion", cx, script); + if (!desc) { + disable(); + return; + } PerfSpewer::saveJSProfile(code, desc, script); } @@ -1114,8 +1267,16 @@ void js::jit::CollectPerfSpewerJitCodeProfile(JitCode* code, const char* msg) { if (size > 0) { AutoLockPerfSpewer lock; + JS::JitCodeRecord* maybeProfilerRecord = nullptr; + if (!MaybeCreateProfilerEntry(lock, maybeProfilerRecord)) { + return; // Allocation failure. + } UniqueChars desc = JS_smprintf("%s", msg); - PerfSpewer::CollectJitCodeInfo(desc, code, lock); + if (!desc) { + DisablePerfSpewer(); + return; + } + PerfSpewer::CollectJitCodeInfo(desc, code, maybeProfilerRecord, lock); } } @@ -1128,9 +1289,17 @@ void js::jit::CollectPerfSpewerJitCodeProfile(uintptr_t base, uint64_t size, if (size > 0) { AutoLockPerfSpewer lock; + JS::JitCodeRecord* maybeProfilerRecord = nullptr; + if (!MaybeCreateProfilerEntry(lock, maybeProfilerRecord)) { + return; // Allocation failure. + } UniqueChars desc = JS_smprintf("%s", msg); + if (!desc) { + DisablePerfSpewer(); + return; + } PerfSpewer::CollectJitCodeInfo(desc, reinterpret_cast(base), size, - lock); + maybeProfilerRecord, lock); } } @@ -1140,9 +1309,13 @@ void js::jit::CollectPerfSpewerWasmMap(uintptr_t base, uintptr_t size, return; } AutoLockPerfSpewer lock; + JS::JitCodeRecord* maybeProfilerRecord = nullptr; + if (!MaybeCreateProfilerEntry(lock, maybeProfilerRecord)) { + return; // Allocation failure. + } PerfSpewer::CollectJitCodeInfo(desc, reinterpret_cast(base), - uint64_t(size), lock); + uint64_t(size), maybeProfilerRecord, lock); } void js::jit::PerfSpewerRangeRecorder::appendEntry(UniqueChars& desc) { @@ -1157,6 +1330,10 @@ void js::jit::PerfSpewerRangeRecorder::recordOffset(const char* name) { return; } UniqueChars desc = DuplicateString(name); + if (!desc) { + DisablePerfSpewer(); + return; + } appendEntry(desc); } @@ -1166,6 +1343,10 @@ void js::jit::PerfSpewerRangeRecorder::recordVMWrapperOffset(const char* name) { } UniqueChars desc = JS_smprintf("VMWrapper: %s", name); + if (!desc) { + DisablePerfSpewer(); + return; + } appendEntry(desc); } @@ -1176,6 +1357,10 @@ void js::jit::PerfSpewerRangeRecorder::recordOffset(const char* name, return; } UniqueChars desc = GetFunctionDesc(name, cx, script); + if (!desc) { + DisablePerfSpewer(); + return; + } appendEntry(desc); } diff --git a/js/src/jit/PerfSpewer.h b/js/src/jit/PerfSpewer.h index 72a4685b80152..8df794283dbe1 100644 --- a/js/src/jit/PerfSpewer.h +++ b/js/src/jit/PerfSpewer.h @@ -12,6 +12,7 @@ #endif #include "js/AllocPolicy.h" #include "js/ColumnNumber.h" +#include "js/JitCodeAPI.h" #include "js/Vector.h" #ifdef JS_JITSPEW @@ -39,6 +40,10 @@ class MIRGraph; class LInstruction; enum class CacheOp : uint16_t; +using ProfilerJitCodeVector = Vector; + +void ResetPerfSpewer(bool enabled); + struct AutoLockPerfSpewer { AutoLockPerfSpewer(); ~AutoLockPerfSpewer(); @@ -85,16 +90,20 @@ class PerfSpewer { // Save the debugInfo_ vector to the JIT dump file. void saveDebugInfo(const char* filename, uintptr_t base, + JS::JitCodeRecord* maybeProfilerRecord, AutoLockPerfSpewer& lock); // Save the generated IR file, if any, and the debug info to the JIT dump // file. void saveJitCodeDebugInfo(JSScript* script, JitCode* code, + JS::JitCodeRecord* maybeProfilerRecord, AutoLockPerfSpewer& lock); // Save the generated IR file, if any, and the debug info to the JIT dump // file. - void saveWasmCodeDebugInfo(uintptr_t codeBase, AutoLockPerfSpewer& lock); + void saveWasmCodeDebugInfo(uintptr_t codeBase, + JS::JitCodeRecord* maybeProfilerRecord, + AutoLockPerfSpewer& lock); void saveJSProfile(JitCode* code, JS::UniqueChars& desc, JSScript* script); void saveWasmProfile(uintptr_t codeBase, size_t codeSize, @@ -124,10 +133,21 @@ class PerfSpewer { static void Init(); static void CollectJitCodeInfo(JS::UniqueChars& function_name, JitCode* code, + JS::JitCodeRecord* maybeProfilerRecord, AutoLockPerfSpewer& lock); static void CollectJitCodeInfo(JS::UniqueChars& function_name, void* code_addr, uint64_t code_size, + JS::JitCodeRecord* maybeProfilerRecord, AutoLockPerfSpewer& lock); + + // Explicitly free heap memory allocated using the system allocator. This must + // be called when the PerfSpewer is allocated in a LifoAlloc, since the + // destructor won't be called when the LifoAlloc is freed. + void reset() { + endRecording(); + debugInfo_.clearAndFree(); + irFileName_ = JS::UniqueChars(); + } }; void CollectPerfSpewerJitCodeProfile(JitCode* code, const char* msg); diff --git a/js/src/jit/WarpBuilder.cpp b/js/src/jit/WarpBuilder.cpp index e467333edce93..43c3867e02a1e 100644 --- a/js/src/jit/WarpBuilder.cpp +++ b/js/src/jit/WarpBuilder.cpp @@ -674,7 +674,8 @@ bool WarpBuilder::buildBody() { return false; } #endif - bool wantPreciseLineNumbers = js::jit::PerfEnabled(); + bool wantPreciseLineNumbers = + js::jit::PerfEnabled() || mirGen().isProfilerInstrumentationEnabled(); if (wantPreciseLineNumbers && !hasTerminatedBlock()) { current->updateTrackedSite(newBytecodeSite(loc)); } diff --git a/js/src/jit/WarpOracle.cpp b/js/src/jit/WarpOracle.cpp index 3539425af80f8..b03841f81f3b5 100644 --- a/js/src/jit/WarpOracle.cpp +++ b/js/src/jit/WarpOracle.cpp @@ -1570,6 +1570,7 @@ bool WarpScriptOracle::replaceNurseryAndAllocSitePointers( switch (fieldType) { case StubField::Type::RawInt32: case StubField::Type::RawPointer: + case StubField::Type::ICScript: case StubField::Type::RawInt64: case StubField::Type::Double: break; diff --git a/js/src/jit/WarpSnapshot.cpp b/js/src/jit/WarpSnapshot.cpp index 8d75514776158..5d807947a8cc0 100644 --- a/js/src/jit/WarpSnapshot.cpp +++ b/js/src/jit/WarpSnapshot.cpp @@ -379,6 +379,7 @@ void WarpCacheIRBase::traceData(JSTracer* trc) { switch (fieldType) { case StubField::Type::RawInt32: case StubField::Type::RawPointer: + case StubField::Type::ICScript: case StubField::Type::RawInt64: case StubField::Type::Double: break; diff --git a/js/src/jit/shared/CodeGenerator-shared.cpp b/js/src/jit/shared/CodeGenerator-shared.cpp index c31517c7da864..da2fb2bf6c452 100644 --- a/js/src/jit/shared/CodeGenerator-shared.cpp +++ b/js/src/jit/shared/CodeGenerator-shared.cpp @@ -760,7 +760,7 @@ bool CodeGeneratorShared::createNativeToBytecodeScriptList( // Add script from current tree. bool found = false; for (uint32_t i = 0; i < scripts.length(); i++) { - if (scripts[i].sourceAndExtent.matches(tree->script())) { + if (scripts[i].scriptData.sourceAndExtent.matches(tree->script())) { found = true; break; } diff --git a/js/src/jsapi-tests/testFrontendErrors.cpp b/js/src/jsapi-tests/testFrontendErrors.cpp index 47acf559d79e9..9a0e725cb2f08 100644 --- a/js/src/jsapi-tests/testFrontendErrors.cpp +++ b/js/src/jsapi-tests/testFrontendErrors.cpp @@ -89,8 +89,8 @@ BEGIN_TEST(testFrontendErrors_error) { CHECK(exception.isObject()); JS::Rooted exceptionObj(cx, &exception.toObject()); - const JSErrorReport* report = JS_ErrorFromException(cx, exceptionObj); - CHECK(report); + JS::BorrowedErrorReport report(cx); + CHECK(JS_ErrorFromException(cx, exceptionObj, report)); CHECK(report->errorNumber == JSMSG_UNEXPECTED_TOKEN_NO_EXPECT); // Runtime's error report doesn't borrow the filename. @@ -269,8 +269,8 @@ BEGIN_TEST(testFrontendErrors_overRecursed) { CHECK(exception.isObject()); JS::Rooted exceptionObj(cx, &exception.toObject()); - const JSErrorReport* report = JS_ErrorFromException(cx, exceptionObj); - CHECK(report); + JS::BorrowedErrorReport report(cx); + CHECK(JS_ErrorFromException(cx, exceptionObj, report)); CHECK(report->errorNumber == JSMSG_OVER_RECURSED); } @@ -327,8 +327,8 @@ BEGIN_TEST(testFrontendErrors_allocationOverflow) { CHECK(exception.isObject()); JS::Rooted exceptionObj(cx, &exception.toObject()); - const JSErrorReport* report = JS_ErrorFromException(cx, exceptionObj); - CHECK(report); + JS::BorrowedErrorReport report(cx); + CHECK(JS_ErrorFromException(cx, exceptionObj, report)); CHECK(report->errorNumber == JSMSG_ALLOC_OVERFLOW); } diff --git a/js/src/jsapi-tests/testWeakMap.cpp b/js/src/jsapi-tests/testWeakMap.cpp index ab94559451500..943482e1ad775 100644 --- a/js/src/jsapi-tests/testWeakMap.cpp +++ b/js/src/jsapi-tests/testWeakMap.cpp @@ -90,7 +90,8 @@ BEGIN_TEST(testWeakMap_setWeakMapEntry_invalid_key) { JS::Rooted exn(cx); CHECK(JS_GetPendingException(cx, &exn)); JS::Rooted obj(cx, &exn.toObject()); - JSErrorReport* err = JS_ErrorFromException(cx, obj); + JS::BorrowedErrorReport err(cx); + CHECK(JS_ErrorFromException(cx, obj, err)); CHECK(err->exnType == JSEXN_TYPEERR); JS_ClearPendingException(cx); diff --git a/js/src/jsapi.cpp b/js/src/jsapi.cpp index fdd31505b7761..e75ece4ff41c2 100644 --- a/js/src/jsapi.cpp +++ b/js/src/jsapi.cpp @@ -4121,12 +4121,12 @@ JS::AutoSaveExceptionState::~AutoSaveExceptionState() { } } -JS_PUBLIC_API JSErrorReport* JS_ErrorFromException(JSContext* cx, - HandleObject obj) { +JS_PUBLIC_API bool JS_ErrorFromException(JSContext* cx, HandleObject obj, + JS::BorrowedErrorReport& errorReport) { AssertHeapIsIdle(); CHECK_THREAD(cx); cx->check(obj); - return ErrorFromException(cx, obj); + return ErrorFromException(cx, obj, errorReport); } void JSErrorReport::initBorrowedLinebuf(const char16_t* linebufArg, diff --git a/js/src/moz.build b/js/src/moz.build index 32f26a75f8ad9..767c92fc1d707 100644 --- a/js/src/moz.build +++ b/js/src/moz.build @@ -136,6 +136,7 @@ EXPORTS.js += [ "../public/Initialization.h", "../public/Interrupt.h", "../public/Iterator.h", + "../public/JitCodeAPI.h", "../public/JSON.h", "../public/LocaleSensitive.h", "../public/MapAndSet.h", diff --git a/js/src/tests/jstests.list b/js/src/tests/jstests.list index 19f1ed50fe291..d3bee8e009f5a 100644 --- a/js/src/tests/jstests.list +++ b/js/src/tests/jstests.list @@ -904,93 +904,3 @@ skip script test262/built-ins/Temporal/ZonedDateTime/from/argument-string-limits skip script test262/built-ins/Temporal/ZonedDateTime/prototype/equals/argument-string-limits.js skip script test262/built-ins/Temporal/ZonedDateTime/prototype/since/argument-string-limits.js skip script test262/built-ins/Temporal/ZonedDateTime/prototype/until/argument-string-limits.js - -# https://github.com/tc39/proposal-temporal/pull/3253 -skip script test262/built-ins/Temporal/PlainYearMonth/prototype/add/overflow.js -skip script test262/built-ins/Temporal/PlainYearMonth/prototype/add/subtract-from-last-representable-month.js -skip script test262/built-ins/Temporal/PlainYearMonth/prototype/add/argument-lower-units.js -skip script test262/built-ins/Temporal/PlainYearMonth/prototype/add/options-read-before-algorithmic-validation.js -skip script test262/built-ins/Temporal/PlainYearMonth/prototype/subtract/overflow.js -skip script test262/built-ins/Temporal/PlainYearMonth/prototype/subtract/subtract-from-last-representable-month.js -skip script test262/built-ins/Temporal/PlainYearMonth/prototype/subtract/argument-lower-units.js -skip script test262/built-ins/Temporal/PlainYearMonth/prototype/subtract/options-read-before-algorithmic-validation.js - -# https://github.com/tc39/proposal-intl-era-monthcode/pull/102 -skip script test262/intl402/Temporal/ZonedDateTime/from/era-boundary-japanese.js -skip script test262/intl402/Temporal/PlainDate/from/era-boundary-japanese.js -skip script test262/intl402/Temporal/PlainDateTime/from/era-boundary-japanese.js -skip script test262/intl402/Temporal/PlainYearMonth/from/era-boundary-japanese.js - -# https://github.com/tc39/proposal-intl-era-monthcode/issues/96 -skip script test262/intl402/Temporal/PlainDateTime/prototype/since/leap-months-chinese.js -skip script test262/intl402/Temporal/PlainDateTime/prototype/since/leap-months-dangi.js -skip script test262/intl402/Temporal/PlainDateTime/prototype/since/leap-months-hebrew.js -skip script test262/intl402/Temporal/PlainDateTime/prototype/until/leap-months-chinese.js -skip script test262/intl402/Temporal/PlainDateTime/prototype/until/leap-months-dangi.js -skip script test262/intl402/Temporal/PlainDateTime/prototype/until/leap-months-hebrew.js -skip script test262/intl402/Temporal/ZonedDateTime/prototype/since/leap-months-chinese.js -skip script test262/intl402/Temporal/ZonedDateTime/prototype/since/leap-months-dangi.js -skip script test262/intl402/Temporal/ZonedDateTime/prototype/since/leap-months-hebrew.js -skip script test262/intl402/Temporal/ZonedDateTime/prototype/until/leap-months-chinese.js -skip script test262/intl402/Temporal/ZonedDateTime/prototype/until/leap-months-dangi.js -skip script test262/intl402/Temporal/ZonedDateTime/prototype/until/leap-months-hebrew.js -skip script test262/intl402/Temporal/PlainDate/prototype/since/leap-months-chinese.js -skip script test262/intl402/Temporal/PlainDate/prototype/since/leap-months-dangi.js -skip script test262/intl402/Temporal/PlainDate/prototype/since/leap-months-hebrew.js -skip script test262/intl402/Temporal/PlainDate/prototype/until/leap-months-chinese.js -skip script test262/intl402/Temporal/PlainDate/prototype/until/leap-months-dangi.js -skip script test262/intl402/Temporal/PlainDate/prototype/until/leap-months-hebrew.js -skip script test262/intl402/Temporal/PlainYearMonth/prototype/until/leap-months-hebrew.js -skip script test262/intl402/Temporal/PlainYearMonth/prototype/until/leap-months-dangi.js -skip script test262/intl402/Temporal/PlainYearMonth/prototype/until/leap-months-chinese.js -skip script test262/intl402/Temporal/PlainYearMonth/prototype/since/leap-months-hebrew.js -skip script test262/intl402/Temporal/PlainYearMonth/prototype/since/leap-months-dangi.js -skip script test262/intl402/Temporal/PlainYearMonth/prototype/since/leap-months-chinese.js - -# https://github.com/tc39/proposal-intl-era-monthcode/pull/101 -skip script test262/intl402/Temporal/ZonedDateTime/prototype/since/wrapping-at-end-of-month-hebrew.js -skip script test262/intl402/Temporal/ZonedDateTime/prototype/since/wrapping-at-end-of-month-chinese.js -skip script test262/intl402/Temporal/ZonedDateTime/prototype/since/wrapping-at-end-of-month-dangi.js -skip script test262/intl402/Temporal/ZonedDateTime/prototype/until/wrapping-at-end-of-month-hebrew.js -skip script test262/intl402/Temporal/ZonedDateTime/prototype/until/wrapping-at-end-of-month-chinese.js -skip script test262/intl402/Temporal/ZonedDateTime/prototype/until/wrapping-at-end-of-month-dangi.js -skip script test262/intl402/Temporal/PlainDate/prototype/since/wrapping-at-end-of-month-hebrew.js -skip script test262/intl402/Temporal/PlainDate/prototype/since/wrapping-at-end-of-month-chinese.js -skip script test262/intl402/Temporal/PlainDate/prototype/since/wrapping-at-end-of-month-dangi.js -skip script test262/intl402/Temporal/PlainDate/prototype/until/wrapping-at-end-of-month-hebrew.js -skip script test262/intl402/Temporal/PlainDate/prototype/until/wrapping-at-end-of-month-chinese.js -skip script test262/intl402/Temporal/PlainDate/prototype/until/wrapping-at-end-of-month-dangi.js -skip script test262/intl402/Temporal/PlainDateTime/prototype/since/wrapping-at-end-of-month-hebrew.js -skip script test262/intl402/Temporal/PlainDateTime/prototype/since/wrapping-at-end-of-month-chinese.js -skip script test262/intl402/Temporal/PlainDateTime/prototype/since/wrapping-at-end-of-month-dangi.js -skip script test262/intl402/Temporal/PlainDateTime/prototype/until/wrapping-at-end-of-month-hebrew.js -skip script test262/intl402/Temporal/PlainDateTime/prototype/until/wrapping-at-end-of-month-chinese.js -skip script test262/intl402/Temporal/PlainDateTime/prototype/until/wrapping-at-end-of-month-dangi.js -skip script test262/intl402/Temporal/PlainYearMonth/prototype/add/leap-year-japanese.js -skip script test262/intl402/Temporal/PlainYearMonth/prototype/add/leap-year-roc.js -skip script test262/intl402/Temporal/PlainYearMonth/prototype/add/leap-year-islamic-tbla.js -skip script test262/intl402/Temporal/PlainYearMonth/prototype/add/leap-year-ethioaa.js -skip script test262/intl402/Temporal/PlainYearMonth/prototype/add/leap-year-islamic-civil.js -skip script test262/intl402/Temporal/PlainYearMonth/prototype/add/leap-year-gregory.js -skip script test262/intl402/Temporal/PlainYearMonth/prototype/add/leap-year-coptic.js -skip script test262/intl402/Temporal/PlainYearMonth/prototype/add/leap-year-ethiopic.js -skip script test262/intl402/Temporal/PlainYearMonth/prototype/add/leap-months-dangi.js -skip script test262/intl402/Temporal/PlainYearMonth/prototype/add/leap-year-persian.js -skip script test262/intl402/Temporal/PlainYearMonth/prototype/add/leap-year-indian.js -skip script test262/intl402/Temporal/PlainYearMonth/prototype/add/leap-months-hebrew.js -skip script test262/intl402/Temporal/PlainYearMonth/prototype/add/leap-months-chinese.js -skip script test262/intl402/Temporal/PlainYearMonth/prototype/add/leap-year-buddhist.js -skip script test262/intl402/Temporal/PlainYearMonth/prototype/subtract/leap-year-japanese.js -skip script test262/intl402/Temporal/PlainYearMonth/prototype/subtract/leap-year-roc.js -skip script test262/intl402/Temporal/PlainYearMonth/prototype/subtract/leap-year-islamic-tbla.js -skip script test262/intl402/Temporal/PlainYearMonth/prototype/subtract/leap-year-ethioaa.js -skip script test262/intl402/Temporal/PlainYearMonth/prototype/subtract/leap-year-islamic-civil.js -skip script test262/intl402/Temporal/PlainYearMonth/prototype/subtract/leap-year-coptic.js -skip script test262/intl402/Temporal/PlainYearMonth/prototype/subtract/leap-year-gregory.js -skip script test262/intl402/Temporal/PlainYearMonth/prototype/subtract/leap-year-ethiopic.js -skip script test262/intl402/Temporal/PlainYearMonth/prototype/subtract/leap-months-dangi.js -skip script test262/intl402/Temporal/PlainYearMonth/prototype/subtract/leap-year-persian.js -skip script test262/intl402/Temporal/PlainYearMonth/prototype/subtract/leap-year-indian.js -skip script test262/intl402/Temporal/PlainYearMonth/prototype/subtract/leap-months-hebrew.js -skip script test262/intl402/Temporal/PlainYearMonth/prototype/subtract/leap-months-chinese.js -skip script test262/intl402/Temporal/PlainYearMonth/prototype/subtract/leap-year-buddhist.js diff --git a/js/src/tests/non262/Temporal/PlainMonthDay/reference-dates-chinese-calendar.js b/js/src/tests/non262/Temporal/PlainMonthDay/reference-dates-chinese-calendar.js index e9fc352eb0af9..b4a407dac2301 100644 --- a/js/src/tests/non262/Temporal/PlainMonthDay/reference-dates-chinese-calendar.js +++ b/js/src/tests/non262/Temporal/PlainMonthDay/reference-dates-chinese-calendar.js @@ -70,10 +70,10 @@ const tests = [ "1941-08-22[u-ca=chinese]", "1938-09-23[u-ca=chinese]", "1691-10-21[u-ca=chinese]", - "-005738-11-17[u-ca=chinese]", - "-004098-12-19[u-ca=chinese]", - "-002172-01-19[u-ca=chinese]", - "-000179-02-18[u-ca=chinese]", + "1843-11-21[u-ca=chinese]", + "1737-12-21[u-ca=chinese]", + "1890-01-20[u-ca=chinese]", + "1784-02-20[u-ca=chinese]", ], }, ]; @@ -83,14 +83,30 @@ for (let {day, leapMonth, expected} of tests) { for (let i = 1; i <= 12; ++i) { let expectedToString = expected[i - 1]; + let monthCode = "M" + String(i).padStart(2, "0") + (leapMonth ? "L" : ""); + + // Ensure the expected reference years are still correct. (Updating ICU4X + // may require to adjust the expected results.) + let pd = Temporal.PlainDate.from(expectedToString); + assertEq(pd.monthCode, monthCode); + assertEq(pd.day, day); + assertEq(pd.toString(), expectedToString); - // Skip over dates which are too far into the past (and therefore are likely - // incorrect anyway). This avoids slowing down this test. - if (expectedToString.startsWith("-")) { - continue; + // This is subject to change: + // https://github.com/tc39/proposal-intl-era-monthcode/issues/113 + { + let pmd = pd.toPlainMonthDay(); + assertEq(pmd.monthCode, monthCode); + assertEq(pmd.day, day); + assertEq(pmd.toString(), expectedToString); } - let monthCode = "M" + String(i).padStart(2, "0") + (leapMonth ? "L" : ""); + // Dates before ISO year 1900 are changed to use the non-leap month. + if (leapMonth && pd.withCalendar("iso8601").year < 1900) { + // Use the expected string from the non-leap month case. + expectedToString = tests.find(e => e.day === day && !e.leapMonth).expected[i - 1]; + monthCode = monthCode.slice(0, -1); + } let pmd = Temporal.PlainMonthDay.from({calendar, monthCode, day}); assertEq(pmd.monthCode, monthCode); diff --git a/js/src/vm/ErrorObject.cpp b/js/src/vm/ErrorObject.cpp index 4bab5897dd6d0..41a6a26d1417a 100644 --- a/js/src/vm/ErrorObject.cpp +++ b/js/src/vm/ErrorObject.cpp @@ -1264,7 +1264,8 @@ JSString* js::ComputeStackString(JSContext* cx) { return str.get(); } -JSErrorReport* js::ErrorFromException(JSContext* cx, HandleObject objArg) { +bool js::ErrorFromException(JSContext* cx, HandleObject objArg, + JS::BorrowedErrorReport& errorReport) { // It's ok to UncheckedUnwrap here, since all we do is get the // JSErrorReport, and consumers are careful with the information they get // from that anyway. Anyone doing things that would expose anything in the @@ -1273,16 +1274,21 @@ JSErrorReport* js::ErrorFromException(JSContext* cx, HandleObject objArg) { // will fail if they can't unwrap it. RootedObject obj(cx, UncheckedUnwrap(objArg)); if (!obj->is()) { - return nullptr; + return false; } JSErrorReport* report = obj->as().getOrCreateErrorReport(cx); if (!report) { MOZ_ASSERT(cx->isThrowingOutOfMemory()); cx->recoverFromOutOfMemory(); + return false; } - return report; + // Note: it's important to use the unwrapped object here. CCWs can be cut when + // nuking wrappers so they're not guaranteed to keep the target object and its + // JSErrorReport alive. + errorReport.init(obj, report); + return true; } JS_PUBLIC_API JSObject* JS::ExceptionStackOrNull(HandleObject objArg) { @@ -1502,7 +1508,7 @@ static JSString* ErrorReportToString(JSContext* cx, HandleObject exn, } JS::ErrorReportBuilder::ErrorReportBuilder(JSContext* cx) - : reportp(nullptr), exnObject(cx) {} + : reportp(nullptr), borrowedReport(cx) {} JS::ErrorReportBuilder::~ErrorReportBuilder() = default; @@ -1566,14 +1572,20 @@ bool JS::ErrorReportBuilder::init(JSContext* cx, MOZ_ASSERT(!cx->isExceptionPending()); MOZ_ASSERT(!reportp); + Rooted exnObject(cx); + if (exnStack.exception().isObject()) { // Because ToString below could error and an exception object could become // unrooted, we must root our exception object, if any. exnObject = &exnStack.exception().toObject(); - reportp = ErrorFromException(cx, exnObject); - if (reportp && reportp->isMuted) { - sniffingBehavior = SniffingBehavior::NoSideEffects; + if (ErrorFromException(cx, exnObject, borrowedReport)) { + reportp = borrowedReport.get(); + if (reportp->isMuted) { + sniffingBehavior = SniffingBehavior::NoSideEffects; + } + } else { + reportp = nullptr; } } diff --git a/js/src/vm/ErrorObject.h b/js/src/vm/ErrorObject.h index e6bf7af363bfa..6d1fe4566df8b 100644 --- a/js/src/vm/ErrorObject.h +++ b/js/src/vm/ErrorObject.h @@ -202,7 +202,8 @@ JSString* ComputeStackString(JSContext* cx); extern bool ErrorToException(JSContext* cx, JSErrorReport* reportp, JSErrorCallback callback, void* userRef); -extern JSErrorReport* ErrorFromException(JSContext* cx, HandleObject obj); +extern bool ErrorFromException(JSContext* cx, HandleObject obj, + JS::BorrowedErrorReport& errorReport); /* * Make a copy of errobj parented to cx's compartment's global. diff --git a/js/src/vm/GeckoProfiler.cpp b/js/src/vm/GeckoProfiler.cpp index 56e180a0ca324..4fcb7e1276812 100644 --- a/js/src/vm/GeckoProfiler.cpp +++ b/js/src/vm/GeckoProfiler.cpp @@ -123,6 +123,9 @@ void GeckoProfilerRuntime::enable(bool enabled) { cx->jitActivation->setLastProfilingCallSite(nullptr); } + // Enable/disable JIT code info collection for the Gecko Profiler. + jit::ResetPerfSpewer(enabled); + enabled_ = enabled; scriptSources_.writeLock()->clear(); diff --git a/js/src/vm/Stack.cpp b/js/src/vm/Stack.cpp index 2968272ad4192..3f6e2deb8078b 100644 --- a/js/src/vm/Stack.cpp +++ b/js/src/vm/Stack.cpp @@ -668,6 +668,8 @@ JS::ProfilingFrameIterator::getPhysicalFrameAndEntry( // TODO: get the realm ID of wasm frames. Bug 1596235. frame.realmID = 0; frame.sourceId = 0; + frame.line = 0; + frame.column = 0; return mozilla::Some(frame); } @@ -733,6 +735,10 @@ JS::ProfilingFrameIterator::getPhysicalFrameAndEntry( } frame.activation = activation_; frame.endStackAddress = endStackAddress_; + // Initialize line and column info (will be populated later during + // extractStack) + frame.line = 0; + frame.column = 0; return mozilla::Some(frame); } @@ -775,6 +781,8 @@ uint32_t JS::ProfilingFrameIterator::extractStack(Frame* frames, frames[offset + i] = physicalFrame.value(); frames[offset + i].label = frameInfos[i].label; frames[offset + i].sourceId = frameInfos[i].sourceId; + frames[offset + i].line = frameInfos[i].line; + frames[offset + i].column = frameInfos[i].column; } return depth; diff --git a/js/xpconnect/src/XPCComponents.cpp b/js/xpconnect/src/XPCComponents.cpp index c96e67eee53c6..a4474f8f09abf 100644 --- a/js/xpconnect/src/XPCComponents.cpp +++ b/js/xpconnect/src/XPCComponents.cpp @@ -1406,8 +1406,8 @@ nsXPCComponents_Utils::ReportError(HandleValue error, HandleValue stack, scripterr = CreateScriptError(win, exception, nullptr, nullptr); } - JSErrorReport* err = errorObj ? JS_ErrorFromException(cx, errorObj) : nullptr; - if (err) { + JS::BorrowedErrorReport err(cx); + if (errorObj && JS_ErrorFromException(cx, errorObj, err)) { // It's a proper JS Error uint32_t flags = err->isWarning() ? nsIScriptError::warningFlag : nsIScriptError::errorFlag; diff --git a/js/xpconnect/src/XPCConvert.cpp b/js/xpconnect/src/XPCConvert.cpp index 59f97da941671..54faeb6447fc9 100644 --- a/js/xpconnect/src/XPCConvert.cpp +++ b/js/xpconnect/src/XPCConvert.cpp @@ -1191,15 +1191,15 @@ nsresult XPCConvert::JSValToXPCException(JSContext* cx, MutableHandleValue s, // If it is an engine Error with an error report then let's // extract the report and build an xpcexception from that - const JSErrorReport* report; - if (nullptr != (report = JS_ErrorFromException(cx, obj))) { + JS::BorrowedErrorReport report(cx); + if (JS_ErrorFromException(cx, obj, report)) { JS::UniqueChars toStringResult; RootedString str(cx, ToString(cx, s)); if (str) { toStringResult = JS_EncodeStringToUTF8(cx, str); } return JSErrorToXPCException(cx, toStringResult.get(), ifaceName, - methodName, report, exceptn); + methodName, report.get(), exceptn); } // XXX we should do a check against 'js_ErrorClass' here and diff --git a/layout/base/AnchorPositioningUtils.cpp b/layout/base/AnchorPositioningUtils.cpp index 4943a84593ccd..9da764333927e 100644 --- a/layout/base/AnchorPositioningUtils.cpp +++ b/layout/base/AnchorPositioningUtils.cpp @@ -942,38 +942,42 @@ Maybe AnchorPositioningUtils::GetUsedAnchorName( return Nothing{}; } -nsIFrame* AnchorPositioningUtils::GetAnchorPosImplicitAnchor( - const nsIFrame* aFrame) { - const auto* frameContent = aFrame->GetContent(); - const bool hasElement = frameContent && frameContent->IsElement(); - if (!aFrame->Style()->IsPseudoElement() && !hasElement) { - return nullptr; +auto AnchorPositioningUtils::GetAnchorPosImplicitAnchor(const nsIFrame* aFrame) + -> ImplicitAnchorResult { + const auto* element = dom::Element::FromNodeOrNull(aFrame->GetContent()); + if (!aFrame->Style()->IsPseudoElement() && !element) { + return {}; } - if (MOZ_LIKELY(hasElement)) { - const auto* element = frameContent->AsElement(); - MOZ_ASSERT(element); - const dom::PopoverData* popoverData = element->GetPopoverData(); - if (MOZ_UNLIKELY(popoverData)) { + if (element) [[likely]] { + if (const dom::PopoverData* popoverData = element->GetPopoverData()) + [[unlikely]] { if (const RefPtr& invoker = popoverData->GetInvoker()) { - return invoker->GetPrimaryFrame(); + return {invoker->GetPrimaryFrame(), ImplicitAnchorKind::Popover}; } } } const auto* pseudoRoot = aFrame->GetClosestNativeAnonymousSubtreeRoot(); if (!pseudoRoot) { - return nullptr; + return {}; } auto* pseudoRootFrame = pseudoRoot->GetPrimaryFrame(); if (!pseudoRootFrame) { - return nullptr; + return {}; } - return pseudoRootFrame->HasAnyStateBits(NS_FRAME_OUT_OF_FLOW) - ? pseudoRootFrame->GetPlaceholderFrame()->GetParent() - : pseudoRootFrame->GetParent(); + // FIXME(emilio, bug 2013896): Is this really right? It's just returning the + // in-flow parent of the pseudo-element, but + // GetClosestNativeAnonymousSubtreeRootParent()'s primary frame seems most + // likely to be the intended thing in presence of anonymous boxes like + // fieldsets and so on... + auto* implicitAnchor = + pseudoRootFrame->HasAnyStateBits(NS_FRAME_OUT_OF_FLOW) + ? pseudoRootFrame->GetPlaceholderFrame()->GetParent() + : pseudoRootFrame->GetParent(); + return {implicitAnchor, ImplicitAnchorKind::PseudoElement}; } AnchorPositioningUtils::ContainingBlockInfo diff --git a/layout/base/AnchorPositioningUtils.h b/layout/base/AnchorPositioningUtils.h index 505798cfb0010..8c30881425b07 100644 --- a/layout/base/AnchorPositioningUtils.h +++ b/layout/base/AnchorPositioningUtils.h @@ -7,11 +7,9 @@ #ifndef AnchorPositioningUtils_h_ #define AnchorPositioningUtils_h_ -#include "WritingModes.h" #include "mozilla/Maybe.h" -#include "nsHashKeys.h" +#include "mozilla/WritingModes.h" #include "nsRect.h" -#include "nsTHashMap.h" class nsAtom; class nsIFrame; @@ -360,7 +358,13 @@ struct AnchorPositioningUtils { * element. For popovers, this returns the primary frame of the invoker. In * all other cases, returns null. */ - static nsIFrame* GetAnchorPosImplicitAnchor(const nsIFrame* aFrame); + enum class ImplicitAnchorKind : uint8_t { None, Popover, PseudoElement }; + struct ImplicitAnchorResult { + nsIFrame* mAnchorFrame = nullptr; + ImplicitAnchorKind mKind = ImplicitAnchorKind::None; + }; + static ImplicitAnchorResult GetAnchorPosImplicitAnchor( + const nsIFrame* aFrame); struct NearestScrollFrameInfo { const nsIFrame* mScrollContainer = nullptr; diff --git a/layout/base/PresShell.cpp b/layout/base/PresShell.cpp index b72d07ef415e6..74484b875d320 100644 --- a/layout/base/PresShell.cpp +++ b/layout/base/PresShell.cpp @@ -11335,7 +11335,8 @@ nsIFrame* PresShell::GetAnchorPosAnchor( MOZ_ASSERT(!aName.mName->IsEmpty()); MOZ_ASSERT(mLazyAnchorPosAnchorChanges.IsEmpty()); if (aName.mName == nsGkAtoms::AnchorPosImplicitAnchor) { - return AnchorPositioningUtils::GetAnchorPosImplicitAnchor(aPositionedFrame); + return AnchorPositioningUtils::GetAnchorPosImplicitAnchor(aPositionedFrame) + .mAnchorFrame; } if (const auto& entry = mAnchorPosAnchors.Lookup(aName.mName)) { return AnchorPositioningUtils::FindFirstAcceptableAnchor( diff --git a/layout/forms/moz.build b/layout/forms/moz.build index 3ff5cdb4bbb4b..73bc8d3f34abc 100644 --- a/layout/forms/moz.build +++ b/layout/forms/moz.build @@ -10,10 +10,6 @@ with Files("**"): MOCHITEST_MANIFESTS += ["test/mochitest.toml"] MOCHITEST_CHROME_MANIFESTS += ["test/chrome.toml"] -EXPORTS += [ - "nsISelectControlFrame.h", -] - UNIFIED_SOURCES += [ "ButtonControlFrame.cpp", "HTMLSelectEventListener.cpp", diff --git a/layout/forms/nsISelectControlFrame.h b/layout/forms/nsISelectControlFrame.h deleted file mode 100644 index 92dcd0a374941..0000000000000 --- a/layout/forms/nsISelectControlFrame.h +++ /dev/null @@ -1,42 +0,0 @@ -/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ -/* vim: set ts=8 sts=2 et sw=2 tw=80: */ -/* 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/. */ - -#ifndef nsISelectControlFrame_h_ -#define nsISelectControlFrame_h_ - -#include "nsQueryFrame.h" - -/** - * nsISelectControlFrame is the interface for combo boxes and listboxes - */ -class nsISelectControlFrame : public nsQueryFrame { - public: - NS_DECL_QUERYFRAME_TARGET(nsISelectControlFrame) - - /** Adds an option to the list at index */ - NS_IMETHOD AddOption(int32_t index) = 0; - - /** - * Removes the option at index. The caller must have a live script - * blocker while calling this method. - */ - NS_IMETHOD RemoveOption(int32_t index) = 0; - - /** Called when the parser is done adding children */ - NS_IMETHOD DoneAddingChildren() = 0; - - /** Notify the frame when an option is selected */ - NS_IMETHOD OnOptionSelected(int32_t aIndex, bool aSelected) = 0; - - /** - * Notify the frame when selectedIndex was changed. This might - * destroy the frame. - */ - NS_IMETHOD_(void) - OnSetSelectedIndex(int32_t aOldIndex, int32_t aNewIndex) = 0; -}; - -#endif diff --git a/layout/forms/nsListControlFrame.cpp b/layout/forms/nsListControlFrame.cpp index c06a9378bd2b1..2986a90e87338 100644 --- a/layout/forms/nsListControlFrame.cpp +++ b/layout/forms/nsListControlFrame.cpp @@ -87,7 +87,6 @@ bool nsListControlFrame::IsFocused() const { void nsListControlFrame::InvalidateFocus() { InvalidateFrame(); } NS_QUERYFRAME_HEAD(nsListControlFrame) - NS_QUERYFRAME_ENTRY(nsISelectControlFrame) NS_QUERYFRAME_ENTRY(nsListControlFrame) NS_QUERYFRAME_TAIL_INHERITING(ScrollContainerFrame) @@ -579,12 +578,10 @@ dom::HTMLOptionElement* nsListControlFrame::GetOption(uint32_t aIndex) const { return Select().Item(aIndex); } -NS_IMETHODIMP -nsListControlFrame::OnOptionSelected(int32_t aIndex, bool aSelected) { +void nsListControlFrame::OnOptionSelected(int32_t aIndex, bool aSelected) { if (aSelected) { ScrollToIndex(aIndex); } - return NS_OK; } void nsListControlFrame::OnContentReset() { ResetList(true); } @@ -646,27 +643,15 @@ uint32_t nsListControlFrame::GetNumberOfOptions() { return options->Length(); } -//---------------------------------------------------------------------- -// nsISelectControlFrame -//---------------------------------------------------------------------- - -NS_IMETHODIMP -nsListControlFrame::DoneAddingChildren() { - ResetList(true); - return NS_OK; -} +void nsListControlFrame::DoneAddingChildren() { ResetList(true); } -NS_IMETHODIMP -nsListControlFrame::AddOption(int32_t aIndex) { +void nsListControlFrame::AddOption(int32_t aIndex) { // Make sure we scroll to the selected option as needed mNeedToReset = true; - if (!Select().IsDoneAddingChildren()) { - return NS_OK; + if (Select().IsDoneAddingChildren()) { + mPostChildrenLoadedReset = true; } - - mPostChildrenLoadedReset = true; - return NS_OK; } static int32_t DecrementAndClamp(int32_t aSelectionIndex, int32_t aLength) { @@ -674,8 +659,7 @@ static int32_t DecrementAndClamp(int32_t aSelectionIndex, int32_t aLength) { : std::max(0, aSelectionIndex - 1); } -NS_IMETHODIMP -nsListControlFrame::RemoveOption(int32_t aIndex) { +void nsListControlFrame::RemoveOption(int32_t aIndex) { MOZ_ASSERT(aIndex >= 0, "negative