Merge autoland to mozilla-central. a=merge

This commit is contained in:
Marian-Vasile Laza 2022-01-24 23:40:54 +02:00
Родитель de9b832460 7dade2da45
Коммит 02bd3e02b7
173 изменённых файлов: 3257 добавлений и 1979 удалений

Просмотреть файл

@ -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/.*

Просмотреть файл

@ -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]),

Просмотреть файл

@ -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

Просмотреть файл

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

Просмотреть файл

@ -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,
}
);
}

Просмотреть файл

@ -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",

Просмотреть файл

@ -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"] += [

Просмотреть файл

@ -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

Просмотреть файл

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

Просмотреть файл

@ -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]

Просмотреть файл

@ -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();
});

Просмотреть файл

@ -3639,6 +3639,8 @@ Toolbox.prototype = {
);
this.commands.targetCommand.selectTarget(target);
this.emit("switched-host-to-tab");
},
/**

Просмотреть файл

@ -21,12 +21,15 @@ const TEST_URI = `
.shift {
margin-left: 300px;
}
.has-before::before {
content: "-";
}
</style>
<div id="top" class="parent">
<div id="child1" class="fixed shift">
<div id="child2" class="fixed"></div>
</div>
<div id="child3" class="shift">
<div id="child3" class="shift has-before">
<div id="child4" class="fixed"></div>
</div>
</div>

Просмотреть файл

@ -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 = `
<body>
<button id="foo">Button</button>
<button id="btn-eval">Eval</button>
<button id="btn-dom0" onclick="console.info('bloup')">DOM0</button>
<script>
var script = \`
function foo() {
@ -14,7 +27,7 @@ var script = \`
\`;
eval(script);
var button = document.getElementById("foo");
var button = document.getElementById("btn-eval");
button.addEventListener("click", foo, false);
</script>
</body>`;
@ -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;
}

Просмотреть файл

@ -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.
*/

Просмотреть файл

@ -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;
}

Просмотреть файл

@ -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"

Просмотреть файл

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

Просмотреть файл

@ -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 {

Просмотреть файл

@ -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,

Просмотреть файл

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

Просмотреть файл

@ -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],

Просмотреть файл

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

Просмотреть файл

@ -110,7 +110,6 @@ NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN_INHERITED(AbortSignal,
DOMEventTargetHelper)
AbortSignalImpl::Unlink(static_cast<AbortSignalImpl*>(tmp));
AbortFollower::Unlink(static_cast<AbortFollower*>(tmp));
NS_IMPL_CYCLE_COLLECTION_UNLINK_END
NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(AbortSignal)

Просмотреть файл

@ -284,6 +284,10 @@ DOMInterfaces = {
'concrete': True,
},
'FileSystemHandle': {
'concrete': True,
},
'FluentBundle': {
'nativeType': 'mozilla::intl::FluentBundle',
},

Просмотреть файл

@ -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.

Просмотреть файл

