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);