diff --git a/.clang-format-ignore b/.clang-format-ignore index 65b9e2920315..9828ead34b54 100644 --- a/.clang-format-ignore +++ b/.clang-format-ignore @@ -157,6 +157,7 @@ media/libvorbis/.* media/libvpx/.* media/libwebp/.* media/libyuv/.* +media/mozva/va/.* media/openmax_dl/.* media/openmax_il/.* media/webrtc/signaling/src/sdp/sipcc/.* diff --git a/browser/base/content/browser.css b/browser/base/content/browser.css index 0784af042d69..0593867b200e 100644 --- a/browser/base/content/browser.css +++ b/browser/base/content/browser.css @@ -202,14 +202,6 @@ panelview[mainview] > .panel-header { visibility: hidden; } -.tab-icon-image[fadein], -.tab-close-button[fadein], -.tabbrowser-tab[fadein]::after, -.tab-background[fadein] { - /* This transition is only wanted for opening tabs. */ - transition: visibility 0ms 25ms; -} - .tab-icon-pending:not([fadein]), .tab-icon-image:not([fadein]), .tab-close-button:not([fadein]), diff --git a/browser/components/preferences/main.js b/browser/components/preferences/main.js index 0823f3b301c5..9dfdd10a23bd 100644 --- a/browser/components/preferences/main.js +++ b/browser/components/preferences/main.js @@ -2616,6 +2616,9 @@ var gMainPane = { * Sort the list when the user clicks on a column header. */ sort(event) { + if (event.button != 0) { + return; + } var column = event.target; // If the user clicked on a new sort column, remove the direction indicator diff --git a/browser/extensions/webcompat/data/injections.js b/browser/extensions/webcompat/data/injections.js index ce68e8b168b1..b6d9f129f668 100644 --- a/browser/extensions/webcompat/data/injections.js +++ b/browser/extensions/webcompat/data/injections.js @@ -604,21 +604,6 @@ const AVAILABLE_INJECTIONS = [ ], }, }, - { - id: "bug1746883", - platform: "all", - domain: "zoom.us", - bug: "1746883", - contentScripts: { - matches: ["*://*.zoom.us/*"], - js: [ - { - file: "injections/js/bug1746883-zoom.us-OffscreenCanvas.js", - }, - ], - allFrames: true, - }, - }, ]; module.exports = AVAILABLE_INJECTIONS; diff --git a/browser/extensions/webcompat/injections/js/bug1746883-zoom.us-OffscreenCanvas.js b/browser/extensions/webcompat/injections/js/bug1746883-zoom.us-OffscreenCanvas.js deleted file mode 100644 index d19642206ce1..000000000000 --- a/browser/extensions/webcompat/injections/js/bug1746883-zoom.us-OffscreenCanvas.js +++ /dev/null @@ -1,28 +0,0 @@ -"use strict"; - -/** - * Bug 1746883 - disable OffscreenCanvas for Zoom - * - * When OffscreenCanvas is enabled, Zoom breaks due to Canvas2D not being - * supported yet. As such, we disable OffscreenCanvas for now on Zoom. - */ - -if (window.OffscreenCanvas) { - console.info( - "OffscreenCanvas has been disabled for compatibility reasons. See https://bugzilla.mozilla.org/show_bug.cgi?id=1746883 for details." - ); - - Object.defineProperty(window.wrappedJSObject, "OffscreenCanvas", { - get: undefined, - set: undefined, - }); - - Object.defineProperty( - HTMLCanvasElement.wrappedJSObject.prototype, - "transferControlToOffscreen", - { - get: undefined, - set: undefined, - } - ); -} diff --git a/browser/extensions/webcompat/manifest.json b/browser/extensions/webcompat/manifest.json index c4067beffcdf..db384f0b880a 100644 --- a/browser/extensions/webcompat/manifest.json +++ b/browser/extensions/webcompat/manifest.json @@ -2,7 +2,7 @@ "manifest_version": 2, "name": "Web Compatibility Interventions", "description": "Urgent post-release fixes for web compatibility.", - "version": "29.8.1", + "version": "29.9.0", "applications": { "gecko": { "id": "webcompat@mozilla.org", diff --git a/browser/extensions/webcompat/moz.build b/browser/extensions/webcompat/moz.build index 063cefcef283..c20049310097 100644 --- a/browser/extensions/webcompat/moz.build +++ b/browser/extensions/webcompat/moz.build @@ -84,7 +84,6 @@ FINAL_TARGET_FILES.features["webcompat@mozilla.org"]["injections"]["js"] += [ "injections/js/bug1724764-amextravel.com-window-print.js", "injections/js/bug1724868-news.yahoo.co.jp-ua-override.js", "injections/js/bug1731825-office365-email-handling-prompt-autohide.js", - "injections/js/bug1746883-zoom.us-OffscreenCanvas.js", ] FINAL_TARGET_FILES.features["webcompat@mozilla.org"]["shims"] += [ diff --git a/config/check_spidermonkey_style.py b/config/check_spidermonkey_style.py index 1cb6c6242f52..21d48fe223ec 100644 --- a/config/check_spidermonkey_style.py +++ b/config/check_spidermonkey_style.py @@ -65,6 +65,7 @@ included_inclnames_to_ignore = set( "frontend/smoosh_generated.h", # generated in $OBJDIR "gc/StatsPhasesGenerated.h", # generated in $OBJDIR "gc/StatsPhasesGenerated.inc", # generated in $OBJDIR + "jit/AtomicOperationsGenerated.h", # generated in $OBJDIR "jit/CacheIROpsGenerated.h", # generated in $OBJDIR "jit/LIROpsGenerated.h", # generated in $OBJDIR "jit/MIROpsGenerated.h", # generated in $OBJDIR diff --git a/devtools/client/framework/devtools.js b/devtools/client/framework/devtools.js index ae957d426ce7..33d65e1ea7a7 100644 --- a/devtools/client/framework/devtools.js +++ b/devtools/client/framework/devtools.js @@ -57,6 +57,7 @@ const { const FORBIDDEN_IDS = new Set(["toolbox", ""]); const MAX_ORDINAL = 99; +const POPUP_DEBUG_PREF = "devtools.popups.debug"; /** * DevTools is a class that represents a set of developer tools, it holds a @@ -585,7 +586,10 @@ DevTools.prototype = { ) { // Popups are debugged via the toolbox of their opener document/tab. // So avoid opening dedicated toolbox for them. - if (tab.linkedBrowser.browsingContext.opener) { + if ( + tab.linkedBrowser.browsingContext.opener && + Services.prefs.getBoolPref(POPUP_DEBUG_PREF) + ) { const openerTab = tab.ownerGlobal.gBrowser.getTabForBrowser( tab.linkedBrowser.browsingContext.opener.embedderElement ); diff --git a/devtools/client/framework/test/browser.ini b/devtools/client/framework/test/browser.ini index b0b079f1bf24..0f8e6818d6af 100644 --- a/devtools/client/framework/test/browser.ini +++ b/devtools/client/framework/test/browser.ini @@ -124,6 +124,7 @@ skip-if = [browser_toolbox_options_enable_serviceworkers_testing.js] [browser_toolbox_options_frames_button.js] [browser_toolbox_options_panel_toggle.js] +[browser_toolbox_popups_debugging.js] [browser_toolbox_raise.js] disabled=Bug 962258 [browser_toolbox_races.js] diff --git a/devtools/client/framework/test/browser_toolbox_popups_debugging.js b/devtools/client/framework/test/browser_toolbox_popups_debugging.js new file mode 100644 index 000000000000..7dabefac3364 --- /dev/null +++ b/devtools/client/framework/test/browser_toolbox_popups_debugging.js @@ -0,0 +1,56 @@ +/* Any copyright is dedicated to the Public Domain. + * http://creativecommons.org/publicdomain/zero/1.0/ */ + +// Test opening toolboxes against a tab and its popup + +const TEST_URL = "data:text/html,test for debugging popups"; +const POPUP_URL = "data:text/html,popup"; + +const POPUP_DEBUG_PREF = "devtools.popups.debug"; + +add_task(async function() { + const isPopupDebuggingEnabled = Services.prefs.getBoolPref(POPUP_DEBUG_PREF); + + info("Open a tab and debug it"); + const tab = await addTab(TEST_URL); + const toolbox = await gDevTools.showToolboxForTab(tab, { + toolId: "webconsole", + }); + + info("Open a popup"); + const onTabOpened = once(gBrowser.tabContainer, "TabOpen"); + const onToolboxSwitchedToTab = toolbox.once("switched-host-to-tab"); + await SpecialPowers.spawn(tab.linkedBrowser, [POPUP_URL], url => { + content.open(url); + }); + const tabOpenEvent = await onTabOpened; + const popupTab = tabOpenEvent.target; + + const popupToolbox = await gDevTools.showToolboxForTab(popupTab); + if (isPopupDebuggingEnabled) { + ok( + !popupToolbox, + "When popup debugging is enabled, the popup should be debugged via the same toolbox as the original tab" + ); + info("Wait for internal event notifying about the toolbox being moved"); + await onToolboxSwitchedToTab; + const browserContainer = gBrowser.getBrowserContainer( + popupTab.linkedBrowser + ); + const iframe = browserContainer.querySelector( + ".devtools-toolbox-bottom-iframe" + ); + ok(iframe, "The original tab's toolbox moved to the popup tab"); + } else { + ok(popupToolbox, "We were able to spawn a toolbox for the popup"); + info("Close the popup toolbox and its tab"); + await popupToolbox.destroy(); + } + + info("Close the popup tab"); + gBrowser.removeCurrentTab(); + + info("Close the original tab toolbox and itself"); + await toolbox.destroy(); + gBrowser.removeCurrentTab(); +}); diff --git a/devtools/client/framework/toolbox.js b/devtools/client/framework/toolbox.js index 6528dcf4054f..a6fe0563b9d9 100644 --- a/devtools/client/framework/toolbox.js +++ b/devtools/client/framework/toolbox.js @@ -3639,6 +3639,8 @@ Toolbox.prototype = { ); this.commands.targetCommand.selectTarget(target); + + this.emit("switched-host-to-tab"); }, /** diff --git a/devtools/client/inspector/markup/test/browser_markup_scrollable_badge_click.js b/devtools/client/inspector/markup/test/browser_markup_scrollable_badge_click.js index 1a788a83f27c..973523a035df 100644 --- a/devtools/client/inspector/markup/test/browser_markup_scrollable_badge_click.js +++ b/devtools/client/inspector/markup/test/browser_markup_scrollable_badge_click.js @@ -21,12 +21,15 @@ const TEST_URI = ` .shift { margin-left: 300px; } + .has-before::before { + content: "-"; + }
-
+
diff --git a/devtools/client/inspector/markup/test/browser_markup_view-source.js b/devtools/client/inspector/markup/test/browser_markup_view-source.js index 426713599150..e184d7b84b4f 100644 --- a/devtools/client/inspector/markup/test/browser_markup_view-source.js +++ b/devtools/client/inspector/markup/test/browser_markup_view-source.js @@ -3,9 +3,22 @@ "use strict"; +/* import-globals-from ../../../debugger/test/mochitest/helpers.js */ +Services.scriptloader.loadSubScript( + "chrome://mochitests/content/browser/devtools/client/debugger/test/mochitest/helpers.js", + this +); + +/* import-globals-from ../../../debugger/test/mochitest/helpers/context.js */ +Services.scriptloader.loadSubScript( + "chrome://mochitests/content/browser/devtools/client/debugger/test/mochitest/helpers/context.js", + this +); + const DOCUMENT_SRC = ` - + + `; @@ -22,36 +35,94 @@ button.addEventListener("click", foo, false); const TEST_URI = "data:text/html;charset=utf-8," + DOCUMENT_SRC; add_task(async function() { - // Test that event handler links go to the right debugger source when it - // came from an eval(). - const { inspector, tab, toolbox } = await openInspectorForURL(TEST_URI); + const { inspector, toolbox } = await openInspectorForURL(TEST_URI); - const nodeFront = await getNodeFront("#foo", inspector); + info( + "Test that event handler links go to the right debugger source when it came from an eval()" + ); + const evaledSource = await clickOnJumpToDebuggerIconForNode( + inspector, + toolbox, + "#btn-eval" + ); + is(evaledSource.url, null, "no expected url for eval source"); + + info("Add a breakpoint in opened source"); + const debuggerContext = createDebuggerContext(toolbox); + await addBreakpoint( + debuggerContext, + debuggerContext.selectors.getSelectedSource(), + 1 + ); + await safeSynthesizeMouseEventAtCenterInContentPage("#btn-eval"); + + await waitForPaused(debuggerContext); + ok(true, "The debugger paused on the evaled source breakpoint"); + await resume(debuggerContext); + + info( + "Test that event handler links go to the right debugger source when it's a dom0 event listener." + ); + await toolbox.selectTool("inspector"); + const dom0Source = await clickOnJumpToDebuggerIconForNode( + inspector, + toolbox, + "#btn-dom0" + ); + is(dom0Source.url, null, "no expected url for dom0 event listener source"); + await addBreakpoint( + debuggerContext, + debuggerContext.selectors.getSelectedSource(), + 1 + ); + await safeSynthesizeMouseEventAtCenterInContentPage("#btn-dom0"); + await waitForPaused(debuggerContext); + ok(true, "The debugger paused on the dom0 source breakpoint"); + await resume(debuggerContext); +}); + +async function clickOnJumpToDebuggerIconForNode( + inspector, + toolbox, + nodeSelector +) { + const nodeFront = await getNodeFront(nodeSelector, inspector); const container = getContainerForNodeFront(nodeFront, inspector); const evHolder = container.elt.querySelector( ".inspector-badge.interactive[data-event]" ); - evHolder.scrollIntoView(); + info(`Display event tooltip for node "${nodeSelector}"`); EventUtils.synthesizeMouseAtCenter( evHolder, {}, inspector.markup.doc.defaultView ); - const tooltip = inspector.markup.eventDetailsTooltip; await tooltip.once("shown"); + info(`Tooltip displayed, click on the "jump to debugger" icon`); const debuggerIcon = tooltip.panel.querySelector( ".event-tooltip-debugger-icon" ); + + if (!debuggerIcon) { + ok( + false, + `There is no jump to debugger icon in event tooltip for node "${nodeSelector}"` + ); + return null; + } + + const onDebuggerSelected = toolbox.once(`jsdebugger-selected`); EventUtils.synthesizeMouse(debuggerIcon, 2, 2, {}, debuggerIcon.ownerGlobal); - await gDevTools.showToolboxForTab(tab, { toolId: "jsdebugger" }); - const dbg = toolbox.getPanel("jsdebugger"); + const dbg = await onDebuggerSelected; + ok(true, "The debugger was opened"); let source; + info("Wait for source to be opened"); await BrowserTestUtils.waitForCondition( () => { source = dbg._selectors.getSelectedSource(dbg._getState()); @@ -61,6 +132,5 @@ add_task(async function() { 100, 20 ); - - is(source.url, null, "expected no url for eval source"); -}); + return source; +} diff --git a/devtools/client/inspector/markup/views/read-only-editor.js b/devtools/client/inspector/markup/views/read-only-editor.js index 63611043a234..0a93bcde624a 100644 --- a/devtools/client/inspector/markup/views/read-only-editor.js +++ b/devtools/client/inspector/markup/views/read-only-editor.js @@ -59,6 +59,18 @@ ReadOnlyEditor.prototype = { this.tag = null; }, + /** + * Show overflow highlight if showOverflowHighlight is true, otherwise hide it. + * + * @param {Boolean} showOverflowHighlight + */ + setOverflowHighlight: function(showOverflowHighlight) { + this.container.tagState.classList.toggle( + "overflow-causing-highlighted", + showOverflowHighlight + ); + }, + /** * Stub method for consistency with ElementEditor. */ diff --git a/devtools/client/inspector/rules/test/browser_rules_content_01.js b/devtools/client/inspector/rules/test/browser_rules_content_01.js index feb7e0bba88e..a6ca96108063 100644 --- a/devtools/client/inspector/rules/test/browser_rules_content_01.js +++ b/devtools/client/inspector/rules/test/browser_rules_content_01.js @@ -41,17 +41,21 @@ add_task(async function() { await selectNode("#testid", inspector); let linkText = getRuleViewLinkTextByIndex(view, 1); + is(linkText, "inline:3", "link text at index 1 has expected content."); + + const mediaText = getRuleViewMediaTextByIndex(view, 1); is( - linkText, - "inline:3 @screen and (min-width: 10px)", - "link text at index 1 contains media query text." + mediaText, + "@media screen and (min-width: 10px)", + "media text at index 1 has expected content" ); linkText = getRuleViewLinkTextByIndex(view, 2); + is(linkText, "inline:7", "link text at index 2 has expected content."); is( - linkText, - "inline:7", - "link text at index 2 contains no media query text." + getRuleViewMediaElementByIndex(view, 2), + null, + "There is no media text element for rule at index 2" ); const selector = getRuleViewRuleEditor(view, 2).selectorText; @@ -66,3 +70,13 @@ add_task(async function() { ".unmatched should not be matched." ); }); + +function getRuleViewMediaElementByIndex(view, ruleIndex) { + return view.styleDocument.querySelector( + `.ruleview-rule:nth-of-type(${ruleIndex + 1}) .ruleview-rule-parent-data` + ); +} + +function getRuleViewMediaTextByIndex(view, ruleIndex) { + return getRuleViewMediaElementByIndex(view, ruleIndex)?.textContent; +} diff --git a/devtools/client/inspector/rules/views/rule-editor.js b/devtools/client/inspector/rules/views/rule-editor.js index 924d396261b2..a197741b6663 100644 --- a/devtools/client/inspector/rules/views/rule-editor.js +++ b/devtools/client/inspector/rules/views/rule-editor.js @@ -135,6 +135,18 @@ RuleEditor.prototype = { this.updateSourceLink(); + if (this.rule.mediaText) { + const text = `@media ${this.rule.mediaText}`; + createChild(this.element, "span", { + class: "ruleview-rule-parent-data theme-link", + // We force the string to be LTR in CSS, but for some reason, the `@` char is seen + // as not part of the string in the tooltip, and is displayed "at the end" of the + // string in RTL locales. To workaround this, we force LTR with \u202D + title: `\u202A${text}`, + textContent: text, + }); + } + const code = createChild(this.element, "div", { class: "ruleview-code", }); @@ -302,10 +314,6 @@ RuleEditor.prototype = { sourceTextContent += ":" + line; title += ":" + line; } - if (this.rule.mediaText) { - sourceTextContent += " @" + this.rule.mediaText; - title += " @" + this.rule.mediaText; - } const sourceLabel = this.element.querySelector( ".ruleview-rule-source-label" diff --git a/devtools/client/themes/rules.css b/devtools/client/themes/rules.css index 33c24cb2a0d6..6bdbb0a5597f 100644 --- a/devtools/client/themes/rules.css +++ b/devtools/client/themes/rules.css @@ -148,10 +148,6 @@ padding: 0; } -.ruleview-code { - direction: ltr; -} - .ruleview-property:not(:hover) > .ruleview-enableproperty { pointer-events: none; } @@ -267,6 +263,7 @@ .ruleview-rule { border-bottom: 1px solid var(--theme-splitter-color); padding: 2px 4px; + direction: ltr; } #ruleview-container-focusable > .ruleview-rule:last-child { @@ -696,6 +693,17 @@ border-bottom-color: hsl(0,0%,50%); } +/* @media and @layer rule info element */ +.ruleview-rule-parent-data { + max-width: 100%; + display: inline-block; + overflow: hidden; + text-overflow: ellipsis; + /* we only want to have it displayed on a single line. The whole string is available on + the title attribute of the element anyway */ + white-space: nowrap; +} + .ruleview-selectorcontainer { word-wrap: break-word; cursor: text; diff --git a/devtools/server/actors/inspector/event-collector.js b/devtools/server/actors/inspector/event-collector.js index 683e6c01c5a8..23b47d715ea6 100644 --- a/devtools/server/actors/inspector/event-collector.js +++ b/devtools/server/actors/inspector/event-collector.js @@ -931,7 +931,6 @@ class EventCollector { const override = listener.override || {}; const tags = listener.tags || ""; const type = listener.type || ""; - let isScriptBoundToNonScriptElement = false; const enabled = !!listener.enabled; let functionSource = handler.toString(); let line = 0; @@ -968,13 +967,6 @@ class EventCollector { if (script) { const scriptSource = script.source.text; - // Scripts are provided via script tags. If it wasn't provided by a - // script tag it must be a DOM0 event. - if (script.source.element) { - isScriptBoundToNonScriptElement = - script.source.element.class !== "HTMLScriptElement"; - } - line = script.startLine; column = script.startColumn; url = script.url; @@ -1032,9 +1024,7 @@ class EventCollector { } else { origin = url + - (isScriptBoundToNonScriptElement || line === 0 - ? "" - : ":" + line + (column === null ? "" : ":" + column)); + (line ? ":" + line + (column === null ? "" : ":" + column) : ""); } eventObj = { @@ -1056,7 +1046,7 @@ class EventCollector { // Hide the debugger icon for DOM0 and native listeners. DOM0 listeners are // generated dynamically from e.g. an onclick="" attribute so the script // doesn't actually exist. - if (native || isScriptBoundToNonScriptElement) { + if (!sourceActor) { eventObj.hide.debugger = true; } } finally { diff --git a/devtools/server/actors/targets/window-global.js b/devtools/server/actors/targets/window-global.js index d83d37ee2e3b..280a51e7ecfd 100644 --- a/devtools/server/actors/targets/window-global.js +++ b/devtools/server/actors/targets/window-global.js @@ -594,7 +594,13 @@ const windowGlobalTargetPrototype = { originalBrowsingContext.currentWindowContext.innerWindowId; const parentInnerWindowId = originalBrowsingContext.parent?.currentWindowContext.innerWindowId; - const isPopup = !!originalBrowsingContext.opener; + // Doesn't only check `!!opener` as some iframe might have an opener + // if their location was loaded via `window.open(url, "iframe-name")`. + // So also ensure that the document is opened in a distinct tab. + const isPopup = + !!originalBrowsingContext.opener && + originalBrowsingContext.browserId != + originalBrowsingContext.opener.browserId; const response = { actor: this.actorID, diff --git a/devtools/shared/commands/resource/tests/browser_resources_network_events_parent_process.js b/devtools/shared/commands/resource/tests/browser_resources_network_events_parent_process.js index d9bbee0da03b..f8066c29f437 100644 --- a/devtools/shared/commands/resource/tests/browser_resources_network_events_parent_process.js +++ b/devtools/shared/commands/resource/tests/browser_resources_network_events_parent_process.js @@ -3,12 +3,40 @@ "use strict"; +/** + * !! AFTER MOVING OR RENAMING THIS METHOD, UPDATE `EXPECTED` CONSTANTS BELOW !! + */ +const createParentProcessRequests = async () => { + info("Do some requests from the parent process"); + // The line:column for `fetch` should be EXPECTED_REQUEST_LINE_1/COL_1 + await fetch(FETCH_URI); + + const img = new Image(); + const onLoad = new Promise(r => img.addEventListener("load", r)); + // The line:column for `img` below should be EXPECTED_REQUEST_LINE_2/COL_2 + img.src = IMAGE_URI; + await onLoad; +}; + +const EXPECTED_METHOD_NAME = "createParentProcessRequests"; +const EXPECTED_REQUEST_LINE_1 = 12; +const EXPECTED_REQUEST_COL_1 = 9; +const EXPECTED_REQUEST_LINE_2 = 17; +const EXPECTED_REQUEST_COL_2 = 3; + // Test the ResourceCommand API around NETWORK_EVENT for the parent process const FETCH_URI = "https://example.com/document-builder.sjs?html=foo"; -const IMAGE_URI = URL_ROOT_SSL + "test_image.png"; +// The img.src request gets cached regardless of `devtools.cache.disabled`. +// Add a random parameter to the request to bypass the cache. +const uuid = `${Date.now()}-${Math.random()}`; +const IMAGE_URI = URL_ROOT_SSL + "test_image.png?" + uuid; add_task(async function testParentProcessRequests() { + // The test expects the main process commands instance to receive resources + // for content process requests. + await pushPref("devtools.browsertoolbox.fission", true); + const commands = await CommandsFactory.forMainProcess(); await commands.targetCommand.startListening(); const { resourceCommand } = commands; @@ -48,13 +76,7 @@ add_task(async function testParentProcessRequests() { } ); - info("Do some requests from the parent process"); - await fetch(FETCH_URI); - - const img = new Image(); - const onLoad = new Promise(r => img.addEventListener("load", r)); - img.src = IMAGE_URI; - await onLoad; + await createParentProcessRequests(); const img2 = new Image(); img2.src = IMAGE_URI; @@ -75,9 +97,9 @@ add_task(async function testParentProcessRequests() { const fetchStacktrace = receivedStacktraces[0].lastFrame; is(receivedStacktraces[0].resourceId, fetchRequest.stacktraceResourceId); is(fetchStacktrace.filename, gTestPath); - is(fetchStacktrace.lineNumber, 52); - is(fetchStacktrace.columnNumber, 9); - is(fetchStacktrace.functionName, "testParentProcessRequests"); + is(fetchStacktrace.lineNumber, EXPECTED_REQUEST_LINE_1); + is(fetchStacktrace.columnNumber, EXPECTED_REQUEST_COL_1); + is(fetchStacktrace.functionName, EXPECTED_METHOD_NAME); is(fetchStacktrace.asyncCause, null); async function getResponseContent(networkEvent) { @@ -103,9 +125,9 @@ add_task(async function testParentProcessRequests() { const firstImageStacktrace = receivedStacktraces[1].lastFrame; is(receivedStacktraces[1].resourceId, firstImageRequest.stacktraceResourceId); is(firstImageStacktrace.filename, gTestPath); - is(firstImageStacktrace.lineNumber, 56); - is(firstImageStacktrace.columnNumber, 3); - is(firstImageStacktrace.functionName, "testParentProcessRequests"); + is(firstImageStacktrace.lineNumber, EXPECTED_REQUEST_LINE_2); + is(firstImageStacktrace.columnNumber, EXPECTED_REQUEST_COL_2); + is(firstImageStacktrace.functionName, EXPECTED_METHOD_NAME); is(firstImageStacktrace.asyncCause, null); info("Assert the second image request"); diff --git a/devtools/shared/commands/target/tests/browser_target_command_frames_popups.js b/devtools/shared/commands/target/tests/browser_target_command_frames_popups.js index 8d3de83be271..0dd02eb36cce 100644 --- a/devtools/shared/commands/target/tests/browser_target_command_frames_popups.js +++ b/devtools/shared/commands/target/tests/browser_target_command_frames_popups.js @@ -12,6 +12,9 @@ const POPUP_SECOND_URL = add_task(async function() { await pushPref("devtools.popups.debug", true); + // We expect to create a target for a same-process iframe + // in the test against window.open to load a document in an iframe. + await pushPref("devtools.every-frame-target.enabled", true); // Create a TargetCommand for a given test tab const tab = await addTab(TEST_URL); @@ -140,7 +143,23 @@ add_task(async function() { ok(!targets[3].isDestroyed(), "The about:blank popup target is still alive"); info("Call about:blank popup method to ensure it really is functional"); - await targets[3].focus(); + await targets[3].logInPage("foo"); + + info( + "Ensure that iframe using window.open to load their document aren't considered as popups" + ); + await SpecialPowers.spawn(gBrowser.selectedBrowser, [], async () => { + const iframe = content.document.createElement("iframe"); + iframe.setAttribute("name", "test-iframe"); + content.document.documentElement.appendChild(iframe); + content.open("data:text/html,iframe", "test-iframe"); + }); + await waitFor(() => targets.length === 6); + is( + targets[5].targetForm.isPopup, + false, + "The iframe target isn't considered as a popup" + ); targetCommand.unwatchTargets({ types: [TYPES.FRAME], diff --git a/dom/abort/AbortFollower.h b/dom/abort/AbortFollower.h index f16fd43d61e5..67c4af7e7414 100644 --- a/dom/abort/AbortFollower.h +++ b/dom/abort/AbortFollower.h @@ -38,8 +38,6 @@ class AbortFollower : public nsISupports { AbortSignalImpl* Signal() const { return mFollowingSignal; } protected: - static void Unlink(AbortFollower* aFollower) { aFollower->Unfollow(); } - virtual ~AbortFollower(); friend class AbortSignalImpl; diff --git a/dom/abort/AbortSignal.cpp b/dom/abort/AbortSignal.cpp index dea1fbc8de86..1e9eab90f4cd 100644 --- a/dom/abort/AbortSignal.cpp +++ b/dom/abort/AbortSignal.cpp @@ -110,7 +110,6 @@ NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN_INHERITED(AbortSignal, DOMEventTargetHelper) AbortSignalImpl::Unlink(static_cast(tmp)); - AbortFollower::Unlink(static_cast(tmp)); NS_IMPL_CYCLE_COLLECTION_UNLINK_END NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(AbortSignal) diff --git a/dom/bindings/Bindings.conf b/dom/bindings/Bindings.conf index 22b74309f226..66f444a57904 100644 --- a/dom/bindings/Bindings.conf +++ b/dom/bindings/Bindings.conf @@ -284,6 +284,10 @@ DOMInterfaces = { 'concrete': True, }, +'FileSystemHandle': { + 'concrete': True, +}, + 'FluentBundle': { 'nativeType': 'mozilla::intl::FluentBundle', }, diff --git a/dom/canvas/CanvasRenderingContext2D.cpp b/dom/canvas/CanvasRenderingContext2D.cpp index 087e78a89da0..3256a02713ed 100644 --- a/dom/canvas/CanvasRenderingContext2D.cpp +++ b/dom/canvas/CanvasRenderingContext2D.cpp @@ -977,7 +977,8 @@ CanvasRenderingContext2D::CanvasRenderingContext2D( mHasPendingStableStateCallback(false), mIsEntireFrameInvalid(false), mPredictManyRedrawCalls(false), - mIsCapturedFrameInvalid(false), + mFrameCaptureState(FrameCaptureState::CLEAN, + "CanvasRenderingContext2D::mFrameCaptureState"), mPathTransformWillUpdate(false), mInvalidateCount(0), mWriteOnly(false) { @@ -1054,7 +1055,7 @@ nsresult CanvasRenderingContext2D::Reset() { // no longer be valid. mIsEntireFrameInvalid = false; mPredictManyRedrawCalls = false; - mIsCapturedFrameInvalid = false; + mFrameCaptureState = FrameCaptureState::CLEAN; return NS_OK; } @@ -1122,7 +1123,7 @@ void CanvasRenderingContext2D::StyleColorToString(const nscolor& aColor, } nsresult CanvasRenderingContext2D::Redraw() { - mIsCapturedFrameInvalid = true; + mFrameCaptureState = FrameCaptureState::DIRTY; if (mIsEntireFrameInvalid) { return NS_OK; @@ -1143,7 +1144,7 @@ nsresult CanvasRenderingContext2D::Redraw() { } void CanvasRenderingContext2D::Redraw(const gfx::Rect& aR) { - mIsCapturedFrameInvalid = true; + mFrameCaptureState = FrameCaptureState::DIRTY; ++mInvalidateCount; @@ -1169,7 +1170,7 @@ void CanvasRenderingContext2D::Redraw(const gfx::Rect& aR) { void CanvasRenderingContext2D::DidRefresh() {} void CanvasRenderingContext2D::RedrawUser(const gfxRect& aR) { - mIsCapturedFrameInvalid = true; + mFrameCaptureState = FrameCaptureState::DIRTY; if (mIsEntireFrameInvalid) { ++mInvalidateCount; @@ -1360,12 +1361,12 @@ bool CanvasRenderingContext2D::EnsureTarget(const gfx::Rect* aCoveredRect, if (mCanvasElement) { mCanvasElement->InvalidateCanvas(); } - // EnsureTarget hasn't drawn anything. Preserve mIsCapturedFrameInvalid. - bool capturedFrameInvalid = mIsCapturedFrameInvalid; + // EnsureTarget hasn't drawn anything. Preserve mFrameCaptureState. + FrameCaptureState captureState = mFrameCaptureState; // Calling Redraw() tells our invalidation machinery that the entire // canvas is already invalid, which can speed up future drawing. Redraw(); - mIsCapturedFrameInvalid = capturedFrameInvalid; + mFrameCaptureState = captureState; return true; } @@ -5506,14 +5507,6 @@ void CanvasRenderingContext2D::MarkContextClean() { mInvalidateCount = 0; } -void CanvasRenderingContext2D::MarkContextCleanForFrameCapture() { - mIsCapturedFrameInvalid = false; -} - -bool CanvasRenderingContext2D::IsContextCleanForFrameCapture() { - return !mIsCapturedFrameInvalid; -} - void CanvasRenderingContext2D::GetAppUnitsValues(int32_t* aPerDevPixel, int32_t* aPerCSSPixel) { // If we don't have a canvas element, we just return something generic. diff --git a/dom/canvas/CanvasRenderingContext2D.h b/dom/canvas/CanvasRenderingContext2D.h index 9a6b42344810..d7695683c046 100644 --- a/dom/canvas/CanvasRenderingContext2D.h +++ b/dom/canvas/CanvasRenderingContext2D.h @@ -430,8 +430,12 @@ class CanvasRenderingContext2D final : public nsICanvasRenderingContextInternal, bool InitializeCanvasRenderer(nsDisplayListBuilder* aBuilder, CanvasRenderer* aRenderer) override; void MarkContextClean() override; - void MarkContextCleanForFrameCapture() override; - bool IsContextCleanForFrameCapture() override; + void MarkContextCleanForFrameCapture() override { + mFrameCaptureState = FrameCaptureState::CLEAN; + } + Watchable* GetFrameCaptureState() override { + return &mFrameCaptureState; + } NS_IMETHOD SetIsIPC(bool aIsIPC) override; // this rect is in canvas device space void Redraw(const mozilla::gfx::Rect& aR); @@ -750,7 +754,7 @@ class CanvasRenderingContext2D final : public nsICanvasRenderingContextInternal, * case when the canvas is not currently being drawn into and not rendered * but canvas capturing is still ongoing. */ - bool mIsCapturedFrameInvalid; + Watchable mFrameCaptureState; /** * We also have a device space pathbuilder. The reason for this is as diff --git a/dom/canvas/CanvasUtils.cpp b/dom/canvas/CanvasUtils.cpp index e7e757578b60..95bef32f866d 100644 --- a/dom/canvas/CanvasUtils.cpp +++ b/dom/canvas/CanvasUtils.cpp @@ -12,8 +12,12 @@ #include "mozilla/dom/Document.h" #include "mozilla/dom/HTMLCanvasElement.h" #include "mozilla/dom/UserActivation.h" +#include "mozilla/dom/WorkerCommon.h" +#include "mozilla/dom/WorkerPrivate.h" +#include "mozilla/gfx/gfxVars.h" #include "mozilla/BasePrincipal.h" #include "mozilla/StaticPrefs_dom.h" +#include "mozilla/StaticPrefs_gfx.h" #include "mozilla/StaticPrefs_privacy.h" #include "mozilla/StaticPrefs_webgl.h" #include "nsIPrincipal.h" @@ -285,6 +289,35 @@ bool HasDrawWindowPrivilege(JSContext* aCx, JSObject* /* unused */) { nsGkAtoms::all_urlsPermission); } +bool IsOffscreenCanvasEnabled(JSContext* aCx, JSObject* /* unused */) { + if (StaticPrefs::gfx_offscreencanvas_enabled()) { + return true; + } + + if (!StaticPrefs::gfx_offscreencanvas_domain_enabled()) { + return false; + } + + const auto& allowlist = gfxVars::OffscreenCanvasDomainAllowlist(); + + if (!NS_IsMainThread()) { + dom::WorkerPrivate* workerPrivate = dom::GetWorkerPrivateFromContext(aCx); + if (workerPrivate->UsesSystemPrincipal()) { + return true; + } + + return nsContentUtils::IsURIInList(workerPrivate->GetBaseURI(), allowlist); + } + + nsIPrincipal* principal = nsContentUtils::SubjectPrincipal(aCx); + if (principal->IsSystemPrincipal()) { + return true; + } + + nsCOMPtr uri = principal->GetURI(); + return nsContentUtils::IsURIInList(uri, allowlist); +} + bool CheckWriteOnlySecurity(bool aCORSUsed, nsIPrincipal* aPrincipal, bool aHadCrossOriginRedirects) { if (!aPrincipal) { diff --git a/dom/canvas/CanvasUtils.h b/dom/canvas/CanvasUtils.h index 177f7995bce5..9b1d1fe04384 100644 --- a/dom/canvas/CanvasUtils.h +++ b/dom/canvas/CanvasUtils.h @@ -51,6 +51,9 @@ void DoDrawImageSecurityCheck(dom::HTMLCanvasElement* aCanvasElement, // Check if the context is chrome or has the permission to drawWindow bool HasDrawWindowPrivilege(JSContext* aCx, JSObject* aObj); +// Check if the context has permission to use OffscreenCanvas. +bool IsOffscreenCanvasEnabled(JSContext* aCx, JSObject* aObj); + // Check site-specific permission and display prompt if appropriate. bool IsImageExtractionAllowed(dom::Document* aDocument, JSContext* aCx, nsIPrincipal& aPrincipal); diff --git a/dom/canvas/ClientWebGLContext.h b/dom/canvas/ClientWebGLContext.h index d81684e28a41..85fe63541f67 100644 --- a/dom/canvas/ClientWebGLContext.h +++ b/dom/canvas/ClientWebGLContext.h @@ -917,13 +917,14 @@ class ClientWebGLContext final : public nsICanvasRenderingContextInternal, public: bool InitializeCanvasRenderer(nsDisplayListBuilder* aBuilder, layers::CanvasRenderer* aRenderer) override; + + void MarkContextCleanForFrameCapture() override { + mFrameCaptureState = FrameCaptureState::CLEAN; + } // Note that 'clean' here refers to its invalidation state, not the // contents of the buffer. - bool IsContextCleanForFrameCapture() override { - return !mCapturedFrameInvalidated; - } - void MarkContextCleanForFrameCapture() override { - mCapturedFrameInvalidated = false; + Watchable* GetFrameCaptureState() override { + return &mFrameCaptureState; } void OnMemoryPressure() override; @@ -986,7 +987,8 @@ class ClientWebGLContext final : public nsICanvasRenderingContextInternal, protected: layers::LayersBackend GetCompositorBackendType() const; - bool mCapturedFrameInvalidated = false; + Watchable mFrameCaptureState = { + FrameCaptureState::CLEAN, "ClientWebGLContext::mFrameCaptureState"}; // ------------------------------------------------------------------------- // WebGLRenderingContext Basic Properties and Methods diff --git a/dom/canvas/DrawTargetWebgl.cpp b/dom/canvas/DrawTargetWebgl.cpp index e9349ed3e4d3..28bff59807c1 100644 --- a/dom/canvas/DrawTargetWebgl.cpp +++ b/dom/canvas/DrawTargetWebgl.cpp @@ -1256,11 +1256,9 @@ bool PathCacheEntry::MatchesPath(const SkPath& aPath, const Pattern* aPattern, const StrokeOptions* aStrokeOptions, const Matrix& aTransform, const IntRect& aBounds, HashNumber aHash) { - if (aHash != mHash || !HasMatchingScale(aTransform, mTransform) || - aBounds.Size() != mBounds.Size() || aPath == mPath) { - return false; - } - return (!aPattern ? !mPattern : mPattern && *aPattern == *mPattern) && + return aHash == mHash && HasMatchingScale(aTransform, mTransform) && + aBounds.Size() == mBounds.Size() && aPath == mPath && + (!aPattern ? !mPattern : mPattern && *aPattern == *mPattern) && (!aStrokeOptions ? !mStrokeOptions : mStrokeOptions && *aStrokeOptions == *mStrokeOptions); diff --git a/dom/canvas/ImageBitmapRenderingContext.cpp b/dom/canvas/ImageBitmapRenderingContext.cpp index 3c2222bbe7a6..abbe98992256 100644 --- a/dom/canvas/ImageBitmapRenderingContext.cpp +++ b/dom/canvas/ImageBitmapRenderingContext.cpp @@ -14,7 +14,10 @@ namespace mozilla::dom { ImageBitmapRenderingContext::ImageBitmapRenderingContext() - : mWidth(0), mHeight(0), mIsCapturedFrameInvalid(false) {} + : mWidth(0), + mHeight(0), + mFrameCaptureState(FrameCaptureState::CLEAN, + "ImageBitmapRenderingContext::mFrameCaptureState") {} ImageBitmapRenderingContext::~ImageBitmapRenderingContext() { RemovePostRefreshObserver(); @@ -213,7 +216,7 @@ ImageBitmapRenderingContext::Reset() { } mImage = nullptr; - mIsCapturedFrameInvalid = false; + mFrameCaptureState = FrameCaptureState::CLEAN; return NS_OK; } @@ -243,7 +246,7 @@ void ImageBitmapRenderingContext::MarkContextClean() {} NS_IMETHODIMP ImageBitmapRenderingContext::Redraw(const gfxRect& aDirty) { - mIsCapturedFrameInvalid = true; + mFrameCaptureState = FrameCaptureState::DIRTY; if (mOffscreenCanvas) { mOffscreenCanvas->CommitFrameToCompositor(); @@ -260,14 +263,6 @@ ImageBitmapRenderingContext::SetIsIPC(bool aIsIPC) { return NS_OK; } void ImageBitmapRenderingContext::DidRefresh() {} -void ImageBitmapRenderingContext::MarkContextCleanForFrameCapture() { - mIsCapturedFrameInvalid = false; -} - -bool ImageBitmapRenderingContext::IsContextCleanForFrameCapture() { - return !mIsCapturedFrameInvalid; -} - NS_IMPL_CYCLE_COLLECTING_ADDREF(ImageBitmapRenderingContext) NS_IMPL_CYCLE_COLLECTING_RELEASE(ImageBitmapRenderingContext) diff --git a/dom/canvas/ImageBitmapRenderingContext.h b/dom/canvas/ImageBitmapRenderingContext.h index 6c6f391f474d..4233448dc315 100644 --- a/dom/canvas/ImageBitmapRenderingContext.h +++ b/dom/canvas/ImageBitmapRenderingContext.h @@ -93,8 +93,12 @@ class ImageBitmapRenderingContext final virtual void DidRefresh() override; - virtual void MarkContextCleanForFrameCapture() override; - virtual bool IsContextCleanForFrameCapture() override; + void MarkContextCleanForFrameCapture() override { + mFrameCaptureState = FrameCaptureState::CLEAN; + } + Watchable* GetFrameCaptureState() override { + return &mFrameCaptureState; + } protected: already_AddRefed MatchWithIntrinsicSize(); @@ -109,7 +113,7 @@ class ImageBitmapRenderingContext final * case when the canvas is not currently being drawn into and not rendered * but canvas capturing is still ongoing. */ - bool mIsCapturedFrameInvalid; + Watchable mFrameCaptureState; }; } // namespace dom diff --git a/dom/canvas/OffscreenCanvas.cpp b/dom/canvas/OffscreenCanvas.cpp index 975bb3db8426..27db2f9b5f71 100644 --- a/dom/canvas/OffscreenCanvas.cpp +++ b/dom/canvas/OffscreenCanvas.cpp @@ -390,7 +390,7 @@ bool OffscreenCanvas::PrefEnabledOnWorkerThread(JSContext* aCx, return true; } - return StaticPrefs::gfx_offscreencanvas_enabled(); + return CanvasUtils::IsOffscreenCanvasEnabled(aCx, aObj); } NS_IMPL_CYCLE_COLLECTION_INHERITED(OffscreenCanvas, DOMEventTargetHelper, diff --git a/dom/canvas/WebGLContext.cpp b/dom/canvas/WebGLContext.cpp index a0140f503052..a0b8bce3fb20 100644 --- a/dom/canvas/WebGLContext.cpp +++ b/dom/canvas/WebGLContext.cpp @@ -222,7 +222,7 @@ void WebGLContext::DestroyResourcesAndContext() { void ClientWebGLContext::MarkCanvasDirty() { if (!mCanvasElement && !mOffscreenCanvas) return; - mCapturedFrameInvalidated = true; + mFrameCaptureState = FrameCaptureState::DIRTY; if (mIsCanvasDirty) return; mIsCanvasDirty = true; diff --git a/dom/canvas/nsICanvasRenderingContextInternal.h b/dom/canvas/nsICanvasRenderingContextInternal.h index bacae219911c..08d852fb3941 100644 --- a/dom/canvas/nsICanvasRenderingContextInternal.h +++ b/dom/canvas/nsICanvasRenderingContextInternal.h @@ -16,6 +16,7 @@ #include "mozilla/dom/OffscreenCanvas.h" #include "mozilla/Maybe.h" #include "mozilla/RefPtr.h" +#include "mozilla/StateWatching.h" #include "mozilla/UniquePtr.h" #include "mozilla/NotNull.h" #include "mozilla/WeakPtr.h" @@ -52,6 +53,8 @@ class SourceSurface; } // namespace gfx } // namespace mozilla +enum class FrameCaptureState : uint8_t { CLEAN, DIRTY }; + class nsICanvasRenderingContextInternal : public nsISupports, public mozilla::SupportsWeakPtr, public nsAPostRefreshObserver { @@ -160,9 +163,10 @@ class nsICanvasRenderingContextInternal : public nsISupports, // Called when a frame is captured. virtual void MarkContextCleanForFrameCapture() = 0; - // Whether the context is clean or has been invalidated since the last frame - // was captured. - virtual bool IsContextCleanForFrameCapture() = 0; + // Whether the context is clean or has been invalidated (dirty) since the last + // frame was captured. The Watchable allows the caller to get notified of + // state changes. + virtual mozilla::Watchable* GetFrameCaptureState() = 0; // Redraw the dirty rectangle of this canvas. NS_IMETHOD Redraw(const gfxRect& dirty) = 0; diff --git a/dom/canvas/test/mochitest.ini b/dom/canvas/test/mochitest.ini index 38482c536ad2..20f406f8a447 100644 --- a/dom/canvas/test/mochitest.ini +++ b/dom/canvas/test/mochitest.ini @@ -128,6 +128,8 @@ skip-if = (toolkit == 'windows') # bug 1464173 [test_canvas_strokeStyle_getter.html] [test_capture.html] support-files = captureStream_common.js +[test_capture_throttled.html] +support-files = captureStream_common.js [test_drawImageIncomplete.html] [test_drawImage_document_domain.html] [test_drawImage_edge_cases.html] diff --git a/dom/canvas/test/test_capture.html b/dom/canvas/test/test_capture.html index 567ddaa640de..ee37f3046ebe 100644 --- a/dom/canvas/test/test_capture.html +++ b/dom/canvas/test/test_capture.html @@ -8,13 +8,21 @@ diff --git a/dom/canvas/test/test_capture_throttled.html b/dom/canvas/test/test_capture_throttled.html new file mode 100644 index 000000000000..ec0f9af93e0d --- /dev/null +++ b/dom/canvas/test/test_capture_throttled.html @@ -0,0 +1,101 @@ + + + +Canvas2D test: CaptureStream() with throttled rAF + + + + + + + diff --git a/dom/chrome-webidl/PathUtils.webidl b/dom/chrome-webidl/PathUtils.webidl index c3807670d627..68ad2da91ccd 100644 --- a/dom/chrome-webidl/PathUtils.webidl +++ b/dom/chrome-webidl/PathUtils.webidl @@ -89,6 +89,15 @@ namespace PathUtils { [Throws] UTF8String toFileURI(DOMString path); + /** + * Determine if the given path is an absolute or relative path. + * + * @param path A file path that is either relative or absolute. + * + * @return Whether or not the path is absolute. + */ + boolean isAbsolute(DOMString path); + /** * The profile directory. */ diff --git a/dom/events/EventListenerManager.cpp b/dom/events/EventListenerManager.cpp index 5a20f5c09170..f599b3fe5199 100644 --- a/dom/events/EventListenerManager.cpp +++ b/dom/events/EventListenerManager.cpp @@ -2149,7 +2149,6 @@ NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN( EventListenerManager::ListenerSignalFollower) NS_IMPL_CYCLE_COLLECTION_UNLINK(mListener) - AbortFollower::Unlink(static_cast(tmp)); tmp->mListenerManager = nullptr; NS_IMPL_CYCLE_COLLECTION_UNLINK_END diff --git a/dom/fetch/Fetch.cpp b/dom/fetch/Fetch.cpp index 5a14345efb54..1cc3e8084679 100644 --- a/dom/fetch/Fetch.cpp +++ b/dom/fetch/Fetch.cpp @@ -140,47 +140,18 @@ NS_IMPL_CYCLE_COLLECTING_RELEASE(AbortSignalMainThread) class AbortSignalProxy; -class WorkerSignalFollower final : public AbortFollower { - public: - // This runnable propagates changes from the AbortSignalImpl on workers to the - // AbortSignalImpl on main-thread. - class AbortSignalProxyRunnable final : public Runnable { - RefPtr mProxy; - - public: - explicit AbortSignalProxyRunnable(AbortSignalProxy* aProxy) - : Runnable("dom::WorkerSignalFollower::AbortSignalProxyRunnable"), - mProxy(aProxy) {} - - NS_IMETHOD Run() override; - }; +// This runnable propagates changes from the AbortSignalImpl on workers to the +// AbortSignalImpl on main-thread. +class AbortSignalProxyRunnable final : public Runnable { + RefPtr mProxy; public: - NS_DECL_CYCLE_COLLECTING_ISUPPORTS - NS_DECL_CYCLE_COLLECTION_CLASS(WorkerSignalFollower) + explicit AbortSignalProxyRunnable(AbortSignalProxy* aProxy) + : Runnable("dom::AbortSignalProxyRunnable"), mProxy(aProxy) {} - void RunAbortAlgorithm() override {} - - private: - ~WorkerSignalFollower() = default; + NS_IMETHOD Run() override; }; -NS_IMPL_CYCLE_COLLECTION_CLASS(WorkerSignalFollower) - -NS_IMPL_CYCLE_COLLECTING_ADDREF(WorkerSignalFollower) -NS_IMPL_CYCLE_COLLECTING_RELEASE(WorkerSignalFollower) - -NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(WorkerSignalFollower) - AbortFollower::Unlink(static_cast(tmp)); -NS_IMPL_CYCLE_COLLECTION_UNLINK_END - -NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(WorkerSignalFollower) -NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END - -NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(WorkerSignalFollower) - NS_INTERFACE_MAP_ENTRY(nsISupports) -NS_INTERFACE_MAP_END - // This class orchestrates the proxying of AbortSignal operations between the // main thread and a worker thread. class AbortSignalProxy final : public AbortFollower { @@ -240,7 +211,7 @@ class AbortSignalProxy final : public AbortFollower { NS_IMPL_ISUPPORTS0(AbortSignalProxy) -NS_IMETHODIMP WorkerSignalFollower::AbortSignalProxyRunnable::Run() { +NS_IMETHODIMP AbortSignalProxyRunnable::Run() { MOZ_ASSERT(NS_IsMainThread()); AbortSignalImpl* signalImpl = mProxy->GetOrCreateSignalImplForMainThread(); signalImpl->SignalAbort(JS::UndefinedHandleValue); @@ -249,8 +220,6 @@ NS_IMETHODIMP WorkerSignalFollower::AbortSignalProxyRunnable::Run() { void AbortSignalProxy::RunAbortAlgorithm() { MOZ_ASSERT(!NS_IsMainThread()); - using AbortSignalProxyRunnable = - WorkerSignalFollower::AbortSignalProxyRunnable; RefPtr runnable = new AbortSignalProxyRunnable(this); MainThreadEventTarget()->Dispatch(runnable.forget(), NS_DISPATCH_NORMAL); @@ -1803,7 +1772,6 @@ NS_IMPL_RELEASE_INHERITED(EmptyBody, FetchBody) NS_IMPL_CYCLE_COLLECTION_CLASS(EmptyBody) NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN_INHERITED(EmptyBody, FetchBody) - AbortFollower::Unlink(static_cast(tmp)); NS_IMPL_CYCLE_COLLECTION_UNLINK(mOwner) NS_IMPL_CYCLE_COLLECTION_UNLINK(mAbortSignalImpl) NS_IMPL_CYCLE_COLLECTION_UNLINK(mFetchStreamReader) diff --git a/dom/fetch/FetchObserver.cpp b/dom/fetch/FetchObserver.cpp index f3d316f25cf0..0c9d1b9f7aab 100644 --- a/dom/fetch/FetchObserver.cpp +++ b/dom/fetch/FetchObserver.cpp @@ -18,7 +18,6 @@ NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN_INHERITED(FetchObserver, DOMEventTargetHelper) - AbortFollower::Unlink(static_cast(tmp)); NS_IMPL_CYCLE_COLLECTION_UNLINK_END NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(FetchObserver) diff --git a/dom/fetch/Request.cpp b/dom/fetch/Request.cpp index 7a80046c9162..154bd7a94cc6 100644 --- a/dom/fetch/Request.cpp +++ b/dom/fetch/Request.cpp @@ -41,7 +41,6 @@ NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN_INHERITED(Request, FetchBody) NS_IMPL_CYCLE_COLLECTION_UNLINK(mOwner) NS_IMPL_CYCLE_COLLECTION_UNLINK(mHeaders) NS_IMPL_CYCLE_COLLECTION_UNLINK(mSignal) - AbortFollower::Unlink(static_cast(tmp)); NS_IMPL_CYCLE_COLLECTION_UNLINK_PRESERVED_WRAPPER NS_IMPL_CYCLE_COLLECTION_UNLINK_END diff --git a/dom/fetch/Response.cpp b/dom/fetch/Response.cpp index 82918b983762..be98dfdfce74 100644 --- a/dom/fetch/Response.cpp +++ b/dom/fetch/Response.cpp @@ -40,7 +40,6 @@ NS_IMPL_RELEASE_INHERITED(Response, FetchBody) NS_IMPL_CYCLE_COLLECTION_CLASS(Response) NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN_INHERITED(Response, FetchBody) - AbortFollower::Unlink(static_cast(tmp)); NS_IMPL_CYCLE_COLLECTION_UNLINK(mOwner) NS_IMPL_CYCLE_COLLECTION_UNLINK(mHeaders) NS_IMPL_CYCLE_COLLECTION_UNLINK(mSignalImpl) diff --git a/dom/fs/FileSystemDirectoryHandle.cpp b/dom/fs/FileSystemDirectoryHandle.cpp new file mode 100644 index 000000000000..42866d4ddd11 --- /dev/null +++ b/dom/fs/FileSystemDirectoryHandle.cpp @@ -0,0 +1,105 @@ +/* -*- 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/. */ + +#include "FileSystemDirectoryHandle.h" + +#include "mozilla/ErrorResult.h" +#include "mozilla/dom/FileSystemDirectoryHandleBinding.h" +#include "mozilla/dom/FileSystemDirectoryIterator.h" +#include "mozilla/dom/FileSystemHandleBinding.h" +#include "mozilla/dom/Promise.h" + +namespace mozilla::dom { + +NS_IMPL_ISUPPORTS_CYCLE_COLLECTION_INHERITED_0(FileSystemDirectoryHandle, + FileSystemHandle) +NS_IMPL_CYCLE_COLLECTION_INHERITED(FileSystemDirectoryHandle, FileSystemHandle) + +// WebIDL Boilerplate + +JSObject* FileSystemDirectoryHandle::WrapObject( + JSContext* aCx, JS::Handle aGivenProto) { + return FileSystemDirectoryHandle_Binding::Wrap(aCx, this, aGivenProto); +} + +// WebIDL Interface + +FileSystemHandleKind FileSystemDirectoryHandle::Kind() { + return FileSystemHandleKind::Directory; +} + +already_AddRefed +FileSystemDirectoryHandle::Entries() { + return MakeRefPtr(GetParentObject()).forget(); +} + +already_AddRefed +FileSystemDirectoryHandle::Keys() { + return MakeRefPtr(GetParentObject()).forget(); +} + +already_AddRefed +FileSystemDirectoryHandle::Values() { + return MakeRefPtr(GetParentObject()).forget(); +} + +already_AddRefed FileSystemDirectoryHandle::GetFileHandle( + const nsAString& aName, const FileSystemGetFileOptions& aOptions) { + IgnoredErrorResult rv; + + RefPtr promise = Promise::Create(GetParentObject(), rv); + if (rv.Failed()) { + return nullptr; + } + + promise->MaybeReject(NS_ERROR_NOT_IMPLEMENTED); + + return promise.forget(); +} + +already_AddRefed FileSystemDirectoryHandle::GetDirectoryHandle( + const nsAString& aName, const FileSystemGetDirectoryOptions& aOptions) { + IgnoredErrorResult rv; + + RefPtr promise = Promise::Create(GetParentObject(), rv); + if (rv.Failed()) { + return nullptr; + } + + promise->MaybeReject(NS_ERROR_NOT_IMPLEMENTED); + + return promise.forget(); +} + +already_AddRefed FileSystemDirectoryHandle::RemoveEntry( + const nsAString& aName, const FileSystemRemoveOptions& aOptions) { + IgnoredErrorResult rv; + + RefPtr promise = Promise::Create(GetParentObject(), rv); + if (rv.Failed()) { + return nullptr; + } + + promise->MaybeReject(NS_ERROR_NOT_IMPLEMENTED); + + return promise.forget(); +} + +already_AddRefed FileSystemDirectoryHandle::Resolve( + FileSystemHandle& aPossibleDescendant) { + IgnoredErrorResult rv; + + RefPtr promise = Promise::Create(GetParentObject(), rv); + if (rv.Failed()) { + return nullptr; + } + + promise->MaybeReject(NS_ERROR_NOT_IMPLEMENTED); + + return promise.forget(); +} + +} // namespace mozilla::dom diff --git a/dom/fs/FileSystemDirectoryHandle.h b/dom/fs/FileSystemDirectoryHandle.h new file mode 100644 index 000000000000..47af5a3c5d59 --- /dev/null +++ b/dom/fs/FileSystemDirectoryHandle.h @@ -0,0 +1,55 @@ +/* -*- 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 DOM_FS_FILESYSTEMDIRECTORYHANDLE_H_ +#define DOM_FS_FILESYSTEMDIRECTORYHANDLE_H_ + +#include "mozilla/dom/FileSystemHandle.h" + +namespace mozilla::dom { + +class FileSystemDirectoryIterator; +struct FileSystemGetFileOptions; +struct FileSystemGetDirectoryOptions; +struct FileSystemRemoveOptions; + +class FileSystemDirectoryHandle final : public FileSystemHandle { + public: + NS_DECL_ISUPPORTS_INHERITED + NS_DECL_CYCLE_COLLECTION_CLASS_INHERITED(FileSystemDirectoryHandle, + FileSystemHandle) + + // WebIDL Boilerplate + JSObject* WrapObject(JSContext* aCx, + JS::Handle aGivenProto) override; + + // WebIDL Interface + FileSystemHandleKind Kind() override; + + [[nodiscard]] already_AddRefed Entries(); + + [[nodiscard]] already_AddRefed Keys(); + + [[nodiscard]] already_AddRefed Values(); + + already_AddRefed GetFileHandle( + const nsAString& aName, const FileSystemGetFileOptions& aOptions); + + already_AddRefed GetDirectoryHandle( + const nsAString& aName, const FileSystemGetDirectoryOptions& aOptions); + + already_AddRefed RemoveEntry( + const nsAString& aName, const FileSystemRemoveOptions& aOptions); + + already_AddRefed Resolve(FileSystemHandle& aPossibleDescendant); + + private: + ~FileSystemDirectoryHandle() = default; +}; + +} // namespace mozilla::dom + +#endif // DOM_FS_FILESYSTEMDIRECTORYHANDLE_H_ diff --git a/dom/fs/FileSystemDirectoryIterator.cpp b/dom/fs/FileSystemDirectoryIterator.cpp new file mode 100644 index 000000000000..6b670a0d2b1f --- /dev/null +++ b/dom/fs/FileSystemDirectoryIterator.cpp @@ -0,0 +1,53 @@ +/* -*- 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/. */ + +#include "FileSystemDirectoryIterator.h" + +#include "mozilla/ErrorResult.h" +#include "mozilla/dom/FileSystemDirectoryIteratorBinding.h" +#include "mozilla/dom/Promise.h" + +namespace mozilla::dom { + +FileSystemDirectoryIterator::FileSystemDirectoryIterator( + nsIGlobalObject* aGlobal) + : mGlobal(aGlobal) {} + +NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(FileSystemDirectoryIterator) + NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY + NS_INTERFACE_MAP_ENTRY(nsISupports) +NS_INTERFACE_MAP_END +NS_IMPL_CYCLE_COLLECTING_ADDREF(FileSystemDirectoryIterator); +NS_IMPL_CYCLE_COLLECTING_RELEASE(FileSystemDirectoryIterator); +NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE(FileSystemDirectoryIterator, mGlobal); + +// WebIDL Boilerplate + +nsIGlobalObject* FileSystemDirectoryIterator::GetParentObject() const { + return mGlobal; +} + +JSObject* FileSystemDirectoryIterator::WrapObject( + JSContext* aCx, JS::Handle aGivenProto) { + return FileSystemDirectoryIterator_Binding::Wrap(aCx, this, aGivenProto); +} + +// WebIDL Interface + +already_AddRefed FileSystemDirectoryIterator::Next() { + IgnoredErrorResult rv; + + RefPtr promise = Promise::Create(GetParentObject(), rv); + if (rv.Failed()) { + return nullptr; + } + + promise->MaybeReject(NS_ERROR_NOT_IMPLEMENTED); + + return promise.forget(); +} + +} // namespace mozilla::dom diff --git a/dom/fs/FileSystemDirectoryIterator.h b/dom/fs/FileSystemDirectoryIterator.h new file mode 100644 index 000000000000..374699324ade --- /dev/null +++ b/dom/fs/FileSystemDirectoryIterator.h @@ -0,0 +1,44 @@ +/* -*- 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 DOM_FS_FILESYSTEMDIRECTORYITERATOR_H_ +#define DOM_FS_FILESYSTEMDIRECTORYITERATOR_H_ + +#include "nsCOMPtr.h" +#include "nsISupports.h" +#include "nsWrapperCache.h" + +class nsIGlobalObject; + +namespace mozilla::dom { + +class Promise; + +class FileSystemDirectoryIterator : public nsISupports, public nsWrapperCache { + public: + explicit FileSystemDirectoryIterator(nsIGlobalObject* aGlobal); + + NS_DECL_CYCLE_COLLECTING_ISUPPORTS + NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS(FileSystemDirectoryIterator) + + // WebIDL Boilerplate + nsIGlobalObject* GetParentObject() const; + + JSObject* WrapObject(JSContext* aCx, + JS::Handle aGivenProto) override; + + // WebIDL Interface + already_AddRefed Next(); + + protected: + virtual ~FileSystemDirectoryIterator() = default; + + nsCOMPtr mGlobal; +}; + +} // namespace mozilla::dom + +#endif // DOM_FS_FILESYSTEMDIRECTORYITERATOR_H_ diff --git a/dom/fs/FileSystemFileHandle.cpp b/dom/fs/FileSystemFileHandle.cpp new file mode 100644 index 000000000000..64abb968cb4f --- /dev/null +++ b/dom/fs/FileSystemFileHandle.cpp @@ -0,0 +1,75 @@ +/* -*- 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/. */ + +#include "FileSystemFileHandle.h" + +#include "mozilla/ErrorResult.h" +#include "mozilla/dom/FileSystemFileHandleBinding.h" +#include "mozilla/dom/FileSystemHandleBinding.h" +#include "mozilla/dom/Promise.h" + +namespace mozilla::dom { + +NS_IMPL_ISUPPORTS_CYCLE_COLLECTION_INHERITED_0(FileSystemFileHandle, + FileSystemHandle) +NS_IMPL_CYCLE_COLLECTION_INHERITED(FileSystemFileHandle, FileSystemHandle) + +// WebIDL Boilerplate + +JSObject* FileSystemFileHandle::WrapObject(JSContext* aCx, + JS::Handle aGivenProto) { + return FileSystemFileHandle_Binding::Wrap(aCx, this, aGivenProto); +} + +// WebIDL Interface + +FileSystemHandleKind FileSystemFileHandle::Kind() { + return FileSystemHandleKind::File; +} + +already_AddRefed FileSystemFileHandle::GetFile() { + IgnoredErrorResult rv; + + RefPtr promise = Promise::Create(GetParentObject(), rv); + if (rv.Failed()) { + return nullptr; + } + + promise->MaybeReject(NS_ERROR_NOT_IMPLEMENTED); + + return promise.forget(); +} + +#ifdef MOZ_DOM_STREAMS +already_AddRefed FileSystemFileHandle::CreateWritable( + const FileSystemCreateWritableOptions& aOptions) { + IgnoredErrorResult rv; + + RefPtr promise = Promise::Create(GetParentObject(), rv); + if (rv.Failed()) { + return nullptr; + } + + promise->MaybeReject(NS_ERROR_NOT_IMPLEMENTED); + + return promise.forget(); +} +#endif + +already_AddRefed FileSystemFileHandle::CreateSyncAccessHandle() { + IgnoredErrorResult rv; + + RefPtr promise = Promise::Create(GetParentObject(), rv); + if (rv.Failed()) { + return nullptr; + } + + promise->MaybeReject(NS_ERROR_NOT_IMPLEMENTED); + + return promise.forget(); +} + +} // namespace mozilla::dom diff --git a/dom/fs/FileSystemFileHandle.h b/dom/fs/FileSystemFileHandle.h new file mode 100644 index 000000000000..462ef4955e9d --- /dev/null +++ b/dom/fs/FileSystemFileHandle.h @@ -0,0 +1,44 @@ +/* -*- 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 DOM_FS_FILESYSTEMFILEHANDLE_H_ +#define DOM_FS_FILESYSTEMFILEHANDLE_H_ + +#include "mozilla/dom/FileSystemHandle.h" + +namespace mozilla::dom { + +struct FileSystemCreateWritableOptions; + +class FileSystemFileHandle final : public FileSystemHandle { + public: + NS_DECL_ISUPPORTS_INHERITED + NS_DECL_CYCLE_COLLECTION_CLASS_INHERITED(FileSystemFileHandle, + FileSystemHandle) + + // WebIDL Boilerplate + JSObject* WrapObject(JSContext* aCx, + JS::Handle aGivenProto) override; + + // WebIDL interface + FileSystemHandleKind Kind() override; + + already_AddRefed GetFile(); + +#ifdef MOZ_DOM_STREAMS + already_AddRefed CreateWritable( + const FileSystemCreateWritableOptions& aOptions); +#endif + + already_AddRefed CreateSyncAccessHandle(); + + private: + ~FileSystemFileHandle() = default; +}; + +} // namespace mozilla::dom + +#endif // DOM_FS_FILESYSTEMFILEHANDLE_H_ diff --git a/dom/fs/FileSystemHandle.cpp b/dom/fs/FileSystemHandle.cpp new file mode 100644 index 000000000000..a63aff258e0c --- /dev/null +++ b/dom/fs/FileSystemHandle.cpp @@ -0,0 +1,50 @@ +/* -*- 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/. */ + +#include "FileSystemHandle.h" + +#include "mozilla/ErrorResult.h" +#include "mozilla/dom/FileSystemHandleBinding.h" +#include "mozilla/dom/Promise.h" + +namespace mozilla::dom { + +NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(FileSystemHandle) + NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY + NS_INTERFACE_MAP_ENTRY(nsISupports) +NS_INTERFACE_MAP_END +NS_IMPL_CYCLE_COLLECTING_ADDREF(FileSystemHandle); +NS_IMPL_CYCLE_COLLECTING_RELEASE(FileSystemHandle); +NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE(FileSystemHandle, mGlobal); + +// WebIDL Boilerplate + +nsIGlobalObject* FileSystemHandle::GetParentObject() const { return mGlobal; } + +JSObject* FileSystemHandle::WrapObject(JSContext* aCx, + JS::Handle aGivenProto) { + return FileSystemHandle_Binding::Wrap(aCx, this, aGivenProto); +} + +// WebIDL Interface + +void FileSystemHandle::GetName(DOMString& aResult) { aResult.SetNull(); } + +already_AddRefed FileSystemHandle::IsSameEntry( + FileSystemHandle& aOther) { + IgnoredErrorResult rv; + + RefPtr promise = Promise::Create(GetParentObject(), rv); + if (rv.Failed()) { + return nullptr; + } + + promise->MaybeReject(NS_ERROR_NOT_IMPLEMENTED); + + return promise.forget(); +} + +} // namespace mozilla::dom diff --git a/dom/fs/FileSystemHandle.h b/dom/fs/FileSystemHandle.h new file mode 100644 index 000000000000..77a1ab7da42f --- /dev/null +++ b/dom/fs/FileSystemHandle.h @@ -0,0 +1,48 @@ +/* -*- 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 DOM_FS_FILESYSTEMHANDLE_H_ +#define DOM_FS_FILESYSTEMHANDLE_H_ + +#include "nsCOMPtr.h" +#include "nsISupports.h" +#include "nsWrapperCache.h" + +class nsIGlobalObject; + +namespace mozilla::dom { + +class DOMString; +enum class FileSystemHandleKind : uint8_t; +class Promise; + +class FileSystemHandle : public nsISupports, public nsWrapperCache { + public: + NS_DECL_CYCLE_COLLECTING_ISUPPORTS + NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS(FileSystemHandle) + + // WebIDL Boilerplate + nsIGlobalObject* GetParentObject() const; + + JSObject* WrapObject(JSContext* aCx, + JS::Handle aGivenProto) override; + + // WebIDL Interface + virtual FileSystemHandleKind Kind() = 0; + + void GetName(DOMString& aResult); + + already_AddRefed IsSameEntry(FileSystemHandle& aOther); + + protected: + virtual ~FileSystemHandle() = default; + + nsCOMPtr mGlobal; +}; + +} // namespace mozilla::dom + +#endif // DOM_FS_FILESYSTEMHANDLE_H_ diff --git a/dom/fs/FileSystemSyncAccessHandle.cpp b/dom/fs/FileSystemSyncAccessHandle.cpp new file mode 100644 index 000000000000..c8af0c9963ef --- /dev/null +++ b/dom/fs/FileSystemSyncAccessHandle.cpp @@ -0,0 +1,100 @@ +/* -*- 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/. */ + +#include "FileSystemSyncAccessHandle.h" + +#include "mozilla/ErrorResult.h" +#include "mozilla/dom/FileSystemSyncAccessHandleBinding.h" +#include "mozilla/dom/Promise.h" + +namespace mozilla::dom { + +NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(FileSystemSyncAccessHandle) + NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY + NS_INTERFACE_MAP_ENTRY(nsISupports) +NS_INTERFACE_MAP_END +NS_IMPL_CYCLE_COLLECTING_ADDREF(FileSystemSyncAccessHandle); +NS_IMPL_CYCLE_COLLECTING_RELEASE(FileSystemSyncAccessHandle); +NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE(FileSystemSyncAccessHandle, mGlobal); + +// WebIDL Boilerplate + +nsIGlobalObject* FileSystemSyncAccessHandle::GetParentObject() const { + return mGlobal; +} + +JSObject* FileSystemSyncAccessHandle::WrapObject( + JSContext* aCx, JS::Handle aGivenProto) { + return FileSystemSyncAccessHandle_Binding::Wrap(aCx, this, aGivenProto); +} + +// WebIDL Interface + +uint64_t FileSystemSyncAccessHandle::Read( + const MaybeSharedArrayBufferViewOrMaybeSharedArrayBuffer& aBuffer, + const FileSystemReadWriteOptions& aOptions) { + return 0; +} + +uint64_t FileSystemSyncAccessHandle::Write( + const MaybeSharedArrayBufferViewOrMaybeSharedArrayBuffer& aBuffer, + const FileSystemReadWriteOptions& aOptions) { + return 0; +} + +already_AddRefed FileSystemSyncAccessHandle::Truncate(uint64_t aSize) { + IgnoredErrorResult rv; + + RefPtr promise = Promise::Create(GetParentObject(), rv); + if (rv.Failed()) { + return nullptr; + } + + promise->MaybeReject(NS_ERROR_NOT_IMPLEMENTED); + + return promise.forget(); +} + +already_AddRefed FileSystemSyncAccessHandle::GetSize() { + IgnoredErrorResult rv; + + RefPtr promise = Promise::Create(GetParentObject(), rv); + if (rv.Failed()) { + return nullptr; + } + + promise->MaybeReject(NS_ERROR_NOT_IMPLEMENTED); + + return promise.forget(); +} + +already_AddRefed FileSystemSyncAccessHandle::Flush() { + IgnoredErrorResult rv; + + RefPtr promise = Promise::Create(GetParentObject(), rv); + if (rv.Failed()) { + return nullptr; + } + + promise->MaybeReject(NS_ERROR_NOT_IMPLEMENTED); + + return promise.forget(); +} + +already_AddRefed FileSystemSyncAccessHandle::Close() { + IgnoredErrorResult rv; + + RefPtr promise = Promise::Create(GetParentObject(), rv); + if (rv.Failed()) { + return nullptr; + } + + promise->MaybeReject(NS_ERROR_NOT_IMPLEMENTED); + + return promise.forget(); +} + +} // namespace mozilla::dom diff --git a/dom/fs/FileSystemSyncAccessHandle.h b/dom/fs/FileSystemSyncAccessHandle.h new file mode 100644 index 000000000000..42d321b37ac8 --- /dev/null +++ b/dom/fs/FileSystemSyncAccessHandle.h @@ -0,0 +1,59 @@ +/* -*- 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 DOM_FS_FILESYSTEMSYNCACCESSHANDLE_H_ +#define DOM_FS_FILESYSTEMSYNCACCESSHANDLE_H_ + +#include "nsCOMPtr.h" +#include "nsISupports.h" +#include "nsWrapperCache.h" + +class nsIGlobalObject; + +namespace mozilla::dom { + +struct FileSystemReadWriteOptions; +class MaybeSharedArrayBufferViewOrMaybeSharedArrayBuffer; +class Promise; + +class FileSystemSyncAccessHandle final : public nsISupports, + public nsWrapperCache { + public: + NS_DECL_CYCLE_COLLECTING_ISUPPORTS + NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS(FileSystemSyncAccessHandle) + + // WebIDL Boilerplate + nsIGlobalObject* GetParentObject() const; + + JSObject* WrapObject(JSContext* aCx, + JS::Handle aGivenProto) override; + + // WebIDL Interface + uint64_t Read( + const MaybeSharedArrayBufferViewOrMaybeSharedArrayBuffer& aBuffer, + const FileSystemReadWriteOptions& aOptions); + + uint64_t Write( + const MaybeSharedArrayBufferViewOrMaybeSharedArrayBuffer& aBuffer, + const FileSystemReadWriteOptions& aOptions); + + already_AddRefed Truncate(uint64_t aSize); + + already_AddRefed GetSize(); + + already_AddRefed Flush(); + + already_AddRefed Close(); + + private: + virtual ~FileSystemSyncAccessHandle() = default; + + nsCOMPtr mGlobal; +}; + +} // namespace mozilla::dom + +#endif // DOM_FS_FILESYSTEMSYNCACCESSHANDLE_H_ diff --git a/dom/fs/FileSystemWritableFileStream.cpp b/dom/fs/FileSystemWritableFileStream.cpp new file mode 100644 index 000000000000..9e1afff5fbb3 --- /dev/null +++ b/dom/fs/FileSystemWritableFileStream.cpp @@ -0,0 +1,70 @@ +/* -*- 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/. */ + +#include "FileSystemWritableFileStream.h" + +#include "mozilla/ErrorResult.h" +#include "mozilla/dom/FileSystemWritableFileStreamBinding.h" +#include "mozilla/dom/Promise.h" + +namespace mozilla::dom { + +NS_IMPL_ISUPPORTS_CYCLE_COLLECTION_INHERITED_0(FileSystemWritableFileStream, + WritableStream) +NS_IMPL_CYCLE_COLLECTION_INHERITED(FileSystemWritableFileStream, WritableStream) + +// WebIDL Boilerplate + +JSObject* FileSystemWritableFileStream::WrapObject( + JSContext* aCx, JS::Handle aGivenProto) { + return FileSystemWritableFileStream_Binding::Wrap(aCx, this, aGivenProto); +} + +// WebIDL Interface + +already_AddRefed FileSystemWritableFileStream::Write( + const ArrayBufferViewOrArrayBufferOrBlobOrUSVStringOrWriteParams& aData) { + IgnoredErrorResult rv; + + RefPtr promise = Promise::Create(GetParentObject(), rv); + if (rv.Failed()) { + return nullptr; + } + + promise->MaybeReject(NS_ERROR_NOT_IMPLEMENTED); + + return promise.forget(); +} + +already_AddRefed FileSystemWritableFileStream::Seek( + uint64_t aPosition) { + IgnoredErrorResult rv; + + RefPtr promise = Promise::Create(GetParentObject(), rv); + if (rv.Failed()) { + return nullptr; + } + + promise->MaybeReject(NS_ERROR_NOT_IMPLEMENTED); + + return promise.forget(); +} + +already_AddRefed FileSystemWritableFileStream::Truncate( + uint64_t aSize) { + IgnoredErrorResult rv; + + RefPtr promise = Promise::Create(GetParentObject(), rv); + if (rv.Failed()) { + return nullptr; + } + + promise->MaybeReject(NS_ERROR_NOT_IMPLEMENTED); + + return promise.forget(); +} + +} // namespace mozilla::dom diff --git a/dom/fs/FileSystemWritableFileStream.h b/dom/fs/FileSystemWritableFileStream.h new file mode 100644 index 000000000000..e17226a71c80 --- /dev/null +++ b/dom/fs/FileSystemWritableFileStream.h @@ -0,0 +1,40 @@ +/* -*- 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 DOM_FS_FILESYSTEMWRITABLEFILESTREAM_H_ +#define DOM_FS_FILESYSTEMWRITABLEFILESTREAM_H_ + +#include "mozilla/dom/WritableStream.h" + +namespace mozilla::dom { + +class ArrayBufferViewOrArrayBufferOrBlobOrUSVStringOrWriteParams; + +class FileSystemWritableFileStream final : public WritableStream { + public: + NS_DECL_ISUPPORTS_INHERITED + NS_DECL_CYCLE_COLLECTION_CLASS_INHERITED(FileSystemWritableFileStream, + WritableStream) + + // WebIDL Boilerplate + JSObject* WrapObject(JSContext* aCx, + JS::Handle aGivenProto) override; + + // WebIDL Interface + already_AddRefed Write( + const ArrayBufferViewOrArrayBufferOrBlobOrUSVStringOrWriteParams& aData); + + already_AddRefed Seek(uint64_t aPosition); + + already_AddRefed Truncate(uint64_t aSize); + + private: + ~FileSystemWritableFileStream() = default; +}; + +} // namespace mozilla::dom + +#endif // DOM_FS_FILESYSTEMWRITABLEFILESTREAM_H_ diff --git a/dom/fs/moz.build b/dom/fs/moz.build new file mode 100644 index 000000000000..d8323a5229c5 --- /dev/null +++ b/dom/fs/moz.build @@ -0,0 +1,33 @@ +# -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*- +# vim: set filetype=python: +# 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/. + +EXPORTS.mozilla.dom += [ + "FileSystemDirectoryHandle.h", + "FileSystemDirectoryIterator.h", + "FileSystemFileHandle.h", + "FileSystemHandle.h", + "FileSystemSyncAccessHandle.h", +] + +UNIFIED_SOURCES += [ + "FileSystemDirectoryHandle.cpp", + "FileSystemDirectoryIterator.cpp", + "FileSystemFileHandle.cpp", + "FileSystemHandle.cpp", + "FileSystemSyncAccessHandle.cpp", +] + +if CONFIG["MOZ_DOM_STREAMS"]: + EXPORTS.mozilla.dom += [ + "FileSystemWritableFileStream.h", + ] + UNIFIED_SOURCES += [ + "FileSystemWritableFileStream.cpp", + ] + +include("/ipc/chromium/chromium-config.mozbuild") + +FINAL_LIBRARY = "xul" diff --git a/dom/html/HTMLCanvasElement.cpp b/dom/html/HTMLCanvasElement.cpp index 9264ea5fdd1d..48e42bb9ba18 100644 --- a/dom/html/HTMLCanvasElement.cpp +++ b/dom/html/HTMLCanvasElement.cpp @@ -71,7 +71,9 @@ class RequestedFrameRefreshObserver : public nsARefreshObserver { : mRegistered(false), mReturnPlaceholderData(aReturnPlaceholderData), mOwningElement(aOwningElement), - mRefreshDriver(aRefreshDriver) { + mRefreshDriver(aRefreshDriver), + mWatchManager(this, AbstractThread::MainThread()), + mPendingThrottledCapture(false) { MOZ_ASSERT(mOwningElement); } @@ -118,7 +120,57 @@ class RequestedFrameRefreshObserver : public nsARefreshObserver { mReturnPlaceholderData = aReturnPlaceholderData; } - void WillRefresh(TimeStamp aTime) override { + void NotifyCaptureStateChange() { + if (mPendingThrottledCapture) { + return; + } + + if (!mOwningElement) { + return; + } + + Watchable* captureState = + mOwningElement->GetFrameCaptureState(); + if (!captureState) { + return; + } + + if (captureState->Ref() == FrameCaptureState::CLEAN) { + return; + } + + if (!mRefreshDriver) { + return; + } + + if (!mRefreshDriver->IsThrottled()) { + return; + } + + TimeStamp next = mLastCaptureTime + TimeDuration::FromMilliseconds( + nsRefreshDriver::DefaultInterval()); + TimeStamp now = TimeStamp::Now(); + if (mLastCaptureTime.IsNull() || next < now) { + CaptureFrame(now); + return; + } + + mPendingThrottledCapture = true; + AbstractThread::MainThread()->DelayedDispatch( + NS_NewRunnableFunction( + __func__, + [this, self = RefPtr(this), next] { + mPendingThrottledCapture = false; + CaptureFrame(next); + }), + // next >= now, so this is a guard for (next - now) flooring to 0. + std::max( + 1, static_cast((next - now).ToMilliseconds()))); + } + + void WillRefresh(TimeStamp aTime) override { CaptureFrame(aTime); } + + void CaptureFrame(TimeStamp aTime) { MOZ_ASSERT(NS_IsMainThread()); AUTO_PROFILER_LABEL("RequestedFrameRefreshObserver::WillRefresh", OTHER); @@ -131,7 +183,9 @@ class RequestedFrameRefreshObserver : public nsARefreshObserver { return; } - if (mOwningElement->IsContextCleanForFrameCapture()) { + if (auto* captureStateWatchable = mOwningElement->GetFrameCaptureState(); + captureStateWatchable && + *captureStateWatchable == FrameCaptureState::CLEAN) { return; } @@ -161,6 +215,11 @@ class RequestedFrameRefreshObserver : public nsARefreshObserver { } } + if (!mLastCaptureTime.IsNull() && aTime <= mLastCaptureTime) { + aTime = mLastCaptureTime + TimeDuration::FromMilliseconds(1); + } + mLastCaptureTime = aTime; + { AUTO_PROFILER_LABEL("RequestedFrameRefreshObserver::WillRefresh:SetFrame", OTHER); @@ -175,6 +234,7 @@ class RequestedFrameRefreshObserver : public nsARefreshObserver { Unregister(); mRefreshDriver = nullptr; + mWatchManager.Shutdown(); } void Register() { @@ -188,6 +248,17 @@ class RequestedFrameRefreshObserver : public nsARefreshObserver { "Canvas frame capture listeners"); mRegistered = true; } + + if (!mOwningElement) { + return; + } + + if (Watchable* captureState = + mOwningElement->GetFrameCaptureState()) { + mWatchManager.Watch( + *captureState, + &RequestedFrameRefreshObserver::NotifyCaptureStateChange); + } } void Unregister() { @@ -200,6 +271,17 @@ class RequestedFrameRefreshObserver : public nsARefreshObserver { mRefreshDriver->RemoveRefreshObserver(this, FlushType::Display); mRegistered = false; } + + if (!mOwningElement) { + return; + } + + if (Watchable* captureState = + mOwningElement->GetFrameCaptureState()) { + mWatchManager.Unwatch( + *captureState, + &RequestedFrameRefreshObserver::NotifyCaptureStateChange); + } } private: @@ -210,8 +292,11 @@ class RequestedFrameRefreshObserver : public nsARefreshObserver { bool mRegistered; bool mReturnPlaceholderData; - HTMLCanvasElement* const mOwningElement; + const WeakPtr mOwningElement; RefPtr mRefreshDriver; + WatchManager mWatchManager; + TimeStamp mLastCaptureTime; + bool mPendingThrottledCapture; }; // --------------------------------------------------------------------------- @@ -1191,8 +1276,11 @@ void HTMLCanvasElement::MarkContextCleanForFrameCapture() { mCurrentContext->MarkContextCleanForFrameCapture(); } -bool HTMLCanvasElement::IsContextCleanForFrameCapture() { - return mCurrentContext && mCurrentContext->IsContextCleanForFrameCapture(); +Watchable* HTMLCanvasElement::GetFrameCaptureState() { + if (!mCurrentContext) { + return nullptr; + } + return mCurrentContext->GetFrameCaptureState(); } nsresult HTMLCanvasElement::RegisterFrameCaptureListener( diff --git a/dom/html/HTMLCanvasElement.h b/dom/html/HTMLCanvasElement.h index af1e934b025a..3bb4aebad752 100644 --- a/dom/html/HTMLCanvasElement.h +++ b/dom/html/HTMLCanvasElement.h @@ -7,6 +7,7 @@ # define mozilla_dom_HTMLCanvasElement_h # include "mozilla/Attributes.h" +# include "mozilla/StateWatching.h" # include "mozilla/WeakPtr.h" # include "nsIDOMEventListener.h" # include "nsIObserver.h" @@ -23,6 +24,7 @@ class nsICanvasRenderingContextInternal; class nsIInputStream; class nsITimerCallback; enum class gfxAlphaType; +enum class FrameCaptureState : uint8_t; namespace mozilla { @@ -300,8 +302,9 @@ class HTMLCanvasElement final : public nsGenericHTMLElement, // copies for future frames when no drawing has occurred. void MarkContextCleanForFrameCapture(); - // Starts returning false when something is drawn. - bool IsContextCleanForFrameCapture(); + // Returns non-null when the current context supports captureStream(). + // The FrameCaptureState gets set to DIRTY when something is drawn. + Watchable* GetFrameCaptureState(); nsresult GetContext(const nsAString& aContextId, nsISupports** aContext); diff --git a/dom/media/platforms/ffmpeg/FFmpegVideoDecoder.cpp b/dom/media/platforms/ffmpeg/FFmpegVideoDecoder.cpp index 27ebcb605602..e409f5b336a9 100644 --- a/dom/media/platforms/ffmpeg/FFmpegVideoDecoder.cpp +++ b/dom/media/platforms/ffmpeg/FFmpegVideoDecoder.cpp @@ -785,12 +785,12 @@ MediaResult FFmpegVideoDecoder::CreateImageVAAPI( } MOZ_ASSERT(mTaskQueue->IsOnCurrentThread()); - auto surface = mVideoFramePool->GetVideoFrameSurface(vaDesc); + auto surface = mVideoFramePool->GetVideoFrameSurface(vaDesc, mCodecContext, + mFrame, mLib); if (!surface) { return MediaResult(NS_ERROR_OUT_OF_MEMORY, RESULT_DETAIL("VAAPI dmabuf allocation error")); } - surface->LockVAAPIData(mCodecContext, mFrame, mLib); surface->SetYUVColorSpace(GetFrameColorSpace()); if (mLib->av_frame_get_color_range) { diff --git a/dom/media/platforms/ffmpeg/FFmpegVideoFramePool.cpp b/dom/media/platforms/ffmpeg/FFmpegVideoFramePool.cpp index 3d5fa88d7bb6..db50ec043d1d 100644 --- a/dom/media/platforms/ffmpeg/FFmpegVideoFramePool.cpp +++ b/dom/media/platforms/ffmpeg/FFmpegVideoFramePool.cpp @@ -23,6 +23,7 @@ VideoFrameSurfaceDMABuf::VideoFrameSurfaceDMABuf(DMABufSurface* aSurface) MOZ_ASSERT(mSurface); MOZ_RELEASE_ASSERT(mSurface->GetAsDMABufSurfaceYUV()); mSurface->GlobalRefCountCreate(); + mSurface->GlobalRefAdd(); FFMPEG_LOG("VideoFrameSurfaceDMABuf: creating surface UID = %d", mSurface->GetUID()); } @@ -68,17 +69,23 @@ VideoFrameSurfaceVAAPI::~VideoFrameSurfaceVAAPI() { ReleaseVAAPIData(/* aForFrameRecycle */ false); } -VideoFramePool::VideoFramePool(bool aUseVAAPI) : mUseVAAPI(aUseVAAPI) {} +VideoFramePool::VideoFramePool(bool aUseVAAPI) + : mUseVAAPI(aUseVAAPI), mSurfaceLock("VideoFramePoolSurfaceLock") {} -VideoFramePool::~VideoFramePool() { mDMABufSurfaces.Clear(); } +VideoFramePool::~VideoFramePool() { + MutexAutoLock lock(mSurfaceLock); + mDMABufSurfaces.Clear(); +} void VideoFramePool::ReleaseUnusedVAAPIFrames() { if (!mUseVAAPI) { return; } + MutexAutoLock lock(mSurfaceLock); for (const auto& surface : mDMABufSurfaces) { - if (!surface->IsUsed()) { - surface->ReleaseVAAPIData(); + auto* dmabufSurface = surface->AsVideoFrameSurfaceVAAPI(); + if (!dmabufSurface->IsUsed()) { + dmabufSurface->ReleaseVAAPIData(); } } } @@ -86,7 +93,13 @@ void VideoFramePool::ReleaseUnusedVAAPIFrames() { RefPtr VideoFramePool::GetFreeVideoFrameSurface() { int len = mDMABufSurfaces.Length(); for (int i = 0; i < len; i++) { - if (!mDMABufSurfaces[i]->IsUsed()) { + auto* dmabufSurface = mDMABufSurfaces[i]->AsVideoFrameSurfaceDMABuf(); + if (!dmabufSurface->IsUsed()) { + auto* vaapiSurface = dmabufSurface->AsVideoFrameSurfaceVAAPI(); + if (vaapiSurface) { + vaapiSurface->ReleaseVAAPIData(); + } + dmabufSurface->MarkAsUsed(); return mDMABufSurfaces[i]; } } @@ -94,7 +107,8 @@ RefPtr VideoFramePool::GetFreeVideoFrameSurface() { } RefPtr VideoFramePool::GetVideoFrameSurface( - VADRMPRIMESurfaceDescriptor& aVaDesc) { + VADRMPRIMESurfaceDescriptor& aVaDesc, AVCodecContext* aAVCodecContext, + AVFrame* aAVFrame, FFmpegLibWrapper* aLib) { // VADRMPRIMESurfaceDescriptor can be used with VA-API only. MOZ_ASSERT(mUseVAAPI); @@ -104,6 +118,7 @@ RefPtr VideoFramePool::GetVideoFrameSurface( return nullptr; } + MutexAutoLock lock(mSurfaceLock); auto videoSurface = GetFreeVideoFrameSurface(); if (!videoSurface) { RefPtr surface = @@ -114,17 +129,18 @@ RefPtr VideoFramePool::GetVideoFrameSurface( FFMPEG_LOG("Created new VA-API DMABufSurface UID = %d", surface->GetUID()); videoSurface = new VideoFrameSurfaceVAAPI(surface); mDMABufSurfaces.AppendElement(videoSurface); - return videoSurface; + } else { + RefPtr surface = videoSurface->GetDMABufSurface(); + if (!surface->UpdateYUVData(aVaDesc)) { + return nullptr; + } + FFMPEG_LOG("Reusing VA-API DMABufSurface UID = %d", surface->GetUID()); } - // Release VAAPI surface data before we reuse it. - videoSurface->ReleaseVAAPIData(); - - RefPtr surface = videoSurface->GetDMABufSurface(); - if (!surface->UpdateYUVData(aVaDesc)) { - return nullptr; + auto vaapiSurface = videoSurface->AsVideoFrameSurfaceVAAPI(); + if (vaapiSurface) { + vaapiSurface->LockVAAPIData(aAVCodecContext, aAVFrame, aLib); } - FFMPEG_LOG("Reusing VA-API DMABufSurface UID = %d", surface->GetUID()); return videoSurface; } @@ -139,6 +155,7 @@ RefPtr VideoFramePool::GetVideoFrameSurface( return nullptr; } + MutexAutoLock lock(mSurfaceLock); auto videoSurface = GetFreeVideoFrameSurface(); if (!videoSurface) { RefPtr surface = DMABufSurfaceYUV::CreateYUVSurface( diff --git a/dom/media/platforms/ffmpeg/FFmpegVideoFramePool.h b/dom/media/platforms/ffmpeg/FFmpegVideoFramePool.h index ac2f4bde090e..afd3c81b5843 100644 --- a/dom/media/platforms/ffmpeg/FFmpegVideoFramePool.h +++ b/dom/media/platforms/ffmpeg/FFmpegVideoFramePool.h @@ -16,22 +16,25 @@ namespace mozilla { +class VideoFramePool; + class VideoFrameSurface { public: NS_INLINE_DECL_THREADSAFE_REFCOUNTING(VideoFrameSurface) - VideoFrameSurface(){}; + VideoFrameSurface() = default; - virtual void LockVAAPIData(AVCodecContext* aAVCodecContext, AVFrame* aAVFrame, - FFmpegLibWrapper* aLib){}; - virtual void ReleaseVAAPIData(bool aForFrameRecycle = true){}; - virtual bool IsUsed() const = 0; + virtual class VideoFrameSurfaceDMABuf* AsVideoFrameSurfaceDMABuf() { + return nullptr; + } + virtual class VideoFrameSurfaceVAAPI* AsVideoFrameSurfaceVAAPI() { + return nullptr; + } virtual void SetYUVColorSpace(mozilla::gfx::YUVColorSpace aColorSpace) = 0; virtual void SetColorRange(mozilla::gfx::ColorRange aColorRange) = 0; virtual RefPtr GetDMABufSurface() { return nullptr; }; - virtual RefPtr GetAsImage() = 0; // Don't allow VideoFrameSurface plain copy as it leads to @@ -47,17 +50,18 @@ class VideoFrameSurface { // VideoFrameSurfaceDMABuf is YUV dmabuf surface used for SW video decoding. // Stores decoded video data in GPU memory. class VideoFrameSurfaceDMABuf : public VideoFrameSurface { + friend class VideoFramePool; + public: explicit VideoFrameSurfaceDMABuf(DMABufSurface* aSurface); - // Check if DMABufSurface is used by any gecko rendering process - // (WebRender or GL compositor) or by DMABUFSurfaceImage/VideoData. - bool IsUsed() const { return mSurface->IsGlobalRefSet(); } + class VideoFrameSurfaceDMABuf* AsVideoFrameSurfaceDMABuf() { + return this; + } void SetYUVColorSpace(mozilla::gfx::YUVColorSpace aColorSpace) { mSurface->GetAsDMABufSurfaceYUV()->SetYUVColorSpace(aColorSpace); } - void SetColorRange(mozilla::gfx::ColorRange aColorRange) { mSurface->GetAsDMABufSurfaceYUV()->SetColorRange(aColorRange); } @@ -69,9 +73,14 @@ class VideoFrameSurfaceDMABuf : public VideoFrameSurface { RefPtr GetAsImage(); protected: - const RefPtr mSurface; + // Check if DMABufSurface is used by any gecko rendering process + // (WebRender or GL compositor) or by DMABUFSurfaceImage/VideoData. + bool IsUsed() const { return mSurface->IsGlobalRefSet(); } + void MarkAsUsed() { mSurface->GlobalRefAdd(); } protected: + const RefPtr mSurface; + ~VideoFrameSurfaceDMABuf(){}; }; @@ -104,18 +113,23 @@ class VideoFrameSurfaceDMABuf : public VideoFrameSurface { // Unfortunately there isn't any obvious way how to mark particular VASurface // as used. The best we can do is to hold a reference to particular AVBuffer // from decoded AVFrame and AVHWFramesContext which owns the AVBuffer. - class VideoFrameSurfaceVAAPI : public VideoFrameSurfaceDMABuf { + friend class VideoFramePool; + public: explicit VideoFrameSurfaceVAAPI(DMABufSurface* aSurface); + virtual class VideoFrameSurfaceVAAPI* AsVideoFrameSurfaceVAAPI() { + return this; + } + + protected: // Lock VAAPI related data void LockVAAPIData(AVCodecContext* aAVCodecContext, AVFrame* aAVFrame, FFmpegLibWrapper* aLib); - // Release VAAPI related data, DMABufSurface can be reused // for another frame. - void ReleaseVAAPIData(bool aForFrameRecycle); + void ReleaseVAAPIData(bool aForFrameRecycle = true); private: ~VideoFrameSurfaceVAAPI(); @@ -125,13 +139,15 @@ class VideoFrameSurfaceVAAPI : public VideoFrameSurfaceDMABuf { AVBufferRef* mHWAVBuffer; }; +// VideoFramePool class is thread-safe. class VideoFramePool final { public: explicit VideoFramePool(bool aUseVAAPI); ~VideoFramePool(); RefPtr GetVideoFrameSurface( - VADRMPRIMESurfaceDescriptor& aVaDesc); + VADRMPRIMESurfaceDescriptor& aVaDesc, AVCodecContext* aAVCodecContext, + AVFrame* aAVFrame, FFmpegLibWrapper* aLib); RefPtr GetVideoFrameSurface(AVPixelFormat aPixelFormat, AVFrame* aFrame); void ReleaseUnusedVAAPIFrames(); @@ -141,6 +157,8 @@ class VideoFramePool final { private: const bool mUseVAAPI; + // Protect mDMABufSurfaces pool access + mozilla::Mutex mSurfaceLock; nsTArray> mDMABufSurfaces; }; diff --git a/dom/media/platforms/ffmpeg/ffmpeg58/moz.build b/dom/media/platforms/ffmpeg/ffmpeg58/moz.build index 5fe8cf5c4f75..d93991e88f47 100644 --- a/dom/media/platforms/ffmpeg/ffmpeg58/moz.build +++ b/dom/media/platforms/ffmpeg/ffmpeg58/moz.build @@ -12,7 +12,7 @@ UNIFIED_SOURCES += [ ] LOCAL_INCLUDES += [ '..', - '/media/ffvpx', + '/media/mozva', 'include', ] diff --git a/dom/media/platforms/ffmpeg/ffvpx/moz.build b/dom/media/platforms/ffmpeg/ffvpx/moz.build index e0d84d1166d0..7e96c9b17302 100644 --- a/dom/media/platforms/ffmpeg/ffvpx/moz.build +++ b/dom/media/platforms/ffmpeg/ffvpx/moz.build @@ -21,7 +21,7 @@ SOURCES += [ LOCAL_INCLUDES += [ "..", "../ffmpeg58/include", - "/media/ffvpx", + "/media/mozva", ] CXXFLAGS += ["-Wno-deprecated-declarations"] diff --git a/dom/moz.build b/dom/moz.build index c9f01f40e3c2..02db6ae31532 100644 --- a/dom/moz.build +++ b/dom/moz.build @@ -52,6 +52,7 @@ DIRS += [ "filehandle", "filesystem", "flex", + "fs", "gamepad", "geolocation", "grid", diff --git a/dom/quota/StorageManager.cpp b/dom/quota/StorageManager.cpp index 152e097a1ec4..347fb581af0b 100644 --- a/dom/quota/StorageManager.cpp +++ b/dom/quota/StorageManager.cpp @@ -752,6 +752,19 @@ already_AddRefed StorageManager::Estimate(ErrorResult& aRv) { aRv); } +already_AddRefed StorageManager::GetDirectory() { + IgnoredErrorResult rv; + + RefPtr promise = Promise::Create(GetParentObject(), rv); + if (rv.Failed()) { + return nullptr; + } + + promise->MaybeReject(NS_ERROR_NOT_IMPLEMENTED); + + return promise.forget(); +} + NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE(StorageManager, mOwner) NS_IMPL_CYCLE_COLLECTING_ADDREF(StorageManager) diff --git a/dom/quota/StorageManager.h b/dom/quota/StorageManager.h index 0d06995f3f7a..0c6d9d7313b2 100644 --- a/dom/quota/StorageManager.h +++ b/dom/quota/StorageManager.h @@ -42,6 +42,8 @@ class StorageManager final : public nsISupports, public nsWrapperCache { already_AddRefed Estimate(ErrorResult& aRv); + already_AddRefed GetDirectory(); + NS_DECL_CYCLE_COLLECTING_ISUPPORTS NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS(StorageManager) diff --git a/dom/streams/WritableStream.h b/dom/streams/WritableStream.h index 0b3f9b8e79b6..46a9e676091a 100644 --- a/dom/streams/WritableStream.h +++ b/dom/streams/WritableStream.h @@ -29,13 +29,13 @@ class Promise; class WritableStreamDefaultController; class WritableStreamDefaultWriter; -class WritableStream final : public nsISupports, public nsWrapperCache { +class WritableStream : public nsISupports, public nsWrapperCache { public: NS_DECL_CYCLE_COLLECTING_ISUPPORTS NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS(WritableStream) protected: - ~WritableStream(); + virtual ~WritableStream(); public: explicit WritableStream(const GlobalObject& aGlobal); diff --git a/dom/system/PathUtils.cpp b/dom/system/PathUtils.cpp index 9b19ff42dfc6..692820e55fbb 100644 --- a/dom/system/PathUtils.cpp +++ b/dom/system/PathUtils.cpp @@ -331,6 +331,12 @@ void PathUtils::ToFileURI(const GlobalObject&, const nsAString& aPath, } } +bool PathUtils::IsAbsolute(const GlobalObject&, const nsAString& aPath) { + nsCOMPtr path = new nsLocalFile(); + nsresult rv = InitFileWithPath(path, aPath); + return NS_SUCCEEDED(rv); +} + already_AddRefed PathUtils::GetProfileDir(const GlobalObject& aGlobal, ErrorResult& aErr) { auto guard = sDirCache.Lock(); diff --git a/dom/system/PathUtils.h b/dom/system/PathUtils.h index 51b57634bb5e..9ce34eb43f43 100644 --- a/dom/system/PathUtils.h +++ b/dom/system/PathUtils.h @@ -62,6 +62,8 @@ class PathUtils final { static void ToFileURI(const GlobalObject&, const nsAString& aPath, nsCString& aResult, ErrorResult& aErr); + static bool IsAbsolute(const GlobalObject&, const nsAString& aPath); + static already_AddRefed GetProfileDir(const GlobalObject& aGlobal, ErrorResult& aErr); diff --git a/dom/system/tests/test_pathutils.html b/dom/system/tests/test_pathutils.html index 0f6375ae6c06..800dbe6f8402 100644 --- a/dom/system/tests/test_pathutils.html +++ b/dom/system/tests/test_pathutils.html @@ -392,6 +392,22 @@ } }); + add_task(async function test_isAbsolute() { + if (Services.appinfo.OS === "WINNT") { + ok(PathUtils.isAbsolute("C:"), "Drive paths are absolute paths on Windows"); + ok(PathUtils.isAbsolute("C:\\Windows"), "Paths from the root are absolute paths on Windows"); + ok(!PathUtils.isAbsolute("foo"), "Paths containing a single item are not absolute paths on Windows"); + ok(!PathUtils.isAbsolute(".\\foo"), "Paths relative to the current working directory are not absolute paths on Windows"); + ok(!PathUtils.isAbsolute("..\\foo"), "Paths relative to the parent directory are not absolute paths on Windows"); + } else { + ok(PathUtils.isAbsolute("/"), "Root paths are absolute paths"); + ok(PathUtils.isAbsolute("/home"), "Paths with a root stem are absolute paths"); + ok(!PathUtils.isAbsolute("foo"), "Paths containing a single non-root item are not absolute paths"); + ok(!PathUtils.isAbsolute("./foo"), "Paths relative to the current working directory are not absolute paths"); + ok(!PathUtils.isAbsolute("../foo"), "Paths relative to the parent directory are not absolute paths"); + } + }); + add_task(async function test_getDirectories() { const profile = await PathUtils.getProfileDir(); is( diff --git a/dom/tests/mochitest/general/test_focusrings.xhtml b/dom/tests/mochitest/general/test_focusrings.xhtml index bcdfce556ebe..a826b4fe891c 100644 --- a/dom/tests/mochitest/general/test_focusrings.xhtml +++ b/dom/tests/mochitest/general/test_focusrings.xhtml @@ -105,8 +105,9 @@ function runTest() } } -function testMacFocusesFormControl() +async function testMacFocusesFormControl() { + await SimpleTest.promiseFocus(window); testHTMLElements(htmlElementsMacPrefSet, false); SimpleTest.finish(); } diff --git a/dom/webauthn/WebAuthnManager.cpp b/dom/webauthn/WebAuthnManager.cpp index e5f69f6d7b07..7cec4fb5cca9 100644 --- a/dom/webauthn/WebAuthnManager.cpp +++ b/dom/webauthn/WebAuthnManager.cpp @@ -46,7 +46,6 @@ NS_IMPL_CYCLE_COLLECTION_CLASS(WebAuthnManager) NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN_INHERITED(WebAuthnManager, WebAuthnManagerBase) - AbortFollower::Unlink(static_cast(tmp)); NS_IMPL_CYCLE_COLLECTION_UNLINK(mTransaction) tmp->mTransaction.reset(); NS_IMPL_CYCLE_COLLECTION_UNLINK_END diff --git a/dom/webgpu/CanvasContext.h b/dom/webgpu/CanvasContext.h index a4ae7c56016f..81d69566a3d3 100644 --- a/dom/webgpu/CanvasContext.h +++ b/dom/webgpu/CanvasContext.h @@ -91,7 +91,9 @@ class CanvasContext final : public nsICanvasRenderingContextInternal, void DidRefresh() override {} void MarkContextCleanForFrameCapture() override {} - bool IsContextCleanForFrameCapture() override { return false; } + Watchable* GetFrameCaptureState() override { + return nullptr; + } public: void Configure(const dom::GPUCanvasConfiguration& aDesc); diff --git a/dom/webidl/DedicatedWorkerGlobalScope.webidl b/dom/webidl/DedicatedWorkerGlobalScope.webidl index 332c58ea9845..2273426cf85a 100644 --- a/dom/webidl/DedicatedWorkerGlobalScope.webidl +++ b/dom/webidl/DedicatedWorkerGlobalScope.webidl @@ -31,9 +31,9 @@ interface DedicatedWorkerGlobalScope : WorkerGlobalScope { // https://html.spec.whatwg.org/multipage/imagebitmap-and-animations.html#animation-frames // Ideally we would just include AnimationFrameProvider to add the interface, // but we cannot make an include conditional. - [Pref="gfx.offscreencanvas.enabled", Throws] + [Pref="dom.workers.requestAnimationFrame", Throws] long requestAnimationFrame(FrameRequestCallback callback); - [Pref="gfx.offscreencanvas.enabled", Throws] + [Pref="dom.workers.requestAnimationFrame", Throws] void cancelAnimationFrame(long handle); }; diff --git a/dom/webidl/FileSystemDirectoryHandle.webidl b/dom/webidl/FileSystemDirectoryHandle.webidl new file mode 100644 index 000000000000..e247b3c9f623 --- /dev/null +++ b/dom/webidl/FileSystemDirectoryHandle.webidl @@ -0,0 +1,35 @@ +/* -*- Mode: IDL; tab-width: 2; 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/. */ + +dictionary FileSystemGetFileOptions { + boolean create = false; +}; + +dictionary FileSystemGetDirectoryOptions { + boolean create = false; +}; + +dictionary FileSystemRemoveOptions { + boolean recursive = false; +}; + +// TODO: Add Serializzable +[Exposed=(Window,Worker), SecureContext, Pref="dom.fs.enabled"] +interface FileSystemDirectoryHandle : FileSystemHandle { + // This interface defines an async iterable, however that isn't supported yet + // by the bindings. So for now just explicitly define what an async iterable + // definition implies. + //async iterable; + FileSystemDirectoryIterator entries(); + FileSystemDirectoryIterator keys(); + FileSystemDirectoryIterator values(); + + Promise getFileHandle(USVString name, optional FileSystemGetFileOptions options = {}); + Promise getDirectoryHandle(USVString name, optional FileSystemGetDirectoryOptions options = {}); + + Promise removeEntry(USVString name, optional FileSystemRemoveOptions options = {}); + + Promise?> resolve(FileSystemHandle possibleDescendant); +}; diff --git a/dom/webidl/FileSystemDirectoryIterator.webidl b/dom/webidl/FileSystemDirectoryIterator.webidl new file mode 100644 index 000000000000..0e50879f1927 --- /dev/null +++ b/dom/webidl/FileSystemDirectoryIterator.webidl @@ -0,0 +1,11 @@ +/* -*- Mode: IDL; tab-width: 2; 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/. */ + +// To implement FileSystemDirectoryHandle's async iteration until we can use +// a natively supported `async iterable`. +[Exposed=(Window,Worker), SecureContext, LegacyNoInterfaceObject] +interface FileSystemDirectoryIterator { + Promise next(); +}; diff --git a/dom/webidl/FileSystemFileHandle.webidl b/dom/webidl/FileSystemFileHandle.webidl new file mode 100644 index 000000000000..bc63b84a7753 --- /dev/null +++ b/dom/webidl/FileSystemFileHandle.webidl @@ -0,0 +1,22 @@ +/* -*- Mode: IDL; tab-width: 2; 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/. */ + +#ifdef MOZ_DOM_STREAMS +dictionary FileSystemCreateWritableOptions { + boolean keepExistingData = false; +}; +#endif + +// TODO: Add Serializable +[Exposed=(Window,Worker), SecureContext, Pref="dom.fs.enabled"] +interface FileSystemFileHandle : FileSystemHandle { + Promise getFile(); +#ifdef MOZ_DOM_STREAMS + Promise createWritable(optional FileSystemCreateWritableOptions options = {}); +#endif + + [Exposed=DedicatedWorker] + Promise createSyncAccessHandle(); +}; diff --git a/dom/webidl/FileSystemHandle.webidl b/dom/webidl/FileSystemHandle.webidl new file mode 100644 index 000000000000..a4497d482350 --- /dev/null +++ b/dom/webidl/FileSystemHandle.webidl @@ -0,0 +1,18 @@ +/* -*- Mode: IDL; tab-width: 2; 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/. */ + +enum FileSystemHandleKind { + "file", + "directory", +}; + +// TODO: Add Serializable +[Exposed=(Window,Worker), SecureContext, Pref="dom.fs.enabled"] +interface FileSystemHandle { + readonly attribute FileSystemHandleKind kind; + readonly attribute USVString name; + + Promise isSameEntry(FileSystemHandle other); +}; diff --git a/dom/webidl/FileSystemSyncAccessHandle.webidl b/dom/webidl/FileSystemSyncAccessHandle.webidl new file mode 100644 index 000000000000..74c1142e8327 --- /dev/null +++ b/dom/webidl/FileSystemSyncAccessHandle.webidl @@ -0,0 +1,20 @@ +/* -*- Mode: IDL; tab-width: 2; 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/. */ + +dictionary FileSystemReadWriteOptions { + [EnforceRange] unsigned long long at; +}; + +[Exposed=(DedicatedWorker), SecureContext, Pref="dom.fs.enabled"] +interface FileSystemSyncAccessHandle { + // TODO: Use `[AllowShared] BufferSource data` once it works (bug 1696216) + unsigned long long read(([AllowShared] ArrayBufferView or [AllowShared] ArrayBuffer) buffer, optional FileSystemReadWriteOptions options = {}); + unsigned long long write(([AllowShared] ArrayBufferView or [AllowShared] ArrayBuffer) buffer, optional FileSystemReadWriteOptions options = {}); + + Promise truncate([EnforceRange] unsigned long long size); + Promise getSize(); + Promise flush(); + Promise close(); +}; diff --git a/dom/webidl/FileSystemWritableFileStream.webidl b/dom/webidl/FileSystemWritableFileStream.webidl new file mode 100644 index 000000000000..d88f0de37d43 --- /dev/null +++ b/dom/webidl/FileSystemWritableFileStream.webidl @@ -0,0 +1,26 @@ +/* -*- Mode: IDL; tab-width: 2; 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/. */ + +enum WriteCommandType { + "write", + "seek", + "truncate", +}; + +dictionary WriteParams { + required WriteCommandType type; + unsigned long long? size; + unsigned long long? position; + (BufferSource or Blob or USVString)? data; +}; + +typedef (BufferSource or Blob or USVString or WriteParams) FileSystemWriteChunkType; + +[Exposed=(Window,Worker), SecureContext, Pref="dom.fs.enabled"] +interface FileSystemWritableFileStream : WritableStream { + Promise write(FileSystemWriteChunkType data); + Promise seek(unsigned long long position); + Promise truncate(unsigned long long size); +}; diff --git a/dom/webidl/HTMLCanvasElement.webidl b/dom/webidl/HTMLCanvasElement.webidl index e6c068279370..12ece2222e9d 100644 --- a/dom/webidl/HTMLCanvasElement.webidl +++ b/dom/webidl/HTMLCanvasElement.webidl @@ -56,7 +56,7 @@ partial interface HTMLCanvasElement { // For OffscreenCanvas // Reference: https://wiki.whatwg.org/wiki/OffscreenCanvas partial interface HTMLCanvasElement { - [Pref="gfx.offscreencanvas.enabled", Throws] + [Func="CanvasUtils::IsOffscreenCanvasEnabled", Throws] OffscreenCanvas transferControlToOffscreen(); }; diff --git a/dom/webidl/OffscreenCanvas.webidl b/dom/webidl/OffscreenCanvas.webidl index 08ad5079037e..fdd07eaf4e86 100644 --- a/dom/webidl/OffscreenCanvas.webidl +++ b/dom/webidl/OffscreenCanvas.webidl @@ -17,7 +17,7 @@ dictionary ImageEncodeOptions { enum OffscreenRenderingContextId { /* "2d", */ "bitmaprenderer", "webgl", "webgl2", "webgpu" }; [Exposed=(Window,Worker), - Pref="gfx.offscreencanvas.enabled"] + Func="CanvasUtils::IsOffscreenCanvasEnabled"] interface OffscreenCanvas : EventTarget { constructor([EnforceRange] unsigned long width, [EnforceRange] unsigned long height); diff --git a/dom/webidl/StorageManager.webidl b/dom/webidl/StorageManager.webidl index 06e5a97947fc..411839c87d52 100644 --- a/dom/webidl/StorageManager.webidl +++ b/dom/webidl/StorageManager.webidl @@ -26,3 +26,9 @@ dictionary StorageEstimate { unsigned long long usage; unsigned long long quota; }; + +[SecureContext] +partial interface StorageManager { + [Pref="dom.fs.enabled"] + Promise getDirectory(); +}; diff --git a/dom/webidl/moz.build b/dom/webidl/moz.build index 70ff1a49a27e..e84a88fd5ff3 100644 --- a/dom/webidl/moz.build +++ b/dom/webidl/moz.build @@ -388,6 +388,7 @@ GENERATED_WEBIDL_FILES = [ PREPROCESSED_WEBIDL_FILES = [ "Animation.webidl", + "FileSystemFileHandle.webidl", "Node.webidl", "Window.webidl", ] @@ -545,9 +546,13 @@ WEBIDL_FILES = [ "FileReaderSync.webidl", "FileSystem.webidl", "FileSystemDirectoryEntry.webidl", + "FileSystemDirectoryHandle.webidl", + "FileSystemDirectoryIterator.webidl", "FileSystemDirectoryReader.webidl", "FileSystemEntry.webidl", "FileSystemFileEntry.webidl", + "FileSystemHandle.webidl", + "FileSystemSyncAccessHandle.webidl", "FinalizationRegistry.webidl", "FocusEvent.webidl", "FontFace.webidl", @@ -1005,6 +1010,7 @@ WEBIDL_FILES = [ if CONFIG["MOZ_DOM_STREAMS"]: WEBIDL_FILES += [ + "FileSystemWritableFileStream.webidl", "QueuingStrategy.webidl", "ReadableByteStreamController.webidl", "ReadableStream.webidl", diff --git a/gfx/config/gfxVars.h b/gfx/config/gfxVars.h index 995b0c91a866..19ddd80561db 100644 --- a/gfx/config/gfxVars.h +++ b/gfx/config/gfxVars.h @@ -85,7 +85,8 @@ class gfxVarReceiver; _(DrmRenderDevice, nsCString, nsCString()) \ _(UseDMABuf, bool, false) \ _(WebRenderRequiresHardwareDriver, bool, false) \ - _(SupportsThreadsafeGL, bool, false) + _(SupportsThreadsafeGL, bool, false) \ + _(OffscreenCanvasDomainAllowlist, nsCString, nsCString()) /* Add new entries above this line. */ diff --git a/gfx/layers/DMABUFSurfaceImage.cpp b/gfx/layers/DMABUFSurfaceImage.cpp index 94815f2fbca6..2e29ca13c185 100644 --- a/gfx/layers/DMABUFSurfaceImage.cpp +++ b/gfx/layers/DMABUFSurfaceImage.cpp @@ -28,10 +28,14 @@ using namespace mozilla::gl; DMABUFSurfaceImage::DMABUFSurfaceImage(DMABufSurface* aSurface) : Image(nullptr, ImageFormat::DMABUF), mSurface(aSurface) { - mSurface->GlobalRefAdd(); + MOZ_DIAGNOSTIC_ASSERT(mSurface->IsGlobalRefSet(), + "DMABufSurface must be marked as used!"); } -DMABUFSurfaceImage::~DMABUFSurfaceImage() { mSurface->GlobalRefRelease(); } +DMABUFSurfaceImage::~DMABUFSurfaceImage() { + // Unref as we're done with this surface. + mSurface->GlobalRefRelease(); +} StaticRefPtr sSnapshotContext; static StaticMutex sSnapshotContextMutex; diff --git a/gfx/thebes/gfxPlatform.cpp b/gfx/thebes/gfxPlatform.cpp index af4aa7ee12fd..42c40b8c495d 100644 --- a/gfx/thebes/gfxPlatform.cpp +++ b/gfx/thebes/gfxPlatform.cpp @@ -24,6 +24,7 @@ #include "mozilla/ClearOnShutdown.h" #include "mozilla/StaticPrefs_accessibility.h" #include "mozilla/StaticPrefs_apz.h" +#include "mozilla/StaticPrefs_canvas.h" #include "mozilla/StaticPrefs_gfx.h" #include "mozilla/StaticPrefs_layout.h" #include "mozilla/StaticPrefs_layers.h" @@ -937,6 +938,12 @@ void gfxPlatform::Init() { gpu->LaunchGPUProcess(); } + if (XRE_IsParentProcess()) { + nsAutoCString allowlist; + Preferences::GetCString("gfx.offscreencavas.domain-allowlist", allowlist); + gfxVars::SetOffscreenCanvasDomainAllowlist(allowlist); + } + gLastUsedFrameRate = ForceSoftwareVsync() ? GetSoftwareVsyncRate() : -1; Preferences::RegisterCallback( FrameRatePrefChanged, diff --git a/js/src/gc/Nursery.cpp b/js/src/gc/Nursery.cpp index ef75e814ed8e..ce8773f093d6 100644 --- a/js/src/gc/Nursery.cpp +++ b/js/src/gc/Nursery.cpp @@ -210,7 +210,7 @@ js::Nursery::Nursery(GCRuntime* gc) reportPretenuringThreshold_(0), minorGCTriggerReason_(JS::GCReason::NO_REASON), hasRecentGrowthData(false), - smoothedGrowthFactor(1.0), + smoothedTargetSize(0.0), decommitTask(gc) #ifdef JS_GC_ZEAL , @@ -1626,19 +1626,21 @@ void js::Nursery::maybeResizeNursery(JS::GCOptions options, } } -static inline double ClampDouble(double value, double min, double max) { - MOZ_ASSERT(!std::isnan(value) && !std::isnan(min) && !std::isnan(max)); +static inline bool ClampDouble(double* value, double min, double max) { + MOZ_ASSERT(!std::isnan(*value) && !std::isnan(min) && !std::isnan(max)); MOZ_ASSERT(max >= min); - if (value <= min) { - return min; + if (*value <= min) { + *value = min; + return true; } - if (value >= max) { - return max; + if (*value >= max) { + *value = max; + return true; } - return value; + return false; } size_t js::Nursery::targetSize(JS::GCOptions options, JS::GCReason reason) { @@ -1692,31 +1694,33 @@ size_t js::Nursery::targetSize(JS::GCOptions options, JS::GCReason reason) { // Limit the range of the growth factor to prevent transient high promotion // rates from affecting the nursery size too far into the future. static const double GrowthRange = 2.0; - growthFactor = ClampDouble(growthFactor, 1.0 / GrowthRange, GrowthRange); + bool wasClamped = ClampDouble(&growthFactor, 1.0 / GrowthRange, GrowthRange); - // Use exponential smoothing on the desired growth rate to take into account - // the promotion rate from recent previous collections. + // Calculate the target size based on data from this collection. + double target = double(capacity()) * growthFactor; + + // Use exponential smoothing on the target size to take into account data from + // recent previous collections. if (hasRecentGrowthData && now - lastCollectionEndTime() < TimeDuration::FromMilliseconds(200) && !js::SupportDifferentialTesting()) { - growthFactor = 0.75 * smoothedGrowthFactor + 0.25 * growthFactor; + // Pay more attention to large changes. + double fraction = wasClamped ? 0.5 : 0.25; + smoothedTargetSize = + (1 - fraction) * smoothedTargetSize + fraction * target; + } else { + smoothedTargetSize = target; } - hasRecentGrowthData = true; - smoothedGrowthFactor = growthFactor; - // Leave size untouched if we are close to the promotion goal. + // Leave size untouched if we are close to the target. static const double GoalWidth = 1.5; + growthFactor = smoothedTargetSize / double(capacity()); if (growthFactor > (1.0 / GoalWidth) && growthFactor < GoalWidth) { return capacity(); } - // The multiplication below cannot overflow because growthFactor is at - // most two. - MOZ_ASSERT(growthFactor <= 2.0); - MOZ_ASSERT(capacity() < SIZE_MAX / 2); - - return roundSize(size_t(double(capacity()) * growthFactor)); + return roundSize(size_t(smoothedTargetSize)); } void js::Nursery::clearRecentGrowthData() { @@ -1725,7 +1729,7 @@ void js::Nursery::clearRecentGrowthData() { } hasRecentGrowthData = false; - smoothedGrowthFactor = 1.0; + smoothedTargetSize = 0.0; } /* static */ diff --git a/js/src/gc/Nursery.h b/js/src/gc/Nursery.h index 101514ac73b9..58a83ff2f905 100644 --- a/js/src/gc/Nursery.h +++ b/js/src/gc/Nursery.h @@ -479,7 +479,7 @@ class Nursery { PreviousGC previousGC; bool hasRecentGrowthData; - double smoothedGrowthFactor; + double smoothedTargetSize; // Calculate the promotion rate of the most recent minor GC. // The valid_for_tenuring parameter is used to return whether this diff --git a/js/src/jit/AtomicOperations.h b/js/src/jit/AtomicOperations.h index f4a5727d057d..8ad2839b36ed 100644 --- a/js/src/jit/AtomicOperations.h +++ b/js/src/jit/AtomicOperations.h @@ -11,6 +11,7 @@ #include +#include "jit/AtomicOperationsGenerated.h" #include "vm/SharedMem.h" namespace js { @@ -64,7 +65,7 @@ namespace jit { * * It's not a requirement that these functions be inlined; performance * is not a great concern. On some platforms these functions may call - * out to code that's generated at run time. + * functions that use inline assembly. See GenerateAtomicOperations.py. * * In principle these functions will not be written in C++, thus * making races defined behavior if all racy accesses from C++ go via @@ -149,13 +150,6 @@ class AtomicOperations { size_t nbytes); public: - // On some platforms we generate code for the atomics at run-time; that - // happens here. - static bool Initialize(); - - // Deallocate the code segment for generated atomics functions. - static void ShutDown(); - // Test lock-freedom for any int32 value. This implements the // Atomics::isLockFree() operation in the ECMAScript Shared Memory and // Atomics specification, as follows: @@ -347,45 +341,12 @@ constexpr inline bool AtomicOperations::isLockfreeJS(int32_t size) { // participate in the memory exclusivity monitors implemented by the simulator. // Such a solution is likely to be difficult. -#if defined(JS_SIMULATOR_MIPS32) -# if defined(__clang__) || defined(__GNUC__) -# include "jit/mips-shared/AtomicOperations-mips-shared.h" -# else -# error "AtomicOperations on MIPS-32 for unknown compiler" -# endif -#elif defined(__x86_64__) || defined(_M_X64) || defined(__i386__) || \ - defined(_M_IX86) -# if defined(JS_CODEGEN_X86) || defined(JS_CODEGEN_X64) -# include "jit/shared/AtomicOperations-shared-jit.h" -# else -# include "jit/shared/AtomicOperations-feeling-lucky.h" -# endif -#elif defined(__arm__) -# if defined(JS_CODEGEN_ARM) -# include "jit/shared/AtomicOperations-shared-jit.h" -# else -# include "jit/shared/AtomicOperations-feeling-lucky.h" -# endif -#elif defined(__aarch64__) || defined(_M_ARM64) -# if defined(JS_CODEGEN_ARM64) -# include "jit/shared/AtomicOperations-shared-jit.h" -# else -# include "jit/shared/AtomicOperations-feeling-lucky.h" -# endif -#elif defined(__mips__) -# if defined(__clang__) || defined(__GNUC__) -# include "jit/mips-shared/AtomicOperations-mips-shared.h" -# else -# error "AtomicOperations on MIPS for an unknown compiler" -# endif -#elif defined(__ppc__) || defined(__PPC__) || defined(__sparc__) || \ - defined(__ppc64__) || defined(__PPC64__) || defined(__ppc64le__) || \ - defined(__PPC64LE__) || defined(__alpha__) || defined(__hppa__) || \ - defined(__sh__) || defined(__s390__) || defined(__s390x__) || \ - defined(__m68k__) || defined(__riscv) || defined(__wasi__) -# include "jit/shared/AtomicOperations-feeling-lucky.h" +#ifdef JS_HAVE_GENERATED_ATOMIC_OPS +# include "jit/shared/AtomicOperations-shared-jit.h" +#elif defined(JS_SIMULATOR_MIPS32) || defined(__mips__) +# include "jit/mips-shared/AtomicOperations-mips-shared.h" #else -# error "No AtomicOperations support provided for this platform" +# include "jit/shared/AtomicOperations-feeling-lucky.h" #endif #endif // jit_AtomicOperations_h diff --git a/js/src/jit/CacheIRCompiler.cpp b/js/src/jit/CacheIRCompiler.cpp index f5d632151b23..f3ec8ad845b2 100644 --- a/js/src/jit/CacheIRCompiler.cpp +++ b/js/src/jit/CacheIRCompiler.cpp @@ -8118,6 +8118,8 @@ bool CacheIRCompiler::emitAtomicsLoadResult(ObjOperandId objId, // Load the value. BaseIndex source(scratch, index, ScaleFromScalarType(elementType)); + // NOTE: the generated code must match the assembly code in gen_load in + // GenerateAtomicOperations.py auto sync = Synchronization::Load(); masm.memoryBarrierBefore(sync); @@ -8168,6 +8170,8 @@ bool CacheIRCompiler::emitAtomicsStoreResult(ObjOperandId objId, // Store the value. BaseIndex dest(scratch, index, ScaleFromScalarType(elementType)); + // NOTE: the generated code must match the assembly code in gen_store in + // GenerateAtomicOperations.py auto sync = Synchronization::Store(); masm.memoryBarrierBefore(sync); diff --git a/js/src/jit/GenerateAtomicOperations.py b/js/src/jit/GenerateAtomicOperations.py new file mode 100644 index 000000000000..dbde8d80ba3e --- /dev/null +++ b/js/src/jit/GenerateAtomicOperations.py @@ -0,0 +1,861 @@ +# 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/. + +# This script generates jit/AtomicOperationsGenerated.h +# +# See the big comment in jit/AtomicOperations.h for an explanation. + +import buildconfig + +is_64bit = "JS_64BIT" in buildconfig.defines +cpu_arch = buildconfig.substs["CPU_ARCH"] + + +def fmt_insn(s): + return '"' + s + '\\n\\t"\n' + + +def gen_seqcst(fun_name): + if cpu_arch in ("x86", "x86_64"): + return r""" + inline void %(fun_name)s() { + asm volatile ("mfence\n\t" ::: "memory"); + }""" % { + "fun_name": fun_name, + } + if cpu_arch == "aarch64": + return r""" + inline void %(fun_name)s() { + asm volatile ("dmb ish\n\t" ::: "memory"); + }""" % { + "fun_name": fun_name, + } + if cpu_arch == "arm": + return r""" + inline void %(fun_name)s() { + asm volatile ("dmb sy\n\t" ::: "memory"); + }""" % { + "fun_name": fun_name, + } + raise Exception("Unexpected arch") + + +def gen_load(fun_name, cpp_type, size, barrier): + # NOTE: the assembly code must match the generated code in: + # - CacheIRCompiler::emitAtomicsLoadResult + # - LIRGenerator::visitLoadUnboxedScalar + # - CodeGenerator::visitAtomicLoad64 (on 64-bit platforms) + # - MacroAssembler::wasmLoad + if cpu_arch in ("x86", "x86_64"): + insns = "" + if barrier: + insns += fmt_insn("mfence") + if size == 8: + insns += fmt_insn("movb (%[arg]), %[res]") + elif size == 16: + insns += fmt_insn("movw (%[arg]), %[res]") + elif size == 32: + insns += fmt_insn("movl (%[arg]), %[res]") + else: + assert size == 64 + insns += fmt_insn("movq (%[arg]), %[res]") + if barrier: + insns += fmt_insn("mfence") + return """ + inline %(cpp_type)s %(fun_name)s(const %(cpp_type)s* arg) { + %(cpp_type)s res; + asm volatile (%(insns)s + : [res] "=r" (res) + : [arg] "r" (arg) + : "memory"); + return res; + }""" % { + "cpp_type": cpp_type, + "fun_name": fun_name, + "insns": insns, + } + if cpu_arch == "aarch64": + insns = "" + if barrier: + insns += fmt_insn("dmb ish") + if size == 8: + insns += fmt_insn("ldrb %w[res], [%x[arg]]") + elif size == 16: + insns += fmt_insn("ldrh %w[res], [%x[arg]]") + elif size == 32: + insns += fmt_insn("ldr %w[res], [%x[arg]]") + else: + assert size == 64 + insns += fmt_insn("ldr %x[res], [%x[arg]]") + if barrier: + insns += fmt_insn("dmb ish") + return """ + inline %(cpp_type)s %(fun_name)s(const %(cpp_type)s* arg) { + %(cpp_type)s res; + asm volatile (%(insns)s + : [res] "=r" (res) + : [arg] "r" (arg) + : "memory"); + return res; + }""" % { + "cpp_type": cpp_type, + "fun_name": fun_name, + "insns": insns, + } + if cpu_arch == "arm": + insns = "" + if barrier: + insns += fmt_insn("dmb sy") + if size == 8: + insns += fmt_insn("ldrb %[res], [%[arg]]") + elif size == 16: + insns += fmt_insn("ldrh %[res], [%[arg]]") + else: + assert size == 32 + insns += fmt_insn("ldr %[res], [%[arg]]") + if barrier: + insns += fmt_insn("dmb sy") + return """ + inline %(cpp_type)s %(fun_name)s(const %(cpp_type)s* arg) { + %(cpp_type)s res; + asm volatile (%(insns)s + : [res] "=r" (res) + : [arg] "r" (arg) + : "memory"); + return res; + }""" % { + "cpp_type": cpp_type, + "fun_name": fun_name, + "insns": insns, + } + raise Exception("Unexpected arch") + + +def gen_store(fun_name, cpp_type, size, barrier): + # NOTE: the assembly code must match the generated code in: + # - CacheIRCompiler::emitAtomicsStoreResult + # - LIRGenerator::visitStoreUnboxedScalar + # - CodeGenerator::visitAtomicStore64 (on 64-bit platforms) + # - MacroAssembler::wasmStore + if cpu_arch in ("x86", "x86_64"): + insns = "" + if barrier: + insns += fmt_insn("mfence") + if size == 8: + insns += fmt_insn("movb %[val], (%[addr])") + elif size == 16: + insns += fmt_insn("movw %[val], (%[addr])") + elif size == 32: + insns += fmt_insn("movl %[val], (%[addr])") + else: + assert size == 64 + insns += fmt_insn("movq %[val], (%[addr])") + if barrier: + insns += fmt_insn("mfence") + return """ + inline void %(fun_name)s(%(cpp_type)s* addr, %(cpp_type)s val) { + asm volatile (%(insns)s + : + : [addr] "r" (addr), [val] "r"(val) + : "memory"); + }""" % { + "cpp_type": cpp_type, + "fun_name": fun_name, + "insns": insns, + } + if cpu_arch == "aarch64": + insns = "" + if barrier: + insns += fmt_insn("dmb ish") + if size == 8: + insns += fmt_insn("strb %w[val], [%x[addr]]") + elif size == 16: + insns += fmt_insn("strh %w[val], [%x[addr]]") + elif size == 32: + insns += fmt_insn("str %w[val], [%x[addr]]") + else: + assert size == 64 + insns += fmt_insn("str %x[val], [%x[addr]]") + if barrier: + insns += fmt_insn("dmb ish") + return """ + inline void %(fun_name)s(%(cpp_type)s* addr, %(cpp_type)s val) { + asm volatile (%(insns)s + : + : [addr] "r" (addr), [val] "r"(val) + : "memory"); + }""" % { + "cpp_type": cpp_type, + "fun_name": fun_name, + "insns": insns, + } + if cpu_arch == "arm": + insns = "" + if barrier: + insns += fmt_insn("dmb sy") + if size == 8: + insns += fmt_insn("strb %[val], [%[addr]]") + elif size == 16: + insns += fmt_insn("strh %[val], [%[addr]]") + else: + assert size == 32 + insns += fmt_insn("str %[val], [%[addr]]") + if barrier: + insns += fmt_insn("dmb sy") + return """ + inline void %(fun_name)s(%(cpp_type)s* addr, %(cpp_type)s val) { + asm volatile (%(insns)s + : + : [addr] "r" (addr), [val] "r"(val) + : "memory"); + }""" % { + "cpp_type": cpp_type, + "fun_name": fun_name, + "insns": insns, + } + raise Exception("Unexpected arch") + + +def gen_exchange(fun_name, cpp_type, size): + # NOTE: the assembly code must match the generated code in: + # - MacroAssembler::atomicExchange + # - MacroAssembler::atomicExchange64 (on 64-bit platforms) + if cpu_arch in ("x86", "x86_64"): + # Request an input/output register for `val` so that we can simply XCHG it + # with *addr. + insns = "" + if size == 8: + insns += fmt_insn("xchgb %[val], (%[addr])") + elif size == 16: + insns += fmt_insn("xchgw %[val], (%[addr])") + elif size == 32: + insns += fmt_insn("xchgl %[val], (%[addr])") + else: + assert size == 64 + insns += fmt_insn("xchgq %[val], (%[addr])") + return """ + inline %(cpp_type)s %(fun_name)s(%(cpp_type)s* addr, %(cpp_type)s val) { + asm volatile (%(insns)s + : [val] "+r" (val) + : [addr] "r" (addr) + : "memory"); + return val; + }""" % { + "cpp_type": cpp_type, + "fun_name": fun_name, + "insns": insns, + } + if cpu_arch == "aarch64": + insns = "" + insns += fmt_insn("dmb ish") + insns += fmt_insn("0:") + if size == 8: + insns += fmt_insn("ldxrb %w[res], [%x[addr]]") + insns += fmt_insn("stxrb %w[scratch], %w[val], [%x[addr]]") + elif size == 16: + insns += fmt_insn("ldxrh %w[res], [%x[addr]]") + insns += fmt_insn("stxrh %w[scratch], %w[val], [%x[addr]]") + elif size == 32: + insns += fmt_insn("ldxr %w[res], [%x[addr]]") + insns += fmt_insn("stxr %w[scratch], %w[val], [%x[addr]]") + else: + assert size == 64 + insns += fmt_insn("ldxr %x[res], [%x[addr]]") + insns += fmt_insn("stxr %w[scratch], %x[val], [%x[addr]]") + insns += fmt_insn("cbnz %w[scratch], 0b") + insns += fmt_insn("dmb ish") + return """ + inline %(cpp_type)s %(fun_name)s(%(cpp_type)s* addr, %(cpp_type)s val) { + %(cpp_type)s res; + uint32_t scratch; + asm volatile (%(insns)s + : [res] "=&r"(res), [scratch] "=&r"(scratch) + : [addr] "r" (addr), [val] "r"(val) + : "memory", "cc"); + return res; + }""" % { + "cpp_type": cpp_type, + "fun_name": fun_name, + "insns": insns, + } + if cpu_arch == "arm": + insns = "" + insns += fmt_insn("dmb sy") + insns += fmt_insn("0:") + if size == 8: + insns += fmt_insn("ldrexb %[res], [%[addr]]") + insns += fmt_insn("strexb %[scratch], %[val], [%[addr]]") + elif size == 16: + insns += fmt_insn("ldrexh %[res], [%[addr]]") + insns += fmt_insn("strexh %[scratch], %[val], [%[addr]]") + else: + assert size == 32 + insns += fmt_insn("ldrex %[res], [%[addr]]") + insns += fmt_insn("strex %[scratch], %[val], [%[addr]]") + insns += fmt_insn("cmp %[scratch], #1") + insns += fmt_insn("beq 0b") + insns += fmt_insn("dmb sy") + return """ + inline %(cpp_type)s %(fun_name)s(%(cpp_type)s* addr, %(cpp_type)s val) { + %(cpp_type)s res; + uint32_t scratch; + asm volatile (%(insns)s + : [res] "=&r"(res), [scratch] "=&r"(scratch) + : [addr] "r" (addr), [val] "r"(val) + : "memory", "cc"); + return res; + }""" % { + "cpp_type": cpp_type, + "fun_name": fun_name, + "insns": insns, + } + raise Exception("Unexpected arch") + + +def gen_cmpxchg(fun_name, cpp_type, size): + # NOTE: the assembly code must match the generated code in: + # - MacroAssembler::compareExchange + # - MacroAssembler::compareExchange64 + if cpu_arch == "x86" and size == 64: + # Use a +A constraint to load `oldval` into EDX:EAX as input/output. + # `newval` is loaded into ECX:EBX. + return r""" + inline %(cpp_type)s %(fun_name)s(%(cpp_type)s* addr, + %(cpp_type)s oldval, + %(cpp_type)s newval) { + asm volatile ("lock; cmpxchg8b (%%[addr])\n\t" + : "+A" (oldval) + : [addr] "r" (addr), + "b" (uint32_t(newval & 0xffff'ffff)), + "c" (uint32_t(newval >> 32)) + : "memory", "cc"); + return oldval; + }""" % { + "cpp_type": cpp_type, + "fun_name": fun_name, + } + if cpu_arch == "arm" and size == 64: + return r""" + inline %(cpp_type)s %(fun_name)s(%(cpp_type)s* addr, + %(cpp_type)s oldval, + %(cpp_type)s newval) { + uint32_t oldval0 = oldval & 0xffff'ffff; + uint32_t oldval1 = oldval >> 32; + uint32_t newval0 = newval & 0xffff'ffff; + uint32_t newval1 = newval >> 32; + asm volatile ( + "dmb sy\n\t" + "0: ldrexd r0, r1, [%%[addr]]\n\t" + "cmp r0, %%[oldval0]\n\t" + "bne 1f\n\t" + "cmp r1, %%[oldval1]\n\t" + "bne 1f\n\t" + "mov r2, %%[newval0]\n\t" + "mov r3, %%[newval1]\n\t" + "strexd r4, r2, r3, [%%[addr]]\n\t" + "cmp r4, #1\n\t" + "beq 0b\n\t" + "1: dmb sy\n\t" + "mov %%[oldval0], r0\n\t" + "mov %%[oldval1], r1\n\t" + : [oldval0] "+&r" (oldval0), [oldval1] "+&r"(oldval1) + : [addr] "r" (addr), [newval0] "r" (newval0), [newval1] "r" (newval1) + : "memory", "cc", "r0", "r1", "r2", "r3", "r4"); + return uint64_t(oldval0) | (uint64_t(oldval1) << 32); + }""" % { + "cpp_type": cpp_type, + "fun_name": fun_name, + } + if cpu_arch in ("x86", "x86_64"): + # Use a +a constraint to load `oldval` into RAX as input/output register. + insns = "" + if size == 8: + insns += fmt_insn("lock; cmpxchgb %[newval], (%[addr])") + elif size == 16: + insns += fmt_insn("lock; cmpxchgw %[newval], (%[addr])") + elif size == 32: + insns += fmt_insn("lock; cmpxchgl %[newval], (%[addr])") + else: + assert size == 64 + insns += fmt_insn("lock; cmpxchgq %[newval], (%[addr])") + return """ + inline %(cpp_type)s %(fun_name)s(%(cpp_type)s* addr, + %(cpp_type)s oldval, + %(cpp_type)s newval) { + asm volatile (%(insns)s + : [oldval] "+a" (oldval) + : [addr] "r" (addr), [newval] "r" (newval) + : "memory", "cc"); + return oldval; + }""" % { + "cpp_type": cpp_type, + "fun_name": fun_name, + "insns": insns, + } + if cpu_arch == "aarch64": + insns = "" + insns += fmt_insn("dmb ish") + insns += fmt_insn("0:") + if size == 8: + insns += fmt_insn("uxtb %w[scratch], %w[oldval]") + insns += fmt_insn("ldxrb %w[res], [%x[addr]]") + insns += fmt_insn("cmp %w[res], %w[scratch]") + insns += fmt_insn("b.ne 1f") + insns += fmt_insn("stxrb %w[scratch], %w[newval], [%x[addr]]") + elif size == 16: + insns += fmt_insn("uxth %w[scratch], %w[oldval]") + insns += fmt_insn("ldxrh %w[res], [%x[addr]]") + insns += fmt_insn("cmp %w[res], %w[scratch]") + insns += fmt_insn("b.ne 1f") + insns += fmt_insn("stxrh %w[scratch], %w[newval], [%x[addr]]") + elif size == 32: + insns += fmt_insn("mov %w[scratch], %w[oldval]") + insns += fmt_insn("ldxr %w[res], [%x[addr]]") + insns += fmt_insn("cmp %w[res], %w[scratch]") + insns += fmt_insn("b.ne 1f") + insns += fmt_insn("stxr %w[scratch], %w[newval], [%x[addr]]") + else: + assert size == 64 + insns += fmt_insn("mov %x[scratch], %x[oldval]") + insns += fmt_insn("ldxr %x[res], [%x[addr]]") + insns += fmt_insn("cmp %x[res], %x[scratch]") + insns += fmt_insn("b.ne 1f") + insns += fmt_insn("stxr %w[scratch], %x[newval], [%x[addr]]") + insns += fmt_insn("cbnz %w[scratch], 0b") + insns += fmt_insn("1: dmb ish") + return """ + inline %(cpp_type)s %(fun_name)s(%(cpp_type)s* addr, + %(cpp_type)s oldval, + %(cpp_type)s newval) { + %(cpp_type)s res, scratch; + asm volatile (%(insns)s + : [res] "=&r" (res), [scratch] "=&r" (scratch) + : [addr] "r" (addr), [oldval] "r"(oldval), [newval] "r" (newval) + : "memory", "cc"); + return res; + }""" % { + "cpp_type": cpp_type, + "fun_name": fun_name, + "insns": insns, + } + if cpu_arch == "arm": + insns = "" + insns += fmt_insn("dmb sy") + insns += fmt_insn("0:") + if size == 8: + insns += fmt_insn("uxtb %[scratch], %[oldval]") + insns += fmt_insn("ldrexb %[res], [%[addr]]") + insns += fmt_insn("cmp %[res], %[scratch]") + insns += fmt_insn("bne 1f") + insns += fmt_insn("strexb %[scratch], %[newval], [%[addr]]") + elif size == 16: + insns += fmt_insn("uxth %[scratch], %[oldval]") + insns += fmt_insn("ldrexh %[res], [%[addr]]") + insns += fmt_insn("cmp %[res], %[scratch]") + insns += fmt_insn("bne 1f") + insns += fmt_insn("strexh %[scratch], %[newval], [%[addr]]") + else: + assert size == 32 + insns += fmt_insn("mov %[scratch], %[oldval]") + insns += fmt_insn("ldrex %[res], [%[addr]]") + insns += fmt_insn("cmp %[res], %[scratch]") + insns += fmt_insn("bne 1f") + insns += fmt_insn("strex %[scratch], %[newval], [%[addr]]") + insns += fmt_insn("cmp %[scratch], #1") + insns += fmt_insn("beq 0b") + insns += fmt_insn("1: dmb sy") + return """ + inline %(cpp_type)s %(fun_name)s(%(cpp_type)s* addr, + %(cpp_type)s oldval, + %(cpp_type)s newval) { + %(cpp_type)s res, scratch; + asm volatile (%(insns)s + : [res] "=&r" (res), [scratch] "=&r" (scratch) + : [addr] "r" (addr), [oldval] "r"(oldval), [newval] "r" (newval) + : "memory", "cc"); + return res; + }""" % { + "cpp_type": cpp_type, + "fun_name": fun_name, + "insns": insns, + } + raise Exception("Unexpected arch") + + +def gen_fetchop(fun_name, cpp_type, size, op): + # NOTE: the assembly code must match the generated code in: + # - MacroAssembler::atomicFetchOp + # - MacroAssembler::atomicFetchOp64 (on 64-bit platforms) + if cpu_arch in ("x86", "x86_64"): + # The `add` operation can be optimized with XADD. + if op == "add": + insns = "" + if size == 8: + insns += fmt_insn("lock; xaddb %[val], (%[addr])") + elif size == 16: + insns += fmt_insn("lock; xaddw %[val], (%[addr])") + elif size == 32: + insns += fmt_insn("lock; xaddl %[val], (%[addr])") + else: + assert size == 64 + insns += fmt_insn("lock; xaddq %[val], (%[addr])") + return """ + inline %(cpp_type)s %(fun_name)s(%(cpp_type)s* addr, %(cpp_type)s val) { + asm volatile (%(insns)s + : [val] "+&r" (val) + : [addr] "r" (addr) + : "memory", "cc"); + return val; + }""" % { + "cpp_type": cpp_type, + "fun_name": fun_name, + "insns": insns, + } + # Use a +a constraint to ensure `res` is stored in RAX. This is required + # for the CMPXCHG instruction. + insns = "" + if size == 8: + insns += fmt_insn("movb (%[addr]), %[res]") + insns += fmt_insn("0: movb %[res], %[scratch]") + insns += fmt_insn("OPb %[val], %[scratch]") + insns += fmt_insn("lock; cmpxchgb %[scratch], (%[addr])") + elif size == 16: + insns += fmt_insn("movw (%[addr]), %[res]") + insns += fmt_insn("0: movw %[res], %[scratch]") + insns += fmt_insn("OPw %[val], %[scratch]") + insns += fmt_insn("lock; cmpxchgw %[scratch], (%[addr])") + elif size == 32: + insns += fmt_insn("movl (%[addr]), %[res]") + insns += fmt_insn("0: movl %[res], %[scratch]") + insns += fmt_insn("OPl %[val], %[scratch]") + insns += fmt_insn("lock; cmpxchgl %[scratch], (%[addr])") + else: + assert size == 64 + insns += fmt_insn("movq (%[addr]), %[res]") + insns += fmt_insn("0: movq %[res], %[scratch]") + insns += fmt_insn("OPq %[val], %[scratch]") + insns += fmt_insn("lock; cmpxchgq %[scratch], (%[addr])") + insns = insns.replace("OP", op) + insns += fmt_insn("jnz 0b") + return """ + inline %(cpp_type)s %(fun_name)s(%(cpp_type)s* addr, %(cpp_type)s val) { + %(cpp_type)s res, scratch; + asm volatile (%(insns)s + : [res] "=&a" (res), [scratch] "=&r" (scratch) + : [addr] "r" (addr), [val] "r"(val) + : "memory", "cc"); + return res; + }""" % { + "cpp_type": cpp_type, + "fun_name": fun_name, + "insns": insns, + } + if cpu_arch == "aarch64": + insns = "" + insns += fmt_insn("dmb ish") + insns += fmt_insn("0:") + if size == 8: + insns += fmt_insn("ldxrb %w[res], [%x[addr]]") + insns += fmt_insn("OP %x[scratch1], %x[res], %x[val]") + insns += fmt_insn("stxrb %w[scratch2], %w[scratch1], [%x[addr]]") + elif size == 16: + insns += fmt_insn("ldxrh %w[res], [%x[addr]]") + insns += fmt_insn("OP %x[scratch1], %x[res], %x[val]") + insns += fmt_insn("stxrh %w[scratch2], %w[scratch1], [%x[addr]]") + elif size == 32: + insns += fmt_insn("ldxr %w[res], [%x[addr]]") + insns += fmt_insn("OP %x[scratch1], %x[res], %x[val]") + insns += fmt_insn("stxr %w[scratch2], %w[scratch1], [%x[addr]]") + else: + assert size == 64 + insns += fmt_insn("ldxr %x[res], [%x[addr]]") + insns += fmt_insn("OP %x[scratch1], %x[res], %x[val]") + insns += fmt_insn("stxr %w[scratch2], %x[scratch1], [%x[addr]]") + cpu_op = op + if cpu_op == "or": + cpu_op = "orr" + if cpu_op == "xor": + cpu_op = "eor" + insns = insns.replace("OP", cpu_op) + insns += fmt_insn("cbnz %w[scratch2], 0b") + insns += fmt_insn("dmb ish") + return """ + inline %(cpp_type)s %(fun_name)s(%(cpp_type)s* addr, %(cpp_type)s val) { + %(cpp_type)s res; + uintptr_t scratch1, scratch2; + asm volatile (%(insns)s + : [res] "=&r" (res), [scratch1] "=&r" (scratch1), [scratch2] "=&r"(scratch2) + : [addr] "r" (addr), [val] "r"(val) + : "memory", "cc"); + return res; + }""" % { + "cpp_type": cpp_type, + "fun_name": fun_name, + "insns": insns, + } + if cpu_arch == "arm": + insns = "" + insns += fmt_insn("dmb sy") + insns += fmt_insn("0:") + if size == 8: + insns += fmt_insn("ldrexb %[res], [%[addr]]") + insns += fmt_insn("OP %[scratch1], %[res], %[val]") + insns += fmt_insn("strexb %[scratch2], %[scratch1], [%[addr]]") + elif size == 16: + insns += fmt_insn("ldrexh %[res], [%[addr]]") + insns += fmt_insn("OP %[scratch1], %[res], %[val]") + insns += fmt_insn("strexh %[scratch2], %[scratch1], [%[addr]]") + else: + assert size == 32 + insns += fmt_insn("ldrex %[res], [%[addr]]") + insns += fmt_insn("OP %[scratch1], %[res], %[val]") + insns += fmt_insn("strex %[scratch2], %[scratch1], [%[addr]]") + cpu_op = op + if cpu_op == "or": + cpu_op = "orr" + if cpu_op == "xor": + cpu_op = "eor" + insns = insns.replace("OP", cpu_op) + insns += fmt_insn("cmp %[scratch2], #1") + insns += fmt_insn("beq 0b") + insns += fmt_insn("dmb sy") + return """ + inline %(cpp_type)s %(fun_name)s(%(cpp_type)s* addr, %(cpp_type)s val) { + %(cpp_type)s res; + uintptr_t scratch1, scratch2; + asm volatile (%(insns)s + : [res] "=&r" (res), [scratch1] "=&r" (scratch1), [scratch2] "=&r"(scratch2) + : [addr] "r" (addr), [val] "r"(val) + : "memory", "cc"); + return res; + }""" % { + "cpp_type": cpp_type, + "fun_name": fun_name, + "insns": insns, + } + raise Exception("Unexpected arch") + + +def gen_copy(fun_name, cpp_type, size, unroll, direction): + assert direction in ("down", "up") + offset = 0 + if direction == "up": + offset = unroll - 1 + insns = "" + for i in range(unroll): + if cpu_arch in ("x86", "x86_64"): + if size == 1: + insns += fmt_insn("movb OFFSET(%[src]), %[scratch]") + insns += fmt_insn("movb %[scratch], OFFSET(%[dst])") + elif size == 4: + insns += fmt_insn("movl OFFSET(%[src]), %[scratch]") + insns += fmt_insn("movl %[scratch], OFFSET(%[dst])") + else: + assert size == 8 + insns += fmt_insn("movq OFFSET(%[src]), %[scratch]") + insns += fmt_insn("movq %[scratch], OFFSET(%[dst])") + elif cpu_arch == "aarch64": + if size == 1: + insns += fmt_insn("ldrb %w[scratch], [%x[src], OFFSET]") + insns += fmt_insn("strb %w[scratch], [%x[dst], OFFSET]") + else: + assert size == 8 + insns += fmt_insn("ldr %x[scratch], [%x[src], OFFSET]") + insns += fmt_insn("str %x[scratch], [%x[dst], OFFSET]") + elif cpu_arch == "arm": + if size == 1: + insns += fmt_insn("ldrb %[scratch], [%[src], OFFSET]") + insns += fmt_insn("strb %[scratch], [%[dst], OFFSET]") + else: + assert size == 4 + insns += fmt_insn("ldr %[scratch], [%[src], OFFSET]") + insns += fmt_insn("str %[scratch], [%[dst], OFFSET]") + else: + raise Exception("Unexpected arch") + insns = insns.replace("OFFSET", str(offset * size)) + + if direction == "down": + offset += 1 + else: + offset -= 1 + + return """ + inline void %(fun_name)s(uint8_t* dst, const uint8_t* src) { + %(cpp_type)s* dst_ = reinterpret_cast<%(cpp_type)s*>(dst); + const %(cpp_type)s* src_ = reinterpret_cast(src); + %(cpp_type)s scratch; + asm volatile (%(insns)s + : [scratch] "=&r" (scratch) + : [dst] "r" (dst_), [src] "r"(src_) + : "memory"); + }""" % { + "cpp_type": cpp_type, + "fun_name": fun_name, + "insns": insns, + } + + +HEADER_TEMPLATE = """\ +/* 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 jit_AtomicOperationsGenerated_h +#define jit_AtomicOperationsGenerated_h + +/* This file is generated by jit/GenerateAtomicOperations.py. Do not edit! */ + +namespace js { +namespace jit { + +%(contents)s + +} // namespace jit +} // namespace js + +#endif // jit_AtomicOperationsGenerated_h +""" + + +def generate_atomics_header(c_out): + contents = "" + if cpu_arch in ("x86", "x86_64", "arm", "aarch64"): + contents += "#define JS_HAVE_GENERATED_ATOMIC_OPS 1" + + # `fence` performs a full memory barrier. + contents += gen_seqcst("AtomicFenceSeqCst") + + contents += gen_load("AtomicLoad8SeqCst", "uint8_t", 8, True) + contents += gen_load("AtomicLoad16SeqCst", "uint16_t", 16, True) + contents += gen_load("AtomicLoad32SeqCst", "uint32_t", 32, True) + if is_64bit: + contents += gen_load("AtomicLoad64SeqCst", "uint64_t", 64, True) + + # These are access-atomic up to sizeof(uintptr_t). + contents += gen_load("AtomicLoad8Unsynchronized", "uint8_t", 8, False) + contents += gen_load("AtomicLoad16Unsynchronized", "uint16_t", 16, False) + contents += gen_load("AtomicLoad32Unsynchronized", "uint32_t", 32, False) + if is_64bit: + contents += gen_load("AtomicLoad64Unsynchronized", "uint64_t", 64, False) + + contents += gen_store("AtomicStore8SeqCst", "uint8_t", 8, True) + contents += gen_store("AtomicStore16SeqCst", "uint16_t", 16, True) + contents += gen_store("AtomicStore32SeqCst", "uint32_t", 32, True) + if is_64bit: + contents += gen_store("AtomicStore64SeqCst", "uint64_t", 64, True) + + # These are access-atomic up to sizeof(uintptr_t). + contents += gen_store("AtomicStore8Unsynchronized", "uint8_t", 8, False) + contents += gen_store("AtomicStore16Unsynchronized", "uint16_t", 16, False) + contents += gen_store("AtomicStore32Unsynchronized", "uint32_t", 32, False) + if is_64bit: + contents += gen_store("AtomicStore64Unsynchronized", "uint64_t", 64, False) + + # `exchange` takes a cell address and a value. It stores it in the cell and + # returns the value previously in the cell. + contents += gen_exchange("AtomicExchange8SeqCst", "uint8_t", 8) + contents += gen_exchange("AtomicExchange16SeqCst", "uint16_t", 16) + contents += gen_exchange("AtomicExchange32SeqCst", "uint32_t", 32) + if is_64bit: + contents += gen_exchange("AtomicExchange64SeqCst", "uint64_t", 64) + + # `cmpxchg` takes a cell address, an expected value and a replacement value. + # If the value in the cell equals the expected value then the replacement value + # is stored in the cell. It always returns the value previously in the cell. + contents += gen_cmpxchg("AtomicCmpXchg8SeqCst", "uint8_t", 8) + contents += gen_cmpxchg("AtomicCmpXchg16SeqCst", "uint16_t", 16) + contents += gen_cmpxchg("AtomicCmpXchg32SeqCst", "uint32_t", 32) + contents += gen_cmpxchg("AtomicCmpXchg64SeqCst", "uint64_t", 64) + + # `add` adds a value atomically to the cell and returns the old value in the + # cell. (There is no `sub`; just add the negated value.) + contents += gen_fetchop("AtomicAdd8SeqCst", "uint8_t", 8, "add") + contents += gen_fetchop("AtomicAdd16SeqCst", "uint16_t", 16, "add") + contents += gen_fetchop("AtomicAdd32SeqCst", "uint32_t", 32, "add") + if is_64bit: + contents += gen_fetchop("AtomicAdd64SeqCst", "uint64_t", 64, "add") + + # `and` bitwise-ands a value atomically into the cell and returns the old value + # in the cell. + contents += gen_fetchop("AtomicAnd8SeqCst", "uint8_t", 8, "and") + contents += gen_fetchop("AtomicAnd16SeqCst", "uint16_t", 16, "and") + contents += gen_fetchop("AtomicAnd32SeqCst", "uint32_t", 32, "and") + if is_64bit: + contents += gen_fetchop("AtomicAnd64SeqCst", "uint64_t", 64, "and") + + # `or` bitwise-ors a value atomically into the cell and returns the old value + # in the cell. + contents += gen_fetchop("AtomicOr8SeqCst", "uint8_t", 8, "or") + contents += gen_fetchop("AtomicOr16SeqCst", "uint16_t", 16, "or") + contents += gen_fetchop("AtomicOr32SeqCst", "uint32_t", 32, "or") + if is_64bit: + contents += gen_fetchop("AtomicOr64SeqCst", "uint64_t", 64, "or") + + # `xor` bitwise-xors a value atomically into the cell and returns the old value + # in the cell. + contents += gen_fetchop("AtomicXor8SeqCst", "uint8_t", 8, "xor") + contents += gen_fetchop("AtomicXor16SeqCst", "uint16_t", 16, "xor") + contents += gen_fetchop("AtomicXor32SeqCst", "uint32_t", 32, "xor") + if is_64bit: + contents += gen_fetchop("AtomicXor64SeqCst", "uint64_t", 64, "xor") + + # See comment in jit/AtomicOperations-shared-jit.cpp for an explanation. + wordsize = 8 if is_64bit else 4 + words_in_block = 8 + blocksize = words_in_block * wordsize + + contents += gen_copy( + "AtomicCopyUnalignedBlockDownUnsynchronized", + "uint8_t", + 1, + blocksize, + "down", + ) + contents += gen_copy( + "AtomicCopyUnalignedBlockUpUnsynchronized", "uint8_t", 1, blocksize, "up" + ) + + contents += gen_copy( + "AtomicCopyUnalignedWordDownUnsynchronized", "uint8_t", 1, wordsize, "down" + ) + contents += gen_copy( + "AtomicCopyUnalignedWordUpUnsynchronized", "uint8_t", 1, wordsize, "up" + ) + + contents += gen_copy( + "AtomicCopyBlockDownUnsynchronized", + "uintptr_t", + wordsize, + words_in_block, + "down", + ) + contents += gen_copy( + "AtomicCopyBlockUpUnsynchronized", + "uintptr_t", + wordsize, + words_in_block, + "up", + ) + + contents += gen_copy( + "AtomicCopyWordUnsynchronized", "uintptr_t", wordsize, 1, "down" + ) + contents += gen_copy("AtomicCopyByteUnsynchronized", "uint8_t", 1, 1, "down") + + contents += "\n" + contents += ( + "constexpr size_t JS_GENERATED_ATOMICS_BLOCKSIZE = " + + str(blocksize) + + ";\n" + ) + contents += ( + "constexpr size_t JS_GENERATED_ATOMICS_WORDSIZE = " + str(wordsize) + ";\n" + ) + + c_out.write( + HEADER_TEMPLATE + % { + "contents": contents, + } + ) diff --git a/js/src/jit/JitContext.cpp b/js/src/jit/JitContext.cpp index e50e0f94aeac..1ff26c343ddb 100644 --- a/js/src/jit/JitContext.cpp +++ b/js/src/jit/JitContext.cpp @@ -13,6 +13,7 @@ #include "jit/CacheIRSpewer.h" #include "jit/CompileWrappers.h" +#include "jit/Ion.h" #include "jit/JitCode.h" #include "jit/JitOptions.h" #include "jit/JitSpewer.h" @@ -97,6 +98,11 @@ bool jit::InitializeJit() { } #endif +#if defined(JS_CODEGEN_X86) || defined(JS_CODEGEN_X64) + // Compute flags. + js::jit::CPUInfo::GetSSEVersion(); +#endif + #if defined(JS_CODEGEN_ARM) InitARMFlags(); #endif @@ -105,6 +111,10 @@ bool jit::InitializeJit() { ComputeJitSupportFlags(); CheckPerf(); + +#ifndef JS_CODEGEN_NONE + MOZ_ASSERT(js::jit::CPUFlagsHaveBeenComputed()); +#endif return true; } diff --git a/js/src/jit/Lowering.cpp b/js/src/jit/Lowering.cpp index e52b28b3e1bf..6ec2e7f8a7c2 100644 --- a/js/src/jit/Lowering.cpp +++ b/js/src/jit/Lowering.cpp @@ -3764,6 +3764,8 @@ void LIRGenerator::visitLoadUnboxedScalar(MLoadUnboxedScalar* ins) { const LAllocation index = useRegisterOrIndexConstant( ins->index(), ins->storageType(), ins->offsetAdjustment()); + // NOTE: the generated code must match the assembly code in gen_load in + // GenerateAtomicOperations.py Synchronization sync = Synchronization::Load(); if (ins->requiresMemoryBarrier()) { LMemoryBarrier* fence = new (alloc()) LMemoryBarrier(sync.barrierBefore); @@ -3951,6 +3953,9 @@ void LIRGenerator::visitStoreUnboxedScalar(MStoreUnboxedScalar* ins) { // is a store instruction that incorporates the necessary // barriers, and we could use that instead of separate barrier and // store instructions. See bug #1077027. + // + // NOTE: the generated code must match the assembly code in gen_store in + // GenerateAtomicOperations.py Synchronization sync = Synchronization::Store(); if (ins->requiresMemoryBarrier()) { LMemoryBarrier* fence = new (alloc()) LMemoryBarrier(sync.barrierBefore); diff --git a/js/src/jit/arm/MacroAssembler-arm.cpp b/js/src/jit/arm/MacroAssembler-arm.cpp index 4050e5dfcb6f..aa1a8373b69b 100644 --- a/js/src/jit/arm/MacroAssembler-arm.cpp +++ b/js/src/jit/arm/MacroAssembler-arm.cpp @@ -4935,6 +4935,8 @@ static void CompareExchange(MacroAssembler& masm, ScratchRegisterScope scratch(masm); + // NOTE: the generated code must match the assembly code in gen_cmpxchg in + // GenerateAtomicOperations.py masm.memoryBarrierBefore(sync); masm.bind(&again); @@ -5038,6 +5040,8 @@ static void AtomicExchange(MacroAssembler& masm, ScratchRegisterScope scratch(masm); + // NOTE: the generated code must match the assembly code in gen_exchange in + // GenerateAtomicOperations.py masm.memoryBarrierBefore(sync); masm.bind(&again); @@ -5139,6 +5143,8 @@ static void AtomicFetchOp(MacroAssembler& masm, SecondScratchRegisterScope scratch2(masm); Register ptr = ComputePointerForAtomic(masm, mem, scratch2); + // NOTE: the generated code must match the assembly code in gen_fetchop in + // GenerateAtomicOperations.py masm.memoryBarrierBefore(sync); ScratchRegisterScope scratch(masm); @@ -5394,6 +5400,8 @@ static void CompareExchange64(MacroAssembler& masm, SecondScratchRegisterScope scratch2(masm); Register ptr = ComputePointerForAtomic(masm, mem, scratch2); + // NOTE: the generated code must match the assembly code in gen_cmpxchg in + // GenerateAtomicOperations.py masm.memoryBarrierBefore(sync); masm.bind(&again); @@ -6152,6 +6160,8 @@ void MacroAssemblerARM::wasmLoadImpl(const wasm::MemoryAccessDesc& access, type == Scalar::Int32 || type == Scalar::Int64; unsigned byteSize = access.byteSize(); + // NOTE: the generated code must match the assembly code in gen_load in + // GenerateAtomicOperations.py asMasm().memoryBarrierBefore(access.sync()); BufferOffset load; @@ -6267,6 +6277,8 @@ void MacroAssemblerARM::wasmStoreImpl(const wasm::MemoryAccessDesc& access, } } + // NOTE: the generated code must match the assembly code in gen_store in + // GenerateAtomicOperations.py asMasm().memoryBarrierAfter(access.sync()); BufferOffset store; diff --git a/js/src/jit/arm64/CodeGenerator-arm64.cpp b/js/src/jit/arm64/CodeGenerator-arm64.cpp index 1667ba5b925f..920e409f6572 100644 --- a/js/src/jit/arm64/CodeGenerator-arm64.cpp +++ b/js/src/jit/arm64/CodeGenerator-arm64.cpp @@ -1985,6 +1985,8 @@ void CodeGenerator::visitAtomicLoad64(LAtomicLoad64* lir) { Scalar::Type storageType = mir->storageType(); + // NOTE: the generated code must match the assembly code in gen_load in + // GenerateAtomicOperations.py auto sync = Synchronization::Load(); masm.memoryBarrierBefore(sync); @@ -2011,6 +2013,8 @@ void CodeGenerator::visitAtomicStore64(LAtomicStore64* lir) { masm.loadBigInt64(value, temp1); + // NOTE: the generated code must match the assembly code in gen_store in + // GenerateAtomicOperations.py auto sync = Synchronization::Store(); masm.memoryBarrierBefore(sync); diff --git a/js/src/jit/arm64/MacroAssembler-arm64.cpp b/js/src/jit/arm64/MacroAssembler-arm64.cpp index c8c8c75a94fe..7bde4672bf8f 100644 --- a/js/src/jit/arm64/MacroAssembler-arm64.cpp +++ b/js/src/jit/arm64/MacroAssembler-arm64.cpp @@ -474,6 +474,8 @@ void MacroAssemblerCompat::wasmLoadImpl(const wasm::MemoryAccessDesc& access, instructionsExpected++; } + // NOTE: the generated code must match the assembly code in gen_load in + // GenerateAtomicOperations.py asMasm().memoryBarrierBefore(access.sync()); { @@ -625,6 +627,8 @@ void MacroAssemblerCompat::wasmStoreImpl(const wasm::MemoryAccessDesc& access, void MacroAssemblerCompat::wasmStoreImpl(const wasm::MemoryAccessDesc& access, MemOperand dstAddr, AnyRegister valany, Register64 val64) { + // NOTE: the generated code must match the assembly code in gen_store in + // GenerateAtomicOperations.py asMasm().memoryBarrierBefore(access.sync()); { @@ -2334,6 +2338,8 @@ static void CompareExchange(MacroAssembler& masm, MOZ_ASSERT(ptr.base().asUnsized() != output); + // NOTE: the generated code must match the assembly code in gen_cmpxchg in + // GenerateAtomicOperations.py masm.memoryBarrierBefore(sync); Register scratch = temps.AcquireX().asUnsized(); @@ -2365,6 +2371,8 @@ static void AtomicExchange(MacroAssembler& masm, Register scratch2 = temps.AcquireX().asUnsized(); MemOperand ptr = ComputePointerForAtomic(masm, mem, scratch2); + // NOTE: the generated code must match the assembly code in gen_exchange in + // GenerateAtomicOperations.py masm.memoryBarrierBefore(sync); Register scratch = temps.AcquireX().asUnsized(); @@ -2395,6 +2403,8 @@ static void AtomicFetchOp(MacroAssembler& masm, Register scratch2 = temps.AcquireX().asUnsized(); MemOperand ptr = ComputePointerForAtomic(masm, mem, scratch2); + // NOTE: the generated code must match the assembly code in gen_fetchop in + // GenerateAtomicOperations.py masm.memoryBarrierBefore(sync); Register scratch = temps.AcquireX().asUnsized(); diff --git a/js/src/jit/moz.build b/js/src/jit/moz.build index f3e3f1ce68ec..0465ce2a8be0 100644 --- a/js/src/jit/moz.build +++ b/js/src/jit/moz.build @@ -75,6 +75,7 @@ UNIFIED_SOURCES += [ "Safepoints.cpp", "ScalarReplacement.cpp", "shared/Assembler-shared.cpp", + "shared/AtomicOperations-shared-jit.cpp", "shared/CodeGenerator-shared.cpp", "shared/Disassembler-shared.cpp", "shared/Lowering-shared.cpp", @@ -98,7 +99,6 @@ if CONFIG["JS_CODEGEN_NONE"]: UNIFIED_SOURCES += ["none/Trampoline-none.cpp"] elif CONFIG["JS_CODEGEN_X86"] or CONFIG["JS_CODEGEN_X64"]: UNIFIED_SOURCES += [ - "shared/AtomicOperations-shared-jit.cpp", "x86-shared/Architecture-x86-shared.cpp", "x86-shared/Assembler-x86-shared.cpp", "x86-shared/AssemblerBuffer-x86-shared.cpp", @@ -139,7 +139,6 @@ elif CONFIG["JS_CODEGEN_ARM"]: "arm/MacroAssembler-arm.cpp", "arm/MoveEmitter-arm.cpp", "arm/Trampoline-arm.cpp", - "shared/AtomicOperations-shared-jit.cpp", ] if CONFIG["JS_SIMULATOR_ARM"]: UNIFIED_SOURCES += ["arm/Simulator-arm.cpp"] @@ -168,7 +167,6 @@ elif CONFIG["JS_CODEGEN_ARM64"]: "arm64/vixl/MozCpu-vixl.cpp", "arm64/vixl/MozInstructions-vixl.cpp", "arm64/vixl/Utils-vixl.cpp", - "shared/AtomicOperations-shared-jit.cpp", ] vixl_werror_sources = [ "arm64/vixl/Disasm-vixl.cpp", @@ -248,5 +246,12 @@ GeneratedFile( inputs=["CacheIROps.yaml"], ) +GeneratedFile( + "AtomicOperationsGenerated.h", + script="GenerateAtomicOperations.py", + entry_point="generate_atomics_header", + inputs=[], +) + if CONFIG["FUZZING_INTERFACES"] or CONFIG["FUZZING_JS_FUZZILLI"]: include("/tools/fuzzing/libfuzzer-config.mozbuild") diff --git a/js/src/jit/shared/AtomicOperations-feeling-lucky-gcc.h b/js/src/jit/shared/AtomicOperations-feeling-lucky-gcc.h index e063d9f97174..6f762741d2a9 100644 --- a/js/src/jit/shared/AtomicOperations-feeling-lucky-gcc.h +++ b/js/src/jit/shared/AtomicOperations-feeling-lucky-gcc.h @@ -30,11 +30,9 @@ // Explicitly exclude tier-1 platforms. -#if ((defined(__x86_64__) || defined(_M_X64)) && defined(JS_CODEGEN_X64)) || \ - ((defined(__i386__) || defined(_M_IX86)) && defined(JS_CODEGEN_X86)) || \ - (defined(__arm__) && defined(JS_CODEGEN_ARM)) || \ - ((defined(__aarch64__) || defined(_M_ARM64)) && defined(JS_CODEGEN_ARM64)) -# error "Do not use this code on a tier-1 platform when a JIT is available" +#if (defined(__x86_64__) || defined(_M_X64) || defined(__i386__) || \ + defined(_M_IX86) || defined(__arm__) || defined(__aarch64__)) +# error "Do not use on a tier-1 platform where inline assembly is available" #endif #if !(defined(__clang__) || defined(__GNUC__)) @@ -103,15 +101,6 @@ // Try to avoid platform #ifdefs below this point. -inline bool js::jit::AtomicOperations::Initialize() { - // Nothing - return true; -} - -inline void js::jit::AtomicOperations::ShutDown() { - // Nothing -} - // When compiling with Clang on 32-bit linux it will be necessary to link with // -latomic to get the proper 64-bit intrinsics. diff --git a/js/src/jit/shared/AtomicOperations-feeling-lucky-msvc.h b/js/src/jit/shared/AtomicOperations-feeling-lucky-msvc.h deleted file mode 100644 index c1c90474a24f..000000000000 --- a/js/src/jit/shared/AtomicOperations-feeling-lucky-msvc.h +++ /dev/null @@ -1,373 +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 jit_shared_AtomicOperations_feeling_lucky_msvc_h -#define jit_shared_AtomicOperations_feeling_lucky_msvc_h - -#include "mozilla/Assertions.h" -#include "mozilla/Types.h" - -// Explicitly exclude tier-1 platforms. - -#if ((defined(__x86_64__) || defined(_M_X64)) && defined(JS_CODEGEN_X64)) || \ - ((defined(__i386__) || defined(_M_IX86)) && defined(JS_CODEGEN_X86)) || \ - (defined(__arm__) && defined(JS_CODEGEN_ARM)) || \ - ((defined(__aarch64__) || defined(_M_ARM64)) && defined(JS_CODEGEN_ARM64)) -# error "Do not use this code on a tier-1 platform when a JIT is available" -#endif - -#if !defined(_MSC_VER) -# error "This file only for Microsoft Visual C++" -#endif - -// For overall documentation, see jit/AtomicOperations.h. - -// Below, _ReadWriteBarrier is a compiler directive, preventing reordering of -// instructions and reuse of memory values across it in the compiler, but having -// no impact on what the CPU does. - -// Note, here we use MSVC intrinsics directly. But MSVC supports a slightly -// higher level of function which uses the intrinsic when possible (8, 16, and -// 32-bit operations, and 64-bit operations on 64-bit systems) and otherwise -// falls back on CMPXCHG8B for 64-bit operations on 32-bit systems. We could be -// using those functions in many cases here (though not all). I have not done -// so because I don't yet know how far back those functions are supported. - -// Note, _InterlockedCompareExchange takes the *new* value as the second -// argument and the *comparand* (expected old value) as the third argument. - -inline bool js::jit::AtomicOperations::Initialize() { - // Nothing - return true; -} - -inline void js::jit::AtomicOperations::ShutDown() { - // Nothing -} - -inline bool js::jit::AtomicOperations::hasAtomic8() { return true; } - -inline bool js::jit::AtomicOperations::isLockfree8() { - // The MSDN docs suggest very strongly that if code is compiled for Pentium - // or better the 64-bit primitives will be lock-free, see eg the "Remarks" - // secion of the page for _InterlockedCompareExchange64, currently here: - // https://msdn.microsoft.com/en-us/library/ttk2z1ws%28v=vs.85%29.aspx - // - // But I've found no way to assert that at compile time or run time, there - // appears to be no WinAPI is_lock_free() test. - - return true; -} - -inline void js::jit::AtomicOperations::fenceSeqCst() { - _ReadWriteBarrier(); -#if defined(_M_IX86) || defined(_M_X64) - _mm_mfence(); -#elif defined(_M_ARM64) - // MemoryBarrier is defined in winnt.h, which we don't want to include here. - // This expression is the expansion of MemoryBarrier. - __dmb(_ARM64_BARRIER_SY); -#else -# error "Unknown hardware for MSVC" -#endif -} - -template -inline T js::jit::AtomicOperations::loadSeqCst(T* addr) { - _ReadWriteBarrier(); - T v = *addr; - _ReadWriteBarrier(); - return v; -} - -#ifdef _M_IX86 -namespace js { -namespace jit { - -# define MSC_LOADOP(T) \ - template <> \ - inline T AtomicOperations::loadSeqCst(T* addr) { \ - _ReadWriteBarrier(); \ - return (T)_InterlockedCompareExchange64((__int64 volatile*)addr, 0, 0); \ - } - -MSC_LOADOP(int64_t) -MSC_LOADOP(uint64_t) - -# undef MSC_LOADOP - -} // namespace jit -} // namespace js -#endif // _M_IX86 - -template -inline void js::jit::AtomicOperations::storeSeqCst(T* addr, T val) { - _ReadWriteBarrier(); - *addr = val; - fenceSeqCst(); -} - -#ifdef _M_IX86 -namespace js { -namespace jit { - -# define MSC_STOREOP(T) \ - template <> \ - inline void AtomicOperations::storeSeqCst(T* addr, T val) { \ - _ReadWriteBarrier(); \ - T oldval = *addr; \ - for (;;) { \ - T nextval = (T)_InterlockedCompareExchange64( \ - (__int64 volatile*)addr, (__int64)val, (__int64)oldval); \ - if (nextval == oldval) break; \ - oldval = nextval; \ - } \ - _ReadWriteBarrier(); \ - } - -MSC_STOREOP(int64_t) -MSC_STOREOP(uint64_t) - -# undef MSC_STOREOP - -} // namespace jit -} // namespace js -#endif // _M_IX86 - -#define MSC_EXCHANGEOP(T, U, xchgop) \ - template <> \ - inline T AtomicOperations::exchangeSeqCst(T* addr, T val) { \ - return (T)xchgop((U volatile*)addr, (U)val); \ - } - -#ifdef _M_IX86 -# define MSC_EXCHANGEOP_CAS(T) \ - template <> \ - inline T AtomicOperations::exchangeSeqCst(T* addr, T val) { \ - _ReadWriteBarrier(); \ - T oldval = *addr; \ - for (;;) { \ - T nextval = (T)_InterlockedCompareExchange64( \ - (__int64 volatile*)addr, (__int64)val, (__int64)oldval); \ - if (nextval == oldval) break; \ - oldval = nextval; \ - } \ - _ReadWriteBarrier(); \ - return oldval; \ - } -#endif // _M_IX86 - -namespace js { -namespace jit { - -MSC_EXCHANGEOP(int8_t, char, _InterlockedExchange8) -MSC_EXCHANGEOP(uint8_t, char, _InterlockedExchange8) -MSC_EXCHANGEOP(int16_t, short, _InterlockedExchange16) -MSC_EXCHANGEOP(uint16_t, short, _InterlockedExchange16) -MSC_EXCHANGEOP(int32_t, long, _InterlockedExchange) -MSC_EXCHANGEOP(uint32_t, long, _InterlockedExchange) - -#ifdef _M_IX86 -MSC_EXCHANGEOP_CAS(int64_t) -MSC_EXCHANGEOP_CAS(uint64_t) -#else -MSC_EXCHANGEOP(int64_t, __int64, _InterlockedExchange64) -MSC_EXCHANGEOP(uint64_t, __int64, _InterlockedExchange64) -#endif - -} // namespace jit -} // namespace js - -#undef MSC_EXCHANGEOP -#undef MSC_EXCHANGEOP_CAS - -#define MSC_CAS(T, U, cmpxchg) \ - template <> \ - inline T AtomicOperations::compareExchangeSeqCst(T* addr, T oldval, \ - T newval) { \ - return (T)cmpxchg((U volatile*)addr, (U)newval, (U)oldval); \ - } - -namespace js { -namespace jit { - -MSC_CAS(int8_t, char, _InterlockedCompareExchange8) -MSC_CAS(uint8_t, char, _InterlockedCompareExchange8) -MSC_CAS(int16_t, short, _InterlockedCompareExchange16) -MSC_CAS(uint16_t, short, _InterlockedCompareExchange16) -MSC_CAS(int32_t, long, _InterlockedCompareExchange) -MSC_CAS(uint32_t, long, _InterlockedCompareExchange) -MSC_CAS(int64_t, __int64, _InterlockedCompareExchange64) -MSC_CAS(uint64_t, __int64, _InterlockedCompareExchange64) - -} // namespace jit -} // namespace js - -#undef MSC_CAS - -#define MSC_FETCHADDOP(T, U, xadd) \ - template <> \ - inline T AtomicOperations::fetchAddSeqCst(T* addr, T val) { \ - return (T)xadd((U volatile*)addr, (U)val); \ - } - -#define MSC_FETCHSUBOP(T) \ - template <> \ - inline T AtomicOperations::fetchSubSeqCst(T* addr, T val) { \ - return fetchAddSeqCst(addr, (T)(0 - val)); \ - } - -#ifdef _M_IX86 -# define MSC_FETCHADDOP_CAS(T) \ - template <> \ - inline T AtomicOperations::fetchAddSeqCst(T* addr, T val) { \ - _ReadWriteBarrier(); \ - T oldval = *addr; \ - for (;;) { \ - T nextval = (T)_InterlockedCompareExchange64((__int64 volatile*)addr, \ - (__int64)(oldval + val), \ - (__int64)oldval); \ - if (nextval == oldval) break; \ - oldval = nextval; \ - } \ - _ReadWriteBarrier(); \ - return oldval; \ - } -#endif // _M_IX86 - -namespace js { -namespace jit { - -MSC_FETCHADDOP(int8_t, char, _InterlockedExchangeAdd8) -MSC_FETCHADDOP(uint8_t, char, _InterlockedExchangeAdd8) -MSC_FETCHADDOP(int16_t, short, _InterlockedExchangeAdd16) -MSC_FETCHADDOP(uint16_t, short, _InterlockedExchangeAdd16) -MSC_FETCHADDOP(int32_t, long, _InterlockedExchangeAdd) -MSC_FETCHADDOP(uint32_t, long, _InterlockedExchangeAdd) - -#ifdef _M_IX86 -MSC_FETCHADDOP_CAS(int64_t) -MSC_FETCHADDOP_CAS(uint64_t) -#else -MSC_FETCHADDOP(int64_t, __int64, _InterlockedExchangeAdd64) -MSC_FETCHADDOP(uint64_t, __int64, _InterlockedExchangeAdd64) -#endif - -MSC_FETCHSUBOP(int8_t) -MSC_FETCHSUBOP(uint8_t) -MSC_FETCHSUBOP(int16_t) -MSC_FETCHSUBOP(uint16_t) -MSC_FETCHSUBOP(int32_t) -MSC_FETCHSUBOP(uint32_t) -MSC_FETCHSUBOP(int64_t) -MSC_FETCHSUBOP(uint64_t) - -} // namespace jit -} // namespace js - -#undef MSC_FETCHADDOP -#undef MSC_FETCHADDOP_CAS -#undef MSC_FETCHSUBOP - -#define MSC_FETCHBITOPX(T, U, name, op) \ - template <> \ - inline T AtomicOperations::name(T* addr, T val) { \ - return (T)op((U volatile*)addr, (U)val); \ - } - -#define MSC_FETCHBITOP(T, U, andop, orop, xorop) \ - MSC_FETCHBITOPX(T, U, fetchAndSeqCst, andop) \ - MSC_FETCHBITOPX(T, U, fetchOrSeqCst, orop) \ - MSC_FETCHBITOPX(T, U, fetchXorSeqCst, xorop) - -#ifdef _M_IX86 -# define AND_OP & -# define OR_OP | -# define XOR_OP ^ -# define MSC_FETCHBITOPX_CAS(T, name, OP) \ - template <> \ - inline T AtomicOperations::name(T* addr, T val) { \ - _ReadWriteBarrier(); \ - T oldval = *addr; \ - for (;;) { \ - T nextval = (T)_InterlockedCompareExchange64((__int64 volatile*)addr, \ - (__int64)(oldval OP val), \ - (__int64)oldval); \ - if (nextval == oldval) break; \ - oldval = nextval; \ - } \ - _ReadWriteBarrier(); \ - return oldval; \ - } - -# define MSC_FETCHBITOP_CAS(T) \ - MSC_FETCHBITOPX_CAS(T, fetchAndSeqCst, AND_OP) \ - MSC_FETCHBITOPX_CAS(T, fetchOrSeqCst, OR_OP) \ - MSC_FETCHBITOPX_CAS(T, fetchXorSeqCst, XOR_OP) - -#endif - -namespace js { -namespace jit { - -MSC_FETCHBITOP(int8_t, char, _InterlockedAnd8, _InterlockedOr8, - _InterlockedXor8) -MSC_FETCHBITOP(uint8_t, char, _InterlockedAnd8, _InterlockedOr8, - _InterlockedXor8) -MSC_FETCHBITOP(int16_t, short, _InterlockedAnd16, _InterlockedOr16, - _InterlockedXor16) -MSC_FETCHBITOP(uint16_t, short, _InterlockedAnd16, _InterlockedOr16, - _InterlockedXor16) -MSC_FETCHBITOP(int32_t, long, _InterlockedAnd, _InterlockedOr, _InterlockedXor) -MSC_FETCHBITOP(uint32_t, long, _InterlockedAnd, _InterlockedOr, _InterlockedXor) - -#ifdef _M_IX86 -MSC_FETCHBITOP_CAS(int64_t) -MSC_FETCHBITOP_CAS(uint64_t) -#else -MSC_FETCHBITOP(int64_t, __int64, _InterlockedAnd64, _InterlockedOr64, - _InterlockedXor64) -MSC_FETCHBITOP(uint64_t, __int64, _InterlockedAnd64, _InterlockedOr64, - _InterlockedXor64) -#endif - -} // namespace jit -} // namespace js - -#undef MSC_FETCHBITOPX_CAS -#undef MSC_FETCHBITOPX -#undef MSC_FETCHBITOP_CAS -#undef MSC_FETCHBITOP - -template -inline T js::jit::AtomicOperations::loadSafeWhenRacy(T* addr) { - // This is also appropriate for double, int64, and uint64 on 32-bit - // platforms since there are no guarantees of access-atomicity. - return *addr; -} - -template -inline void js::jit::AtomicOperations::storeSafeWhenRacy(T* addr, T val) { - // This is also appropriate for double, int64, and uint64 on 32-bit - // platforms since there are no guarantees of access-atomicity. - *addr = val; -} - -inline void js::jit::AtomicOperations::memcpySafeWhenRacy(void* dest, - const void* src, - size_t nbytes) { - MOZ_ASSERT(!((char*)dest <= (char*)src && (char*)src < (char*)dest + nbytes)); - MOZ_ASSERT(!((char*)src <= (char*)dest && (char*)dest < (char*)src + nbytes)); - ::memcpy(dest, src, nbytes); -} - -inline void js::jit::AtomicOperations::memmoveSafeWhenRacy(void* dest, - const void* src, - size_t nbytes) { - ::memmove(dest, src, nbytes); -} - -#endif // jit_shared_AtomicOperations_feeling_lucky_msvc_h diff --git a/js/src/jit/shared/AtomicOperations-feeling-lucky.h b/js/src/jit/shared/AtomicOperations-feeling-lucky.h index fd510ee49a9c..4aa7883fd4c1 100644 --- a/js/src/jit/shared/AtomicOperations-feeling-lucky.h +++ b/js/src/jit/shared/AtomicOperations-feeling-lucky.h @@ -9,8 +9,6 @@ #if defined(__clang__) || defined(__GNUC__) # include "jit/shared/AtomicOperations-feeling-lucky-gcc.h" -#elif defined(_MSC_VER) -# include "jit/shared/AtomicOperations-feeling-lucky-msvc.h" #else # error "No AtomicOperations support for this platform+compiler combination" #endif diff --git a/js/src/jit/shared/AtomicOperations-shared-jit.cpp b/js/src/jit/shared/AtomicOperations-shared-jit.cpp index 14f60822ac2d..cc760b8ea7dd 100644 --- a/js/src/jit/shared/AtomicOperations-shared-jit.cpp +++ b/js/src/jit/shared/AtomicOperations-shared-jit.cpp @@ -4,156 +4,17 @@ * 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/. */ -#include "mozilla/Atomics.h" - -#ifdef JS_CODEGEN_ARM -# include "jit/arm/Architecture-arm.h" -#endif #include "jit/AtomicOperations.h" -#include "jit/IonTypes.h" -#include "jit/MacroAssembler.h" -#include "jit/RegisterSets.h" -#include "js/ScalarType.h" // js::Scalar::Type -#include "util/Poison.h" -#include "jit/MacroAssembler-inl.h" +#ifdef JS_HAVE_GENERATED_ATOMIC_OPS + +# include + +# include "js/GCAPI.h" using namespace js; using namespace js::jit; -// Assigned registers must follow these rules: -// -// - if they overlap the argument registers (for arguments we use) then they -// -// M M U U SSSS TTTTT -// ====\ MM MM U U S T /==== -// =====> M M M U U SSS T <===== -// ====/ M M U U S T \==== -// M M UUU SSSS T -// -// require no register movement, even for 64-bit registers. (If this becomes -// too complex to handle then we need to create an abstraction that uses the -// MoveResolver, see comments on bug 1394420.) -// -// - they should be volatile when possible so that we don't have to save and -// restore them. -// -// Note that the functions we're generating have a very limited number of -// signatures, and the register assignments need only work for these signatures. -// The signatures are these: -// -// () -// (ptr) -// (ptr, val/val64) -// (ptr, ptr) -// (ptr, val/val64, val/val64) -// -// It would be nice to avoid saving and restoring all the nonvolatile registers -// for all the operations, and instead save and restore only the registers used -// by each specific operation, but the amount of protocol needed to accomplish -// that probably does not pay for itself. - -#if defined(JS_CODEGEN_X64) - -// Selected registers match the argument registers exactly, and none of them -// overlap the result register. - -static const LiveRegisterSet AtomicNonVolatileRegs; - -static constexpr Register AtomicPtrReg = IntArgReg0; -static constexpr Register AtomicPtr2Reg = IntArgReg1; -static constexpr Register AtomicValReg = IntArgReg1; -static constexpr Register64 AtomicValReg64(IntArgReg1); -static constexpr Register AtomicVal2Reg = IntArgReg2; -static constexpr Register64 AtomicVal2Reg64(IntArgReg2); -static constexpr Register AtomicTemp = IntArgReg3; -static constexpr Register64 AtomicTemp64(IntArgReg3); - -static constexpr Register64 AtomicReturnReg64 = ReturnReg64; - -#elif defined(JS_CODEGEN_ARM64) - -// Selected registers match the argument registers, except that the Ptr is not -// in IntArgReg0 so as not to conflict with the result register. - -static const LiveRegisterSet AtomicNonVolatileRegs; - -static constexpr Register AtomicPtrReg = IntArgReg4; -static constexpr Register AtomicPtr2Reg = IntArgReg1; -static constexpr Register AtomicValReg = IntArgReg1; -static constexpr Register64 AtomicValReg64(IntArgReg1); -static constexpr Register AtomicVal2Reg = IntArgReg2; -static constexpr Register64 AtomicVal2Reg64(IntArgReg2); -static constexpr Register AtomicTemp = IntArgReg3; -static constexpr Register64 AtomicTemp64(IntArgReg3); - -static constexpr Register64 AtomicReturnReg64 = ReturnReg64; - -#elif defined(JS_CODEGEN_ARM) - -// Assigned registers except temp are disjoint from the argument registers, -// since accounting for both 32-bit and 64-bit arguments and constraints on the -// result register is much too messy. The temp is in an argument register since -// it won't be used until we've moved all arguments to other registers. -// -// Save LR because it's the second scratch register. The first scratch register -// is r12 (IP). The atomics implementation in the MacroAssembler uses both. - -static const LiveRegisterSet AtomicNonVolatileRegs = LiveRegisterSet( - GeneralRegisterSet( - (uint32_t(1) << Registers::r4) | (uint32_t(1) << Registers::r5) | - (uint32_t(1) << Registers::r6) | (uint32_t(1) << Registers::r7) | - (uint32_t(1) << Registers::r8) | (uint32_t(1) << Registers::lr)), - FloatRegisterSet(0)); - -static constexpr Register AtomicPtrReg = r8; -static constexpr Register AtomicPtr2Reg = r6; -static constexpr Register AtomicTemp = r3; -static constexpr Register AtomicValReg = r6; -static constexpr Register64 AtomicValReg64(r7, r6); -static constexpr Register AtomicVal2Reg = r4; -static constexpr Register64 AtomicVal2Reg64(r5, r4); - -static constexpr Register64 AtomicReturnReg64 = ReturnReg64; - -#elif defined(JS_CODEGEN_X86) - -// There are no argument registers. - -static const LiveRegisterSet AtomicNonVolatileRegs = LiveRegisterSet( - GeneralRegisterSet((1 << X86Encoding::rbx) | (1 << X86Encoding::rsi)), - FloatRegisterSet(0)); - -static constexpr Register AtomicPtrReg = esi; -static constexpr Register AtomicPtr2Reg = ebx; -static constexpr Register AtomicValReg = ebx; -static constexpr Register AtomicVal2Reg = ecx; -static constexpr Register AtomicTemp = edx; - -// 64-bit registers for cmpxchg8b. ValReg/Val2Reg/Temp are not used in this -// case. - -static constexpr Register64 AtomicValReg64(edx, eax); -static constexpr Register64 AtomicVal2Reg64(ecx, ebx); - -// AtomicReturnReg64 is unused on x86. - -#else -# error "Unsupported platform" -#endif - -// These are useful shorthands and hide the meaningless uint/int distinction. - -static constexpr Scalar::Type SIZE8 = Scalar::Uint8; -static constexpr Scalar::Type SIZE16 = Scalar::Uint16; -static constexpr Scalar::Type SIZE32 = Scalar::Uint32; -static constexpr Scalar::Type SIZE64 = Scalar::Int64; -#ifdef JS_64BIT -static constexpr Scalar::Type SIZEWORD = SIZE64; -#else -static constexpr Scalar::Type SIZEWORD = SIZE32; -#endif - // A "block" is a sequence of bytes that is a reasonable quantum to copy to // amortize call overhead when implementing memcpy and memmove. A block will // not fit in registers on all platforms and copying it without using @@ -166,489 +27,49 @@ static constexpr Scalar::Type SIZEWORD = SIZE32; // Blocks and words can be aligned or unaligned; specific (generated) copying // functions handle this in platform-specific ways. -static constexpr size_t WORDSIZE = - sizeof(uintptr_t); // Also see SIZEWORD above +static constexpr size_t WORDSIZE = sizeof(uintptr_t); static constexpr size_t BLOCKSIZE = 8 * WORDSIZE; // Must be a power of 2 static_assert(BLOCKSIZE % WORDSIZE == 0, "A block is an integral number of words"); +// Constants must match the ones in GenerateAtomicOperations.py +static_assert(JS_GENERATED_ATOMICS_BLOCKSIZE == BLOCKSIZE); +static_assert(JS_GENERATED_ATOMICS_WORDSIZE == WORDSIZE); + static constexpr size_t WORDMASK = WORDSIZE - 1; static constexpr size_t BLOCKMASK = BLOCKSIZE - 1; -struct ArgIterator { - ABIArgGenerator abi; - unsigned argBase = 0; -}; - -static void GenGprArg(MacroAssembler& masm, MIRType t, ArgIterator* iter, - Register reg) { - MOZ_ASSERT(t == MIRType::Pointer || t == MIRType::Int32); - ABIArg arg = iter->abi.next(t); - switch (arg.kind()) { - case ABIArg::GPR: { - if (arg.gpr() != reg) { - masm.movePtr(arg.gpr(), reg); - } - break; - } - case ABIArg::Stack: { - Address src(masm.getStackPointer(), - iter->argBase + arg.offsetFromArgBase()); - masm.loadPtr(src, reg); - break; - } - default: { - MOZ_CRASH("Not possible"); - } - } -} - -static void GenGpr64Arg(MacroAssembler& masm, ArgIterator* iter, - Register64 reg) { - ABIArg arg = iter->abi.next(MIRType::Int64); - switch (arg.kind()) { - case ABIArg::GPR: { - if (arg.gpr64() != reg) { - masm.move64(arg.gpr64(), reg); - } - break; - } - case ABIArg::Stack: { - Address src(masm.getStackPointer(), - iter->argBase + arg.offsetFromArgBase()); -#ifdef JS_64BIT - masm.load64(src, reg); -#else - masm.load32(LowWord(src), reg.low); - masm.load32(HighWord(src), reg.high); -#endif - break; - } -#if defined(JS_CODEGEN_REGISTER_PAIR) - case ABIArg::GPR_PAIR: { - if (arg.gpr64() != reg) { - masm.move32(arg.oddGpr(), reg.high); - masm.move32(arg.evenGpr(), reg.low); - } - break; - } -#endif - default: { - MOZ_CRASH("Not possible"); - } - } -} - -static uint32_t GenPrologue(MacroAssembler& masm, ArgIterator* iter) { - masm.assumeUnreachable("Shouldn't get here"); - masm.flushBuffer(); - masm.haltingAlign(CodeAlignment); - masm.setFramePushed(0); - uint32_t start = masm.currentOffset(); - masm.PushRegsInMask(AtomicNonVolatileRegs); -#if defined(JS_CODEGEN_ARM) || defined(JS_CODEGEN_ARM64) - // The return address is among the nonvolatile registers, if pushed at all. - iter->argBase = masm.framePushed(); -#elif defined(JS_CODEGEN_X86) || defined(JS_CODEGEN_X64) - // The return address is pushed separately. - iter->argBase = sizeof(void*) + masm.framePushed(); -#else -# error "Unsupported platform" -#endif - return start; -} - -static void GenEpilogue(MacroAssembler& masm) { - masm.PopRegsInMask(AtomicNonVolatileRegs); - MOZ_ASSERT(masm.framePushed() == 0); -#if defined(JS_CODEGEN_ARM64) - masm.Ret(); -#elif defined(JS_CODEGEN_ARM) - masm.mov(lr, pc); -#elif defined(JS_CODEGEN_X86) || defined(JS_CODEGEN_X64) - masm.ret(); -#endif -} - -#ifndef JS_64BIT -static uint32_t GenNop(MacroAssembler& masm) { - ArgIterator iter; - uint32_t start = GenPrologue(masm, &iter); - GenEpilogue(masm); - return start; -} -#endif - -static uint32_t GenFenceSeqCst(MacroAssembler& masm) { - ArgIterator iter; - uint32_t start = GenPrologue(masm, &iter); - masm.memoryBarrier(MembarFull); - GenEpilogue(masm); - return start; -} - -static uint32_t GenLoad(MacroAssembler& masm, Scalar::Type size, - Synchronization sync) { - ArgIterator iter; - uint32_t start = GenPrologue(masm, &iter); - GenGprArg(masm, MIRType::Pointer, &iter, AtomicPtrReg); - - masm.memoryBarrier(sync.barrierBefore); - Address addr(AtomicPtrReg, 0); - switch (size) { - case SIZE8: - masm.load8ZeroExtend(addr, ReturnReg); - break; - case SIZE16: - masm.load16ZeroExtend(addr, ReturnReg); - break; - case SIZE32: - masm.load32(addr, ReturnReg); - break; - case SIZE64: -#if defined(JS_64BIT) - masm.load64(addr, AtomicReturnReg64); - break; -#else - MOZ_CRASH("64-bit atomic load not available on this platform"); -#endif - default: - MOZ_CRASH("Unknown size"); - } - masm.memoryBarrier(sync.barrierAfter); - - GenEpilogue(masm); - return start; -} - -static uint32_t GenStore(MacroAssembler& masm, Scalar::Type size, - Synchronization sync) { - ArgIterator iter; - uint32_t start = GenPrologue(masm, &iter); - GenGprArg(masm, MIRType::Pointer, &iter, AtomicPtrReg); - - masm.memoryBarrier(sync.barrierBefore); - Address addr(AtomicPtrReg, 0); - switch (size) { - case SIZE8: - GenGprArg(masm, MIRType::Int32, &iter, AtomicValReg); - masm.store8(AtomicValReg, addr); - break; - case SIZE16: - GenGprArg(masm, MIRType::Int32, &iter, AtomicValReg); - masm.store16(AtomicValReg, addr); - break; - case SIZE32: - GenGprArg(masm, MIRType::Int32, &iter, AtomicValReg); - masm.store32(AtomicValReg, addr); - break; - case SIZE64: -#if defined(JS_64BIT) - GenGpr64Arg(masm, &iter, AtomicValReg64); - masm.store64(AtomicValReg64, addr); - break; -#else - MOZ_CRASH("64-bit atomic store not available on this platform"); -#endif - default: - MOZ_CRASH("Unknown size"); - } - masm.memoryBarrier(sync.barrierAfter); - - GenEpilogue(masm); - return start; -} - -enum class CopyDir { - DOWN, // Move data down, ie, iterate toward higher addresses - UP // The other way -}; - -static uint32_t GenCopy(MacroAssembler& masm, Scalar::Type size, - uint32_t unroll, CopyDir direction) { - ArgIterator iter; - uint32_t start = GenPrologue(masm, &iter); - - Register dest = AtomicPtrReg; - Register src = AtomicPtr2Reg; - - GenGprArg(masm, MIRType::Pointer, &iter, dest); - GenGprArg(masm, MIRType::Pointer, &iter, src); - - uint32_t offset = direction == CopyDir::DOWN ? 0 : unroll - 1; - for (uint32_t i = 0; i < unroll; i++) { - switch (size) { - case SIZE8: - masm.load8ZeroExtend(Address(src, offset), AtomicTemp); - masm.store8(AtomicTemp, Address(dest, offset)); - break; - case SIZE16: - masm.load16ZeroExtend(Address(src, offset * 2), AtomicTemp); - masm.store16(AtomicTemp, Address(dest, offset * 2)); - break; - case SIZE32: - masm.load32(Address(src, offset * 4), AtomicTemp); - masm.store32(AtomicTemp, Address(dest, offset * 4)); - break; - case SIZE64: -#if defined(JS_64BIT) - masm.load64(Address(src, offset * 8), AtomicTemp64); - masm.store64(AtomicTemp64, Address(dest, offset * 8)); - break; -#else - MOZ_CRASH("64-bit atomic load/store not available on this platform"); -#endif - default: - MOZ_CRASH("Unknown size"); - } - offset += direction == CopyDir::DOWN ? 1 : -1; - } - - GenEpilogue(masm); - return start; -} - -static uint32_t GenCmpxchg(MacroAssembler& masm, Scalar::Type size, - Synchronization sync) { - ArgIterator iter; - uint32_t start = GenPrologue(masm, &iter); - GenGprArg(masm, MIRType::Pointer, &iter, AtomicPtrReg); - - Address addr(AtomicPtrReg, 0); - switch (size) { - case SIZE8: - case SIZE16: - case SIZE32: - GenGprArg(masm, MIRType::Int32, &iter, AtomicValReg); - GenGprArg(masm, MIRType::Int32, &iter, AtomicVal2Reg); - masm.compareExchange(size, sync, addr, AtomicValReg, AtomicVal2Reg, - ReturnReg); - break; - case SIZE64: - GenGpr64Arg(masm, &iter, AtomicValReg64); - GenGpr64Arg(masm, &iter, AtomicVal2Reg64); -#if defined(JS_CODEGEN_X86) - static_assert(AtomicValReg64 == Register64(edx, eax)); - static_assert(AtomicVal2Reg64 == Register64(ecx, ebx)); - - // The return register edx:eax is a compiler/ABI assumption that is not - // necessarily the same as ReturnReg64, so it's correct not to use - // ReturnReg64 here. - masm.lock_cmpxchg8b(edx, eax, ecx, ebx, Operand(addr)); -#else - masm.compareExchange64(sync, addr, AtomicValReg64, AtomicVal2Reg64, - AtomicReturnReg64); -#endif - break; - default: - MOZ_CRASH("Unknown size"); - } - - GenEpilogue(masm); - return start; -} - -static uint32_t GenExchange(MacroAssembler& masm, Scalar::Type size, - Synchronization sync) { - ArgIterator iter; - uint32_t start = GenPrologue(masm, &iter); - GenGprArg(masm, MIRType::Pointer, &iter, AtomicPtrReg); - - Address addr(AtomicPtrReg, 0); - switch (size) { - case SIZE8: - case SIZE16: - case SIZE32: - GenGprArg(masm, MIRType::Int32, &iter, AtomicValReg); - masm.atomicExchange(size, sync, addr, AtomicValReg, ReturnReg); - break; - case SIZE64: -#if defined(JS_64BIT) - GenGpr64Arg(masm, &iter, AtomicValReg64); - masm.atomicExchange64(sync, addr, AtomicValReg64, AtomicReturnReg64); - break; -#else - MOZ_CRASH("64-bit atomic exchange not available on this platform"); -#endif - default: - MOZ_CRASH("Unknown size"); - } - - GenEpilogue(masm); - return start; -} - -static uint32_t GenFetchOp(MacroAssembler& masm, Scalar::Type size, AtomicOp op, - Synchronization sync) { - ArgIterator iter; - uint32_t start = GenPrologue(masm, &iter); - GenGprArg(masm, MIRType::Pointer, &iter, AtomicPtrReg); - - Address addr(AtomicPtrReg, 0); - switch (size) { - case SIZE8: - case SIZE16: - case SIZE32: { -#if defined(JS_CODEGEN_X86) || defined(JS_CODEGEN_X64) - Register tmp = op == AtomicFetchAddOp || op == AtomicFetchSubOp - ? Register::Invalid() - : AtomicTemp; -#else - Register tmp = AtomicTemp; -#endif - GenGprArg(masm, MIRType::Int32, &iter, AtomicValReg); - masm.atomicFetchOp(size, sync, op, AtomicValReg, addr, tmp, ReturnReg); - break; - } - case SIZE64: { -#if defined(JS_64BIT) -# if defined(JS_CODEGEN_X64) - Register64 tmp = op == AtomicFetchAddOp || op == AtomicFetchSubOp - ? Register64::Invalid() - : AtomicTemp64; -# else - Register64 tmp = AtomicTemp64; -# endif - GenGpr64Arg(masm, &iter, AtomicValReg64); - masm.atomicFetchOp64(sync, op, AtomicValReg64, addr, tmp, - AtomicReturnReg64); - break; -#else - MOZ_CRASH("64-bit atomic fetchOp not available on this platform"); -#endif - } - default: - MOZ_CRASH("Unknown size"); - } - - GenEpilogue(masm); - return start; -} - namespace js { namespace jit { -void (*AtomicFenceSeqCst)(); - -#ifndef JS_64BIT -void (*AtomicCompilerFence)(); -#endif - -uint8_t (*AtomicLoad8SeqCst)(const uint8_t* addr); -uint16_t (*AtomicLoad16SeqCst)(const uint16_t* addr); -uint32_t (*AtomicLoad32SeqCst)(const uint32_t* addr); -#ifdef JS_64BIT -uint64_t (*AtomicLoad64SeqCst)(const uint64_t* addr); -#endif - -uint8_t (*AtomicLoad8Unsynchronized)(const uint8_t* addr); -uint16_t (*AtomicLoad16Unsynchronized)(const uint16_t* addr); -uint32_t (*AtomicLoad32Unsynchronized)(const uint32_t* addr); -#ifdef JS_64BIT -uint64_t (*AtomicLoad64Unsynchronized)(const uint64_t* addr); -#endif - -uint8_t (*AtomicStore8SeqCst)(uint8_t* addr, uint8_t val); -uint16_t (*AtomicStore16SeqCst)(uint16_t* addr, uint16_t val); -uint32_t (*AtomicStore32SeqCst)(uint32_t* addr, uint32_t val); -#ifdef JS_64BIT -uint64_t (*AtomicStore64SeqCst)(uint64_t* addr, uint64_t val); -#endif - -uint8_t (*AtomicStore8Unsynchronized)(uint8_t* addr, uint8_t val); -uint16_t (*AtomicStore16Unsynchronized)(uint16_t* addr, uint16_t val); -uint32_t (*AtomicStore32Unsynchronized)(uint32_t* addr, uint32_t val); -#ifdef JS_64BIT -uint64_t (*AtomicStore64Unsynchronized)(uint64_t* addr, uint64_t val); -#endif - -// See the definitions of BLOCKSIZE and WORDSIZE earlier. The "unaligned" -// functions perform individual byte copies (and must always be "down" or "up"). -// The others ignore alignment issues, and thus either depend on unaligned -// accesses being OK or not being invoked on unaligned addresses. -// -// src and dest point to the lower addresses of the respective data areas -// irrespective of "up" or "down". - -static void (*AtomicCopyUnalignedBlockDownUnsynchronized)(uint8_t* dest, - const uint8_t* src); -static void (*AtomicCopyUnalignedBlockUpUnsynchronized)(uint8_t* dest, - const uint8_t* src); -static void (*AtomicCopyUnalignedWordDownUnsynchronized)(uint8_t* dest, - const uint8_t* src); -static void (*AtomicCopyUnalignedWordUpUnsynchronized)(uint8_t* dest, - const uint8_t* src); - -static void (*AtomicCopyBlockDownUnsynchronized)(uint8_t* dest, - const uint8_t* src); -static void (*AtomicCopyBlockUpUnsynchronized)(uint8_t* dest, - const uint8_t* src); -static void (*AtomicCopyWordUnsynchronized)(uint8_t* dest, const uint8_t* src); -static void (*AtomicCopyByteUnsynchronized)(uint8_t* dest, const uint8_t* src); - -uint8_t (*AtomicCmpXchg8SeqCst)(uint8_t* addr, uint8_t oldval, uint8_t newval); -uint16_t (*AtomicCmpXchg16SeqCst)(uint16_t* addr, uint16_t oldval, - uint16_t newval); -uint32_t (*AtomicCmpXchg32SeqCst)(uint32_t* addr, uint32_t oldval, - uint32_t newval); -uint64_t (*AtomicCmpXchg64SeqCst)(uint64_t* addr, uint64_t oldval, - uint64_t newval); - -uint8_t (*AtomicExchange8SeqCst)(uint8_t* addr, uint8_t val); -uint16_t (*AtomicExchange16SeqCst)(uint16_t* addr, uint16_t val); -uint32_t (*AtomicExchange32SeqCst)(uint32_t* addr, uint32_t val); -#ifdef JS_64BIT -uint64_t (*AtomicExchange64SeqCst)(uint64_t* addr, uint64_t val); -#endif - -uint8_t (*AtomicAdd8SeqCst)(uint8_t* addr, uint8_t val); -uint16_t (*AtomicAdd16SeqCst)(uint16_t* addr, uint16_t val); -uint32_t (*AtomicAdd32SeqCst)(uint32_t* addr, uint32_t val); -#ifdef JS_64BIT -uint64_t (*AtomicAdd64SeqCst)(uint64_t* addr, uint64_t val); -#endif - -uint8_t (*AtomicAnd8SeqCst)(uint8_t* addr, uint8_t val); -uint16_t (*AtomicAnd16SeqCst)(uint16_t* addr, uint16_t val); -uint32_t (*AtomicAnd32SeqCst)(uint32_t* addr, uint32_t val); -#ifdef JS_64BIT -uint64_t (*AtomicAnd64SeqCst)(uint64_t* addr, uint64_t val); -#endif - -uint8_t (*AtomicOr8SeqCst)(uint8_t* addr, uint8_t val); -uint16_t (*AtomicOr16SeqCst)(uint16_t* addr, uint16_t val); -uint32_t (*AtomicOr32SeqCst)(uint32_t* addr, uint32_t val); -#ifdef JS_64BIT -uint64_t (*AtomicOr64SeqCst)(uint64_t* addr, uint64_t val); -#endif - -uint8_t (*AtomicXor8SeqCst)(uint8_t* addr, uint8_t val); -uint16_t (*AtomicXor16SeqCst)(uint16_t* addr, uint16_t val); -uint32_t (*AtomicXor32SeqCst)(uint32_t* addr, uint32_t val); -#ifdef JS_64BIT -uint64_t (*AtomicXor64SeqCst)(uint64_t* addr, uint64_t val); -#endif - static bool UnalignedAccessesAreOK() { -#ifdef DEBUG +# ifdef DEBUG const char* flag = getenv("JS_NO_UNALIGNED_MEMCPY"); if (flag && *flag == '1') return false; -#endif -#if defined(JS_CODEGEN_X86) || defined(JS_CODEGEN_X64) +# endif +# if defined(__x86_64__) || defined(__i386__) return true; -#elif defined(JS_CODEGEN_ARM) +# elif defined(__arm__) return !HasAlignmentFault(); -#elif defined(JS_CODEGEN_ARM64) +# elif defined(__aarch64__) // This is not necessarily true but it's the best guess right now. return true; -#else -# error "Unsupported platform" -#endif +# else +# error "Unsupported platform" +# endif } +# ifndef JS_64BIT +void AtomicCompilerFence() { + std::atomic_signal_fence(std::memory_order_acq_rel); +} +# endif + void AtomicMemcpyDownUnsynchronized(uint8_t* dest, const uint8_t* src, size_t nbytes) { + JS::AutoSuppressGCAnalysis nogc; + const uint8_t* lim = src + nbytes; // Set up bulk copying. The cases are ordered the way they are on the @@ -702,6 +123,8 @@ void AtomicMemcpyDownUnsynchronized(uint8_t* dest, const uint8_t* src, void AtomicMemcpyUpUnsynchronized(uint8_t* dest, const uint8_t* src, size_t nbytes) { + JS::AutoSuppressGCAnalysis nogc; + const uint8_t* lim = src; src += nbytes; @@ -747,293 +170,7 @@ void AtomicMemcpyUpUnsynchronized(uint8_t* dest, const uint8_t* src, } } -// These will be read and written only by the main thread during startup and -// shutdown. - -static uint8_t* codeSegment; -static uint32_t codeSegmentSize; - -bool InitializeJittedAtomics() { - // We should only initialize once. - MOZ_ASSERT(!codeSegment); - - LifoAlloc lifo(4096); - TempAllocator alloc(&lifo); - JitContext jcx(&alloc); - StackMacroAssembler masm; - AutoCreatedBy acb(masm, "InitializeJittedAtomics"); - - uint32_t fenceSeqCst = GenFenceSeqCst(masm); - -#ifndef JS_64BIT - uint32_t nop = GenNop(masm); -#endif - - Synchronization Full = Synchronization::Full(); - Synchronization None = Synchronization::None(); - - uint32_t load8SeqCst = GenLoad(masm, SIZE8, Full); - uint32_t load16SeqCst = GenLoad(masm, SIZE16, Full); - uint32_t load32SeqCst = GenLoad(masm, SIZE32, Full); -#ifdef JS_64BIT - uint32_t load64SeqCst = GenLoad(masm, SIZE64, Full); -#endif - - uint32_t load8Unsynchronized = GenLoad(masm, SIZE8, None); - uint32_t load16Unsynchronized = GenLoad(masm, SIZE16, None); - uint32_t load32Unsynchronized = GenLoad(masm, SIZE32, None); -#ifdef JS_64BIT - uint32_t load64Unsynchronized = GenLoad(masm, SIZE64, None); -#endif - - uint32_t store8SeqCst = GenStore(masm, SIZE8, Full); - uint32_t store16SeqCst = GenStore(masm, SIZE16, Full); - uint32_t store32SeqCst = GenStore(masm, SIZE32, Full); -#ifdef JS_64BIT - uint32_t store64SeqCst = GenStore(masm, SIZE64, Full); -#endif - - uint32_t store8Unsynchronized = GenStore(masm, SIZE8, None); - uint32_t store16Unsynchronized = GenStore(masm, SIZE16, None); - uint32_t store32Unsynchronized = GenStore(masm, SIZE32, None); -#ifdef JS_64BIT - uint32_t store64Unsynchronized = GenStore(masm, SIZE64, None); -#endif - - uint32_t copyUnalignedBlockDownUnsynchronized = - GenCopy(masm, SIZE8, BLOCKSIZE, CopyDir::DOWN); - uint32_t copyUnalignedBlockUpUnsynchronized = - GenCopy(masm, SIZE8, BLOCKSIZE, CopyDir::UP); - uint32_t copyUnalignedWordDownUnsynchronized = - GenCopy(masm, SIZE8, WORDSIZE, CopyDir::DOWN); - uint32_t copyUnalignedWordUpUnsynchronized = - GenCopy(masm, SIZE8, WORDSIZE, CopyDir::UP); - - uint32_t copyBlockDownUnsynchronized = - GenCopy(masm, SIZEWORD, BLOCKSIZE / WORDSIZE, CopyDir::DOWN); - uint32_t copyBlockUpUnsynchronized = - GenCopy(masm, SIZEWORD, BLOCKSIZE / WORDSIZE, CopyDir::UP); - uint32_t copyWordUnsynchronized = GenCopy(masm, SIZEWORD, 1, CopyDir::DOWN); - uint32_t copyByteUnsynchronized = GenCopy(masm, SIZE8, 1, CopyDir::DOWN); - - uint32_t cmpxchg8SeqCst = GenCmpxchg(masm, SIZE8, Full); - uint32_t cmpxchg16SeqCst = GenCmpxchg(masm, SIZE16, Full); - uint32_t cmpxchg32SeqCst = GenCmpxchg(masm, SIZE32, Full); - uint32_t cmpxchg64SeqCst = GenCmpxchg(masm, SIZE64, Full); - - uint32_t exchange8SeqCst = GenExchange(masm, SIZE8, Full); - uint32_t exchange16SeqCst = GenExchange(masm, SIZE16, Full); - uint32_t exchange32SeqCst = GenExchange(masm, SIZE32, Full); -#ifdef JS_64BIT - uint32_t exchange64SeqCst = GenExchange(masm, SIZE64, Full); -#endif - - uint32_t add8SeqCst = GenFetchOp(masm, SIZE8, AtomicFetchAddOp, Full); - uint32_t add16SeqCst = GenFetchOp(masm, SIZE16, AtomicFetchAddOp, Full); - uint32_t add32SeqCst = GenFetchOp(masm, SIZE32, AtomicFetchAddOp, Full); -#ifdef JS_64BIT - uint32_t add64SeqCst = GenFetchOp(masm, SIZE64, AtomicFetchAddOp, Full); -#endif - - uint32_t and8SeqCst = GenFetchOp(masm, SIZE8, AtomicFetchAndOp, Full); - uint32_t and16SeqCst = GenFetchOp(masm, SIZE16, AtomicFetchAndOp, Full); - uint32_t and32SeqCst = GenFetchOp(masm, SIZE32, AtomicFetchAndOp, Full); -#ifdef JS_64BIT - uint32_t and64SeqCst = GenFetchOp(masm, SIZE64, AtomicFetchAndOp, Full); -#endif - - uint32_t or8SeqCst = GenFetchOp(masm, SIZE8, AtomicFetchOrOp, Full); - uint32_t or16SeqCst = GenFetchOp(masm, SIZE16, AtomicFetchOrOp, Full); - uint32_t or32SeqCst = GenFetchOp(masm, SIZE32, AtomicFetchOrOp, Full); -#ifdef JS_64BIT - uint32_t or64SeqCst = GenFetchOp(masm, SIZE64, AtomicFetchOrOp, Full); -#endif - - uint32_t xor8SeqCst = GenFetchOp(masm, SIZE8, AtomicFetchXorOp, Full); - uint32_t xor16SeqCst = GenFetchOp(masm, SIZE16, AtomicFetchXorOp, Full); - uint32_t xor32SeqCst = GenFetchOp(masm, SIZE32, AtomicFetchXorOp, Full); -#ifdef JS_64BIT - uint32_t xor64SeqCst = GenFetchOp(masm, SIZE64, AtomicFetchXorOp, Full); -#endif - - masm.finish(); - if (masm.oom()) { - return false; - } - - // Allocate executable memory. - uint32_t codeLength = masm.bytesNeeded(); - size_t roundedCodeLength = RoundUp(codeLength, ExecutableCodePageSize); - uint8_t* code = (uint8_t*)AllocateExecutableMemory( - roundedCodeLength, ProtectionSetting::Writable, - MemCheckKind::MakeUndefined); - if (!code) { - return false; - } - - // Zero the padding. - memset(code + codeLength, 0, roundedCodeLength - codeLength); - - // Copy the code into place. - masm.executableCopy(code); - - // Reprotect the whole region to avoid having separate RW and RX mappings. - if (!ExecutableAllocator::makeExecutableAndFlushICache( - FlushICacheSpec::LocalThreadOnly, code, roundedCodeLength)) { - DeallocateExecutableMemory(code, roundedCodeLength); - return false; - } - - // Create the function pointers. - - AtomicFenceSeqCst = (void (*)())(code + fenceSeqCst); - -#ifndef JS_64BIT - AtomicCompilerFence = (void (*)())(code + nop); -#endif - - AtomicLoad8SeqCst = (uint8_t(*)(const uint8_t* addr))(code + load8SeqCst); - AtomicLoad16SeqCst = (uint16_t(*)(const uint16_t* addr))(code + load16SeqCst); - AtomicLoad32SeqCst = (uint32_t(*)(const uint32_t* addr))(code + load32SeqCst); -#ifdef JS_64BIT - AtomicLoad64SeqCst = (uint64_t(*)(const uint64_t* addr))(code + load64SeqCst); -#endif - - AtomicLoad8Unsynchronized = - (uint8_t(*)(const uint8_t* addr))(code + load8Unsynchronized); - AtomicLoad16Unsynchronized = - (uint16_t(*)(const uint16_t* addr))(code + load16Unsynchronized); - AtomicLoad32Unsynchronized = - (uint32_t(*)(const uint32_t* addr))(code + load32Unsynchronized); -#ifdef JS_64BIT - AtomicLoad64Unsynchronized = - (uint64_t(*)(const uint64_t* addr))(code + load64Unsynchronized); -#endif - - AtomicStore8SeqCst = - (uint8_t(*)(uint8_t * addr, uint8_t val))(code + store8SeqCst); - AtomicStore16SeqCst = - (uint16_t(*)(uint16_t * addr, uint16_t val))(code + store16SeqCst); - AtomicStore32SeqCst = - (uint32_t(*)(uint32_t * addr, uint32_t val))(code + store32SeqCst); -#ifdef JS_64BIT - AtomicStore64SeqCst = - (uint64_t(*)(uint64_t * addr, uint64_t val))(code + store64SeqCst); -#endif - - AtomicStore8Unsynchronized = - (uint8_t(*)(uint8_t * addr, uint8_t val))(code + store8Unsynchronized); - AtomicStore16Unsynchronized = (uint16_t(*)(uint16_t * addr, uint16_t val))( - code + store16Unsynchronized); - AtomicStore32Unsynchronized = (uint32_t(*)(uint32_t * addr, uint32_t val))( - code + store32Unsynchronized); -#ifdef JS_64BIT - AtomicStore64Unsynchronized = (uint64_t(*)(uint64_t * addr, uint64_t val))( - code + store64Unsynchronized); -#endif - - AtomicCopyUnalignedBlockDownUnsynchronized = - (void (*)(uint8_t * dest, const uint8_t* src))( - code + copyUnalignedBlockDownUnsynchronized); - AtomicCopyUnalignedBlockUpUnsynchronized = - (void (*)(uint8_t * dest, const uint8_t* src))( - code + copyUnalignedBlockUpUnsynchronized); - AtomicCopyUnalignedWordDownUnsynchronized = - (void (*)(uint8_t * dest, const uint8_t* src))( - code + copyUnalignedWordDownUnsynchronized); - AtomicCopyUnalignedWordUpUnsynchronized = - (void (*)(uint8_t * dest, const uint8_t* src))( - code + copyUnalignedWordUpUnsynchronized); - - AtomicCopyBlockDownUnsynchronized = (void (*)( - uint8_t * dest, const uint8_t* src))(code + copyBlockDownUnsynchronized); - AtomicCopyBlockUpUnsynchronized = (void (*)( - uint8_t * dest, const uint8_t* src))(code + copyBlockUpUnsynchronized); - AtomicCopyWordUnsynchronized = (void (*)(uint8_t * dest, const uint8_t* src))( - code + copyWordUnsynchronized); - AtomicCopyByteUnsynchronized = (void (*)(uint8_t * dest, const uint8_t* src))( - code + copyByteUnsynchronized); - - AtomicCmpXchg8SeqCst = (uint8_t(*)(uint8_t * addr, uint8_t oldval, - uint8_t newval))(code + cmpxchg8SeqCst); - AtomicCmpXchg16SeqCst = - (uint16_t(*)(uint16_t * addr, uint16_t oldval, uint16_t newval))( - code + cmpxchg16SeqCst); - AtomicCmpXchg32SeqCst = - (uint32_t(*)(uint32_t * addr, uint32_t oldval, uint32_t newval))( - code + cmpxchg32SeqCst); - AtomicCmpXchg64SeqCst = - (uint64_t(*)(uint64_t * addr, uint64_t oldval, uint64_t newval))( - code + cmpxchg64SeqCst); - - AtomicExchange8SeqCst = - (uint8_t(*)(uint8_t * addr, uint8_t val))(code + exchange8SeqCst); - AtomicExchange16SeqCst = - (uint16_t(*)(uint16_t * addr, uint16_t val))(code + exchange16SeqCst); - AtomicExchange32SeqCst = - (uint32_t(*)(uint32_t * addr, uint32_t val))(code + exchange32SeqCst); -#ifdef JS_64BIT - AtomicExchange64SeqCst = - (uint64_t(*)(uint64_t * addr, uint64_t val))(code + exchange64SeqCst); -#endif - - AtomicAdd8SeqCst = - (uint8_t(*)(uint8_t * addr, uint8_t val))(code + add8SeqCst); - AtomicAdd16SeqCst = - (uint16_t(*)(uint16_t * addr, uint16_t val))(code + add16SeqCst); - AtomicAdd32SeqCst = - (uint32_t(*)(uint32_t * addr, uint32_t val))(code + add32SeqCst); -#ifdef JS_64BIT - AtomicAdd64SeqCst = - (uint64_t(*)(uint64_t * addr, uint64_t val))(code + add64SeqCst); -#endif - - AtomicAnd8SeqCst = - (uint8_t(*)(uint8_t * addr, uint8_t val))(code + and8SeqCst); - AtomicAnd16SeqCst = - (uint16_t(*)(uint16_t * addr, uint16_t val))(code + and16SeqCst); - AtomicAnd32SeqCst = - (uint32_t(*)(uint32_t * addr, uint32_t val))(code + and32SeqCst); -#ifdef JS_64BIT - AtomicAnd64SeqCst = - (uint64_t(*)(uint64_t * addr, uint64_t val))(code + and64SeqCst); -#endif - - AtomicOr8SeqCst = (uint8_t(*)(uint8_t * addr, uint8_t val))(code + or8SeqCst); - AtomicOr16SeqCst = - (uint16_t(*)(uint16_t * addr, uint16_t val))(code + or16SeqCst); - AtomicOr32SeqCst = - (uint32_t(*)(uint32_t * addr, uint32_t val))(code + or32SeqCst); -#ifdef JS_64BIT - AtomicOr64SeqCst = - (uint64_t(*)(uint64_t * addr, uint64_t val))(code + or64SeqCst); -#endif - - AtomicXor8SeqCst = - (uint8_t(*)(uint8_t * addr, uint8_t val))(code + xor8SeqCst); - AtomicXor16SeqCst = - (uint16_t(*)(uint16_t * addr, uint16_t val))(code + xor16SeqCst); - AtomicXor32SeqCst = - (uint32_t(*)(uint32_t * addr, uint32_t val))(code + xor32SeqCst); -#ifdef JS_64BIT - AtomicXor64SeqCst = - (uint64_t(*)(uint64_t * addr, uint64_t val))(code + xor64SeqCst); -#endif - - codeSegment = code; - codeSegmentSize = roundedCodeLength; - - return true; -} - -void ShutDownJittedAtomics() { - // Must have been initialized. - MOZ_ASSERT(codeSegment); - - DeallocateExecutableMemory(codeSegment, codeSegmentSize); - codeSegment = nullptr; - codeSegmentSize = 0; -} - } // namespace jit } // namespace js + +#endif // JS_HAVE_GENERATED_ATOMIC_OPS diff --git a/js/src/jit/shared/AtomicOperations-shared-jit.h b/js/src/jit/shared/AtomicOperations-shared-jit.h index 39b8f2303574..ca66a6f9b9d0 100644 --- a/js/src/jit/shared/AtomicOperations-shared-jit.h +++ b/js/src/jit/shared/AtomicOperations-shared-jit.h @@ -22,118 +22,19 @@ #include #include -#include "js/GCAPI.h" +#include "jit/AtomicOperationsGenerated.h" #include "vm/Uint8Clamped.h" namespace js { namespace jit { -// The function pointers in this section all point to jitted code. -// -// On 32-bit systems we assume for simplicity's sake that we don't have any -// 64-bit atomic operations except cmpxchg (this is a concession to x86 but it's -// not a hardship). On 32-bit systems we therefore implement other 64-bit -// atomic operations in terms of cmpxchg along with some C++ code and a local -// reordering fence to prevent other loads and stores from being intermingled -// with operations in the implementation of the atomic. - -// `fence` performs a full memory barrier. -extern void (*AtomicFenceSeqCst)(); - #ifndef JS_64BIT -// `compiler_fence` erects a reordering boundary for operations on the current -// thread. We use it to prevent the compiler from reordering loads and stores -// inside larger primitives that are synthesized from cmpxchg. -extern void (*AtomicCompilerFence)(); +// `AtomicCompilerFence` erects a reordering boundary for operations on the +// current thread. We use it to prevent the compiler from reordering loads and +// stores inside larger primitives that are synthesized from cmpxchg. +extern void AtomicCompilerFence(); #endif -extern uint8_t (*AtomicLoad8SeqCst)(const uint8_t* addr); -extern uint16_t (*AtomicLoad16SeqCst)(const uint16_t* addr); -extern uint32_t (*AtomicLoad32SeqCst)(const uint32_t* addr); -#ifdef JS_64BIT -extern uint64_t (*AtomicLoad64SeqCst)(const uint64_t* addr); -#endif - -// These are access-atomic up to sizeof(uintptr_t). -extern uint8_t (*AtomicLoad8Unsynchronized)(const uint8_t* addr); -extern uint16_t (*AtomicLoad16Unsynchronized)(const uint16_t* addr); -extern uint32_t (*AtomicLoad32Unsynchronized)(const uint32_t* addr); -#ifdef JS_64BIT -extern uint64_t (*AtomicLoad64Unsynchronized)(const uint64_t* addr); -#endif - -extern uint8_t (*AtomicStore8SeqCst)(uint8_t* addr, uint8_t val); -extern uint16_t (*AtomicStore16SeqCst)(uint16_t* addr, uint16_t val); -extern uint32_t (*AtomicStore32SeqCst)(uint32_t* addr, uint32_t val); -#ifdef JS_64BIT -extern uint64_t (*AtomicStore64SeqCst)(uint64_t* addr, uint64_t val); -#endif - -// These are access-atomic up to sizeof(uintptr_t). -extern uint8_t (*AtomicStore8Unsynchronized)(uint8_t* addr, uint8_t val); -extern uint16_t (*AtomicStore16Unsynchronized)(uint16_t* addr, uint16_t val); -extern uint32_t (*AtomicStore32Unsynchronized)(uint32_t* addr, uint32_t val); -#ifdef JS_64BIT -extern uint64_t (*AtomicStore64Unsynchronized)(uint64_t* addr, uint64_t val); -#endif - -// `exchange` takes a cell address and a value. It stores it in the cell and -// returns the value previously in the cell. -extern uint8_t (*AtomicExchange8SeqCst)(uint8_t* addr, uint8_t val); -extern uint16_t (*AtomicExchange16SeqCst)(uint16_t* addr, uint16_t val); -extern uint32_t (*AtomicExchange32SeqCst)(uint32_t* addr, uint32_t val); -#ifdef JS_64BIT -extern uint64_t (*AtomicExchange64SeqCst)(uint64_t* addr, uint64_t val); -#endif - -// `add` adds a value atomically to the cell and returns the old value in the -// cell. (There is no `sub`; just add the negated value.) -extern uint8_t (*AtomicAdd8SeqCst)(uint8_t* addr, uint8_t val); -extern uint16_t (*AtomicAdd16SeqCst)(uint16_t* addr, uint16_t val); -extern uint32_t (*AtomicAdd32SeqCst)(uint32_t* addr, uint32_t val); -#ifdef JS_64BIT -extern uint64_t (*AtomicAdd64SeqCst)(uint64_t* addr, uint64_t val); -#endif - -// `and` bitwise-ands a value atomically into the cell and returns the old value -// in the cell. -extern uint8_t (*AtomicAnd8SeqCst)(uint8_t* addr, uint8_t val); -extern uint16_t (*AtomicAnd16SeqCst)(uint16_t* addr, uint16_t val); -extern uint32_t (*AtomicAnd32SeqCst)(uint32_t* addr, uint32_t val); -#ifdef JS_64BIT -extern uint64_t (*AtomicAnd64SeqCst)(uint64_t* addr, uint64_t val); -#endif - -// `or` bitwise-ors a value atomically into the cell and returns the old value -// in the cell. -extern uint8_t (*AtomicOr8SeqCst)(uint8_t* addr, uint8_t val); -extern uint16_t (*AtomicOr16SeqCst)(uint16_t* addr, uint16_t val); -extern uint32_t (*AtomicOr32SeqCst)(uint32_t* addr, uint32_t val); -#ifdef JS_64BIT -extern uint64_t (*AtomicOr64SeqCst)(uint64_t* addr, uint64_t val); -#endif - -// `xor` bitwise-xors a value atomically into the cell and returns the old value -// in the cell. -extern uint8_t (*AtomicXor8SeqCst)(uint8_t* addr, uint8_t val); -extern uint16_t (*AtomicXor16SeqCst)(uint16_t* addr, uint16_t val); -extern uint32_t (*AtomicXor32SeqCst)(uint32_t* addr, uint32_t val); -#ifdef JS_64BIT -extern uint64_t (*AtomicXor64SeqCst)(uint64_t* addr, uint64_t val); -#endif - -// `cmpxchg` takes a cell address, an expected value and a replacement value. -// If the value in the cell equals the expected value then the replacement value -// is stored in the cell. It always returns the value previously in the cell. -extern uint8_t (*AtomicCmpXchg8SeqCst)(uint8_t* addr, uint8_t oldval, - uint8_t newval); -extern uint16_t (*AtomicCmpXchg16SeqCst)(uint16_t* addr, uint16_t oldval, - uint16_t newval); -extern uint32_t (*AtomicCmpXchg32SeqCst)(uint32_t* addr, uint32_t oldval, - uint32_t newval); -extern uint64_t (*AtomicCmpXchg64SeqCst)(uint64_t* addr, uint64_t oldval, - uint64_t newval); - // `...MemcpyDown` moves bytes toward lower addresses in memory: dest <= src. // `...MemcpyUp` moves bytes toward higher addresses in memory: dest >= src. extern void AtomicMemcpyDownUnsynchronized(uint8_t* dest, const uint8_t* src, @@ -153,7 +54,6 @@ inline void js::jit::AtomicOperations::fenceSeqCst() { AtomicFenceSeqCst(); } #define JIT_LOADOP(T, U, loadop) \ template <> \ inline T AtomicOperations::loadSeqCst(T* addr) { \ - JS::AutoSuppressGCAnalysis nogc; \ return (T)loadop((U*)addr); \ } @@ -161,7 +61,6 @@ inline void js::jit::AtomicOperations::fenceSeqCst() { AtomicFenceSeqCst(); } # define JIT_LOADOP_CAS(T) \ template <> \ inline T AtomicOperations::loadSeqCst(T* addr) { \ - JS::AutoSuppressGCAnalysis nogc; \ AtomicCompilerFence(); \ return (T)AtomicCmpXchg64SeqCst((uint64_t*)addr, 0, 0); \ } @@ -194,7 +93,6 @@ JIT_LOADOP(uint64_t, uint64_t, AtomicLoad64SeqCst) #define JIT_STOREOP(T, U, storeop) \ template <> \ inline void AtomicOperations::storeSeqCst(T* addr, T val) { \ - JS::AutoSuppressGCAnalysis nogc; \ storeop((U*)addr, val); \ } @@ -202,7 +100,6 @@ JIT_LOADOP(uint64_t, uint64_t, AtomicLoad64SeqCst) # define JIT_STOREOP_CAS(T) \ template <> \ inline void AtomicOperations::storeSeqCst(T* addr, T val) { \ - JS::AutoSuppressGCAnalysis nogc; \ AtomicCompilerFence(); \ T oldval = *addr; /* good initial approximation */ \ for (;;) { \ @@ -244,7 +141,6 @@ JIT_STOREOP(uint64_t, uint64_t, AtomicStore64SeqCst) #define JIT_EXCHANGEOP(T, U, xchgop) \ template <> \ inline T AtomicOperations::exchangeSeqCst(T* addr, T val) { \ - JS::AutoSuppressGCAnalysis nogc; \ return (T)xchgop((U*)addr, (U)val); \ } @@ -252,7 +148,6 @@ JIT_STOREOP(uint64_t, uint64_t, AtomicStore64SeqCst) # define JIT_EXCHANGEOP_CAS(T) \ template <> \ inline T AtomicOperations::exchangeSeqCst(T* addr, T val) { \ - JS::AutoSuppressGCAnalysis nogc; \ AtomicCompilerFence(); \ T oldval = *addr; \ for (;;) { \ @@ -296,7 +191,6 @@ JIT_EXCHANGEOP(uint64_t, uint64_t, AtomicExchange64SeqCst) template <> \ inline T AtomicOperations::compareExchangeSeqCst(T* addr, T oldval, \ T newval) { \ - JS::AutoSuppressGCAnalysis nogc; \ return (T)cmpxchg((U*)addr, (U)oldval, (U)newval); \ } @@ -320,14 +214,12 @@ JIT_CAS(uint64_t, uint64_t, AtomicCmpXchg64SeqCst) #define JIT_FETCHADDOP(T, U, xadd) \ template <> \ inline T AtomicOperations::fetchAddSeqCst(T* addr, T val) { \ - JS::AutoSuppressGCAnalysis nogc; \ return (T)xadd((U*)addr, (U)val); \ } #define JIT_FETCHSUBOP(T) \ template <> \ inline T AtomicOperations::fetchSubSeqCst(T* addr, T val) { \ - JS::AutoSuppressGCAnalysis nogc; \ return fetchAddSeqCst(addr, (T)(0 - val)); \ } @@ -335,7 +227,6 @@ JIT_CAS(uint64_t, uint64_t, AtomicCmpXchg64SeqCst) # define JIT_FETCHADDOP_CAS(T) \ template <> \ inline T AtomicOperations::fetchAddSeqCst(T* addr, T val) { \ - JS::AutoSuppressGCAnalysis nogc; \ AtomicCompilerFence(); \ T oldval = *addr; /* Good initial approximation */ \ for (;;) { \ @@ -388,7 +279,6 @@ JIT_FETCHSUBOP(uint64_t) #define JIT_FETCHBITOPX(T, U, name, op) \ template <> \ inline T AtomicOperations::name(T* addr, T val) { \ - JS::AutoSuppressGCAnalysis nogc; \ return (T)op((U*)addr, (U)val); \ } @@ -406,7 +296,6 @@ JIT_FETCHSUBOP(uint64_t) # define JIT_FETCHBITOPX_CAS(T, name, OP) \ template <> \ inline T AtomicOperations::name(T* addr, T val) { \ - JS::AutoSuppressGCAnalysis nogc; \ AtomicCompilerFence(); \ T oldval = *addr; \ for (;;) { \ @@ -465,7 +354,6 @@ JIT_FETCHBITOP(uint64_t, uint64_t, AtomicAnd64SeqCst, AtomicOr64SeqCst, #define JIT_LOADSAFE(T, U, loadop) \ template <> \ inline T js::jit::AtomicOperations::loadSafeWhenRacy(T* addr) { \ - JS::AutoSuppressGCAnalysis nogc; \ union { \ U u; \ T t; \ @@ -478,7 +366,6 @@ JIT_FETCHBITOP(uint64_t, uint64_t, AtomicAnd64SeqCst, AtomicOr64SeqCst, # define JIT_LOADSAFE_TEARING(T) \ template <> \ inline T js::jit::AtomicOperations::loadSafeWhenRacy(T* addr) { \ - JS::AutoSuppressGCAnalysis nogc; \ MOZ_ASSERT(sizeof(T) == 8); \ union { \ uint32_t u[2]; \ @@ -527,7 +414,6 @@ inline uint8_clamped js::jit::AtomicOperations::loadSafeWhenRacy( #define JIT_STORESAFE(T, U, storeop) \ template <> \ inline void js::jit::AtomicOperations::storeSafeWhenRacy(T* addr, T val) { \ - JS::AutoSuppressGCAnalysis nogc; \ union { \ U u; \ T t; \ @@ -540,7 +426,6 @@ inline uint8_clamped js::jit::AtomicOperations::loadSafeWhenRacy( # define JIT_STORESAFE_TEARING(T) \ template <> \ inline void js::jit::AtomicOperations::storeSafeWhenRacy(T* addr, T val) { \ - JS::AutoSuppressGCAnalysis nogc; \ union { \ uint32_t u[2]; \ T t; \ @@ -587,7 +472,6 @@ inline void js::jit::AtomicOperations::storeSafeWhenRacy(uint8_clamped* addr, void js::jit::AtomicOperations::memcpySafeWhenRacy(void* dest, const void* src, size_t nbytes) { - JS::AutoSuppressGCAnalysis nogc; MOZ_ASSERT(!((char*)dest <= (char*)src && (char*)src < (char*)dest + nbytes)); MOZ_ASSERT(!((char*)src <= (char*)dest && (char*)dest < (char*)src + nbytes)); AtomicMemcpyDownUnsynchronized((uint8_t*)dest, (const uint8_t*)src, nbytes); @@ -596,7 +480,6 @@ void js::jit::AtomicOperations::memcpySafeWhenRacy(void* dest, const void* src, inline void js::jit::AtomicOperations::memmoveSafeWhenRacy(void* dest, const void* src, size_t nbytes) { - JS::AutoSuppressGCAnalysis nogc; if ((char*)dest <= (char*)src) { AtomicMemcpyDownUnsynchronized((uint8_t*)dest, (const uint8_t*)src, nbytes); } else { @@ -604,19 +487,4 @@ inline void js::jit::AtomicOperations::memmoveSafeWhenRacy(void* dest, } } -namespace js { -namespace jit { - -extern bool InitializeJittedAtomics(); -extern void ShutDownJittedAtomics(); - -} // namespace jit -} // namespace js - -inline bool js::jit::AtomicOperations::Initialize() { - return InitializeJittedAtomics(); -} - -inline void js::jit::AtomicOperations::ShutDown() { ShutDownJittedAtomics(); } - #endif // jit_shared_AtomicOperations_shared_jit_h diff --git a/js/src/jit/x64/CodeGenerator-x64.cpp b/js/src/jit/x64/CodeGenerator-x64.cpp index 96b575d01b73..ab2bbb86cf67 100644 --- a/js/src/jit/x64/CodeGenerator-x64.cpp +++ b/js/src/jit/x64/CodeGenerator-x64.cpp @@ -325,6 +325,8 @@ void CodeGenerator::visitAtomicLoad64(LAtomicLoad64* lir) { Scalar::Type storageType = mir->storageType(); + // NOTE: the generated code must match the assembly code in gen_load in + // GenerateAtomicOperations.py auto sync = Synchronization::Load(); masm.memoryBarrierBefore(sync); @@ -351,6 +353,8 @@ void CodeGenerator::visitAtomicStore64(LAtomicStore64* lir) { masm.loadBigInt64(value, temp1); + // NOTE: the generated code must match the assembly code in gen_store in + // GenerateAtomicOperations.py auto sync = Synchronization::Store(); masm.memoryBarrierBefore(sync); diff --git a/js/src/jit/x64/MacroAssembler-x64.cpp b/js/src/jit/x64/MacroAssembler-x64.cpp index 6c49627c7c72..cb8d5fb86129 100644 --- a/js/src/jit/x64/MacroAssembler-x64.cpp +++ b/js/src/jit/x64/MacroAssembler-x64.cpp @@ -922,6 +922,8 @@ void MacroAssembler::PushBoxed(FloatRegister reg) { void MacroAssembler::wasmLoad(const wasm::MemoryAccessDesc& access, Operand srcAddr, AnyRegister out) { + // NOTE: the generated code must match the assembly code in gen_load in + // GenerateAtomicOperations.py memoryBarrierBefore(access.sync()); MOZ_ASSERT_IF( @@ -1015,6 +1017,8 @@ void MacroAssembler::wasmLoad(const wasm::MemoryAccessDesc& access, void MacroAssembler::wasmLoadI64(const wasm::MemoryAccessDesc& access, Operand srcAddr, Register64 out) { + // NOTE: the generated code must match the assembly code in gen_load in + // GenerateAtomicOperations.py memoryBarrierBefore(access.sync()); append(access, size()); @@ -1057,6 +1061,8 @@ void MacroAssembler::wasmLoadI64(const wasm::MemoryAccessDesc& access, void MacroAssembler::wasmStore(const wasm::MemoryAccessDesc& access, AnyRegister value, Operand dstAddr) { + // NOTE: the generated code must match the assembly code in gen_store in + // GenerateAtomicOperations.py memoryBarrierBefore(access.sync()); append(access, masm.size()); @@ -1342,6 +1348,8 @@ static void AtomicFetchOp64(MacroAssembler& masm, const wasm::MemoryAccessDesc* access, AtomicOp op, Register value, const T& mem, Register temp, Register output) { + // NOTE: the generated code must match the assembly code in gen_fetchop in + // GenerateAtomicOperations.py if (op == AtomicFetchAddOp) { if (value != output) { masm.movq(value, output); @@ -1441,6 +1449,8 @@ void MacroAssembler::compareExchange64(const Synchronization&, const Address& mem, Register64 expected, Register64 replacement, Register64 output) { + // NOTE: the generated code must match the assembly code in gen_cmpxchg in + // GenerateAtomicOperations.py MOZ_ASSERT(output.reg == rax); if (expected != output) { movq(expected.reg, output.reg); @@ -1463,6 +1473,8 @@ void MacroAssembler::compareExchange64(const Synchronization&, void MacroAssembler::atomicExchange64(const Synchronization&, const Address& mem, Register64 value, Register64 output) { + // NOTE: the generated code must match the assembly code in gen_exchange in + // GenerateAtomicOperations.py if (value != output) { movq(value.reg, output.reg); } diff --git a/js/src/jit/x86-shared/MacroAssembler-x86-shared.cpp b/js/src/jit/x86-shared/MacroAssembler-x86-shared.cpp index 59b858e40bcb..4cf983f96157 100644 --- a/js/src/jit/x86-shared/MacroAssembler-x86-shared.cpp +++ b/js/src/jit/x86-shared/MacroAssembler-x86-shared.cpp @@ -1108,6 +1108,8 @@ static void CompareExchange(MacroAssembler& masm, masm.append(*access, masm.size()); } + // NOTE: the generated code must match the assembly code in gen_cmpxchg in + // GenerateAtomicOperations.py switch (Scalar::byteSize(type)) { case 1: CheckBytereg(newval); @@ -1153,7 +1155,8 @@ static void AtomicExchange(MacroAssembler& masm, const wasm::MemoryAccessDesc* access, Scalar::Type type, const T& mem, Register value, Register output) - +// NOTE: the generated code must match the assembly code in gen_exchange in +// GenerateAtomicOperations.py { if (value != output) { masm.movl(value, output); @@ -1230,6 +1233,8 @@ static void AtomicFetchOp(MacroAssembler& masm, const T& mem, Register temp, Register output) { // Note value can be an Imm or a Register. + // NOTE: the generated code must match the assembly code in gen_fetchop in + // GenerateAtomicOperations.py #define ATOMIC_BITOP_BODY(LOAD, OP, LOCK_CMPXCHG) \ do { \ MOZ_ASSERT(output != temp); \ diff --git a/js/src/jit/x86/MacroAssembler-x86.cpp b/js/src/jit/x86/MacroAssembler-x86.cpp index 86a254c1f0c0..43d56e769aac 100644 --- a/js/src/jit/x86/MacroAssembler-x86.cpp +++ b/js/src/jit/x86/MacroAssembler-x86.cpp @@ -958,6 +958,8 @@ void MacroAssembler::wasmLoad(const wasm::MemoryAccessDesc& access, access.type() == Scalar::Float32 || access.type() == Scalar::Float64); MOZ_ASSERT_IF(access.isWidenSimd128Load(), access.type() == Scalar::Float64); + // NOTE: the generated code must match the assembly code in gen_load in + // GenerateAtomicOperations.py memoryBarrierBefore(access.sync()); append(access, size()); @@ -1121,6 +1123,8 @@ void MacroAssembler::wasmStore(const wasm::MemoryAccessDesc& access, MOZ_ASSERT(dstAddr.kind() == Operand::MEM_REG_DISP || dstAddr.kind() == Operand::MEM_SCALE); + // NOTE: the generated code must match the assembly code in gen_store in + // GenerateAtomicOperations.py memoryBarrierBefore(access.sync()); append(access, size()); @@ -1217,6 +1221,8 @@ static void CompareExchange64(MacroAssembler& masm, MOZ_ASSERT(replacement.high == ecx); MOZ_ASSERT(replacement.low == ebx); + // NOTE: the generated code must match the assembly code in gen_cmpxchg in + // GenerateAtomicOperations.py if (access) { masm.append(*access, masm.size()); } diff --git a/js/src/vm/Initialization.cpp b/js/src/vm/Initialization.cpp index 6c87bfd68365..109185131388 100644 --- a/js/src/vm/Initialization.cpp +++ b/js/src/vm/Initialization.cpp @@ -187,8 +187,6 @@ JS_PUBLIC_API const char* JS::detail::InitWithFailureDiagnostic( RETURN_IF_FAIL(js::vtune::Initialize()); #endif - RETURN_IF_FAIL(js::jit::AtomicOperations::Initialize()); - #if JS_HAS_INTL_API if (mozilla::intl::ICU4CLibrary::Initialize().isErr()) { return "ICU4CLibrary::Initialize() failed"; @@ -209,7 +207,7 @@ JS_PUBLIC_API const char* JS::detail::InitWithFailureDiagnostic( #endif #ifndef JS_CODEGEN_NONE - // Normally this is forced by the compilation of atomic operations. + // This is forced by InitializeJit. MOZ_ASSERT(js::jit::CPUFlagsHaveBeenComputed()); #endif @@ -275,8 +273,6 @@ JS_PUBLIC_API void JS_ShutDown(void) { js::jit::SimulatorProcess::destroy(); #endif - js::jit::AtomicOperations::ShutDown(); - #ifdef JS_TRACE_LOGGING js::DestroyTraceLoggerThreadState(); js::DestroyTraceLoggerGraphState(); diff --git a/js/src/vm/SharedMem.h b/js/src/vm/SharedMem.h index 067f75248a9d..dcb74da84671 100644 --- a/js/src/vm/SharedMem.h +++ b/js/src/vm/SharedMem.h @@ -7,6 +7,8 @@ #ifndef vm_SharedMem_h #define vm_SharedMem_h +#include "mozilla/Assertions.h" + #include template diff --git a/layout/base/nsLayoutDebugger.cpp b/layout/base/nsLayoutDebugger.cpp index 317a2d2b470b..565cf262f24e 100644 --- a/layout/base/nsLayoutDebugger.cpp +++ b/layout/base/nsLayoutDebugger.cpp @@ -129,6 +129,7 @@ static void PrintDisplayItemTo(nsDisplayListBuilder* aBuilder, } MOZ_ASSERT_UNREACHABLE(); + return ""; }; aStream << nsPrintfCString(" reuse-state(%s)", diff --git a/layout/base/nsRefreshDriver.h b/layout/base/nsRefreshDriver.h index fde6e44c57e9..59a7dc48e955 100644 --- a/layout/base/nsRefreshDriver.h +++ b/layout/base/nsRefreshDriver.h @@ -279,6 +279,8 @@ class nsRefreshDriver final : public mozilla::layers::TransactionIdAllocator, bool IsFrozen() const { return mFreezeCount > 0; } + bool IsThrottled() const { return mThrottled; } + /** * Freeze the refresh driver. It should stop delivering future * refreshes until thawed. Note that the number of calls to Freeze() must diff --git a/layout/generic/nsIFrame.cpp b/layout/generic/nsIFrame.cpp index 85f7ee22f6cb..ac624f9fc6ea 100644 --- a/layout/generic/nsIFrame.cpp +++ b/layout/generic/nsIFrame.cpp @@ -1087,8 +1087,8 @@ void nsIFrame::RemoveDisplayItemDataForDeletion() { if (!updateData) { // No RetainedDisplayListData to update. - MOZ_RELEASE_ASSERT(!data->IsModified(this), - "Deleted frame is in modified frame list"); + MOZ_DIAGNOSTIC_ASSERT(!data->IsModified(this), + "Deleted frame is in modified frame list"); return; } diff --git a/layout/style/FontFace.cpp b/layout/style/FontFace.cpp index 9a216843e48e..8408c9abdb72 100644 --- a/layout/style/FontFace.cpp +++ b/layout/style/FontFace.cpp @@ -160,6 +160,11 @@ already_AddRefed FontFace::Constructor( const FontFaceDescriptors& aDescriptors, ErrorResult& aRv) { nsISupports* global = aGlobal.GetAsSupports(); nsCOMPtr window = do_QueryInterface(global); + if (!window) { + aRv.Throw(NS_ERROR_FAILURE); + return nullptr; + } + Document* doc = window->GetDoc(); if (!doc) { aRv.Throw(NS_ERROR_FAILURE); diff --git a/layout/xul/nsMenuBarFrame.cpp b/layout/xul/nsMenuBarFrame.cpp index c116bebfbaa8..6f0424018a9d 100644 --- a/layout/xul/nsMenuBarFrame.cpp +++ b/layout/xul/nsMenuBarFrame.cpp @@ -190,8 +190,7 @@ nsMenuFrame* nsMenuBarFrame::FindMenuWithShortcut(KeyboardEvent* aKeyEvent, // behavior on Windows - this item is on the menu bar, beep and deactivate // the menu bar if (mIsActive) { - nsCOMPtr soundInterface = - do_CreateInstance("@mozilla.org/sound;1"); + nsCOMPtr soundInterface = do_GetService("@mozilla.org/sound;1"); if (soundInterface) soundInterface->Beep(); } diff --git a/layout/xul/nsMenuFrame.cpp b/layout/xul/nsMenuFrame.cpp index 9f1ed90578f4..5f0a622a8fbf 100644 --- a/layout/xul/nsMenuFrame.cpp +++ b/layout/xul/nsMenuFrame.cpp @@ -867,7 +867,7 @@ void nsMenuFrame::UpdateMenuSpecialState() { } void nsMenuFrame::Execute(WidgetGUIEvent* aEvent) { - nsCOMPtr sound(do_CreateInstance("@mozilla.org/sound;1")); + nsCOMPtr sound(do_GetService("@mozilla.org/sound;1")); if (sound) sound->PlayEventSound(nsISound::EVENT_MENU_EXECUTE); // Create a trusted event if the triggering event was trusted, or if diff --git a/layout/xul/nsMenuPopupFrame.cpp b/layout/xul/nsMenuPopupFrame.cpp index fa173f88bcae..6b1ae44ffde7 100644 --- a/layout/xul/nsMenuPopupFrame.cpp +++ b/layout/xul/nsMenuPopupFrame.cpp @@ -1001,7 +1001,7 @@ void nsMenuPopupFrame::ShowPopup(bool aIsContextMenu) { NS_FRAME_HAS_DIRTY_CHILDREN); if (mPopupType == ePopupTypeMenu) { - nsCOMPtr sound(do_CreateInstance("@mozilla.org/sound;1")); + nsCOMPtr sound(do_GetService("@mozilla.org/sound;1")); if (sound) sound->PlayEventSound(nsISound::EVENT_MENU_POPUP); } } @@ -2134,8 +2134,7 @@ nsMenuFrame* nsMenuPopupFrame::FindMenuWithShortcut(KeyboardEvent* aKeyEvent, return nullptr; } #ifdef XP_WIN - nsCOMPtr soundInterface = - do_CreateInstance("@mozilla.org/sound;1"); + nsCOMPtr soundInterface = do_GetService("@mozilla.org/sound;1"); if (soundInterface) soundInterface->Beep(); #endif // #ifdef XP_WIN } @@ -2262,8 +2261,7 @@ nsMenuFrame* nsMenuPopupFrame::FindMenuWithShortcut(KeyboardEvent* aKeyEvent, // behavior on Windows - this item is in a menu popup off of the // menu bar, so beep and do nothing else if (isMenu) { - nsCOMPtr soundInterface = - do_CreateInstance("@mozilla.org/sound;1"); + nsCOMPtr soundInterface = do_GetService("@mozilla.org/sound;1"); if (soundInterface) soundInterface->Beep(); } #endif // #ifdef XP_WIN diff --git a/media/ffvpx/libavcodec/moz.build b/media/ffvpx/libavcodec/moz.build index 50756faef171..d952987a044d 100644 --- a/media/ffvpx/libavcodec/moz.build +++ b/media/ffvpx/libavcodec/moz.build @@ -103,6 +103,7 @@ if not CONFIG['MOZ_FFVPX_AUDIOONLY']: 'vp9recon.c' ] if CONFIG['MOZ_WAYLAND']: + LOCAL_INCLUDES += ['/media/mozva'] SOURCES += [ 'atsc_a53.c', 'libdav1d.c', diff --git a/media/ffvpx/libavutil/moz.build b/media/ffvpx/libavutil/moz.build index 472a8c27fad5..671f6aaabaea 100644 --- a/media/ffvpx/libavutil/moz.build +++ b/media/ffvpx/libavutil/moz.build @@ -60,6 +60,7 @@ if not CONFIG['MOZ_FFVPX_AUDIOONLY']: 'video_enc_params.c' ] if CONFIG["MOZ_WAYLAND"]: + LOCAL_INCLUDES += ['/media/mozva'] SOURCES += [ 'hwcontext_vaapi.c', 'mastering_display_metadata.c', diff --git a/media/ffvpx/moz.build b/media/ffvpx/moz.build index 86f6df4bcdf5..e749b5a72393 100644 --- a/media/ffvpx/moz.build +++ b/media/ffvpx/moz.build @@ -11,8 +11,3 @@ DIRS += [ 'libavutil', 'libavcodec' ] - -if CONFIG['MOZ_WAYLAND']: - DIRS += [ - 'mozva', - ] diff --git a/media/ffvpx/mozva/moz.build b/media/mozva/moz.build similarity index 81% rename from media/ffvpx/mozva/moz.build rename to media/mozva/moz.build index df1d9954baad..c0ca1ab8be7c 100644 --- a/media/ffvpx/mozva/moz.build +++ b/media/mozva/moz.build @@ -5,9 +5,7 @@ # file, You can obtain one at http://mozilla.org/MPL/2.0/. SOURCES += [ - 'mozva.c', + "mozva.c", ] -LOCAL_INCLUDES += ['/media/ffvpx'] - -Library('mozva') +Library("mozva") diff --git a/media/ffvpx/mozva/mozva.c b/media/mozva/mozva.c similarity index 96% rename from media/ffvpx/mozva/mozva.c rename to media/mozva/mozva.c index 16c969ff171e..140dbd9c15f6 100644 --- a/media/ffvpx/mozva/mozva.c +++ b/media/mozva/mozva.c @@ -89,14 +89,14 @@ static VAStatus (*vaInitializeFn)(VADisplay dpy, int* major_version, /* out */ static VAStatus (*vaSetDriverNameFn)(VADisplay dpy, char* driver_name); static int (*vaMaxNumEntrypointsFn)(VADisplay dpy); static VAStatus (*vaQueryConfigEntrypointsFn)(VADisplay dpy, VAProfile profile, - VAEntrypoint *entrypoint_list, - int *num_entrypoints); + VAEntrypoint* entrypoint_list, + int* num_entrypoints); static VAMessageCallback (*vaSetErrorCallbackFn)(VADisplay dpy, VAMessageCallback callback, - void *user_context); + void* user_context); static VAMessageCallback (*vaSetInfoCallbackFn)(VADisplay dpy, VAMessageCallback callback, - void *user_context); + void* user_context); int LoadVALibrary() { static pthread_mutex_t sVALock = PTHREAD_MUTEX_INITIALIZER; static void* sVALib = NULL; @@ -428,16 +428,17 @@ int vaMaxNumEntrypoints(VADisplay dpy) { } VAStatus vaQueryConfigEntrypoints(VADisplay dpy, VAProfile profile, - VAEntrypoint *entrypoint_list, - int *num_entrypoints) { + VAEntrypoint* entrypoint_list, + int* num_entrypoints) { if (LoadVALibrary()) { - return vaQueryConfigEntrypointsFn(dpy, profile, entrypoint_list, num_entrypoints); + return vaQueryConfigEntrypointsFn(dpy, profile, entrypoint_list, + num_entrypoints); } return VA_STATUS_ERROR_UNIMPLEMENTED; } VAMessageCallback vaSetErrorCallback(VADisplay dpy, VAMessageCallback callback, - void *user_context) { + void* user_context) { if (LoadVALibrary()) { return vaSetErrorCallbackFn(dpy, callback, user_context); } @@ -445,7 +446,7 @@ VAMessageCallback vaSetErrorCallback(VADisplay dpy, VAMessageCallback callback, } VAMessageCallback vaSetInfoCallback(VADisplay dpy, VAMessageCallback callback, - void *user_context) { + void* user_context) { if (LoadVALibrary()) { return vaSetInfoCallbackFn(dpy, callback, user_context); } diff --git a/media/ffvpx/va/README b/media/mozva/va/README similarity index 100% rename from media/ffvpx/va/README rename to media/mozva/va/README diff --git a/media/ffvpx/va/va.h b/media/mozva/va/va.h similarity index 100% rename from media/ffvpx/va/va.h rename to media/mozva/va/va.h diff --git a/media/ffvpx/va/va.patch b/media/mozva/va/va.patch similarity index 100% rename from media/ffvpx/va/va.patch rename to media/mozva/va/va.patch diff --git a/media/ffvpx/va/va_dec_av1.h b/media/mozva/va/va_dec_av1.h similarity index 100% rename from media/ffvpx/va/va_dec_av1.h rename to media/mozva/va/va_dec_av1.h diff --git a/media/ffvpx/va/va_dec_vp8.h b/media/mozva/va/va_dec_vp8.h similarity index 100% rename from media/ffvpx/va/va_dec_vp8.h rename to media/mozva/va/va_dec_vp8.h diff --git a/media/ffvpx/va/va_dec_vp9.h b/media/mozva/va/va_dec_vp9.h similarity index 100% rename from media/ffvpx/va/va_dec_vp9.h rename to media/mozva/va/va_dec_vp9.h diff --git a/media/ffvpx/va/va_version.h b/media/mozva/va/va_version.h similarity index 100% rename from media/ffvpx/va/va_version.h rename to media/mozva/va/va_version.h diff --git a/modules/libpref/init/StaticPrefList.yaml b/modules/libpref/init/StaticPrefList.yaml index 75e5fdd5cd1e..da4a85508901 100644 --- a/modules/libpref/init/StaticPrefList.yaml +++ b/modules/libpref/init/StaticPrefList.yaml @@ -3420,6 +3420,11 @@ value: false mirror: always +- name: dom.workers.requestAnimationFrame + type: RelaxedAtomicBool + value: false + mirror: always + - name: dom.workers.serialized-sab-access type: RelaxedAtomicBool value: false @@ -3456,6 +3461,12 @@ value: @IS_NOT_ANDROID@ mirror: always +# Whether the File System API is enabled +- name: dom.fs.enabled + type: RelaxedAtomicBool + value: false + mirror: always + # Should we abort LocalStorage requests when a sync message from parent is # detected? # @@ -5408,11 +5419,24 @@ mirror: once do_not_use_directly: true +# Enable OffscreenCanvas everywhere. - name: gfx.offscreencanvas.enabled type: RelaxedAtomicBool value: false mirror: always +# Enable OffscreenCanvas based on the domain allowlist. +- name: gfx.offscreencanvas.domain-enabled + type: RelaxedAtomicBool + value: false + mirror: always + +# Domains included in the allowlist. +- name: gfx.offscreencanvas.domain-allowlist + type: String + value: "" + mirror: never + - name: gfx.omta.background-color type: bool value: true @@ -7448,7 +7472,7 @@ # Is support for CSS hyphenate-character enabled? - name: layout.css.hyphenate-character.enabled type: RelaxedAtomicBool - value: @IS_NIGHTLY_BUILD@ + value: true mirror: always # Is support for CSS individual transform enabled? diff --git a/mozglue/build/TsanOptions.cpp b/mozglue/build/TsanOptions.cpp index 978b3e4520ae..16ac7030ef3b 100644 --- a/mozglue/build/TsanOptions.cpp +++ b/mozglue/build/TsanOptions.cpp @@ -239,10 +239,6 @@ extern "C" const char* __tsan_default_suppressions() { "race:_dl_deallocate_tls\n" "race:__libc_memalign\n" - // Bug 1664535 - "race:setNeedsIncrementalBarrier\n" - "race:needsIncrementalBarrier\n" - // Bug 1664803 "race:Sampler::sSigHandlerCoordinator\n" diff --git a/security/manager/ssl/nsCertOverrideService.cpp b/security/manager/ssl/nsCertOverrideService.cpp index 07836e651425..9089442005f6 100644 --- a/security/manager/ssl/nsCertOverrideService.cpp +++ b/security/manager/ssl/nsCertOverrideService.cpp @@ -324,12 +324,10 @@ nsresult nsCertOverrideService::Read(const MutexAutoLock& aProofOfLock) { Tokenizer parser(buffer); nsDependentCSubstring host; if (parser.CheckChar('[')) { // this is a IPv6 address - parser.Record(Tokenizer::INCLUDE_LAST); if (!parser.ReadUntil(Tokenizer::Token::Char(']'), host) || host.Length() == 0 || !parser.CheckChar(':')) { continue; } - parser.Claim(host); } else if (!parser.ReadUntil(Tokenizer::Token::Char(':'), host) || host.Length() == 0) { continue; @@ -818,7 +816,16 @@ nsCertOverrideService::GetOverrides( void nsCertOverrideService::GetHostWithPort(const nsACString& aHostName, int32_t aPort, nsACString& aRetval) { - nsAutoCString hostPort(aHostName); + nsAutoCString hostPort; + if (aHostName.Contains(':')) { + // if aHostName is an IPv6 address, add brackets to match the internal + // representation, which always stores IPv6 addresses with brackets + hostPort.Append('['); + hostPort.Append(aHostName); + hostPort.Append(']'); + } else { + hostPort.Append(aHostName); + } if (aPort == -1) { aPort = 443; } diff --git a/security/manager/ssl/tests/unit/test_cert_override_read.js b/security/manager/ssl/tests/unit/test_cert_override_read.js index 2bbf74501ea4..d03a5ab46146 100644 --- a/security/manager/ssl/tests/unit/test_cert_override_read.js +++ b/security/manager/ssl/tests/unit/test_cert_override_read.js @@ -134,7 +134,7 @@ function run_test() { attributes: {}, }, { - host: "[::1]", + host: "::1", port: 443, cert: cert2, bits: Ci.nsICertOverrideService.ERROR_MISMATCH, diff --git a/security/manager/ssl/tests/unit/test_cert_overrides.js b/security/manager/ssl/tests/unit/test_cert_overrides.js index f6256383c8d0..4801ebe1d903 100644 --- a/security/manager/ssl/tests/unit/test_cert_overrides.js +++ b/security/manager/ssl/tests/unit/test_cert_overrides.js @@ -566,6 +566,14 @@ function add_simple_tests() { expectedBits, false ); + certOverrideService.rememberValidityOverride( + "::1", + 80, + {}, + cert, + expectedBits, + false + ); Assert.ok( certOverrideService.hasMatchingOverride( "example.com", @@ -596,6 +604,10 @@ function add_simple_tests() { ), "Should have added override for example.org:443" ); + Assert.ok( + certOverrideService.hasMatchingOverride("::1", 80, {}, cert, {}, {}), + "Should have added override for [::1]:80" + ); Assert.ok( !certOverrideService.hasMatchingOverride( "example.org", diff --git a/servo/components/style/gecko/selector_parser.rs b/servo/components/style/gecko/selector_parser.rs index 35c5a5c454d0..8fc0900aece7 100644 --- a/servo/components/style/gecko/selector_parser.rs +++ b/servo/components/style/gecko/selector_parser.rs @@ -69,7 +69,7 @@ impl ToCss for NonTSPseudoClass { $(NonTSPseudoClass::$name => concat!(":", $css),)* NonTSPseudoClass::Lang(ref s) => { dest.write_str(":lang(")?; - s.to_css(dest)?; + cssparser::ToCss::to_css(s, dest)?; return dest.write_char(')'); }, NonTSPseudoClass::MozLocaleDir(ref dir) => { diff --git a/servo/components/style/stylesheets/layer_rule.rs b/servo/components/style/stylesheets/layer_rule.rs index 3607e9f2ebf6..ebff5bb9addb 100644 --- a/servo/components/style/stylesheets/layer_rule.rs +++ b/servo/components/style/stylesheets/layer_rule.rs @@ -13,7 +13,7 @@ use crate::values::AtomIdent; use super::CssRules; -use cssparser::{Parser, SourceLocation, ToCss as CssParserToCss, Token}; +use cssparser::{Parser, SourceLocation, Token}; use servo_arc::Arc; use smallvec::SmallVec; use std::fmt::{self, Write}; diff --git a/servo/components/style/stylesheets/namespace_rule.rs b/servo/components/style/stylesheets/namespace_rule.rs index d76703e4f81c..c0d017ab78fb 100644 --- a/servo/components/style/stylesheets/namespace_rule.rs +++ b/servo/components/style/stylesheets/namespace_rule.rs @@ -7,7 +7,7 @@ use crate::shared_lock::{SharedRwLockReadGuard, ToCssWithGuard}; use crate::str::CssStringWriter; use crate::{Namespace, Prefix}; -use cssparser::{self, SourceLocation}; +use cssparser::SourceLocation; use std::fmt::{self, Write}; use style_traits::{CssWriter, ToCss}; @@ -25,15 +25,15 @@ pub struct NamespaceRule { impl ToCssWithGuard for NamespaceRule { // https://drafts.csswg.org/cssom/#serialize-a-css-rule CSSNamespaceRule - fn to_css(&self, _guard: &SharedRwLockReadGuard, dest: &mut CssStringWriter) -> fmt::Result { + fn to_css(&self, _guard: &SharedRwLockReadGuard, dest_str: &mut CssStringWriter) -> fmt::Result { + let mut dest = CssWriter::new(dest_str); dest.write_str("@namespace ")?; if let Some(ref prefix) = self.prefix { - let prefix = prefix.to_string(); - cssparser::serialize_identifier(&prefix, dest)?; - dest.write_str(" ")?; + prefix.to_css(&mut dest)?; + dest.write_char(' ')?; } dest.write_str("url(")?; - self.url.to_string().to_css(&mut CssWriter::new(dest))?; + self.url.to_string().to_css(&mut dest)?; dest.write_str(");") } } diff --git a/servo/components/style/stylesheets/page_rule.rs b/servo/components/style/stylesheets/page_rule.rs index 7bf62f5c9e7b..ee156cf989ea 100644 --- a/servo/components/style/stylesheets/page_rule.rs +++ b/servo/components/style/stylesheets/page_rule.rs @@ -13,7 +13,7 @@ use crate::shared_lock::{SharedRwLock, SharedRwLockReadGuard, ToCssWithGuard}; use crate::str::CssStringWriter; use crate::values::{AtomIdent, CustomIdent}; use style_traits::{CssWriter, ParseError, ToCss}; -use cssparser::{ToCss as CssParserToCss, Parser, SourceLocation}; +use cssparser::{Parser, SourceLocation}; #[cfg(feature = "gecko")] use malloc_size_of::{MallocSizeOf, MallocSizeOfOps, MallocUnconditionalShallowSizeOf}; use servo_arc::Arc; @@ -23,7 +23,7 @@ use std::fmt::{self, Write}; /// /// We do not support pseudo selectors yet. /// [page-selectors]: https://drafts.csswg.org/css2/page.html#page-selectors -#[derive(Clone, Debug, Eq, MallocSizeOf, PartialEq, SpecifiedValueInfo, ToShmem)] +#[derive(Clone, Debug, Eq, MallocSizeOf, PartialEq, SpecifiedValueInfo, ToCss, ToShmem)] pub struct PageSelector(pub AtomIdent); impl PageSelector { @@ -46,15 +46,6 @@ impl Parse for PageSelector { } } -impl ToCss for PageSelector { - fn to_css(&self, dest: &mut CssWriter) -> fmt::Result - where - W: Write - { - (&self.0).to_css(dest) - } -} - /// A list of [`@page`][page selectors] /// /// [page-selectors]: https://drafts.csswg.org/css2/page.html#page-selectors diff --git a/servo/components/style/stylesheets/scroll_timeline_rule.rs b/servo/components/style/stylesheets/scroll_timeline_rule.rs index 589814eff204..12b0d2013a6f 100644 --- a/servo/components/style/stylesheets/scroll_timeline_rule.rs +++ b/servo/components/style/stylesheets/scroll_timeline_rule.rs @@ -324,7 +324,6 @@ impl ToCss for ScrollTimelineSelector { where W: Write, { - use crate::cssparser::ToCss as CssparserToCss; dest.write_str("selector(")?; dest.write_char('#')?; self.0.to_css(dest)?; diff --git a/servo/components/style/values/mod.rs b/servo/components/style/values/mod.rs index ad09320c42b3..050efaa8e3eb 100644 --- a/servo/components/style/values/mod.rs +++ b/servo/components/style/values/mod.rs @@ -293,6 +293,16 @@ impl cssparser::ToCss for AtomIdent { } } +#[cfg(feature = "gecko")] +impl style_traits::ToCss for AtomIdent { + fn to_css(&self, dest: &mut CssWriter) -> fmt::Result + where + W: Write, + { + cssparser::ToCss::to_css(self, dest) + } +} + #[cfg(feature = "gecko")] impl PrecomputedHash for AtomIdent { #[inline] diff --git a/taskcluster/ci/release-msix-push/kind.yml b/taskcluster/ci/release-msix-push/kind.yml new file mode 100644 index 000000000000..712b6d7a16f5 --- /dev/null +++ b/taskcluster/ci/release-msix-push/kind.yml @@ -0,0 +1,41 @@ +# 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/. +--- +loader: gecko_taskgraph.loader.transform:loader + +transforms: + - gecko_taskgraph.transforms.release:run_on_releases + - gecko_taskgraph.transforms.release_deps:transforms + - gecko_taskgraph.transforms.release_msix_push:transforms + - gecko_taskgraph.transforms.task:transforms + +kind-dependencies: + - repackage-shippable-l10n-msix + +job-defaults: + description: Pushes msix archives to Microsoft Store + run-on-projects: [] # to make sure this never runs as part of CI + run-on-releases: [beta] + shipping-phase: ship + treeherder: + platform: win32-shippable/opt + kind: build + tier: 2 + worker-type: + by-release-level: + production: scriptworker-k8s/gecko-3-pushmsix + staging: scriptworker-k8s/gecko-1-pushmsix + worker: + implementation: push-msix + channel: + by-release-type: + beta: beta + release: release + default: mock + +jobs: + firefox: + shipping-product: firefox + treeherder: + symbol: MSIX(push) diff --git a/taskcluster/docs/kinds.rst b/taskcluster/docs/kinds.rst index bdf8ed921f4d..12ac2556d273 100644 --- a/taskcluster/docs/kinds.rst +++ b/taskcluster/docs/kinds.rst @@ -565,6 +565,10 @@ repackage-signing-shippable-l10n-msix Repackage-signing-shippable-l10n-msix takes Windows MSIX packages produced in ```repackage-signing-shippable-l10n-msix``` and signs them. +release-msix-push +-------------------- +Pushes msix repackage to the Microsoft Store. + repo-update ----------- Repo-Update tasks are tasks that perform some action on the project repo itself, diff --git a/taskcluster/gecko_taskgraph/transforms/release_msix_push.py b/taskcluster/gecko_taskgraph/transforms/release_msix_push.py new file mode 100644 index 000000000000..582fcd2b0b8a --- /dev/null +++ b/taskcluster/gecko_taskgraph/transforms/release_msix_push.py @@ -0,0 +1,78 @@ +# 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/. +""" +Transform the release-msix-push kind into an actual task description. +""" + + +from gecko_taskgraph.transforms.base import TransformSequence +from gecko_taskgraph.transforms.task import task_description_schema +from gecko_taskgraph.util.attributes import release_level +from gecko_taskgraph.util.schema import optionally_keyed_by, resolve_keyed_by, Schema +from gecko_taskgraph.util.scriptworker import add_scope_prefix + +from voluptuous import Optional, Required + +push_msix_description_schema = Schema( + { + Required("name"): str, + Required("job-from"): task_description_schema["job-from"], + Required("dependencies"): task_description_schema["dependencies"], + Required("description"): task_description_schema["description"], + Required("treeherder"): task_description_schema["treeherder"], + Required("run-on-projects"): task_description_schema["run-on-projects"], + Required("worker-type"): optionally_keyed_by("release-level", str), + Required("worker"): object, + Optional("scopes"): [str], + Required("shipping-phase"): task_description_schema["shipping-phase"], + Required("shipping-product"): task_description_schema["shipping-product"], + Optional("extra"): task_description_schema["extra"], + Optional("attributes"): task_description_schema["attributes"], + } +) + +transforms = TransformSequence() +transforms.add_validate(push_msix_description_schema) + + +@transforms.add +def make_task_description(config, jobs): + for job in jobs: + + job["worker"]["upstream-artifacts"] = generate_upstream_artifacts( + job["dependencies"] + ) + + resolve_keyed_by( + job, + "worker.channel", + item_name=job["name"], + **{"release-type": config.params["release_type"]}, + ) + resolve_keyed_by( + job, + "worker-type", + item_name=job["name"], + **{"release-level": release_level(config.params["project"])}, + ) + if release_level(config.params["project"]) == "production": + job.setdefault("scopes", []).append( + add_scope_prefix( + config, + "microsoftstore:{}".format(job["worker"]["channel"]), + ) + ) + + yield job + + +def generate_upstream_artifacts(dependencies): + return [ + { + "taskId": {"task-reference": f"<{task_kind}>"}, + "taskType": "build", + "paths": ["public/build/target.store.msix"], + } + for task_kind in dependencies.keys() + ] diff --git a/taskcluster/gecko_taskgraph/transforms/task.py b/taskcluster/gecko_taskgraph/transforms/task.py index 7bb88cd3a273..f55506ef5985 100644 --- a/taskcluster/gecko_taskgraph/transforms/task.py +++ b/taskcluster/gecko_taskgraph/transforms/task.py @@ -1203,6 +1203,28 @@ def build_push_flatpak_payload(config, task, task_def): } +@payload_builder( + "push-msix", + schema={ + Required("channel"): str, + Required("upstream-artifacts"): [ + { + Required("taskId"): taskref_or_string, + Required("taskType"): str, + Required("paths"): [str], + } + ], + }, +) +def build_push_msix_payload(config, task, task_def): + worker = task["worker"] + + task_def["payload"] = { + "channel": worker["channel"], + "upstreamArtifacts": worker["upstream-artifacts"], + } + + @payload_builder( "shipit-shipped", schema={ diff --git a/testing/marionette/client/marionette_driver/marionette.py b/testing/marionette/client/marionette_driver/marionette.py index 5ed471a345e9..16e6a24de921 100644 --- a/testing/marionette/client/marionette_driver/marionette.py +++ b/testing/marionette/client/marionette_driver/marionette.py @@ -492,6 +492,7 @@ class Marionette(object): self.bin = bin self.client = None self.instance = None + self.requested_capabilities = None self.session = None self.session_id = None self.process_id = None @@ -944,7 +945,7 @@ class Marionette(object): self.delete_session() self.instance.restart(prefs) self.raise_for_port() - self.start_session() + self.start_session(self.requested_capabilities) # Restore the context as used before the restart self.set_context(context) @@ -1085,8 +1086,8 @@ class Marionette(object): "restart() can only be called " "on Gecko instances launched by Marionette" ) - context = self._send_message("Marionette:GetContext", key="value") + context = self._send_message("Marionette:GetContext", key="value") restart_details = {"cause": "restart", "forced": False} # Safe mode is only available with in_app restarts. @@ -1167,7 +1168,7 @@ class Marionette(object): "restarting the process".format(restart_details["cause"]) ) - self.start_session() + self.start_session(self.requested_capabilities) # Restore the context as used before the restart self.set_context(context) @@ -1203,6 +1204,7 @@ class Marionette(object): """ if capabilities is None: capabilities = {"strictFileInteractability": True} + self.requested_capabilities = capabilities if timeout is None: timeout = self.startup_timeout diff --git a/testing/marionette/harness/marionette_harness/tests/unit/test_prefs_enforce.py b/testing/marionette/harness/marionette_harness/tests/unit/test_prefs_enforce.py index 6673e27d7398..d7c4618b206f 100644 --- a/testing/marionette/harness/marionette_harness/tests/unit/test_prefs_enforce.py +++ b/testing/marionette/harness/marionette_harness/tests/unit/test_prefs_enforce.py @@ -12,34 +12,45 @@ from marionette_harness import MarionetteTestCase class TestEnforcePreferences(MarionetteTestCase): def setUp(self): super(TestEnforcePreferences, self).setUp() - - self.marionette.enforce_gecko_prefs( - { - "marionette.test.bool": True, - "marionette.test.int": 3, - "marionette.test.string": "testing", - } - ) self.marionette.set_context("chrome") def tearDown(self): - self.marionette.quit(clean=True) + self.marionette.restart(clean=True) super(TestEnforcePreferences, self).tearDown() + def enforce_prefs(self, prefs=None): + test_prefs = { + "marionette.test.bool": True, + "marionette.test.int": 3, + "marionette.test.string": "testing", + } + + self.marionette.enforce_gecko_prefs(prefs or test_prefs) + def test_preferences_are_set(self): + self.enforce_prefs() self.assertTrue(self.marionette.get_pref("marionette.test.bool")) self.assertEqual(self.marionette.get_pref("marionette.test.string"), "testing") self.assertEqual(self.marionette.get_pref("marionette.test.int"), 3) - def test_change_preference(self): + def test_change_enforced_preference(self): + self.enforce_prefs() self.assertTrue(self.marionette.get_pref("marionette.test.bool")) - self.marionette.enforce_gecko_prefs({"marionette.test.bool": False}) - + self.enforce_prefs({"marionette.test.bool": False}) self.assertFalse(self.marionette.get_pref("marionette.test.bool")) - def test_restart_with_clean_profile(self): - self.marionette.restart(clean=True) + def test_restart_with_clean_profile_after_enforce_prefs(self): + self.enforce_prefs() + self.assertTrue(self.marionette.get_pref("marionette.test.bool")) + self.marionette.restart(clean=True) self.assertEqual(self.marionette.get_pref("marionette.test.bool"), None) + + def test_restart_preserves_requested_capabilities(self): + self.marionette.delete_session() + self.marionette.start_session(capabilities={"moz:fooBar": True}) + + self.enforce_prefs() + self.assertEqual(self.marionette.session.get("moz:fooBar"), True) diff --git a/testing/marionette/harness/marionette_harness/tests/unit/test_profile_management.py b/testing/marionette/harness/marionette_harness/tests/unit/test_profile_management.py index 2230afea3d1c..c39977a5dea3 100644 --- a/testing/marionette/harness/marionette_harness/tests/unit/test_profile_management.py +++ b/testing/marionette/harness/marionette_harness/tests/unit/test_profile_management.py @@ -64,7 +64,11 @@ class ExternalProfileMixin(object): tmp_dir = tempfile.mkdtemp(suffix="external") shutil.rmtree(tmp_dir, ignore_errors=True) - self.external_profile = mozprofile.Profile(profile=tmp_dir) + # Re-use all the required profile arguments (preferences) + profile_args = self.marionette.instance.profile_args + profile_args["profile"] = tmp_dir + self.external_profile = mozprofile.Profile(**profile_args) + # Prevent profile from being removed during cleanup self.external_profile.create_new = False @@ -211,6 +215,9 @@ class TestSwitchProfileWithoutWorkspace(ExternalProfileMixin, BaseProfileManagem self.assertEqual(self.profile_path, self.external_profile.profile) self.assertFalse(os.path.exists(self.orig_profile_path)) + # Check that required preferences have been correctly set + self.assertFalse(self.marionette.get_pref("remote.prefs.recommended")) + # Set a new profile and ensure the external profile has not been deleted self.marionette.quit() self.marionette.instance.profile = None diff --git a/testing/marionette/harness/marionette_harness/tests/unit/test_quit_restart.py b/testing/marionette/harness/marionette_harness/tests/unit/test_quit_restart.py index 2308d69a5f53..15cabac71bb5 100644 --- a/testing/marionette/harness/marionette_harness/tests/unit/test_quit_restart.py +++ b/testing/marionette/harness/marionette_harness/tests/unit/test_quit_restart.py @@ -181,6 +181,13 @@ class TestQuitRestart(MarionetteTestCase): ): self.marionette.restart(in_app=True, clean=True) + def test_restart_preserves_requested_capabilities(self): + self.marionette.delete_session() + self.marionette.start_session(capabilities={"moz:fooBar": True}) + + self.marionette.restart(in_app=False) + self.assertEqual(self.marionette.session.get("moz:fooBar"), True) + def test_restart_safe_mode(self): try: self.assertFalse(self.is_safe_mode, "Safe Mode is unexpectedly enabled") @@ -279,6 +286,13 @@ class TestQuitRestart(MarionetteTestCase): self.marionette.shutdown_timeout = timeout_shutdown self.marionette.startup_timeout = timeout_startup + def test_in_app_restart_preserves_requested_capabilities(self): + self.marionette.delete_session() + self.marionette.start_session(capabilities={"moz:fooBar": True}) + + self.marionette.restart(in_app=True) + self.assertEqual(self.marionette.session.get("moz:fooBar"), True) + def test_in_app_quit(self): details = self.marionette.quit(in_app=True) diff --git a/testing/web-platform/meta/css/css-text/hyphens/__dir__.ini b/testing/web-platform/meta/css/css-text/hyphens/__dir__.ini deleted file mode 100644 index f91f5ebb2bb5..000000000000 --- a/testing/web-platform/meta/css/css-text/hyphens/__dir__.ini +++ /dev/null @@ -1 +0,0 @@ -prefs: [layout.css.hyphenate-character.enabled:true] diff --git a/testing/web-platform/meta/css/css-text/parsing/__dir__.ini b/testing/web-platform/meta/css/css-text/parsing/__dir__.ini deleted file mode 100644 index f91f5ebb2bb5..000000000000 --- a/testing/web-platform/meta/css/css-text/parsing/__dir__.ini +++ /dev/null @@ -1 +0,0 @@ -prefs: [layout.css.hyphenate-character.enabled:true] diff --git a/testing/web-platform/meta/html/canvas/offscreen/__dir__.ini b/testing/web-platform/meta/html/canvas/offscreen/__dir__.ini index 7a12bab3cd9b..8ef8725bdd06 100644 --- a/testing/web-platform/meta/html/canvas/offscreen/__dir__.ini +++ b/testing/web-platform/meta/html/canvas/offscreen/__dir__.ini @@ -1 +1 @@ -prefs: [gfx.offscreencanvas.enabled:true] +prefs: [dom.workers.requestAnimationFrame:true, gfx.offscreencanvas.enabled:true] diff --git a/testing/web-platform/meta/html/dom/idlharness.worker.js.ini b/testing/web-platform/meta/html/dom/idlharness.worker.js.ini index 41abdc1a0253..8af5600e7022 100644 --- a/testing/web-platform/meta/html/dom/idlharness.worker.js.ini +++ b/testing/web-platform/meta/html/dom/idlharness.worker.js.ini @@ -1,5 +1,5 @@ [idlharness.worker.html] - prefs: [gfx.offscreencanvas.enabled:true] + prefs: [dom.workers.requestAnimationFrame:true, gfx.offscreencanvas.enabled:true] [OffscreenCanvasRenderingContext2D interface: operation lineTo(unrestricted double, unrestricted double)] expected: FAIL diff --git a/testing/web-platform/meta/visual-viewport/resize-event-order.html.ini b/testing/web-platform/meta/visual-viewport/resize-event-order.html.ini index 18c2a0acaca5..fa04abb14230 100644 --- a/testing/web-platform/meta/visual-viewport/resize-event-order.html.ini +++ b/testing/web-platform/meta/visual-viewport/resize-event-order.html.ini @@ -12,7 +12,7 @@ if (os == "win") and not debug and not fission and (processor == "x86_64"): [TIMEOUT, PASS, FAIL] if (os == "android") and debug and not swgl: [TIMEOUT, PASS, FAIL] if (os == "android") and debug and swgl: [TIMEOUT, PASS, FAIL] - if (os == "win") and not debug and fission: [FAIL, TIMEOUT] + if (os == "win") and not debug and fission: [FAIL, PASS, TIMEOUT] if (os == "android") and not debug: [TIMEOUT, FAIL] FAIL diff --git a/testing/web-platform/meta/workers/WorkerGlobalScope_requestAnimationFrame.tentative.worker.js.ini b/testing/web-platform/meta/workers/WorkerGlobalScope_requestAnimationFrame.tentative.worker.js.ini index 0c9d00d837ec..e811437aa8e0 100644 --- a/testing/web-platform/meta/workers/WorkerGlobalScope_requestAnimationFrame.tentative.worker.js.ini +++ b/testing/web-platform/meta/workers/WorkerGlobalScope_requestAnimationFrame.tentative.worker.js.ini @@ -1,2 +1,2 @@ [WorkerGlobalScope_requestAnimationFrame.tentative.worker.html] - prefs: [gfx.offscreencanvas.enabled:true] + prefs: [dom.workers.requestAnimationFrame:true] diff --git a/toolkit/components/extensions/parent/.eslintrc.js b/toolkit/components/extensions/parent/.eslintrc.js index 356dda7c70e9..2af2a2b34b62 100644 --- a/toolkit/components/extensions/parent/.eslintrc.js +++ b/toolkit/components/extensions/parent/.eslintrc.js @@ -23,6 +23,7 @@ module.exports = { getCookieStoreIdForContainer: true, getCookieStoreIdForOriginAttributes: true, getCookieStoreIdForTab: true, + getOriginAttributesPatternForCookieStoreId: true, isContainerCookieStoreId: true, isDefaultCookieStoreId: true, isPrivateCookieStoreId: true, diff --git a/toolkit/components/extensions/parent/ext-toolkit.js b/toolkit/components/extensions/parent/ext-toolkit.js index b5c84678e01f..1878b750f9c7 100644 --- a/toolkit/components/extensions/parent/ext-toolkit.js +++ b/toolkit/components/extensions/parent/ext-toolkit.js @@ -29,6 +29,8 @@ var { ExtensionCommon } = ChromeUtils.import( "resource://gre/modules/ExtensionCommon.jsm" ); +var { ExtensionError } = ExtensionUtils; + global.EventEmitter = ExtensionCommon.EventEmitter; global.EventManager = ExtensionCommon.EventManager; @@ -103,3 +105,27 @@ global.isValidCookieStoreId = function(storeId) { isContainerCookieStoreId(storeId) ); }; + +global.getOriginAttributesPatternForCookieStoreId = function(cookieStoreId) { + if (isDefaultCookieStoreId(cookieStoreId)) { + return { + userContextId: Ci.nsIScriptSecurityManager.DEFAULT_USER_CONTEXT_ID, + privateBrowsingId: + Ci.nsIScriptSecurityManager.DEFAULT_PRIVATE_BROWSING_ID, + }; + } + if (isPrivateCookieStoreId(cookieStoreId)) { + return { + userContextId: Ci.nsIScriptSecurityManager.DEFAULT_USER_CONTEXT_ID, + privateBrowsingId: 1, + }; + } + if (isContainerCookieStoreId(cookieStoreId)) { + let userContextId = getContainerForCookieStoreId(cookieStoreId); + if (userContextId !== null) { + return { userContextId }; + } + } + + throw new ExtensionError("Invalid cookieStoreId"); +}; diff --git a/toolkit/components/extensions/parent/ext-userScripts.js b/toolkit/components/extensions/parent/ext-userScripts.js index f971ea7367e7..9ca09e0c2f00 100644 --- a/toolkit/components/extensions/parent/ext-userScripts.js +++ b/toolkit/components/extensions/parent/ext-userScripts.js @@ -48,8 +48,18 @@ class UserScriptParent { userScriptOptions: { scriptMetadata: details.scriptMetadata, }, + originAttributesPatterns: null, }; + if (details.cookieStoreId != null) { + const cookieStoreIds = Array.isArray(details.cookieStoreId) + ? details.cookieStoreId + : [details.cookieStoreId]; + options.originAttributesPatterns = cookieStoreIds.map(cookieStoreId => + getOriginAttributesPatternForCookieStoreId(cookieStoreId) + ); + } + return options; } diff --git a/toolkit/components/extensions/schemas/user_scripts.json b/toolkit/components/extensions/schemas/user_scripts.json index f4ecbd367e90..feb231ca290f 100644 --- a/toolkit/components/extensions/schemas/user_scripts.json +++ b/toolkit/components/extensions/schemas/user_scripts.json @@ -84,6 +84,20 @@ "default": "document_idle", "optional": true, "description": "The soonest that the JavaScript will be injected into the tab. Defaults to \"document_idle\"." + }, + "cookieStoreId": { + "choices": [ + { + "type": "array", + "minItems": 1, + "items": { "type": "string" } + }, + { + "type": "string" + } + ], + "optional": true, + "description": "limit the set of matched tabs to those that belong to the given cookie store id" } } }, diff --git a/toolkit/components/extensions/test/xpcshell/test_ext_userScripts_register.js b/toolkit/components/extensions/test/xpcshell/test_ext_userScripts_register.js new file mode 100644 index 000000000000..99bf39565149 --- /dev/null +++ b/toolkit/components/extensions/test/xpcshell/test_ext_userScripts_register.js @@ -0,0 +1,129 @@ +"use strict"; + +const { createAppInfo } = AddonTestUtils; + +AddonTestUtils.init(this); + +createAppInfo("xpcshell@tests.mozilla.org", "XPCShell", "1", "49"); + +const server = createHttpServer(); +server.registerDirectory("/data/", do_get_file("data")); + +const BASE_URL = `http://localhost:${server.identity.primaryPort}/data`; + +add_task(async function test_userscripts_register_cookieStoreId() { + async function background() { + const matches = [""]; + + await browser.test.assertRejects( + browser.userScripts.register({ + js: [{ code: "" }], + matches, + cookieStoreId: "not_a_valid_cookieStoreId", + }), + /Invalid cookieStoreId/, + "userScript.register with an invalid cookieStoreId" + ); + + await browser.test.assertRejects( + browser.userScripts.register({ + js: [{ code: "" }], + matches, + cookieStoreId: "", + }), + /Invalid cookieStoreId/, + "contentScript.register with an invalid cookieStoreId" + ); + + let cookieStoreIdJSArray = [ + { + id: "firefox-container-1", + code: `document.body.textContent += "1"`, + }, + { + id: ["firefox-container-2", "firefox-container-3"], + code: `document.body.textContent += "2-3"`, + }, + { + id: "firefox-private", + code: `document.body.textContent += "private"`, + }, + { + id: "firefox-default", + code: `document.body.textContent += "default"`, + }, + ]; + + for (let { id, code } of cookieStoreIdJSArray) { + await browser.contentScripts.register({ + js: [{ code }], + matches, + runAt: "document_end", + cookieStoreId: id, + }); + } + + browser.test.sendMessage("background_ready"); + } + + const extension = ExtensionTestUtils.loadExtension({ + manifest: { + permissions: [""], + user_scripts: {}, + }, + background, + incognitoOverride: "spanning", + }); + + await extension.startup(); + await extension.awaitMessage("background_ready"); + + let testCases = [ + { + contentPageOptions: { userContextId: 0 }, + expectedTextContent: "default", + }, + { + contentPageOptions: { userContextId: 1 }, + expectedTextContent: "1", + }, + { + contentPageOptions: { userContextId: 2 }, + expectedTextContent: "2-3", + }, + { + contentPageOptions: { userContextId: 3 }, + expectedTextContent: "2-3", + }, + { + contentPageOptions: { userContextId: 4 }, + expectedTextContent: "", + }, + { + contentPageOptions: { privateBrowsing: true }, + expectedTextContent: "private", + }, + ]; + + for (let test of testCases) { + let contentPage = await ExtensionTestUtils.loadContentPage( + `${BASE_URL}/file_sample.html`, + test.contentPageOptions + ); + + let result = await contentPage.spawn(null, () => { + let textContent = this.content.document.body.textContent; + // Omit the default content from file_sample.html. + return textContent.replace("\n\nSample text\n\n\n\n", ""); + }); + equal( + result, + test.expectedTextContent, + `Expected textContent on content page` + ); + + await contentPage.close(); + } + + await extension.unload(); +}); diff --git a/toolkit/components/extensions/test/xpcshell/xpcshell-common.ini b/toolkit/components/extensions/test/xpcshell/xpcshell-common.ini index fd176da73f34..7b3d333585d2 100644 --- a/toolkit/components/extensions/test/xpcshell/xpcshell-common.ini +++ b/toolkit/components/extensions/test/xpcshell/xpcshell-common.ini @@ -233,6 +233,7 @@ skip-if = true # Too frequent intermittent failures [test_ext_userScripts.js] skip-if = os == "android" # Bug 1700482 [test_ext_userScripts_exports.js] +[test_ext_userScripts_register.js] [test_ext_userScripts_telemetry.js] skip-if = os == "android" # Bug 1700482 [test_ext_webRequest_auth.js] diff --git a/toolkit/components/extensions/webidl-api/ExtensionBrowser.h b/toolkit/components/extensions/webidl-api/ExtensionBrowser.h index f698fa466546..7ab6435381a1 100644 --- a/toolkit/components/extensions/webidl-api/ExtensionBrowser.h +++ b/toolkit/components/extensions/webidl-api/ExtensionBrowser.h @@ -9,6 +9,7 @@ #include "nsCOMPtr.h" #include "nsISupports.h" +#include "nsTHashMap.h" #include "nsWrapperCache.h" class nsIGlobalObject; diff --git a/toolkit/components/prompts/content/selectDialog.js b/toolkit/components/prompts/content/selectDialog.js index 0d5e99e495e2..f5d757d6e2f8 100644 --- a/toolkit/components/prompts/content/selectDialog.js +++ b/toolkit/components/prompts/content/selectDialog.js @@ -61,7 +61,7 @@ function onLoad() { try { if (!args.openedWithTabDialog) { Cc["@mozilla.org/sound;1"] - .createInstance(Ci.nsISound) + .getService(Ci.nsISound) .playEventSound(Ci.nsISound.EVENT_SELECT_DIALOG_OPEN); } } catch (e) {} diff --git a/toolkit/components/prompts/src/CommonDialog.jsm b/toolkit/components/prompts/src/CommonDialog.jsm index 7cbf3b885db9..26d7c9a8bb91 100644 --- a/toolkit/components/prompts/src/CommonDialog.jsm +++ b/toolkit/components/prompts/src/CommonDialog.jsm @@ -236,7 +236,7 @@ CommonDialog.prototype = { try { if (commonDialogEl && this.soundID && !this.args.openedWithTabDialog) { Cc["@mozilla.org/sound;1"] - .createInstance(Ci.nsISound) + .getService(Ci.nsISound) .playEventSound(this.soundID); } } catch (e) { diff --git a/toolkit/components/typeaheadfind/nsTypeAheadFind.cpp b/toolkit/components/typeaheadfind/nsTypeAheadFind.cpp index a2c47ef958a4..8d4b00d5e1fd 100644 --- a/toolkit/components/typeaheadfind/nsTypeAheadFind.cpp +++ b/toolkit/components/typeaheadfind/nsTypeAheadFind.cpp @@ -40,6 +40,7 @@ #include "nsIFormControl.h" #include "nsNameSpaceManager.h" #include "nsIObserverService.h" +#include "nsISound.h" #include "nsFocusManager.h" #include "mozilla/dom/Element.h" #include "mozilla/dom/HTMLInputElement.h" @@ -67,8 +68,8 @@ NS_IMPL_CYCLE_COLLECTING_RELEASE(nsTypeAheadFind) NS_IMPL_CYCLE_COLLECTION_WEAK(nsTypeAheadFind, mFoundLink, mFoundEditable, mCurrentWindow, mStartFindRange, mSearchRange, - mStartPointRange, mEndPointRange, mSoundInterface, - mFind, mFoundRange) + mStartPointRange, mEndPointRange, mFind, + mFoundRange) #define NS_FIND_CONTRACTID "@mozilla.org/embedcomp/rangefind;1" @@ -77,7 +78,6 @@ nsTypeAheadFind::nsTypeAheadFind() mCaretBrowsingOn(false), mDidAddObservers(false), mLastFindLength(0), - mIsSoundInitialized(false), mCaseSensitive(false), mEntireWord(false), mMatchDiacritics(false) {} @@ -120,18 +120,6 @@ nsresult nsTypeAheadFind::Init(nsIDocShell* aDocShell) { } } - if (!mIsSoundInitialized && !mNotFoundSoundURL.IsEmpty()) { - // This makes sure system sound library is loaded so that - // there's no lag before the first sound is played - // by waiting for the first keystroke, we still get the startup time - // benefits. - mIsSoundInitialized = true; - mSoundInterface = do_CreateInstance("@mozilla.org/sound;1"); - if (mSoundInterface && !mNotFoundSoundURL.EqualsLiteral("beep")) { - mSoundInterface->Init(); - } - } - return NS_OK; } @@ -151,19 +139,6 @@ nsresult nsTypeAheadFind::PrefsReset() { mNotFoundSoundURL = soundStr; - if (!mNotFoundSoundURL.IsEmpty() && - !mNotFoundSoundURL.EqualsLiteral("beep")) { - if (!mSoundInterface) { - mSoundInterface = do_CreateInstance("@mozilla.org/sound;1"); - } - - // Init to load the system sound library if the lib is not ready - if (mSoundInterface) { - mIsSoundInitialized = true; - mSoundInterface->Init(); - } - } - prefBranch->GetBoolPref("accessibility.browsewithcaret", &mCaretBrowsingOn); return NS_OK; @@ -308,14 +283,11 @@ void nsTypeAheadFind::PlayNotFoundSound() { if (mNotFoundSoundURL.IsEmpty()) // no sound return; - if (!mSoundInterface) - mSoundInterface = do_CreateInstance("@mozilla.org/sound;1"); - - if (mSoundInterface) { - mIsSoundInitialized = true; + nsCOMPtr soundInterface = do_GetService("@mozilla.org/sound;1"); + if (soundInterface) { if (mNotFoundSoundURL.EqualsLiteral("beep")) { - mSoundInterface->Beep(); + soundInterface->Beep(); return; } @@ -327,7 +299,7 @@ void nsTypeAheadFind::PlayNotFoundSound() { NS_NewURI(getter_AddRefs(soundURI), mNotFoundSoundURL); nsCOMPtr soundURL(do_QueryInterface(soundURI)); - if (soundURL) mSoundInterface->Play(soundURL); + if (soundURL) soundInterface->Play(soundURL); } } diff --git a/toolkit/components/typeaheadfind/nsTypeAheadFind.h b/toolkit/components/typeaheadfind/nsTypeAheadFind.h index b2d28efcb60b..71acd9975a5d 100644 --- a/toolkit/components/typeaheadfind/nsTypeAheadFind.h +++ b/toolkit/components/typeaheadfind/nsTypeAheadFind.h @@ -13,7 +13,6 @@ #include "nsIWebBrowserFind.h" #include "nsWeakReference.h" #include "nsITypeAheadFind.h" -#include "nsISound.h" class nsPIDOMWindowInner; class nsPresContext; @@ -100,11 +99,6 @@ class nsTypeAheadFind : public nsITypeAheadFind, // used for disabling the "not found" sound when using backspace or delete uint32_t mLastFindLength; - // Sound is played asynchronously on some platforms. - // If we destroy mSoundInterface before sound has played, it won't play - nsCOMPtr mSoundInterface; - bool mIsSoundInitialized; - // where selection was when user started the find RefPtr mStartFindRange; RefPtr mSearchRange; diff --git a/toolkit/modules/FinderParent.jsm b/toolkit/modules/FinderParent.jsm index b6443769b343..82ffbe0f319f 100644 --- a/toolkit/modules/FinderParent.jsm +++ b/toolkit/modules/FinderParent.jsm @@ -631,7 +631,7 @@ FinderParent.prototype = { initNotFoundSound() { if (!gSound && isSoundEnabled && notFoundSoundURL) { try { - gSound = Cc["@mozilla.org/sound;1"].createInstance(Ci.nsISound); + gSound = Cc["@mozilla.org/sound;1"].getService(Ci.nsISound); gSound.init(); } catch (ex) {} } diff --git a/toolkit/modules/LayoutUtils.jsm b/toolkit/modules/LayoutUtils.jsm index fc6f9d558d87..9c5d6994877a 100644 --- a/toolkit/modules/LayoutUtils.jsm +++ b/toolkit/modules/LayoutUtils.jsm @@ -21,7 +21,7 @@ var LayoutUtils = { // We need to compensate for ancestor iframes in the same process // that might shift things over. - let parentFrame = win.frameElement; + let parentFrame = win.browsingContext?.embedderElement; while (parentFrame) { win = parentFrame.ownerGlobal; let cstyle = win.getComputedStyle(parentFrame); @@ -36,7 +36,7 @@ var LayoutUtils = { parseFloat(cstyle.borderTopWidth) + parseFloat(cstyle.paddingTop); - parentFrame = win.frameElement; + parentFrame = win.browsingContext?.embedderElement; } return aElement.ownerGlobal.windowUtils.toScreenRectInCSSUnits( diff --git a/toolkit/toolkit.mozbuild b/toolkit/toolkit.mozbuild index e9f7e1bd28f6..1f3898c40f4a 100644 --- a/toolkit/toolkit.mozbuild +++ b/toolkit/toolkit.mozbuild @@ -193,3 +193,7 @@ if CONFIG['ENABLE_TESTS']: if CONFIG['FUZZING']: DIRS += ['/tools/fuzzing'] + +if CONFIG['MOZ_WAYLAND']: + DIRS += ['/media/mozva'] + diff --git a/tools/profiler/core/ProfilerThreadRegistration.cpp b/tools/profiler/core/ProfilerThreadRegistration.cpp index 57bcd41acc7f..fa105384bf96 100644 --- a/tools/profiler/core/ProfilerThreadRegistration.cpp +++ b/tools/profiler/core/ProfilerThreadRegistration.cpp @@ -164,6 +164,21 @@ void ThreadRegistration::UnregisterThread() { // `RegisterThread()` that created this ThreadRegistration on the heap. // Just delete this root registration, it will de-register itself from the // TLS (and from the Profiler). + if (NS_WARN_IF(rootRegistration->mData.mProfilingStack.stackPointer != + 0u)) { + // A non-empty stack is dangerous to destroy (probable UAF when remaining + // labels remove themselves), so it's safer to let the registration leak. + // TODO: Remove this temporary fix once there is a better solution to the + // problem of seemingly mismatched label pushes&pops. See bug 1749978, + // comment 8 for the plan of attack, and its follow-up bugs. + // Capture stack in a marker for debugging. We don't know what name was + // used in the related RegisterThread(). + PROFILER_MARKER_UNTYPED( + "ThreadRegistration::UnregisterThread(), last heap-allocated " + "registration not deleted because of non-empty profiling stack", + OTHER_Profiling, MarkerStack::Capture()); + return; + } delete rootRegistration; return; } diff --git a/tools/rewriting/ThirdPartyPaths.txt b/tools/rewriting/ThirdPartyPaths.txt index aa4137e294c7..88eec71a3e2f 100644 --- a/tools/rewriting/ThirdPartyPaths.txt +++ b/tools/rewriting/ThirdPartyPaths.txt @@ -119,6 +119,7 @@ media/libvorbis/ media/libvpx/ media/libwebp/ media/libyuv/ +media/mozva/va media/mp4parse-rust/ media/openmax_dl/ media/openmax_il/ diff --git a/widget/gtk/GfxInfo.cpp b/widget/gtk/GfxInfo.cpp index 6931beec7893..889b253ce9a4 100644 --- a/widget/gtk/GfxInfo.cpp +++ b/widget/gtk/GfxInfo.cpp @@ -757,14 +757,6 @@ const nsTArray& GfxInfo::GetGfxDriverInfo() { V(21, 0, 0, 0), "FEATURE_FAILURE_WEBRENDER_BUG_1635186", "Mesa 21.0.0.0"); - APPEND_TO_DRIVER_BLOCKLIST_EXT( - OperatingSystem::Linux, ScreenSizeStatus::All, BatteryStatus::All, - DesktopEnvironment::All, WindowProtocol::XWayland, - DriverVendor::NonMesaAll, DeviceFamily::NvidiaAll, - nsIGfxInfo::FEATURE_WEBRENDER, - nsIGfxInfo::FEATURE_BLOCKED_DRIVER_VERSION, DRIVER_LESS_THAN, - V(470, 82, 0, 0), "FEATURE_FAILURE_WEBRENDER_BUG_1635186", "470.82.0"); - //////////////////////////////////// // FEATURE_WEBRENDER - ALLOWLIST @@ -793,13 +785,13 @@ const nsTArray& GfxInfo::GetGfxDriverInfo() { nsIGfxInfo::FEATURE_ALLOW_QUALIFIED, DRIVER_GREATER_THAN_OR_EQUAL, V(18, 2, 0, 0), "FEATURE_ROLLOUT_NVIDIA_MESA", "Mesa 18.2.0.0"); - // Nvidia proprietary driver baseline, see bug 1673752. + // Nvidia proprietary driver baseline, see bug 1742994. APPEND_TO_DRIVER_BLOCKLIST_EXT( OperatingSystem::Linux, ScreenSizeStatus::All, BatteryStatus::All, DesktopEnvironment::All, WindowProtocol::All, DriverVendor::NonMesaAll, DeviceFamily::NvidiaAll, nsIGfxInfo::FEATURE_WEBRENDER, nsIGfxInfo::FEATURE_ALLOW_QUALIFIED, DRIVER_GREATER_THAN_OR_EQUAL, - V(460, 32, 3, 0), "FEATURE_ROLLOUT_NVIDIA_BINARY", "460.32.03"); + V(470, 82, 0, 0), "FEATURE_ROLLOUT_NVIDIA_BINARY", "470.82.0"); // ATI Mesa baseline, chosen arbitrarily. APPEND_TO_DRIVER_BLOCKLIST_EXT( diff --git a/widget/gtk/nsLookAndFeel.cpp b/widget/gtk/nsLookAndFeel.cpp index 1f12f714edbb..279800514e55 100644 --- a/widget/gtk/nsLookAndFeel.cpp +++ b/widget/gtk/nsLookAndFeel.cpp @@ -1703,8 +1703,9 @@ void nsLookAndFeel::PerThemeData::Init() { style = GetStyleContext(MOZ_GTK_MENUITEM); gtk_style_context_get_color(style, GTK_STATE_FLAG_PRELIGHT, &color); mMenuHoverText = GDK_RGBA_TO_NS_RGBA(color); - mMenuHover = - GetBackgroundColor(style, mMenuHoverText, GTK_STATE_FLAG_PRELIGHT); + mMenuHover = NS_ComposeColors( + mMenuBackground, + GetBackgroundColor(style, mMenuHoverText, GTK_STATE_FLAG_PRELIGHT)); GtkWidget* parent = gtk_fixed_new(); GtkWidget* window = gtk_window_new(GTK_WINDOW_POPUP);