@ -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<FrameCaptureState>* 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<FrameCaptureState> mFrameCaptureState;
/**
* We also have a device space pathbuilder. The reason for this is as

Просмотреть файл

@ -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<nsIURI> uri = principal->GetURI();
return nsContentUtils::IsURIInList(uri, allowlist);
}
bool CheckWriteOnlySecurity(bool aCORSUsed, nsIPrincipal* aPrincipal,
bool aHadCrossOriginRedirects) {
if (!aPrincipal) {

Просмотреть файл

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

Просмотреть файл

@ -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<FrameCaptureState>* 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<FrameCaptureState> mFrameCaptureState = {
FrameCaptureState::CLEAN, "ClientWebGLContext::mFrameCaptureState"};
// -------------------------------------------------------------------------
// WebGLRenderingContext Basic Properties and Methods

Просмотреть файл

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

Просмотреть файл

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

Просмотреть файл

@ -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<FrameCaptureState>* GetFrameCaptureState() override {
return &mFrameCaptureState;
}
protected:
already_AddRefed<gfx::DataSourceSurface> 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<FrameCaptureState> mFrameCaptureState;
};
} // namespace dom

Просмотреть файл

@ -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,

Просмотреть файл

@ -222,7 +222,7 @@ void WebGLContext::DestroyResourcesAndContext() {
void ClientWebGLContext::MarkCanvasDirty() {
if (!mCanvasElement && !mOffscreenCanvas) return;
mCapturedFrameInvalidated = true;
mFrameCaptureState = FrameCaptureState::DIRTY;
if (mIsCanvasDirty) return;
mIsCanvasDirty = true;

Просмотреть файл

@ -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<FrameCaptureState>* GetFrameCaptureState() = 0;
// Redraw the dirty rectangle of this canvas.
NS_IMETHOD Redraw(const gfxRect& dirty) = 0;

Просмотреть файл

@ -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]

Просмотреть файл

@ -8,13 +8,21 @@
<link rel="stylesheet" href="/tests/SimpleTest/test.css">
<body>
<script>
var c; // Canvas element captured by streams.
var h; // CaptureStreamTestHelper holding utility test functions.
var vauto; // Video element with captureStream stream in automatic mode.
var vmanual; // Video element with captureStream stream in manual (fps 0) mode.
var vrate; // Video element with captureStream stream with fixed frame rate.
SimpleTest.waitForExplicitFinish();
SimpleTest.requestFlakyTimeout("Ensuring nothing happens until timing out with good margin");
function checkDrawColorInitialRed() {
// CaptureStreamTestHelper holding utility test functions.
const h = new CaptureStreamTestHelper2D();
// Canvas element captured by streams.
const c = h.createAndAppendElement('canvas', 'c');
// Video element with captureStream stream in automatic mode.
const vauto = h.createAndAppendElement('video', 'vauto');
// Video element with captureStream stream in manual (fps 0) mode.
const vmanual = h.createAndAppendElement('video', 'vmanual');
// Video element with captureStream stream with fixed frame rate.
const vrate = h.createAndAppendElement('video', 'vrate');
async function checkDrawColorInitialRed() {
info("Checking that all video elements become red when initiated just after the first drawColor(red).");
h.drawColor(c, h.red);
@ -30,90 +38,94 @@ function checkDrawColorInitialRed() {
ok(h.isPixel(h.getPixel(vmanual), h.blackTransparent, 0),
"vmanual should not be drawn to before stable state");
return Promise.resolve()
.then(() => h.pixelMustBecome(vauto, h.red, {
infoString: "should become red automatically",
}))
.then(() => h.pixelMustBecome(vrate, h.red, {
infoString: "should become red automatically",
}))
.then(() => h.pixelMustBecome(vmanual, h.red, {
infoString: "should become red when we get to stable state (first frame)",
}));
await h.pixelMustBecome(vauto, h.red, {
infoString: "should become red automatically",
});
await h.pixelMustBecome(vrate, h.red, {
infoString: "should become red automatically",
});
await h.pixelMustBecome(vmanual, h.red, {
infoString: "should become red when we get to stable state (first frame)",
});
}
function checkDrawColorGreen() {
async function checkDrawColorGreen() {
info("Checking that drawing green propagates properly to video elements.");
var drawing = h.startDrawing(() => h.drawColor(c, h.green));
const drawing = h.startDrawing(() => h.drawColor(c, h.green));
return Promise.resolve()
.then(() => h.pixelMustBecome(vauto, h.green, {
try {
await h.pixelMustBecome(vauto, h.green, {
infoString: "should become green automatically",
}))
.then(() => h.pixelMustBecome(vrate, h.green, {
});
await h.pixelMustBecome(vrate, h.green, {
infoString: "should become green automatically",
}))
.then(() => h.pixelMustBecome(vmanual, h.red, {
});
await h.pixelMustBecome(vmanual, h.red, {
infoString: "should still be red",
}))
.then(() => h.requestFrame(vmanual))
.then(() => h.pixelMustBecome(vmanual, h.green, {
});
h.requestFrame(vmanual);
await h.pixelMustBecome(vmanual, h.green, {
infoString: "should become green after requstFrame()",
}))
.catch(err => ok(false, "checkDrawColorGreen failed: ", err))
.then(() => drawing.stop());
});
}
catch(err) {
ok(false, "checkDrawColorGreen failed: ", err);
}
drawing.stop();
}
function checkRequestFrameOrderGuarantee() {
async function checkRequestFrameOrderGuarantee() {
info("Checking that requestFrame() immediately after a drawColor() " +
"call results in the expected frame seen in the stream.");
return Promise.resolve()
.then(() => h.pixelMustBecome(vmanual, h.green, {
infoString: "should still be green",
}))
.then(() => h.drawColor(c, h.red)) // 1. Draw canvas red
.then(() => h.requestFrame(vmanual)) // 2. Immediately request a frame
.then(() => h.pixelMustBecome(vmanual, h.red, {
infoString: "should become red after call order test",
}));
await h.pixelMustBecome(vmanual, h.green, {
infoString: "should still be green",
});
h.drawColor(c, h.red); // 1. Draw canvas red
h.requestFrame(vmanual); // 2. Immediately request a frame
await h.pixelMustBecome(vmanual, h.red, {
infoString: "should become red after call order test",
});
}
function checkDrawImageNotCleanRed() {
async function checkDrawImageNotCleanRed() {
info("Checking that drawImage with not origin-clean image renders streams useless.");
var ctx = c.getContext('2d');
var notCleanRed = new Image();
var drawing;
const ctx = c.getContext('2d');
const notCleanRed = new Image();
return new Promise((resolve, reject) => {
await new Promise((resolve, reject) => {
notCleanRed.onload = resolve;
notCleanRed.onerror = () => reject(new Error("Failed to load tainted image."));
notCleanRed.src = "http://example.com/tests/dom/canvas/test/image_red_crossorigin_credentials.png";
document.body.appendChild(notCleanRed);
})
.then(() => drawing = h.startDrawing(() => ctx.drawImage(notCleanRed, 0, 0, c.width, c.height)))
.then(() => h.testNotClean(c))
.then(() => h.pixelMustNotBecome(vauto, h.red, {
});
const drawing = h.startDrawing(
() => ctx.drawImage(notCleanRed, 0, 0, c.width, c.height));
h.testNotClean(c);
try {
await h.pixelMustNotBecome(vauto, h.red, {
timeout: 1000,
infoString: "should not become red",
}))
.then(() => ok(h.isPixelNot(h.getPixel(vrate), h.red, 250),
"should not have become red"))
.then(() => h.pixelMustBecome(vmanual, h.green, {
});
ok(h.isPixelNot(h.getPixel(vrate), h.red, 250),
"should not have become red");
await h.pixelMustBecome(vmanual, h.green, {
infoString: "should still be green",
}))
.then(() => h.requestFrame(vmanual))
.then(() => h.pixelMustNotBecome(vmanual, h.red, {
});
h.requestFrame(vmanual);
await h.pixelMustNotBecome(vmanual, h.red, {
timeout: 1000,
infoString: "should not become red",
}))
.catch(err => ok(false, "checkDrawImageNotCleanRed failed: ", err))
.then(() => drawing.stop());
});
} catch(err) {
ok(false, "checkDrawImageNotCleanRed failed: ", err);
}
drawing.stop();
}
function checkEndedOnStop() {
let promises = [vauto, vmanual, vrate].map(elem => {
async function checkEndedOnStop() {
const promises = [vauto, vmanual, vrate].map(elem => {
elem.srcObject.getTracks()[0].stop();
return new Promise(resolve =>
elem.addEventListener("ended", function endedListener(event) {
@ -122,7 +134,7 @@ function checkEndedOnStop() {
elem.removeEventListener("ended", endedListener);
}));
});
return Promise.all(promises);
await Promise.all(promises);
}
function finish() {
@ -130,27 +142,14 @@ function finish() {
SimpleTest.finish();
}
function beginTest() {
SimpleTest.requestFlakyTimeout("Ensuring nothing happens until timing out with good margin");
h = new CaptureStreamTestHelper2D();
c = h.createAndAppendElement('canvas', 'c');
vauto = h.createAndAppendElement('video', 'vauto');
vmanual = h.createAndAppendElement('video', 'vmanual');
vrate = h.createAndAppendElement('video', 'vrate');
Promise.resolve()
.then(checkDrawColorInitialRed)
.then(checkDrawColorGreen)
.then(checkRequestFrameOrderGuarantee)
.then(checkDrawColorGreen) // Restore video elements to green.
.then(checkDrawImageNotCleanRed)
.then(checkEndedOnStop)
.then(finish);
}
SimpleTest.waitForExplicitFinish();
beginTest();
(async () => {
await checkDrawColorInitialRed();
await checkDrawColorGreen();
await checkRequestFrameOrderGuarantee();
await checkDrawColorGreen(); // Restore video elements to green.
await checkDrawImageNotCleanRed();
await checkEndedOnStop();
finish();
})();
</script>

Просмотреть файл

@ -0,0 +1,101 @@
<!DOCTYPE HTML>
<meta http-equiv="content-type" content="text/html; charset=utf-8" />
<title>Canvas2D test: CaptureStream() with throttled rAF</title>
<script src="/tests/SimpleTest/SimpleTest.js"></script>
<script src="captureStream_common.js"></script>
<link rel="stylesheet" href="/tests/SimpleTest/test.css">
<body>
<script>
SimpleTest.waitForExplicitFinish();
SimpleTest.requestFlakyTimeout("Ensuring nothing happens until timing out with good margin");
const THROTTLED_THRESHOLD = 5;
// CaptureStreamTestHelper holding utility test functions.
const h = new CaptureStreamTestHelper2D();
// Canvas element captured by streams.
const c = h.createAndAppendElement('canvas', 'c');
// Video element with captureStream stream in automatic mode.
const v = h.createAndAppendElement('video', 'v');
async function checkRequestAnimationFrameThrottled() {
const frameRate = await new Promise(resolve => {
let start;
let count;
const tick = time => {
if (!start) {
start = time;
count = 0;
} else {
count += 1;
}
if (time - start > 1000) {
// One second has passed, break.
resolve(count / ((time - start) / 1000));
return;
}
window.requestAnimationFrame(tick);
};
window.requestAnimationFrame(tick);
});
ok(frameRate < THROTTLED_THRESHOLD, `rAF framerate is at ${frameRate} fps`);
}
async function checkSetTimeoutNotThrottled() {
const start = performance.now();
const COUNT = 5;
for(let i = 0; i < COUNT; ++i) {
await new Promise(resolve => setTimeout(resolve, 0));
}
const rate = COUNT / ((performance.now() - start) / 1000);
ok(rate > 30, `setTimeout rate is at ${rate} ticks per second`);
}
async function checkCanvasCaptureNotThrottled() {
const intervalMillis = 1000 / 60;
const ctx = c.getContext('2d');
v.srcObject = c.captureStream();
v.play();
const start = performance.now();
let end;
is(v.mozPaintedFrames, 0, "mozPaintedFrames starts at 0");
while(true) {
h.drawColor(c, h.green);
await new Promise(resolve => setTimeout(resolve, intervalMillis));
end = performance.now();
if (end - start > 1000) {
break;
}
}
const frameRate = v.mozPaintedFrames / ((end - start) / 1000);
ok(frameRate > THROTTLED_THRESHOLD,
`captureStream() framerate is at ${frameRate} fps`);
}
function finish() {
ok(true, 'Test complete.');
SpecialPowers.wrap(window).browsingContext.isActive = true;
SimpleTest.finish();
}
(async () => {
// Disable background timer throttling so we can use setTimeout to draw to the
// canvas while the refresh driver is throttled.
await SpecialPowers.pushPrefEnv({
set: [
["dom.timeout.enable_budget_timer_throttling", false],
["dom.min_background_timeout_value", 0],
["dom.min_background_timeout_value_without_budget_throttling", 0],
],
});
// Throttle the canvas' refresh driver
SpecialPowers.wrap(window).browsingContext.isActive = false;
await checkRequestAnimationFrameThrottled();
await checkSetTimeoutNotThrottled();
await checkCanvasCaptureNotThrottled();
finish();
})();
</script>

Просмотреть файл

@ -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.
*/

Просмотреть файл

@ -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<AbortFollower*>(tmp));
tmp->mListenerManager = nullptr;
NS_IMPL_CYCLE_COLLECTION_UNLINK_END

Просмотреть файл

@ -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<AbortSignalProxy> 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<AbortSignalProxy> 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<AbortFollower*>(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<AbortSignalProxyRunnable> runnable =
new AbortSignalProxyRunnable(this);
MainThreadEventTarget()->Dispatch(runnable.forget(), NS_DISPATCH_NORMAL);
@ -1803,7 +1772,6 @@ NS_IMPL_RELEASE_INHERITED(EmptyBody, FetchBody<EmptyBody>)
NS_IMPL_CYCLE_COLLECTION_CLASS(EmptyBody)
NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN_INHERITED(EmptyBody, FetchBody<EmptyBody>)
AbortFollower::Unlink(static_cast<AbortFollower*>(tmp));
NS_IMPL_CYCLE_COLLECTION_UNLINK(mOwner)
NS_IMPL_CYCLE_COLLECTION_UNLINK(mAbortSignalImpl)
NS_IMPL_CYCLE_COLLECTION_UNLINK(mFetchStreamReader)

Просмотреть файл

@ -18,7 +18,6 @@ NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN_INHERITED(FetchObserver,
DOMEventTargetHelper)
AbortFollower::Unlink(static_cast<AbortFollower*>(tmp));
NS_IMPL_CYCLE_COLLECTION_UNLINK_END
NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(FetchObserver)

Просмотреть файл

@ -41,7 +41,6 @@ NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN_INHERITED(Request, FetchBody<Request>)
NS_IMPL_CYCLE_COLLECTION_UNLINK(mOwner)
NS_IMPL_CYCLE_COLLECTION_UNLINK(mHeaders)
NS_IMPL_CYCLE_COLLECTION_UNLINK(mSignal)
AbortFollower::Unlink(static_cast<AbortFollower*>(tmp));
NS_IMPL_CYCLE_COLLECTION_UNLINK_PRESERVED_WRAPPER
NS_IMPL_CYCLE_COLLECTION_UNLINK_END

Просмотреть файл

@ -40,7 +40,6 @@ NS_IMPL_RELEASE_INHERITED(Response, FetchBody<Response>)
NS_IMPL_CYCLE_COLLECTION_CLASS(Response)
NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN_INHERITED(Response, FetchBody<Response>)
AbortFollower::Unlink(static_cast<AbortFollower*>(tmp));
NS_IMPL_CYCLE_COLLECTION_UNLINK(mOwner)
NS_IMPL_CYCLE_COLLECTION_UNLINK(mHeaders)
NS_IMPL_CYCLE_COLLECTION_UNLINK(mSignalImpl)

Просмотреть файл

@ -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<JSObject*> aGivenProto) {
return FileSystemDirectoryHandle_Binding::Wrap(aCx, this, aGivenProto);
}
// WebIDL Interface
FileSystemHandleKind FileSystemDirectoryHandle::Kind() {
return FileSystemHandleKind::Directory;
}
already_AddRefed<FileSystemDirectoryIterator>
FileSystemDirectoryHandle::Entries() {
return MakeRefPtr<FileSystemDirectoryIterator>(GetParentObject()).forget();
}
already_AddRefed<FileSystemDirectoryIterator>
FileSystemDirectoryHandle::Keys() {
return MakeRefPtr<FileSystemDirectoryIterator>(GetParentObject()).forget();
}
already_AddRefed<FileSystemDirectoryIterator>
FileSystemDirectoryHandle::Values() {
return MakeRefPtr<FileSystemDirectoryIterator>(GetParentObject()).forget();
}
already_AddRefed<Promise> FileSystemDirectoryHandle::GetFileHandle(
const nsAString& aName, const FileSystemGetFileOptions& aOptions) {
IgnoredErrorResult rv;
RefPtr<Promise> promise = Promise::Create(GetParentObject(), rv);
if (rv.Failed()) {
return nullptr;
}
promise->MaybeReject(NS_ERROR_NOT_IMPLEMENTED);
return promise.forget();
}
already_AddRefed<Promise> FileSystemDirectoryHandle::GetDirectoryHandle(
const nsAString& aName, const FileSystemGetDirectoryOptions& aOptions) {
IgnoredErrorResult rv;
RefPtr<Promise> promise = Promise::Create(GetParentObject(), rv);
if (rv.Failed()) {
return nullptr;
}
promise->MaybeReject(NS_ERROR_NOT_IMPLEMENTED);
return promise.forget();
}
already_AddRefed<Promise> FileSystemDirectoryHandle::RemoveEntry(
const nsAString& aName, const FileSystemRemoveOptions& aOptions) {
IgnoredErrorResult rv;
RefPtr<Promise> promise = Promise::Create(GetParentObject(), rv);
if (rv.Failed()) {
return nullptr;
}
promise->MaybeReject(NS_ERROR_NOT_IMPLEMENTED);
return promise.forget();
}
already_AddRefed<Promise> FileSystemDirectoryHandle::Resolve(
FileSystemHandle& aPossibleDescendant) {
IgnoredErrorResult rv;
RefPtr<Promise> promise = Promise::Create(GetParentObject(), rv);
if (rv.Failed()) {
return nullptr;
}
promise->MaybeReject(NS_ERROR_NOT_IMPLEMENTED);
return promise.forget();
}
} // namespace mozilla::dom

Просмотреть файл

@ -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<JSObject*> aGivenProto) override;
// WebIDL Interface
FileSystemHandleKind Kind() override;
[[nodiscard]] already_AddRefed<FileSystemDirectoryIterator> Entries();
[[nodiscard]] already_AddRefed<FileSystemDirectoryIterator> Keys();
[[nodiscard]] already_AddRefed<FileSystemDirectoryIterator> Values();
already_AddRefed<Promise> GetFileHandle(
const nsAString& aName, const FileSystemGetFileOptions& aOptions);
already_AddRefed<Promise> GetDirectoryHandle(
const nsAString& aName, const FileSystemGetDirectoryOptions& aOptions);
already_AddRefed<Promise> RemoveEntry(
const nsAString& aName, const FileSystemRemoveOptions& aOptions);
already_AddRefed<Promise> Resolve(FileSystemHandle& aPossibleDescendant);
private:
~FileSystemDirectoryHandle() = default;
};
} // namespace mozilla::dom
#endif // DOM_FS_FILESYSTEMDIRECTORYHANDLE_H_

Просмотреть файл

@ -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<JSObject*> aGivenProto) {
return FileSystemDirectoryIterator_Binding::Wrap(aCx, this, aGivenProto);
}
// WebIDL Interface
already_AddRefed<Promise> FileSystemDirectoryIterator::Next() {
IgnoredErrorResult rv;
RefPtr<Promise> promise = Promise::Create(GetParentObject(), rv);
if (rv.Failed()) {
return nullptr;
}
promise->MaybeReject(NS_ERROR_NOT_IMPLEMENTED);
return promise.forget();
}
} // namespace mozilla::dom

Просмотреть файл

@ -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<JSObject*> aGivenProto) override;
// WebIDL Interface
already_AddRefed<Promise> Next();
protected:
virtual ~FileSystemDirectoryIterator() = default;
nsCOMPtr<nsIGlobalObject> mGlobal;
};
} // namespace mozilla::dom
#endif // DOM_FS_FILESYSTEMDIRECTORYITERATOR_H_

Просмотреть файл

@ -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<JSObject*> aGivenProto) {
return FileSystemFileHandle_Binding::Wrap(aCx, this, aGivenProto);
}
// WebIDL Interface
FileSystemHandleKind FileSystemFileHandle::Kind() {
return FileSystemHandleKind::File;
}
already_AddRefed<Promise> FileSystemFileHandle::GetFile() {
IgnoredErrorResult rv;
RefPtr<Promise> 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<Promise> FileSystemFileHandle::CreateWritable(
const FileSystemCreateWritableOptions& aOptions) {
IgnoredErrorResult rv;
RefPtr<Promise> promise = Promise::Create(GetParentObject(), rv);
if (rv.Failed()) {
return nullptr;
}
promise->MaybeReject(NS_ERROR_NOT_IMPLEMENTED);
return promise.forget();
}
#endif
already_AddRefed<Promise> FileSystemFileHandle::CreateSyncAccessHandle() {
IgnoredErrorResult rv;
RefPtr<Promise> promise = Promise::Create(GetParentObject(), rv);
if (rv.Failed()) {
return nullptr;
}
promise->MaybeReject(NS_ERROR_NOT_IMPLEMENTED);
return promise.forget();
}
} // namespace mozilla::dom

Просмотреть файл

@ -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<JSObject*> aGivenProto) override;
// WebIDL interface
FileSystemHandleKind Kind() override;
already_AddRefed<Promise> GetFile();
#ifdef MOZ_DOM_STREAMS
already_AddRefed<Promise> CreateWritable(
const FileSystemCreateWritableOptions& aOptions);
#endif
already_AddRefed<Promise> CreateSyncAccessHandle();
private:
~FileSystemFileHandle() = default;
};
} // namespace mozilla::dom
#endif // DOM_FS_FILESYSTEMFILEHANDLE_H_

Просмотреть файл

@ -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<JSObject*> aGivenProto) {
return FileSystemHandle_Binding::Wrap(aCx, this, aGivenProto);
}
// WebIDL Interface
void FileSystemHandle::GetName(DOMString& aResult) { aResult.SetNull(); }
already_AddRefed<Promise> FileSystemHandle::IsSameEntry(
FileSystemHandle& aOther) {
IgnoredErrorResult rv;
RefPtr<Promise> promise = Promise::Create(GetParentObject(), rv);
if (rv.Failed()) {
return nullptr;
}
promise->MaybeReject(NS_ERROR_NOT_IMPLEMENTED);
return promise.forget();
}
} // namespace mozilla::dom

48
dom/fs/FileSystemHandle.h Normal file
Просмотреть файл

@ -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<JSObject*> aGivenProto) override;
// WebIDL Interface
virtual FileSystemHandleKind Kind() = 0;
void GetName(DOMString& aResult);
already_AddRefed<Promise> IsSameEntry(FileSystemHandle& aOther);
protected:
virtual ~FileSystemHandle() = default;
nsCOMPtr<nsIGlobalObject> mGlobal;
};
} // namespace mozilla::dom
#endif // DOM_FS_FILESYSTEMHANDLE_H_

Просмотреть файл

@ -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<JSObject*> 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<Promise> FileSystemSyncAccessHandle::Truncate(uint64_t aSize) {
IgnoredErrorResult rv;
RefPtr<Promise> promise = Promise::Create(GetParentObject(), rv);
if (rv.Failed()) {
return nullptr;
}
promise->MaybeReject(NS_ERROR_NOT_IMPLEMENTED);
return promise.forget();
}
already_AddRefed<Promise> FileSystemSyncAccessHandle::GetSize() {
IgnoredErrorResult rv;
RefPtr<Promise> promise = Promise::Create(GetParentObject(), rv);
if (rv.Failed()) {
return nullptr;
}
promise->MaybeReject(NS_ERROR_NOT_IMPLEMENTED);
return promise.forget();
}
already_AddRefed<Promise> FileSystemSyncAccessHandle::Flush() {
IgnoredErrorResult rv;
RefPtr<Promise> promise = Promise::Create(GetParentObject(), rv);
if (rv.Failed()) {
return nullptr;
}
promise->MaybeReject(NS_ERROR_NOT_IMPLEMENTED);
return promise.forget();
}
already_AddRefed<Promise> FileSystemSyncAccessHandle::Close() {
IgnoredErrorResult rv;
RefPtr<Promise> promise = Promise::Create(GetParentObject(), rv);
if (rv.Failed()) {
return nullptr;
}
promise->MaybeReject(NS_ERROR_NOT_IMPLEMENTED);
return promise.forget();
}
} // namespace mozilla::dom

Просмотреть файл

@ -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<JSObject*> aGivenProto) override;
// WebIDL Interface
uint64_t Read(
const MaybeSharedArrayBufferViewOrMaybeSharedArrayBuffer& aBuffer,
const FileSystemReadWriteOptions& aOptions);
uint64_t Write(
const MaybeSharedArrayBufferViewOrMaybeSharedArrayBuffer& aBuffer,
const FileSystemReadWriteOptions& aOptions);
already_AddRefed<Promise> Truncate(uint64_t aSize);
already_AddRefed<Promise> GetSize();
already_AddRefed<Promise> Flush();
already_AddRefed<Promise> Close();
private:
virtual ~FileSystemSyncAccessHandle() = default;
nsCOMPtr<nsIGlobalObject> mGlobal;
};
} // namespace mozilla::dom
#endif // DOM_FS_FILESYSTEMSYNCACCESSHANDLE_H_

Просмотреть файл

@ -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<JSObject*> aGivenProto) {
return FileSystemWritableFileStream_Binding::Wrap(aCx, this, aGivenProto);
}
// WebIDL Interface
already_AddRefed<Promise> FileSystemWritableFileStream::Write(
const ArrayBufferViewOrArrayBufferOrBlobOrUSVStringOrWriteParams& aData) {
IgnoredErrorResult rv;
RefPtr<Promise> promise = Promise::Create(GetParentObject(), rv);
if (rv.Failed()) {
return nullptr;
}
promise->MaybeReject(NS_ERROR_NOT_IMPLEMENTED);
return promise.forget();
}
already_AddRefed<Promise> FileSystemWritableFileStream::Seek(
uint64_t aPosition) {
IgnoredErrorResult rv;
RefPtr<Promise> promise = Promise::Create(GetParentObject(), rv);
if (rv.Failed()) {
return nullptr;
}
promise->MaybeReject(NS_ERROR_NOT_IMPLEMENTED);
return promise.forget();
}
already_AddRefed<Promise> FileSystemWritableFileStream::Truncate(
uint64_t aSize) {
IgnoredErrorResult rv;
RefPtr<Promise> promise = Promise::Create(GetParentObject(), rv);
if (rv.Failed()) {
return nullptr;
}
promise->MaybeReject(NS_ERROR_NOT_IMPLEMENTED);
return promise.forget();
}
} // namespace mozilla::dom

Просмотреть файл

@ -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<JSObject*> aGivenProto) override;
// WebIDL Interface
already_AddRefed<Promise> Write(
const ArrayBufferViewOrArrayBufferOrBlobOrUSVStringOrWriteParams& aData);
already_AddRefed<Promise> Seek(uint64_t aPosition);
already_AddRefed<Promise> Truncate(uint64_t aSize);
private:
~FileSystemWritableFileStream() = default;
};
} // namespace mozilla::dom
#endif // DOM_FS_FILESYSTEMWRITABLEFILESTREAM_H_

33
dom/fs/moz.build Normal file
Просмотреть файл

@ -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"

Просмотреть файл

@ -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<FrameCaptureState>* 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<RequestedFrameRefreshObserver>(this), next] {
mPendingThrottledCapture = false;
CaptureFrame(next);
}),
// next >= now, so this is a guard for (next - now) flooring to 0.
std::max<uint32_t>(
1, static_cast<uint32_t>((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<FrameCaptureState>* 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<FrameCaptureState>* 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<HTMLCanvasElement> mOwningElement;
RefPtr<nsRefreshDriver> mRefreshDriver;
WatchManager<RequestedFrameRefreshObserver> mWatchManager;
TimeStamp mLastCaptureTime;
bool mPendingThrottledCapture;
};
// ---------------------------------------------------------------------------
@ -1191,8 +1276,11 @@ void HTMLCanvasElement::MarkContextCleanForFrameCapture() {
mCurrentContext->MarkContextCleanForFrameCapture();
}
bool HTMLCanvasElement::IsContextCleanForFrameCapture() {
return mCurrentContext && mCurrentContext->IsContextCleanForFrameCapture();
Watchable<FrameCaptureState>* HTMLCanvasElement::GetFrameCaptureState() {
if (!mCurrentContext) {
return nullptr;
}
return mCurrentContext->GetFrameCaptureState();
}
nsresult HTMLCanvasElement::RegisterFrameCaptureListener(

Просмотреть файл

@ -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<FrameCaptureState>* GetFrameCaptureState();
nsresult GetContext(const nsAString& aContextId, nsISupports** aContext);

Просмотреть файл

@ -785,12 +785,12 @@ MediaResult FFmpegVideoDecoder<LIBAV_VER>::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) {

Просмотреть файл

@ -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<VideoFrameSurface> 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<VideoFrameSurface> VideoFramePool::GetFreeVideoFrameSurface() {
}
RefPtr<VideoFrameSurface> 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<VideoFrameSurface> VideoFramePool::GetVideoFrameSurface(
return nullptr;
}
MutexAutoLock lock(mSurfaceLock);
auto videoSurface = GetFreeVideoFrameSurface();
if (!videoSurface) {
RefPtr<DMABufSurfaceYUV> surface =
@ -114,17 +129,18 @@ RefPtr<VideoFrameSurface> VideoFramePool::GetVideoFrameSurface(
FFMPEG_LOG("Created new VA-API DMABufSurface UID = %d", surface->GetUID());
videoSurface = new VideoFrameSurfaceVAAPI(surface);
mDMABufSurfaces.AppendElement(videoSurface);
return videoSurface;
} else {
RefPtr<DMABufSurfaceYUV> 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<DMABufSurfaceYUV> 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<VideoFrameSurface> VideoFramePool::GetVideoFrameSurface(
return nullptr;
}
MutexAutoLock lock(mSurfaceLock);
auto videoSurface = GetFreeVideoFrameSurface();
if (!videoSurface) {
RefPtr<DMABufSurfaceYUV> surface = DMABufSurfaceYUV::CreateYUVSurface(

Просмотреть файл

@ -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<DMABufSurfaceYUV> GetDMABufSurface() { return nullptr; };
virtual RefPtr<layers::Image> 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<layers::Image> GetAsImage();
protected:
const RefPtr<DMABufSurface> 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<DMABufSurface> 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<VideoFrameSurface> GetVideoFrameSurface(
VADRMPRIMESurfaceDescriptor& aVaDesc);
VADRMPRIMESurfaceDescriptor& aVaDesc, AVCodecContext* aAVCodecContext,
AVFrame* aAVFrame, FFmpegLibWrapper* aLib);
RefPtr<VideoFrameSurface> 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<RefPtr<VideoFrameSurface>> mDMABufSurfaces;
};

Просмотреть файл

@ -12,7 +12,7 @@ UNIFIED_SOURCES += [
]
LOCAL_INCLUDES += [
'..',
'/media/ffvpx',
'/media/mozva',
'include',
]

Просмотреть файл

@ -21,7 +21,7 @@ SOURCES += [
LOCAL_INCLUDES += [
"..",
"../ffmpeg58/include",
"/media/ffvpx",
"/media/mozva",
]
CXXFLAGS += ["-Wno-deprecated-declarations"]

Просмотреть файл

@ -52,6 +52,7 @@ DIRS += [
"filehandle",
"filesystem",
"flex",
"fs",
"gamepad",
"geolocation",
"grid",

Просмотреть файл

@ -752,6 +752,19 @@ already_AddRefed<Promise> StorageManager::Estimate(ErrorResult& aRv) {
aRv);
}
already_AddRefed<Promise> StorageManager::GetDirectory() {
IgnoredErrorResult rv;
RefPtr<Promise> 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)

Просмотреть файл

@ -42,6 +42,8 @@ class StorageManager final : public nsISupports, public nsWrapperCache {
already_AddRefed<Promise> Estimate(ErrorResult& aRv);
already_AddRefed<Promise> GetDirectory();
NS_DECL_CYCLE_COLLECTING_ISUPPORTS
NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS(StorageManager)

Просмотреть файл

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

Просмотреть файл

@ -331,6 +331,12 @@ void PathUtils::ToFileURI(const GlobalObject&, const nsAString& aPath,
}
}
bool PathUtils::IsAbsolute(const GlobalObject&, const nsAString& aPath) {
nsCOMPtr<nsIFile> path = new nsLocalFile();
nsresult rv = InitFileWithPath(path, aPath);
return NS_SUCCEEDED(rv);
}
already_AddRefed<Promise> PathUtils::GetProfileDir(const GlobalObject& aGlobal,
ErrorResult& aErr) {
auto guard = sDirCache.Lock();

Просмотреть файл

@ -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<Promise> GetProfileDir(const GlobalObject& aGlobal,
ErrorResult& aErr);

Просмотреть файл

@ -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(

Просмотреть файл

@ -105,8 +105,9 @@ function runTest()
}
}
function testMacFocusesFormControl()
async function testMacFocusesFormControl()
{
await SimpleTest.promiseFocus(window);
testHTMLElements(htmlElementsMacPrefSet, false);
SimpleTest.finish();
}

Просмотреть файл

@ -46,7 +46,6 @@ NS_IMPL_CYCLE_COLLECTION_CLASS(WebAuthnManager)
NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN_INHERITED(WebAuthnManager,
WebAuthnManagerBase)
AbortFollower::Unlink(static_cast<AbortFollower*>(tmp));
NS_IMPL_CYCLE_COLLECTION_UNLINK(mTransaction)
tmp->mTransaction.reset();
NS_IMPL_CYCLE_COLLECTION_UNLINK_END

Просмотреть файл

@ -91,7 +91,9 @@ class CanvasContext final : public nsICanvasRenderingContextInternal,
void DidRefresh() override {}
void MarkContextCleanForFrameCapture() override {}
bool IsContextCleanForFrameCapture() override { return false; }
Watchable<FrameCaptureState>* GetFrameCaptureState() override {
return nullptr;
}
public:
void Configure(const dom::GPUCanvasConfiguration& aDesc);

Просмотреть файл

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

Просмотреть файл

@ -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<USVString, FileSystemHandle>;
FileSystemDirectoryIterator entries();
FileSystemDirectoryIterator keys();
FileSystemDirectoryIterator values();
Promise<FileSystemFileHandle> getFileHandle(USVString name, optional FileSystemGetFileOptions options = {});
Promise<FileSystemDirectoryHandle> getDirectoryHandle(USVString name, optional FileSystemGetDirectoryOptions options = {});
Promise<void> removeEntry(USVString name, optional FileSystemRemoveOptions options = {});
Promise<sequence<USVString>?> resolve(FileSystemHandle possibleDescendant);
};

Просмотреть файл

@ -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<any> next();
};

Просмотреть файл

@ -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<File> getFile();
#ifdef MOZ_DOM_STREAMS
Promise<FileSystemWritableFileStream> createWritable(optional FileSystemCreateWritableOptions options = {});
#endif
[Exposed=DedicatedWorker]
Promise<FileSystemSyncAccessHandle> createSyncAccessHandle();
};

Просмотреть файл

@ -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<boolean> isSameEntry(FileSystemHandle other);
};

Просмотреть файл

@ -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<void> truncate([EnforceRange] unsigned long long size);
Promise<unsigned long long> getSize();
Promise<void> flush();
Promise<void> close();
};

Просмотреть файл

@ -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<void> write(FileSystemWriteChunkType data);
Promise<void> seek(unsigned long long position);
Promise<void> truncate(unsigned long long size);
};

Просмотреть файл

@ -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();
};

Просмотреть файл

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

Просмотреть файл

@ -26,3 +26,9 @@ dictionary StorageEstimate {
unsigned long long usage;
unsigned long long quota;
};
[SecureContext]
partial interface StorageManager {
[Pref="dom.fs.enabled"]
Promise<FileSystemDirectoryHandle> getDirectory();
};

Просмотреть файл

@ -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",

Просмотреть файл

@ -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. */

Просмотреть файл

@ -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<GLContext> sSnapshotContext;
static StaticMutex sSnapshotContextMutex;

Просмотреть файл

@ -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,

Просмотреть файл

@ -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 */

Просмотреть файл

@ -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

Просмотреть файл

@ -11,6 +11,7 @@
#include <string.h>
#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

Просмотреть файл

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

Просмотреть файл

@ -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<const %(cpp_type)s*>(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,
}
)

Просмотреть файл

@ -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;
}

Просмотреть файл

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

Просмотреть файл

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

Просмотреть файл

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

Просмотреть файл

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

Просмотреть файл

@ -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")

Некоторые файлы не были показаны из-за слишком большого количества измененных файлов Показать больше