зеркало из https://github.com/mozilla/gecko-dev.git
Merge autoland to mozilla-central. a=merge
This commit is contained in:
Коммит
7b9d23ece4
|
@ -25,7 +25,7 @@ rev = "3bfc47d9a571d0842676043ba60716318e946c06"
|
|||
[source."https://github.com/mozilla/midir.git"]
|
||||
git = "https://github.com/mozilla/midir.git"
|
||||
replace-with = "vendored-sources"
|
||||
rev = "4c11f0ffb5d6a10de4aff40a7b81218b33b94e6f"
|
||||
rev = "e1b4dcb767f9e69afe95a860374aaa9635d81e3d"
|
||||
|
||||
[source."https://github.com/mozilla/cubeb-pulse-rs"]
|
||||
git = "https://github.com/mozilla/cubeb-pulse-rs"
|
||||
|
|
|
@ -3291,7 +3291,7 @@ dependencies = [
|
|||
[[package]]
|
||||
name = "midir"
|
||||
version = "0.7.0"
|
||||
source = "git+https://github.com/mozilla/midir.git?rev=4c11f0ffb5d6a10de4aff40a7b81218b33b94e6f#4c11f0ffb5d6a10de4aff40a7b81218b33b94e6f"
|
||||
source = "git+https://github.com/mozilla/midir.git?rev=e1b4dcb767f9e69afe95a860374aaa9635d81e3d#e1b4dcb767f9e69afe95a860374aaa9635d81e3d"
|
||||
dependencies = [
|
||||
"alsa",
|
||||
"bitflags",
|
||||
|
|
|
@ -146,7 +146,7 @@ coremidi = { git = "https://github.com/chris-zen/coremidi.git", rev="fc68464b544
|
|||
fog = { path = "toolkit/components/glean/api" }
|
||||
libudev-sys = { path = "dom/webauthn/libudev-sys" }
|
||||
packed_simd = { package = "packed_simd_2", git = "https://github.com/hsivonen/packed_simd", rev="c149d0a519bf878567c7630096737669ec2ff15f" }
|
||||
midir = { git = "https://github.com/mozilla/midir.git", rev = "4c11f0ffb5d6a10de4aff40a7b81218b33b94e6f" }
|
||||
midir = { git = "https://github.com/mozilla/midir.git", rev = "e1b4dcb767f9e69afe95a860374aaa9635d81e3d" }
|
||||
minidump_writer_linux = { git = "https://github.com/rust-minidump/minidump-writer.git", rev = "75ada456c92a429704691a85e1cb42fef8cafc0d" }
|
||||
|
||||
# application-services overrides to make updating them all simpler.
|
||||
|
|
|
@ -2707,3 +2707,7 @@ pref("browser.snapshots.relevancy.timeOfDayIntervalSeconds", 3600);
|
|||
pref("browser.places.snapshots.expiration.days", 210);
|
||||
// For user managed snapshots we use more than a year, to support yearly tasks.
|
||||
pref("browser.places.snapshots.expiration.userManaged.days", 420);
|
||||
|
||||
// If the user has seen the Firefox View feature tour this value reflects the tour
|
||||
// message id, the id of the last screen they saw, and whether they completed the tour
|
||||
pref("browser.browser.firefoxView.featureTour", "default, default, true");
|
||||
|
|
|
@ -65,6 +65,7 @@ figure > img {
|
|||
margin-block-start: 2rem;
|
||||
max-width: var(--figure-height);
|
||||
max-height: var(--figure-width);
|
||||
object-fit: scale-down;
|
||||
}
|
||||
|
||||
#colorway-customization-panel {
|
||||
|
|
|
@ -232,6 +232,7 @@ body > main > aside {
|
|||
#colorways-collection-figure {
|
||||
max-width: var(--colorways-figure-size);
|
||||
max-height: var(--colorways-figure-size);
|
||||
object-fit: scale-down;
|
||||
}
|
||||
|
||||
[hidden] {
|
||||
|
|
|
@ -196,23 +196,27 @@ const MultiStageAboutWelcome = props => {
|
|||
}
|
||||
}); // Remember that a new screen has loaded for browser navigation
|
||||
|
||||
if (index > window.history.state) {
|
||||
if (props.updateHistory && index > window.history.state) {
|
||||
window.history.pushState(index, "");
|
||||
}
|
||||
}, [index]);
|
||||
(0,react__WEBPACK_IMPORTED_MODULE_0__.useEffect)(() => {
|
||||
// Switch to the screen tracked in state (null for initial state)
|
||||
// or last screen index if a user navigates by pressing back
|
||||
// button from about:home
|
||||
const handler = ({
|
||||
state
|
||||
}) => setScreenIndex(Math.min(state, screens.length - 1)); // Handle page load, e.g., going back to about:welcome from about:home
|
||||
if (props.updateHistory) {
|
||||
// Switch to the screen tracked in state (null for initial state)
|
||||
// or last screen index if a user navigates by pressing back
|
||||
// button from about:home
|
||||
const handler = ({
|
||||
state
|
||||
}) => setScreenIndex(Math.min(state, screens.length - 1)); // Handle page load, e.g., going back to about:welcome from about:home
|
||||
|
||||
|
||||
handler(window.history); // Watch for browser back/forward button navigation events
|
||||
handler(window.history); // Watch for browser back/forward button navigation events
|
||||
|
||||
window.addEventListener("popstate", handler);
|
||||
return () => window.removeEventListener("popstate", handler);
|
||||
window.addEventListener("popstate", handler);
|
||||
return () => window.removeEventListener("popstate", handler);
|
||||
}
|
||||
|
||||
return false;
|
||||
}, []);
|
||||
const [flowParams, setFlowParams] = (0,react__WEBPACK_IMPORTED_MODULE_0__.useState)(null);
|
||||
const {
|
||||
|
@ -1711,6 +1715,7 @@ class AboutWelcome extends (react__WEBPACK_IMPORTED_MODULE_0___default().PureCom
|
|||
return /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_components_MultiStageAboutWelcome__WEBPACK_IMPORTED_MODULE_3__.MultiStageAboutWelcome, {
|
||||
message_id: props.messageId,
|
||||
screens: props.screens,
|
||||
updateHistory: !props.disableHistoryUpdates,
|
||||
metricsFlowUri: this.state.metricsFlowUri,
|
||||
utm_term: props.UTMTerm,
|
||||
transitions: props.transitions,
|
||||
|
|
|
@ -71,6 +71,7 @@ class AboutWelcome extends React.PureComponent {
|
|||
<MultiStageAboutWelcome
|
||||
message_id={props.messageId}
|
||||
screens={props.screens}
|
||||
updateHistory={!props.disableHistoryUpdates}
|
||||
metricsFlowUri={this.state.metricsFlowUri}
|
||||
utm_term={props.UTMTerm}
|
||||
transitions={props.transitions}
|
||||
|
|
|
@ -30,24 +30,27 @@ export const MultiStageAboutWelcome = props => {
|
|||
});
|
||||
|
||||
// Remember that a new screen has loaded for browser navigation
|
||||
if (index > window.history.state) {
|
||||
if (props.updateHistory && index > window.history.state) {
|
||||
window.history.pushState(index, "");
|
||||
}
|
||||
}, [index]);
|
||||
|
||||
useEffect(() => {
|
||||
// Switch to the screen tracked in state (null for initial state)
|
||||
// or last screen index if a user navigates by pressing back
|
||||
// button from about:home
|
||||
const handler = ({ state }) =>
|
||||
setScreenIndex(Math.min(state, screens.length - 1));
|
||||
if (props.updateHistory) {
|
||||
// Switch to the screen tracked in state (null for initial state)
|
||||
// or last screen index if a user navigates by pressing back
|
||||
// button from about:home
|
||||
const handler = ({ state }) =>
|
||||
setScreenIndex(Math.min(state, screens.length - 1));
|
||||
|
||||
// Handle page load, e.g., going back to about:welcome from about:home
|
||||
handler(window.history);
|
||||
// Handle page load, e.g., going back to about:welcome from about:home
|
||||
handler(window.history);
|
||||
|
||||
// Watch for browser back/forward button navigation events
|
||||
window.addEventListener("popstate", handler);
|
||||
return () => window.removeEventListener("popstate", handler);
|
||||
// Watch for browser back/forward button navigation events
|
||||
window.addEventListener("popstate", handler);
|
||||
return () => window.removeEventListener("popstate", handler);
|
||||
}
|
||||
return false;
|
||||
}, []);
|
||||
|
||||
const [flowParams, setFlowParams] = useState(null);
|
||||
|
|
|
@ -55,6 +55,7 @@ Please note that some targeting attributes require stricter controls on the tele
|
|||
* [doesAppNeedPrivatePin](#doesappneedprivatepin)
|
||||
* [isBackgroundTaskMode](#isbackgroundtaskmode)
|
||||
* [backgroundTaskName](#backgroundtaskname)
|
||||
* [userPrefersReducedMotion](#userPrefersReducedMotion)
|
||||
|
||||
## Detailed usage
|
||||
|
||||
|
@ -843,3 +844,8 @@ Checks if this invocation is running in background task mode.
|
|||
|
||||
A non-empty string task name if this invocation is running in background task
|
||||
mode, or `null` if this invocation is not running in background task mode.
|
||||
|
||||
### `userPrefersReducedMotion`
|
||||
|
||||
Checks if user prefers reduced motion as indicated by the value of a media query for `prefers-reduced-motion`.
|
||||
|
||||
|
|
|
@ -1216,6 +1216,10 @@
|
|||
"transitions": {
|
||||
"type": "boolean",
|
||||
"description": "Show transitions within and between screens"
|
||||
},
|
||||
"disableHistoryUpdates": {
|
||||
"type": "boolean",
|
||||
"description": "Don't alter the browser session's history stack - used with messaging surfaces like Feature Callouts"
|
||||
}
|
||||
},
|
||||
"additionalProperties": true,
|
||||
|
|
|
@ -127,6 +127,10 @@
|
|||
"transitions": {
|
||||
"type": "boolean",
|
||||
"description": "Show transitions within and between screens"
|
||||
},
|
||||
"disableHistoryUpdates": {
|
||||
"type": "boolean",
|
||||
"description": "Don't alter the browser session's history stack - used with messaging surfaces like Feature Callouts"
|
||||
}
|
||||
},
|
||||
"additionalProperties": true,
|
||||
|
|
|
@ -214,6 +214,7 @@ export class _DiscoveryStreamBase extends React.PureComponent {
|
|||
readTime={component.properties.readTime}
|
||||
saveToPocketCard={component.saveToPocketCard}
|
||||
pocket_button_enabled={component.pocketButtonEnabled}
|
||||
recentSavesEnabled={this.props.DiscoveryStream.recentSavesEnabled}
|
||||
/>
|
||||
);
|
||||
case "HorizontalRule":
|
||||
|
|
|
@ -180,10 +180,7 @@ export function RecentSavesContainer({
|
|||
|
||||
export class _CardGrid extends React.PureComponent {
|
||||
renderCards() {
|
||||
const { DiscoveryStream } = this.props;
|
||||
const prefs = this.props.Prefs.values;
|
||||
const { recentSavesEnabled } = DiscoveryStream;
|
||||
const showRecentSaves = prefs.showRecentSaves && recentSavesEnabled;
|
||||
const {
|
||||
items,
|
||||
hybridLayout,
|
||||
|
@ -201,7 +198,9 @@ export class _CardGrid extends React.PureComponent {
|
|||
essentialReadsHeader,
|
||||
editorsPicksHeader,
|
||||
widgets,
|
||||
recentSavesEnabled,
|
||||
} = this.props;
|
||||
const showRecentSaves = prefs.showRecentSaves && recentSavesEnabled;
|
||||
|
||||
const recs = this.props.data.recommendations.slice(0, items);
|
||||
const cards = [];
|
||||
|
|
|
@ -8082,14 +8082,7 @@ class _CardGrid extends (external_React_default()).PureComponent {
|
|||
renderCards() {
|
||||
var _widgets$positions, _widgets$data, _essentialReadsCards, _editorsPicksCards;
|
||||
|
||||
const {
|
||||
DiscoveryStream
|
||||
} = this.props;
|
||||
const prefs = this.props.Prefs.values;
|
||||
const {
|
||||
recentSavesEnabled
|
||||
} = DiscoveryStream;
|
||||
const showRecentSaves = prefs.showRecentSaves && recentSavesEnabled;
|
||||
const {
|
||||
items,
|
||||
hybridLayout,
|
||||
|
@ -8106,8 +8099,10 @@ class _CardGrid extends (external_React_default()).PureComponent {
|
|||
readTime,
|
||||
essentialReadsHeader,
|
||||
editorsPicksHeader,
|
||||
widgets
|
||||
widgets,
|
||||
recentSavesEnabled
|
||||
} = this.props;
|
||||
const showRecentSaves = prefs.showRecentSaves && recentSavesEnabled;
|
||||
const recs = this.props.data.recommendations.slice(0, items);
|
||||
const cards = [];
|
||||
let essentialReadsCards = [];
|
||||
|
@ -13628,7 +13623,8 @@ class _DiscoveryStreamBase extends (external_React_default()).PureComponent {
|
|||
editorsPicksHeader: component.properties.editorsPicksHeader,
|
||||
readTime: component.properties.readTime,
|
||||
saveToPocketCard: component.saveToPocketCard,
|
||||
pocket_button_enabled: component.pocketButtonEnabled
|
||||
pocket_button_enabled: component.pocketButtonEnabled,
|
||||
recentSavesEnabled: this.props.DiscoveryStream.recentSavesEnabled
|
||||
});
|
||||
|
||||
case "HorizontalRule":
|
||||
|
|
|
@ -741,6 +741,11 @@ const TargetingGetters = {
|
|||
);
|
||||
return bts?.backgroundTaskName();
|
||||
},
|
||||
|
||||
get userPrefersReducedMotion() {
|
||||
let window = lazy.BrowserWindowTracker.getTopWindow();
|
||||
return window?.matchMedia("(prefers-reduced-motion: reduce)")?.matches;
|
||||
},
|
||||
};
|
||||
|
||||
const ASRouterTargeting = {
|
||||
|
|
|
@ -420,3 +420,43 @@ add_task(async function test_aboutwelcome_split_position() {
|
|||
}
|
||||
);
|
||||
});
|
||||
|
||||
/**
|
||||
* Test rendering a message with session history updates disabled
|
||||
*/
|
||||
add_task(async function test_aboutwelcome_history_updates_disabled() {
|
||||
let screens = [];
|
||||
// we need at least two screens to test the history state
|
||||
for (let i = 1; i < 3; i++) {
|
||||
screens.push(makeTestContent(`TEST_PUSH_STATE_STEP_${i}`));
|
||||
}
|
||||
let doExperimentCleanup = await ExperimentFakes.enrollWithFeatureConfig({
|
||||
featureId: "aboutwelcome",
|
||||
value: {
|
||||
enabled: true,
|
||||
disableHistoryUpdates: true,
|
||||
screens,
|
||||
},
|
||||
});
|
||||
let browser = await openAboutWelcome(JSON.stringify(screens));
|
||||
|
||||
let startHistoryLength = await SpecialPowers.spawn(browser, [], () => {
|
||||
return content.window.history.length;
|
||||
});
|
||||
// Advance to second screen
|
||||
await onButtonClick(browser, "button.primary");
|
||||
let endHistoryLength = await SpecialPowers.spawn(browser, [], async () => {
|
||||
// Ensure next screen has rendered
|
||||
await ContentTaskUtils.waitForCondition(() =>
|
||||
content.document.querySelector(".TEST_PUSH_STATE_STEP_2")
|
||||
);
|
||||
return content.window.history.length;
|
||||
});
|
||||
|
||||
ok(
|
||||
startHistoryLength === endHistoryLength,
|
||||
"No entries added to the session's history stack with history updates disabled"
|
||||
);
|
||||
|
||||
await doExperimentCleanup();
|
||||
});
|
||||
|
|
|
@ -1163,3 +1163,11 @@ add_task(async function check_isBackgroundTaskMode() {
|
|||
"Has no background task name"
|
||||
);
|
||||
});
|
||||
|
||||
add_task(async function check_userPrefersReducedMotion() {
|
||||
is(
|
||||
typeof (await ASRouterTargeting.Environment.userPrefersReducedMotion),
|
||||
"boolean",
|
||||
"Should return a boolean"
|
||||
);
|
||||
});
|
||||
|
|
|
@ -4,8 +4,6 @@
|
|||
|
||||
## Colorway collections
|
||||
|
||||
colorway-collection-life-in-color = Life In Color
|
||||
colorway-collection-true-colors = True Colors
|
||||
colorway-collection-independent-voices = Independent Voices
|
||||
colorway-collection-independent-voices-description = Color can change culture. The latest colorways celebrate voices making the world a better place.
|
||||
# Used as a subheading for the colorway collection card in about:addons
|
||||
|
|
|
@ -265,3 +265,8 @@ editor_free_text_font_color=Font Color
|
|||
editor_free_text_font_size=Font Size
|
||||
editor_ink_line_color=Line Color
|
||||
editor_ink_line_thickness=Line Thickness
|
||||
|
||||
# Editor aria
|
||||
editor_free_text_aria_label=FreeText Editor
|
||||
editor_ink_aria_label=Ink Editor
|
||||
editor_ink_canvas_aria_label=User-created image
|
||||
|
|
|
@ -497,16 +497,10 @@ const ColorwayCollections = [
|
|||
{
|
||||
id: "life-in-color",
|
||||
expiry: "2022-02-08",
|
||||
l10nId: {
|
||||
title: "colorway-collection-life-in-color",
|
||||
},
|
||||
},
|
||||
{
|
||||
id: "true-colors",
|
||||
expiry: "2022-05-03",
|
||||
l10nId: {
|
||||
title: "colorway-collection-true-colors",
|
||||
},
|
||||
},
|
||||
{
|
||||
id: "independent-voices",
|
||||
|
|
|
@ -109,10 +109,8 @@ skip-if = (os == 'win' && bits == 32 && debug) # bug 1609313
|
|||
[browser_rules_cubicbezier-revert-on-ESC.js]
|
||||
[browser_rules_custom.js]
|
||||
[browser_rules_cycle-angle.js]
|
||||
skip-if = (os == "linux") # Bug 1767684
|
||||
[browser_rules_cycle-color.js]
|
||||
[browser_rules_edit-display-grid-property.js]
|
||||
skip-if = (os == "linux") # Bug 1356214
|
||||
[browser_rules_edit-property-cancel.js]
|
||||
[browser_rules_edit-property-click.js]
|
||||
[browser_rules_edit-property-commit.js]
|
||||
|
|
|
@ -20,10 +20,11 @@ add_task(async function() {
|
|||
await addTab("data:text/html;charset=utf-8," + encodeURIComponent(TEST_URI));
|
||||
const { view } = await openRuleView();
|
||||
|
||||
// Bug 1767679 - Frequent intermittents on linux.
|
||||
const property = await waitFor(() =>
|
||||
getRuleViewProperty(view, "body", "background")
|
||||
);
|
||||
// Bug 1767679 - Use { wait: true } to avoid frequent intermittents on linux.
|
||||
const property = await getRuleViewProperty(view, "body", "background", {
|
||||
wait: true,
|
||||
});
|
||||
|
||||
const value = property.valueSpan;
|
||||
const swatch = value.querySelectorAll(".ruleview-colorswatch")[0];
|
||||
const url = value.querySelector(".theme-link");
|
||||
|
|
|
@ -90,7 +90,9 @@ add_task(async function() {
|
|||
selector
|
||||
);
|
||||
|
||||
const prop = getRuleViewProperty(view, selector, propertyName).valueSpan;
|
||||
const prop = (
|
||||
await getRuleViewProperty(view, selector, propertyName, { wait: true })
|
||||
).valueSpan;
|
||||
const swatches = prop.querySelectorAll(".ruleview-colorswatch");
|
||||
|
||||
ok(swatches.length, "Swatches found in the property");
|
||||
|
|
|
@ -41,7 +41,9 @@ async function testColorPickerAppearsOnColorSwatchActivation(
|
|||
property,
|
||||
withKeyboard = false
|
||||
) {
|
||||
const value = getRuleViewProperty(view, "body", property).valueSpan;
|
||||
const value = (
|
||||
await getRuleViewProperty(view, "body", property, { wait: true })
|
||||
).valueSpan;
|
||||
const swatch = value.querySelector(".ruleview-colorswatch");
|
||||
|
||||
const cPicker = view.tooltips.getTooltip("colorPicker");
|
||||
|
|
|
@ -26,7 +26,10 @@ add_task(async function() {
|
|||
|
||||
async function checkAngleCycling(inspector, view) {
|
||||
await selectNode(".turn", inspector);
|
||||
const container = getRuleViewProperty(view, ".turn", "filter").valueSpan;
|
||||
|
||||
const container = (
|
||||
await getRuleViewProperty(view, ".turn", "filter", { wait: true })
|
||||
).valueSpan;
|
||||
const valueNode = container.querySelector(".ruleview-angle");
|
||||
const win = view.styleWindow;
|
||||
|
||||
|
@ -59,7 +62,9 @@ async function checkAngleCycling(inspector, view) {
|
|||
|
||||
async function checkAngleCyclingPersist(inspector, view) {
|
||||
await selectNode(".deg", inspector);
|
||||
let container = getRuleViewProperty(view, ".deg", "filter").valueSpan;
|
||||
let container = (
|
||||
await getRuleViewProperty(view, ".deg", "filter", { wait: true })
|
||||
).valueSpan;
|
||||
let valueNode = container.querySelector(".ruleview-angle");
|
||||
const win = view.styleWindow;
|
||||
|
||||
|
@ -79,7 +84,9 @@ async function checkAngleCyclingPersist(inspector, view) {
|
|||
|
||||
// We have to query for the container and the swatch because
|
||||
// they've been re-generated
|
||||
container = getRuleViewProperty(view, ".deg", "filter").valueSpan;
|
||||
container = (
|
||||
await getRuleViewProperty(view, ".deg", "filter", { wait: true })
|
||||
).valueSpan;
|
||||
valueNode = container.querySelector(".ruleview-angle");
|
||||
is(
|
||||
valueNode.textContent,
|
||||
|
|
|
@ -29,7 +29,9 @@ add_task(async function() {
|
|||
} = getHighlighterTestHelpers(inspector);
|
||||
|
||||
await selectNode("#grid", inspector);
|
||||
const container = getRuleViewProperty(view, "#grid", "display").valueSpan;
|
||||
const container = (
|
||||
await getRuleViewProperty(view, "#grid", "display", { wait: true })
|
||||
).valueSpan;
|
||||
let gridToggle = container.querySelector(".js-toggle-grid-highlighter");
|
||||
|
||||
info("Toggling ON the CSS grid highlighter from the rule-view.");
|
||||
|
|
|
@ -560,7 +560,7 @@ function getRuleViewRule(view, selectorText, index = 0) {
|
|||
|
||||
/**
|
||||
* Get references to the name and value span nodes corresponding to a given
|
||||
* selector and property name in the rule-view
|
||||
* selector and property name in the rule-view.
|
||||
*
|
||||
* @param {CssRuleView} view
|
||||
* The instance of the rule-view panel
|
||||
|
@ -568,25 +568,38 @@ function getRuleViewRule(view, selectorText, index = 0) {
|
|||
* The selector in the rule-view to look for the property in
|
||||
* @param {String} propertyName
|
||||
* The name of the property
|
||||
* @param {Object=} options
|
||||
* @param {Boolean=} options.wait
|
||||
* When true, returns a promise which waits until a valid rule view
|
||||
* property can be retrieved for the provided selectorText & propertyName.
|
||||
* Defaults to false.
|
||||
* @return {Object} An object like {nameSpan: DOMNode, valueSpan: DOMNode}
|
||||
*/
|
||||
function getRuleViewProperty(view, selectorText, propertyName) {
|
||||
let prop;
|
||||
function getRuleViewProperty(view, selectorText, propertyName, options = {}) {
|
||||
if (options.wait) {
|
||||
return waitFor(() =>
|
||||
_syncGetRuleViewProperty(view, selectorText, propertyName)
|
||||
);
|
||||
}
|
||||
return _syncGetRuleViewProperty(view, selectorText, propertyName);
|
||||
}
|
||||
|
||||
function _syncGetRuleViewProperty(view, selectorText, propertyName) {
|
||||
const rule = getRuleViewRule(view, selectorText);
|
||||
if (rule) {
|
||||
// Look for the propertyName in that rule element
|
||||
for (const p of rule.querySelectorAll(".ruleview-property")) {
|
||||
const nameSpan = p.querySelector(".ruleview-propertyname");
|
||||
const valueSpan = p.querySelector(".ruleview-propertyvalue");
|
||||
if (!rule) {
|
||||
return null;
|
||||
}
|
||||
|
||||
if (nameSpan.textContent === propertyName) {
|
||||
prop = { nameSpan: nameSpan, valueSpan: valueSpan };
|
||||
break;
|
||||
}
|
||||
// Look for the propertyName in that rule element
|
||||
for (const p of rule.querySelectorAll(".ruleview-property")) {
|
||||
const nameSpan = p.querySelector(".ruleview-propertyname");
|
||||
const valueSpan = p.querySelector(".ruleview-propertyvalue");
|
||||
|
||||
if (nameSpan.textContent === propertyName) {
|
||||
return { nameSpan: nameSpan, valueSpan: valueSpan };
|
||||
}
|
||||
}
|
||||
return prop;
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -309,3 +309,12 @@
|
|||
outline: 1px solid var(--theme-contrast-border);
|
||||
border-radius: 2px;
|
||||
}
|
||||
|
||||
.cm-non-printable-char {
|
||||
color: var(--theme-comment);
|
||||
font-size: 0.9em;
|
||||
border-radius: 2px;
|
||||
outline: 1px solid currentColor;
|
||||
margin-inline: 2px;
|
||||
padding-inline: 2px;
|
||||
}
|
||||
|
|
|
@ -161,6 +161,24 @@ function Editor(config) {
|
|||
maxHighlightLength: 1000,
|
||||
// Disable codeMirror setTimeout-based cursor blinking (will be replaced by a CSS animation)
|
||||
cursorBlinkRate: 0,
|
||||
// List of non-printable chars that will be displayed in the editor, showing their
|
||||
// unicode version. We only add a few characters to the default list:
|
||||
// - \u202d LEFT-TO-RIGHT OVERRIDE
|
||||
// - \u202e RIGHT-TO-LEFT OVERRIDE
|
||||
// - \u2066 LEFT-TO-RIGHT ISOLATE
|
||||
// - \u2067 RIGHT-TO-LEFT ISOLATE
|
||||
// - \u2069 POP DIRECTIONAL ISOLATE
|
||||
// eslint-disable-next-line no-control-regex
|
||||
specialChars: /[\u0000-\u001f\u007f-\u009f\u00ad\u061c\u200b-\u200f\u2028\u2029\u202d\u202e\u2066\u2067\u2069\ufeff\ufff9-\ufffc]/,
|
||||
specialCharPlaceholder: char => {
|
||||
// Use the doc provided to the setup function if we don't have a reference to a codeMirror
|
||||
// editor yet (this can happen when an Editor is being created with existing content)
|
||||
const doc = this._ownerDoc;
|
||||
const el = doc.createElement("span");
|
||||
el.classList.add("cm-non-printable-char");
|
||||
el.append(doc.createTextNode(`\\u${char.codePointAt(0).toString(16)}`));
|
||||
return el;
|
||||
},
|
||||
};
|
||||
|
||||
// Additional shortcuts.
|
||||
|
@ -353,7 +371,7 @@ Editor.prototype = {
|
|||
* configure CodeMirror with all the right options/modes/etc.
|
||||
*/
|
||||
_setup: function(el, doc) {
|
||||
doc = doc || el.ownerDocument;
|
||||
this._ownerDoc = doc || el.ownerDocument;
|
||||
const win = el.ownerDocument.defaultView;
|
||||
|
||||
Services.scriptloader.loadSubScript(CM_BUNDLE, win);
|
||||
|
@ -421,7 +439,7 @@ Editor.prototype = {
|
|||
|
||||
let popup = this.config.contextMenu;
|
||||
if (typeof popup == "string") {
|
||||
popup = doc.getElementById(this.config.contextMenu);
|
||||
popup = this._ownerDoc.getElementById(this.config.contextMenu);
|
||||
}
|
||||
|
||||
this.emit("popupOpen", ev, popup);
|
||||
|
@ -1433,6 +1451,7 @@ Editor.prototype = {
|
|||
this.container = null;
|
||||
this.config = null;
|
||||
this.version = null;
|
||||
this._ownerDoc = null;
|
||||
|
||||
if (this._prefObserver) {
|
||||
this._prefObserver.off(KEYMAP_PREF, this.setKeyMap);
|
||||
|
|
|
@ -57,5 +57,19 @@ async function test() {
|
|||
ed.setFontSize(ed.getFontSize() + 1);
|
||||
is(ed.getFontSize(), size + 1, "new font size was set");
|
||||
|
||||
info("Check that we display unicode values for non-printable characters");
|
||||
ed.setText("> \u202e \u2066 - \u2069 \u2066 <");
|
||||
|
||||
const doc = win.document.querySelector("iframe").contentWindow.document;
|
||||
const nonPrintableCharElements = Array.from(
|
||||
doc.querySelectorAll(".cm-non-printable-char")
|
||||
);
|
||||
|
||||
Assert.deepEqual(
|
||||
nonPrintableCharElements.map(el => el.textContent),
|
||||
["\\u202e", "\\u2066", "\\u2069", "\\u2066"],
|
||||
"non printable chars are displayed as expected"
|
||||
);
|
||||
|
||||
teardown(ed, win);
|
||||
}
|
||||
|
|
|
@ -0,0 +1,15 @@
|
|||
<html class="reftest-wait">
|
||||
<head>
|
||||
<script>
|
||||
function test() {
|
||||
let ac = new AbortController();
|
||||
scheduler.postTask(()=> { ac.abort(); throw "Foobar"; }, { signal: ac.signal });
|
||||
scheduler.postTask(()=> document.documentElement.removeAttribute('class'));
|
||||
}
|
||||
</script>
|
||||
<style>
|
||||
</style>
|
||||
</head>
|
||||
<body onload="test()">
|
||||
</body>
|
||||
</html>
|
|
@ -267,3 +267,4 @@ skip-if(ThreadSanitizer||Android) load 1712198.html # Mysterious failure that sh
|
|||
skip-if(Android) HTTP load 1728670-1.html
|
||||
load 1757923.html
|
||||
load 1766472.html
|
||||
load 1780790.html
|
||||
|
|
|
@ -4633,8 +4633,7 @@ IPCResult ContentChild::RecvFlushFOGData(FlushFOGDataResolver&& aResolver) {
|
|||
}
|
||||
|
||||
IPCResult ContentChild::RecvUpdateMediaCodecsSupported(
|
||||
RemoteDecodeIn aLocation,
|
||||
const PDMFactory::MediaCodecsSupported& aSupported) {
|
||||
RemoteDecodeIn aLocation, const media::MediaCodecsSupported& aSupported) {
|
||||
RemoteDecoderManagerChild::SetSupported(aLocation, aSupported);
|
||||
|
||||
return IPC_OK();
|
||||
|
|
|
@ -803,8 +803,7 @@ class ContentChild final : public PContentChild,
|
|||
mozilla::ipc::IPCResult RecvFlushFOGData(FlushFOGDataResolver&& aResolver);
|
||||
|
||||
mozilla::ipc::IPCResult RecvUpdateMediaCodecsSupported(
|
||||
RemoteDecodeIn aLocation,
|
||||
const PDMFactory::MediaCodecsSupported& aSupported);
|
||||
RemoteDecodeIn aLocation, const media::MediaCodecsSupported& aSupported);
|
||||
|
||||
#ifdef NIGHTLY_BUILD
|
||||
virtual void OnChannelReceivedMessage(const Message& aMsg) override;
|
||||
|
|
|
@ -363,8 +363,7 @@ namespace dom {
|
|||
|
||||
LazyLogModule gProcessLog("Process");
|
||||
|
||||
static std::map<RemoteDecodeIn, PDMFactory::MediaCodecsSupported>
|
||||
sCodecsSupported;
|
||||
static std::map<RemoteDecodeIn, media::MediaCodecsSupported> sCodecsSupported;
|
||||
|
||||
/* static */
|
||||
uint32_t ContentParent::sMaxContentProcesses = 0;
|
||||
|
@ -1632,12 +1631,21 @@ void ContentParent::BroadcastThemeUpdate(widget::ThemeChangeKind aKind) {
|
|||
|
||||
/*static */
|
||||
void ContentParent::BroadcastMediaCodecsSupportedUpdate(
|
||||
RemoteDecodeIn aLocation,
|
||||
const PDMFactory::MediaCodecsSupported& aSupported) {
|
||||
sCodecsSupported[aLocation] = aSupported;
|
||||
RemoteDecodeIn aLocation, const media::MediaCodecsSupported& aSupported) {
|
||||
// Merge incoming codec support with existing support list
|
||||
media::MCSInfo::AddSupport(aSupported);
|
||||
auto support = media::MCSInfo::GetSupport();
|
||||
|
||||
// Update processes
|
||||
sCodecsSupported[aLocation] = support;
|
||||
for (auto* cp : AllProcesses(eAll)) {
|
||||
Unused << cp->SendUpdateMediaCodecsSupported(aLocation, aSupported);
|
||||
Unused << cp->SendUpdateMediaCodecsSupported(aLocation, support);
|
||||
}
|
||||
|
||||
// Generate + save support string for display in about:support
|
||||
nsCString supportString;
|
||||
media::MCSInfo::GetMediaCodecsSupportedString(supportString, support);
|
||||
gfx::gfxVars::SetCodecSupportInfo(supportString);
|
||||
}
|
||||
|
||||
const nsACString& ContentParent::GetRemoteType() const { return mRemoteType; }
|
||||
|
|
|
@ -254,8 +254,7 @@ class ContentParent final : public PContentParent,
|
|||
static void BroadcastThemeUpdate(widget::ThemeChangeKind);
|
||||
|
||||
static void BroadcastMediaCodecsSupportedUpdate(
|
||||
RemoteDecodeIn aLocation,
|
||||
const PDMFactory::MediaCodecsSupported& aSupported);
|
||||
RemoteDecodeIn aLocation, const media::MediaCodecsSupported& aSupported);
|
||||
|
||||
const nsACString& GetRemoteType() const override;
|
||||
|
||||
|
|
|
@ -148,7 +148,7 @@ using mozilla::PermissionDelegateHandler::DelegatedPermissionList from "mozilla/
|
|||
using class mozilla::dom::SessionHistoryInfo from "mozilla/dom/SessionHistoryEntry.h";
|
||||
using struct nsPoint from "nsPoint.h";
|
||||
using struct mozilla::dom::LoadingSessionHistoryInfo from "mozilla/dom/SessionHistoryEntry.h";
|
||||
using mozilla::PDMFactory::MediaCodecsSupported from "PDMFactory.h";
|
||||
using mozilla::media::MediaCodecsSupported from "MediaCodecsSupport.h";
|
||||
using mozilla::RemoteDecodeIn from "mozilla/RemoteDecoderManagerChild.h";
|
||||
using mozilla::dom::PerformanceTimingData from "mozilla/dom/PerformanceTiming.h";
|
||||
[RefCounted] using mozilla::dom::FeaturePolicy from "mozilla/dom/FeaturePolicy.h";
|
||||
|
|
|
@ -13,7 +13,8 @@ namespace mozilla {
|
|||
/* static */
|
||||
bool ADTSDecoder::IsEnabled() {
|
||||
RefPtr<PDMFactory> platform = new PDMFactory();
|
||||
return platform->SupportsMimeType("audio/mp4a-latm"_ns);
|
||||
return platform->SupportsMimeType("audio/mp4a-latm"_ns) !=
|
||||
media::DecodeSupport::Unsupported;
|
||||
}
|
||||
|
||||
/* static */
|
||||
|
|
|
@ -1164,7 +1164,8 @@ void MediaFormatReader::OnDemuxerInitDone(const MediaResult& aResult) {
|
|||
UniquePtr<TrackInfo> videoInfo = mVideo.mTrackDemuxer->GetInfo();
|
||||
videoActive = videoInfo && videoInfo->IsValid();
|
||||
if (videoActive) {
|
||||
if (platform && !platform->SupportsMimeType(videoInfo->mMimeType)) {
|
||||
if (platform && platform->SupportsMimeType(videoInfo->mMimeType) ==
|
||||
media::DecodeSupport::Unsupported) {
|
||||
// We have no decoder for this track. Error.
|
||||
mMetadataPromise.Reject(NS_ERROR_DOM_MEDIA_METADATA_ERR, __func__);
|
||||
return;
|
||||
|
@ -1195,7 +1196,8 @@ void MediaFormatReader::OnDemuxerInitDone(const MediaResult& aResult) {
|
|||
// We actively ignore audio tracks that we know we can't play.
|
||||
audioActive =
|
||||
audioInfo && audioInfo->IsValid() &&
|
||||
(!platform || platform->SupportsMimeType(audioInfo->mMimeType));
|
||||
(!platform || platform->SupportsMimeType(audioInfo->mMimeType) !=
|
||||
media::DecodeSupport::Unsupported);
|
||||
|
||||
if (audioActive) {
|
||||
mInfo.mAudio = *audioInfo->GetAsAudioInfo();
|
||||
|
|
|
@ -0,0 +1,158 @@
|
|||
/* -*- Mode: C++; 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/. */
|
||||
|
||||
#include "gtest/gtest.h"
|
||||
#include "MediaCodecsSupport.h"
|
||||
|
||||
using namespace mozilla;
|
||||
using namespace media;
|
||||
|
||||
// Test MCSInfo::GetDecodeSupportSet function.
|
||||
// This function is used to retrieve SW/HW support information for a
|
||||
// given codec from a MediaCodecsSupported EnumSet.
|
||||
// We validate that SW, HW, SW+HW, or lack of support information is
|
||||
// properly returned.
|
||||
TEST(MediaCodecsSupport, GetDecodeSupportSet)
|
||||
{
|
||||
// Mock VP8 SW support, VP9 HW support, H264 SW+HW support
|
||||
MediaCodecsSupported supported{MediaCodecsSupport::VP8SoftwareDecode,
|
||||
MediaCodecsSupport::VP9HardwareDecode,
|
||||
MediaCodecsSupport::H264SoftwareDecode,
|
||||
MediaCodecsSupport::H264HardwareDecode};
|
||||
|
||||
MediaCodec codec; // Codec used to generate + filter results
|
||||
DecodeSupportSet RV; // Return value to check for validity
|
||||
|
||||
// Check only SW support returned for VP8
|
||||
codec = MediaCodec::VP8;
|
||||
RV = MCSInfo::GetDecodeSupportSet(codec, supported);
|
||||
EXPECT_TRUE(RV.contains(DecodeSupport::SoftwareDecode));
|
||||
EXPECT_TRUE(RV.size() == 1);
|
||||
|
||||
// Check only HW support returned for VP9
|
||||
codec = MediaCodec::VP9;
|
||||
RV = MCSInfo::GetDecodeSupportSet(codec, supported);
|
||||
EXPECT_TRUE(RV.contains(DecodeSupport::HardwareDecode));
|
||||
EXPECT_TRUE(RV.size() == 1);
|
||||
|
||||
// Check for both SW/HW support returned for H264
|
||||
codec = MediaCodec::H264;
|
||||
RV = MCSInfo::GetDecodeSupportSet(codec, supported);
|
||||
EXPECT_TRUE(RV.contains(DecodeSupport::SoftwareDecode));
|
||||
EXPECT_TRUE(RV.contains(DecodeSupport::HardwareDecode));
|
||||
EXPECT_TRUE(RV.size() == 2);
|
||||
|
||||
// Check empty return if codec not in list of codecs
|
||||
codec = MediaCodec::AV1;
|
||||
RV = MCSInfo::GetDecodeSupportSet(codec, supported);
|
||||
EXPECT_TRUE(RV.size() == 0);
|
||||
}
|
||||
|
||||
// Test MCSInfo::GetDecodeMediaCodecsSupported function.
|
||||
// This function is used to generate codec-specific SW/HW
|
||||
// support information from a generic codec identifier enum and
|
||||
// generic SW/HW support information.
|
||||
// We validate that SW, HW, SW+HW, or lack of support information is
|
||||
// properly returned.
|
||||
TEST(MediaCodecsSupport, GetDecodeMediaCodecsSupported)
|
||||
{
|
||||
MediaCodec codec; // Codec used to generate / filter results
|
||||
MediaCodecsSupported RV; // Return value to check for validity
|
||||
DecodeSupportSet dss; // Non codec-specific SW / HW support information
|
||||
|
||||
// Check SW support returned for VP8
|
||||
codec = MediaCodec::VP8;
|
||||
dss = DecodeSupportSet{DecodeSupport::SoftwareDecode};
|
||||
RV = MCSInfo::GetDecodeMediaCodecsSupported(codec, dss);
|
||||
EXPECT_TRUE(RV.contains(MediaCodecsSupport::VP8SoftwareDecode));
|
||||
EXPECT_TRUE(RV.size() == 1);
|
||||
|
||||
// Check HW support returned for AV1
|
||||
codec = MediaCodec::AV1;
|
||||
dss = DecodeSupportSet{DecodeSupport::HardwareDecode};
|
||||
RV = MCSInfo::GetDecodeMediaCodecsSupported(codec, dss);
|
||||
EXPECT_TRUE(RV.contains(MediaCodecsSupport::AV1HardwareDecode));
|
||||
EXPECT_TRUE(RV.size() == 1);
|
||||
|
||||
// Check SW + HW support returned for VP9
|
||||
codec = MediaCodec::VP9;
|
||||
dss = DecodeSupportSet{DecodeSupport::SoftwareDecode,
|
||||
DecodeSupport::HardwareDecode};
|
||||
RV = MCSInfo::GetDecodeMediaCodecsSupported(codec, dss);
|
||||
EXPECT_TRUE(RV.contains(MediaCodecsSupport::VP9SoftwareDecode));
|
||||
EXPECT_TRUE(RV.contains(MediaCodecsSupport::VP9HardwareDecode));
|
||||
EXPECT_TRUE(RV.size() == 2);
|
||||
|
||||
// Check empty return if codec not supported
|
||||
codec = MediaCodec::AV1;
|
||||
dss = DecodeSupportSet{};
|
||||
RV = MCSInfo::GetDecodeMediaCodecsSupported(codec, dss);
|
||||
EXPECT_TRUE(RV.size() == 0);
|
||||
}
|
||||
|
||||
// Test MCSInfo::AddSupport function.
|
||||
// This function is used to store codec support data.
|
||||
// Incoming support data will be merged with any data that
|
||||
// has already been stored.
|
||||
TEST(MediaCodecsSupport, AddSupport)
|
||||
{
|
||||
// Make sure we're not storing any existing support information.
|
||||
MCSInfo::ResetSupport();
|
||||
EXPECT_TRUE(MCSInfo::GetSupport().size() == 0);
|
||||
|
||||
// Add codec support one at a time via individual calls
|
||||
MCSInfo::AddSupport(MediaCodecsSupport::AACSoftwareDecode);
|
||||
MCSInfo::AddSupport(MediaCodecsSupport::VP9SoftwareDecode);
|
||||
MCSInfo::AddSupport(MediaCodecsSupport::AV1HardwareDecode);
|
||||
|
||||
// Add multiple codec support via MediaCodecsSupported EnumSet
|
||||
MCSInfo::AddSupport(
|
||||
MediaCodecsSupported{MediaCodecsSupport::H264SoftwareDecode,
|
||||
MediaCodecsSupport::H264HardwareDecode});
|
||||
|
||||
// Query MCSInfo for supported codecs
|
||||
MediaCodecsSupported supported = MCSInfo::GetSupport();
|
||||
DecodeSupportSet dss;
|
||||
|
||||
// AAC should only report software decode support
|
||||
dss = MCSInfo::GetDecodeSupportSet(MediaCodec::AAC, supported);
|
||||
EXPECT_TRUE(dss.size() == 1);
|
||||
EXPECT_TRUE(dss.contains(DecodeSupport::SoftwareDecode));
|
||||
|
||||
// AV1 should only report hardware decode support
|
||||
dss = MCSInfo::GetDecodeSupportSet(MediaCodec::AV1, supported);
|
||||
EXPECT_TRUE(dss.size() == 1);
|
||||
EXPECT_TRUE(dss.contains(DecodeSupport::HardwareDecode));
|
||||
|
||||
// H264 should report both SW + HW decode support
|
||||
dss = MCSInfo::GetDecodeSupportSet(MediaCodec::H264, supported);
|
||||
EXPECT_TRUE(dss.size() == 2);
|
||||
EXPECT_TRUE(dss.contains(DecodeSupport::SoftwareDecode));
|
||||
EXPECT_TRUE(dss.contains(DecodeSupport::HardwareDecode));
|
||||
|
||||
// Vorbis should report no decode support
|
||||
dss = MCSInfo::GetDecodeSupportSet(MediaCodec::Vorbis, supported);
|
||||
EXPECT_TRUE(dss.size() == 0);
|
||||
}
|
||||
|
||||
// Test MCSInfo::GetMediaCodecsSupportedString function.
|
||||
// This function returns a human-readable string containing codec
|
||||
// names and SW/HW playback support information.
|
||||
TEST(MediaCodecsSupport, GetMediaCodecsSupportedString)
|
||||
{
|
||||
// Make sure we're not storing any existing support information.
|
||||
MCSInfo::ResetSupport();
|
||||
EXPECT_TRUE(MCSInfo::GetSupport().size() == 0);
|
||||
|
||||
// Add H264 SW/HW support + VP8 Software decode support.
|
||||
MCSInfo::AddSupport({MediaCodecsSupport::H264SoftwareDecode,
|
||||
MediaCodecsSupport::H264HardwareDecode,
|
||||
MediaCodecsSupport::VP8SoftwareDecode});
|
||||
|
||||
nsCString supportString;
|
||||
MCSInfo::GetMediaCodecsSupportedString(supportString, MCSInfo::GetSupport());
|
||||
EXPECT_TRUE(supportString.Equals(
|
||||
"Codec support information:\nH264 SW\nH264 HW\nVP8 SW"_ns));
|
||||
}
|
|
@ -46,6 +46,7 @@ UNIFIED_SOURCES += [
|
|||
"TestGroupId.cpp",
|
||||
"TestIntervalSet.cpp",
|
||||
"TestKeyValueStorage.cpp",
|
||||
"TestMediaCodecsSupport.cpp",
|
||||
"TestMediaDataDecoder.cpp",
|
||||
"TestMediaDataEncoder.cpp",
|
||||
"TestMediaEventSource.cpp",
|
||||
|
|
|
@ -18,7 +18,7 @@ include protocol PSandboxTesting;
|
|||
include "mozilla/ipc/ByteBufUtils.h";
|
||||
|
||||
using mozilla::dom::NativeThreadId from "mozilla/dom/NativeThreadId.h";
|
||||
using mozilla::PDMFactory::MediaCodecsSupported from "PDMFactory.h";
|
||||
using mozilla::media::MediaCodecsSupported from "MediaCodecsSupport.h";
|
||||
|
||||
#if defined(XP_WIN)
|
||||
[MoveOnly] using mozilla::UntrustedModulesData from "mozilla/UntrustedModulesData.h";
|
||||
|
|
|
@ -142,7 +142,7 @@ mozilla::ipc::IPCResult RDDChild::RecvGetModulesTrust(
|
|||
#endif // defined(XP_WIN)
|
||||
|
||||
mozilla::ipc::IPCResult RDDChild::RecvUpdateMediaCodecsSupported(
|
||||
const PDMFactory::MediaCodecsSupported& aSupported) {
|
||||
const media::MediaCodecsSupported& aSupported) {
|
||||
dom::ContentParent::BroadcastMediaCodecsSupportedUpdate(
|
||||
RemoteDecodeIn::RddProcess, aSupported);
|
||||
return IPC_OK();
|
||||
|
|
|
@ -47,7 +47,7 @@ class RDDChild final : public PRDDChild,
|
|||
GetModulesTrustResolver&& aResolver);
|
||||
#endif // defined(XP_WIN)
|
||||
mozilla::ipc::IPCResult RecvUpdateMediaCodecsSupported(
|
||||
const PDMFactory::MediaCodecsSupported& aSupported);
|
||||
const media::MediaCodecsSupported& aSupported);
|
||||
mozilla::ipc::IPCResult RecvFOGData(ByteBuf&& aBuf);
|
||||
|
||||
bool SendRequestMemoryReport(const uint32_t& aGeneration,
|
||||
|
|
|
@ -58,12 +58,12 @@ static StaticRefPtr<RemoteDecoderManagerChild>
|
|||
sRemoteDecoderManagerChildForGPUProcess;
|
||||
static UniquePtr<nsTArray<RefPtr<Runnable>>> sRecreateTasks;
|
||||
|
||||
static StaticDataMutex<Maybe<PDMFactory::MediaCodecsSupported>> sGPUSupported(
|
||||
static StaticDataMutex<Maybe<media::MediaCodecsSupported>> sGPUSupported(
|
||||
"RDMC::sGPUSupported");
|
||||
static StaticDataMutex<Maybe<PDMFactory::MediaCodecsSupported>> sRDDSupported(
|
||||
static StaticDataMutex<Maybe<media::MediaCodecsSupported>> sRDDSupported(
|
||||
"RDMC::sRDDSupported");
|
||||
static StaticDataMutex<Maybe<PDMFactory::MediaCodecsSupported>>
|
||||
sUtilitySupported("RDMC::sUtilitySupported");
|
||||
static StaticDataMutex<Maybe<media::MediaCodecsSupported>> sUtilitySupported(
|
||||
"RDMC::sUtilitySupported");
|
||||
|
||||
class ShutdownObserver final : public nsIObserver {
|
||||
public:
|
||||
|
@ -226,7 +226,7 @@ nsISerialEventTarget* RemoteDecoderManagerChild::GetManagerThread() {
|
|||
bool RemoteDecoderManagerChild::Supports(
|
||||
RemoteDecodeIn aLocation, const SupportDecoderParams& aParams,
|
||||
DecoderDoctorDiagnostics* aDiagnostics) {
|
||||
Maybe<PDMFactory::MediaCodecsSupported> supported;
|
||||
Maybe<media::MediaCodecsSupported> supported;
|
||||
switch (aLocation) {
|
||||
case RemoteDecodeIn::RddProcess: {
|
||||
auto supportedRDD = sRDDSupported.Lock();
|
||||
|
@ -264,7 +264,8 @@ bool RemoteDecoderManagerChild::Supports(
|
|||
// We can ignore the SupportDecoderParams argument for now as creation of the
|
||||
// decoder will actually fail later and fallback PDMs will be tested on later.
|
||||
return PDMFactory::SupportsMimeType(aParams.MimeType(), *supported,
|
||||
aLocation);
|
||||
aLocation) !=
|
||||
media::DecodeSupport::Unsupported;
|
||||
}
|
||||
|
||||
/* static */
|
||||
|
@ -797,8 +798,7 @@ void RemoteDecoderManagerChild::HandleFatalError(const char* aMsg) const {
|
|||
}
|
||||
|
||||
void RemoteDecoderManagerChild::SetSupported(
|
||||
RemoteDecodeIn aLocation,
|
||||
const PDMFactory::MediaCodecsSupported& aSupported) {
|
||||
RemoteDecodeIn aLocation, const media::MediaCodecsSupported& aSupported) {
|
||||
switch (aLocation) {
|
||||
case RemoteDecodeIn::GpuProcess: {
|
||||
auto supported = sGPUSupported.Lock();
|
||||
|
|
|
@ -41,7 +41,7 @@ class RemoteDecoderManagerChild final
|
|||
|
||||
static void Init();
|
||||
static void SetSupported(RemoteDecodeIn aLocation,
|
||||
const PDMFactory::MediaCodecsSupported& aSupported);
|
||||
const media::MediaCodecsSupported& aSupported);
|
||||
|
||||
// Can be called from any thread.
|
||||
static bool Supports(RemoteDecodeIn aLocation,
|
||||
|
|
|
@ -255,7 +255,8 @@ already_AddRefed<Promise> MediaCapabilities::DecodingInfo(
|
|||
InvokeAsync(taskQueue, __func__, [config = std::move(config)]() {
|
||||
RefPtr<PDMFactory> pdm = new PDMFactory();
|
||||
SupportDecoderParams params{*config};
|
||||
if (!pdm->Supports(params, nullptr /* decoder doctor */)) {
|
||||
if (pdm->Supports(params, nullptr /* decoder doctor */) ==
|
||||
media::DecodeSupport::Unsupported) {
|
||||
return CapabilitiesPromise::CreateAndReject(NS_ERROR_FAILURE,
|
||||
__func__);
|
||||
}
|
||||
|
|
|
@ -13,7 +13,8 @@ namespace mozilla {
|
|||
/* static */
|
||||
bool MP3Decoder::IsEnabled() {
|
||||
RefPtr<PDMFactory> platform = new PDMFactory();
|
||||
return platform->SupportsMimeType("audio/mpeg"_ns);
|
||||
return platform->SupportsMimeType("audio/mpeg"_ns) !=
|
||||
media::DecodeSupport::Unsupported;
|
||||
}
|
||||
|
||||
/* static */
|
||||
|
|
|
@ -154,7 +154,8 @@ bool MP4Decoder::IsSupportedType(const MediaContainerType& aType,
|
|||
RefPtr<PDMFactory> platform = new PDMFactory();
|
||||
for (const auto& track : tracks) {
|
||||
if (!track ||
|
||||
!platform->Supports(SupportDecoderParams(*track), aDiagnostics)) {
|
||||
platform->Supports(SupportDecoderParams(*track), aDiagnostics) ==
|
||||
media::DecodeSupport::Unsupported) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
@ -183,7 +184,8 @@ bool MP4Decoder::IsSupportedType(const MediaContainerType& aType,
|
|||
RefPtr<PDMFactory> platform = new PDMFactory();
|
||||
for (const auto& track : tracks) {
|
||||
if (track &&
|
||||
platform->Supports(SupportDecoderParams(*track), aDiagnostics)) {
|
||||
platform->Supports(SupportDecoderParams(*track), aDiagnostics) !=
|
||||
media::DecodeSupport::Unsupported) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,175 @@
|
|||
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* vim:set ts=2 sw=2 sts=2 et cindent: */
|
||||
/* 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 <array>
|
||||
|
||||
#include "MediaCodecsSupport.h"
|
||||
#include "PlatformDecoderModule.h"
|
||||
#include "mozilla/gfx/gfxVars.h"
|
||||
#include "nsTHashMap.h"
|
||||
|
||||
using MediaCodecsSupport = mozilla::media::MediaCodecsSupport;
|
||||
|
||||
namespace mozilla::media {
|
||||
|
||||
#define CODEC_SUPPORT_LOG(msg, ...) \
|
||||
MOZ_LOG(sPDMLog, LogLevel::Debug, ("MediaCodecsSupport, " msg, ##__VA_ARGS__))
|
||||
|
||||
void MCSInfo::AddSupport(const MediaCodecsSupported& aSupport) {
|
||||
StaticMutexAutoLock lock(sUpdateMutex);
|
||||
GetInstance()->mSupport += aSupport;
|
||||
}
|
||||
|
||||
MediaCodecsSupported MCSInfo::GetSupport() {
|
||||
StaticMutexAutoLock lock(sUpdateMutex);
|
||||
return GetInstance()->mSupport;
|
||||
}
|
||||
|
||||
void MCSInfo::ResetSupport() {
|
||||
StaticMutexAutoLock lock(sUpdateMutex);
|
||||
GetInstance()->mSupport.clear();
|
||||
}
|
||||
|
||||
DecodeSupportSet MCSInfo::GetDecodeSupportSet(
|
||||
const MediaCodec& aCodec, const MediaCodecsSupported& aSupported) {
|
||||
DecodeSupportSet support;
|
||||
const auto supportInfo = GetCodecDefinition(aCodec);
|
||||
if (aSupported.contains(supportInfo.swDecodeSupport)) {
|
||||
support += DecodeSupport::SoftwareDecode;
|
||||
}
|
||||
if (aSupported.contains(supportInfo.hwDecodeSupport)) {
|
||||
support += DecodeSupport::HardwareDecode;
|
||||
}
|
||||
return support;
|
||||
}
|
||||
|
||||
MediaCodecsSupported MCSInfo::GetDecodeMediaCodecsSupported(
|
||||
const MediaCodec& aCodec, const DecodeSupportSet& aSupportSet) {
|
||||
MediaCodecsSupported support;
|
||||
|
||||
const auto supportInfo = GetCodecDefinition(aCodec);
|
||||
|
||||
if (aSupportSet.contains(DecodeSupport::SoftwareDecode)) {
|
||||
support += supportInfo.swDecodeSupport;
|
||||
}
|
||||
if (aSupportSet.contains(DecodeSupport::HardwareDecode)) {
|
||||
support += supportInfo.hwDecodeSupport;
|
||||
}
|
||||
return support;
|
||||
}
|
||||
|
||||
void MCSInfo::GetMediaCodecsSupportedString(
|
||||
nsCString& aSupportString, const MediaCodecsSupported& aSupportedCodecs) {
|
||||
CodecDefinition supportInfo;
|
||||
aSupportString = "Codec support information:\n"_ns;
|
||||
for (const auto& it : aSupportedCodecs) {
|
||||
if (!GetInstance()->mHashTableMCS->Get(it, &supportInfo)) {
|
||||
CODEC_SUPPORT_LOG(
|
||||
"Could not find string matching MediaCodecsSupported enum: %d",
|
||||
static_cast<int>(it));
|
||||
aSupportString.Append("Unknown codec entry found!\n"_ns);
|
||||
continue;
|
||||
}
|
||||
// Get codec name string and append SW/HW support info
|
||||
aSupportString.Append(supportInfo.commonName);
|
||||
if (it == supportInfo.swDecodeSupport) {
|
||||
aSupportString.Append(" SW"_ns);
|
||||
}
|
||||
if (it == supportInfo.hwDecodeSupport) {
|
||||
aSupportString.Append(" HW"_ns);
|
||||
}
|
||||
aSupportString.Append("\n"_ns);
|
||||
}
|
||||
// Remove any trailing newline characters
|
||||
if (!aSupportString.IsEmpty()) {
|
||||
aSupportString.Truncate(aSupportString.Length() - 1);
|
||||
}
|
||||
}
|
||||
|
||||
MCSInfo* MCSInfo::GetInstance() {
|
||||
StaticMutexAutoLock lock(sInitMutex);
|
||||
if (!sInstance) {
|
||||
sInstance.reset(new MCSInfo());
|
||||
}
|
||||
return sInstance.get();
|
||||
}
|
||||
|
||||
MCSInfo::MCSInfo() {
|
||||
// Initialize hash tables
|
||||
mHashTableMCS.reset(new nsTHashMap<MediaCodecsSupport, CodecDefinition>());
|
||||
mHashTableCodec.reset(new nsTHashMap<MediaCodec, CodecDefinition>());
|
||||
|
||||
for (const auto& it : GetAllCodecDefinitions()) {
|
||||
// Insert MediaCodecsSupport values as keys
|
||||
mHashTableMCS->InsertOrUpdate(it.swDecodeSupport, it);
|
||||
mHashTableMCS->InsertOrUpdate(it.hwDecodeSupport, it);
|
||||
// Insert codec enum values as keys
|
||||
mHashTableCodec->InsertOrUpdate(it.codec, it);
|
||||
}
|
||||
|
||||
GetMainThreadSerialEventTarget()->Dispatch(
|
||||
NS_NewRunnableFunction("MCSInfo::MCSInfo", [&] {
|
||||
// Ensure hash tables freed on shutdown
|
||||
RunOnShutdown(
|
||||
[&] {
|
||||
mHashTableMCS.reset();
|
||||
mHashTableCodec.reset();
|
||||
},
|
||||
ShutdownPhase::XPCOMShutdown);
|
||||
}));
|
||||
}
|
||||
|
||||
CodecDefinition MCSInfo::GetCodecDefinition(const MediaCodec& aCodec) {
|
||||
CodecDefinition info;
|
||||
if (!GetInstance()->mHashTableCodec->Get(aCodec, &info)) {
|
||||
CODEC_SUPPORT_LOG("Could not find codec definition for codec enum: %d!",
|
||||
static_cast<int>(aCodec));
|
||||
}
|
||||
return info;
|
||||
}
|
||||
|
||||
std::array<CodecDefinition, 12> MCSInfo::GetAllCodecDefinitions() {
|
||||
static constexpr std::array<CodecDefinition, 12> codecDefinitions = {
|
||||
{{MediaCodec::H264, "H264", "video/avc",
|
||||
MediaCodecsSupport::H264SoftwareDecode,
|
||||
MediaCodecsSupport::H264HardwareDecode},
|
||||
{MediaCodec::VP9, "VP9", "video/vp9",
|
||||
MediaCodecsSupport::VP9SoftwareDecode,
|
||||
MediaCodecsSupport::VP9HardwareDecode},
|
||||
{MediaCodec::VP8, "VP8", "video/vp8",
|
||||
MediaCodecsSupport::VP8SoftwareDecode,
|
||||
MediaCodecsSupport::VP8HardwareDecode},
|
||||
{MediaCodec::AV1, "AV1", "video/av1",
|
||||
MediaCodecsSupport::AV1SoftwareDecode,
|
||||
MediaCodecsSupport::AV1HardwareDecode},
|
||||
{MediaCodec::Theora, "Theora", "video/theora",
|
||||
MediaCodecsSupport::TheoraSoftwareDecode,
|
||||
MediaCodecsSupport::TheoraHardwareDecode},
|
||||
{MediaCodec::AAC, "AAC", "audio/mp4a-latm",
|
||||
MediaCodecsSupport::AACSoftwareDecode,
|
||||
MediaCodecsSupport::AACHardwareDecode},
|
||||
{MediaCodec::MP3, "MP3", "audio/mpeg",
|
||||
MediaCodecsSupport::MP3SoftwareDecode,
|
||||
MediaCodecsSupport::MP3HardwareDecode},
|
||||
{MediaCodec::Opus, "Opus", "audio/opus",
|
||||
MediaCodecsSupport::OpusSoftwareDecode,
|
||||
MediaCodecsSupport::OpusHardwareDecode},
|
||||
{MediaCodec::Vorbis, "Vorbis", "audio/vorbis",
|
||||
MediaCodecsSupport::VorbisSoftwareDecode,
|
||||
MediaCodecsSupport::VorbisHardwareDecode},
|
||||
{MediaCodec::FLAC, "FLAC", "audio/flac",
|
||||
MediaCodecsSupport::FLACSoftwareDecode,
|
||||
MediaCodecsSupport::FLACHardwareDecode},
|
||||
{MediaCodec::Wave, "Wave", "audio/x-wav",
|
||||
MediaCodecsSupport::WaveSoftwareDecode,
|
||||
MediaCodecsSupport::WaveHardwareDecode},
|
||||
{MediaCodec::SENTINEL, "Undefined codec name",
|
||||
"Undefined MIME type string", MediaCodecsSupport::SENTINEL,
|
||||
MediaCodecsSupport::SENTINEL}}};
|
||||
return codecDefinitions;
|
||||
}
|
||||
} // namespace mozilla::media
|
||||
|
||||
#undef CODEC_SUPPORT_LOG
|
|
@ -5,18 +5,195 @@
|
|||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
#ifndef DOM_MEDIA_PLATFORMS_MEDIACODECSSUPPORT_H_
|
||||
#define DOM_MEDIA_PLATFORMS_MEDIACODECSSUPPORT_H_
|
||||
#include <array>
|
||||
|
||||
#include "mozilla/Atomics.h"
|
||||
#include "mozilla/ClearOnShutdown.h"
|
||||
#include "mozilla/EnumSet.h"
|
||||
#include "mozilla/StaticMutex.h"
|
||||
#include "nsString.h"
|
||||
#include "nsTHashMap.h"
|
||||
#include "nsThreadUtils.h"
|
||||
|
||||
namespace mozilla::media {
|
||||
// List of codecs we support, used in the below macros
|
||||
// to generate MediaCodec and MediaCodecSupports enums.
|
||||
#define CODEC_LIST \
|
||||
X(H264) \
|
||||
X(VP8) \
|
||||
X(VP9) \
|
||||
X(AV1) \
|
||||
X(Theora) \
|
||||
X(AAC) \
|
||||
X(FLAC) \
|
||||
X(MP3) \
|
||||
X(Opus) \
|
||||
X(Vorbis) \
|
||||
X(Wave)
|
||||
|
||||
enum class DecodeSupport {
|
||||
// Generate MediaCodec enum with the names of each codec we support.
|
||||
// Example: MediaCodec::H264
|
||||
enum class MediaCodec : int {
|
||||
#define X(name) name,
|
||||
CODEC_LIST
|
||||
#undef X
|
||||
SENTINEL
|
||||
};
|
||||
|
||||
// Helper macros used to create codec-specific SW/HW decode enums below.
|
||||
#define SW_DECODE(codec) codec##SoftwareDecode
|
||||
#define HW_DECODE(codec) codec##HardwareDecode
|
||||
|
||||
// Generate the MediaCodecsSupport enum, containing
|
||||
// codec-specific SW/HW decode/encode information.
|
||||
// Entries for HW audio decode/encode should never be set as we
|
||||
// don't support HW audio decode/encode, but they are included
|
||||
// for debug purposes / check for erroneous PDM return values.
|
||||
// Example: MediaCodecsSupport::AACSoftwareDecode
|
||||
enum class MediaCodecsSupport : int {
|
||||
#define X(name) SW_DECODE(name), HW_DECODE(name),
|
||||
CODEC_LIST
|
||||
#undef X
|
||||
SENTINEL
|
||||
};
|
||||
#undef SW_DECODE
|
||||
#undef HW_DECODE
|
||||
#undef CODEC_LIST // end of macros!
|
||||
|
||||
// Enumset containing per-codec SW/HW support
|
||||
using MediaCodecsSupported = EnumSet<MediaCodecsSupport, uint64_t>;
|
||||
|
||||
// Codec-agnostic SW/HW decode support information.
|
||||
enum class DecodeSupport : int {
|
||||
Unsupported = 0,
|
||||
SoftwareDecode,
|
||||
HardwareDecode,
|
||||
};
|
||||
|
||||
using DecodeSupportSet = EnumSet<DecodeSupport, uint64_t>;
|
||||
|
||||
// CodecDefinition stores information needed to convert / index
|
||||
// codec support information between types. See: GetAllCodecDefinitions()
|
||||
struct CodecDefinition {
|
||||
MediaCodec codec = MediaCodec::SENTINEL;
|
||||
const char* commonName = "Undefined codec name";
|
||||
const char* mimeTypeString = "Undefined MIME type string";
|
||||
MediaCodecsSupport swDecodeSupport = MediaCodecsSupport::SENTINEL;
|
||||
MediaCodecsSupport hwDecodeSupport = MediaCodecsSupport::SENTINEL;
|
||||
};
|
||||
|
||||
// Singleton class used to collect, manage, and report codec support data.
|
||||
class MCSInfo final {
|
||||
public:
|
||||
// Add codec support information to our aggregated list of supported codecs.
|
||||
// Incoming support info is merged with the current support info.
|
||||
// This is because different PDMs may report different codec support
|
||||
// information, so merging their results allows us to maintain a
|
||||
// cumulative support list without overwriting any existing data.
|
||||
static void AddSupport(const MediaCodecsSupported& aSupport);
|
||||
|
||||
// Return a cumulative list of codec support information.
|
||||
// Each call to AddSupport adds to or updates this list.
|
||||
// This support information can be used to create user-readable strings
|
||||
// to report codec support information in about:support.
|
||||
static MediaCodecsSupported GetSupport();
|
||||
|
||||
// Reset codec support information saved from calls to AddSupport().
|
||||
static void ResetSupport();
|
||||
|
||||
// Query a MediaCodecsSupported EnumSet for codec-specific SW/HW support enums
|
||||
// and return general support information as stored in a DecodeSupportSet.
|
||||
//
|
||||
// Example input:
|
||||
//
|
||||
// aCodec: MediaCodec::H264
|
||||
// aDecode: MediaCodecsSupport {
|
||||
// MediaCodecsSupport::AACSoftwareDecode
|
||||
// MediaCodecsSupport::H264HardwareDecode,
|
||||
// MediaCodecsSupport::H264SoftwareDecode,
|
||||
// MediaCodecsSupport::VP8SoftwareDecode,
|
||||
// }
|
||||
//
|
||||
// Example output:
|
||||
//
|
||||
// DecodeSupportSet {
|
||||
// DecodeSupport::SoftwareDecode,
|
||||
// DecodeSupport::HardwareDecode
|
||||
// }
|
||||
//
|
||||
static DecodeSupportSet GetDecodeSupportSet(
|
||||
const MediaCodec& aCodec, const MediaCodecsSupported& aSupported);
|
||||
|
||||
// Return codec-specific SW/HW support enums for a given codec.
|
||||
// The DecodeSupportSet argument is used which codec-specific SW/HW
|
||||
// support values are returned, if any.
|
||||
//
|
||||
// Example input:
|
||||
// aCodec: MediaCodec::VP8
|
||||
// aSupportSet: DecodeSupportSet {DecodeSupport::SoftwareDecode}
|
||||
//
|
||||
// Example output:
|
||||
// MediaCodecsSupported {MediaCodecsSupport::VP8SoftwareDecode}
|
||||
//
|
||||
static MediaCodecsSupported GetDecodeMediaCodecsSupported(
|
||||
const MediaCodec& aCodec, const DecodeSupportSet& aSupportSet);
|
||||
|
||||
// Generate a plaintext description for the SW/HW support information
|
||||
// contained in a MediaCodecsSupported EnumSet.
|
||||
//
|
||||
// Example input:
|
||||
// MediaCodecsSupported {
|
||||
// MediaCodecsSupport::H264SoftwareDecode,
|
||||
// MediaCodecsSupport::H264HardwareDecode,
|
||||
// MediaCodecsSupport::VP8SoftwareDecode
|
||||
// }
|
||||
//
|
||||
// Example output (returned via argument aCodecString)
|
||||
//
|
||||
// "SW H264 decode\n
|
||||
// HW H264 decode\n
|
||||
// SW VP8 decode"_ns
|
||||
//
|
||||
static void GetMediaCodecsSupportedString(
|
||||
nsCString& aSupportString, const MediaCodecsSupported& aSupportedCodecs);
|
||||
|
||||
// Returns array of hardcoded codec definitions used to generate hashtables
|
||||
// that convert between types
|
||||
static std::array<CodecDefinition, 12> GetAllCodecDefinitions();
|
||||
|
||||
MCSInfo(MCSInfo const&) = delete;
|
||||
void operator=(MCSInfo const&) = delete;
|
||||
~MCSInfo() = default;
|
||||
|
||||
private:
|
||||
MCSInfo();
|
||||
static MCSInfo* GetInstance();
|
||||
|
||||
// Returns a codec definition by MIME type name ("media/vp9")
|
||||
// or "common" name ("VP9")
|
||||
static CodecDefinition GetCodecDefinition(const MediaCodec& aCodec);
|
||||
|
||||
static inline UniquePtr<MCSInfo> sInstance = nullptr;
|
||||
static inline StaticMutex sInitMutex;
|
||||
static inline StaticMutex sUpdateMutex;
|
||||
UniquePtr<nsTHashMap<MediaCodecsSupport, CodecDefinition>> mHashTableMCS =
|
||||
nullptr;
|
||||
UniquePtr<nsTHashMap<const char*, CodecDefinition>> mHashTableString =
|
||||
nullptr;
|
||||
UniquePtr<nsTHashMap<MediaCodec, CodecDefinition>> mHashTableCodec = nullptr;
|
||||
MediaCodecsSupported mSupport{};
|
||||
};
|
||||
} // namespace mozilla::media
|
||||
|
||||
namespace mozilla {
|
||||
// Used for IPDL serialization.
|
||||
// The 'value' has to be the biggest enum from MediaCodecsSupport.
|
||||
template <typename T>
|
||||
struct MaxEnumValue;
|
||||
template <>
|
||||
struct MaxEnumValue<media::MediaCodecsSupport> {
|
||||
static constexpr unsigned int value =
|
||||
static_cast<unsigned int>(media::MediaCodecsSupport::SENTINEL);
|
||||
};
|
||||
} // namespace mozilla
|
||||
|
||||
#endif /* MediaCodecsSupport_h_ */
|
||||
|
|
|
@ -61,6 +61,13 @@
|
|||
|
||||
#include <functional>
|
||||
|
||||
using DecodeSupport = mozilla::media::DecodeSupport;
|
||||
using DecodeSupportSet = mozilla::media::DecodeSupportSet;
|
||||
using MediaCodec = mozilla::media::MediaCodec;
|
||||
using MediaCodecsSupport = mozilla::media::MediaCodecsSupport;
|
||||
using MediaCodecsSupported = mozilla::media::MediaCodecsSupported;
|
||||
using MCSInfo = mozilla::media::MCSInfo;
|
||||
|
||||
namespace mozilla {
|
||||
|
||||
#define PDM_INIT_LOG(msg, ...) \
|
||||
|
@ -440,24 +447,31 @@ PDMFactory::CreateDecoderWithPDM(PlatformDecoderModule* aPDM,
|
|||
return aPDM->AsyncCreateDecoder(aParams);
|
||||
}
|
||||
|
||||
bool PDMFactory::SupportsMimeType(const nsACString& aMimeType) const {
|
||||
DecodeSupportSet PDMFactory::SupportsMimeType(
|
||||
const nsACString& aMimeType) const {
|
||||
UniquePtr<TrackInfo> trackInfo = CreateTrackInfoWithMIMEType(aMimeType);
|
||||
if (!trackInfo) {
|
||||
return false;
|
||||
return DecodeSupport::Unsupported;
|
||||
}
|
||||
return Supports(SupportDecoderParams(*trackInfo), nullptr);
|
||||
}
|
||||
|
||||
bool PDMFactory::Supports(const SupportDecoderParams& aParams,
|
||||
DecoderDoctorDiagnostics* aDiagnostics) const {
|
||||
DecodeSupportSet PDMFactory::Supports(
|
||||
const SupportDecoderParams& aParams,
|
||||
DecoderDoctorDiagnostics* aDiagnostics) const {
|
||||
if (mEMEPDM) {
|
||||
return mEMEPDM->Supports(aParams, aDiagnostics) !=
|
||||
media::DecodeSupport::Unsupported;
|
||||
return mEMEPDM->Supports(aParams, aDiagnostics);
|
||||
}
|
||||
|
||||
RefPtr<PlatformDecoderModule> current =
|
||||
GetDecoderModule(aParams, aDiagnostics);
|
||||
return !!current;
|
||||
|
||||
if (!current) {
|
||||
return DecodeSupport::Unsupported;
|
||||
}
|
||||
|
||||
// We have a PDM - check for + return SW/HW support info
|
||||
return current->Supports(aParams, aDiagnostics);
|
||||
}
|
||||
|
||||
void PDMFactory::CreatePDMs() {
|
||||
|
@ -744,7 +758,7 @@ void PDMFactory::SetCDMProxy(CDMProxy* aProxy) {
|
|||
}
|
||||
|
||||
/* static */
|
||||
PDMFactory::MediaCodecsSupported PDMFactory::Supported(bool aForceRefresh) {
|
||||
media::MediaCodecsSupported PDMFactory::Supported(bool aForceRefresh) {
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
|
||||
static auto calculate = []() {
|
||||
|
@ -758,53 +772,25 @@ PDMFactory::MediaCodecsSupported PDMFactory::Supported(bool aForceRefresh) {
|
|||
// available.
|
||||
// This logic will have to be revisited if a PDM supporting either codec
|
||||
// will be added in addition to the WMF and FFmpeg PDM (such as OpenH264)
|
||||
if (pdm->SupportsMimeType("video/avc"_ns)) {
|
||||
supported += MediaCodecs::H264;
|
||||
}
|
||||
if (pdm->SupportsMimeType("video/vp9"_ns)) {
|
||||
supported += MediaCodecs::VP9;
|
||||
}
|
||||
if (pdm->SupportsMimeType("video/vp8"_ns)) {
|
||||
supported += MediaCodecs::VP8;
|
||||
}
|
||||
if (pdm->SupportsMimeType("video/av1"_ns)) {
|
||||
supported += MediaCodecs::AV1;
|
||||
}
|
||||
if (pdm->SupportsMimeType("video/theora"_ns)) {
|
||||
supported += MediaCodecs::Theora;
|
||||
}
|
||||
if (pdm->SupportsMimeType("audio/mp4a-latm"_ns)) {
|
||||
supported += MediaCodecs::AAC;
|
||||
}
|
||||
// MP3 can be either decoded by ffvpx or WMF/FFmpeg
|
||||
if (pdm->SupportsMimeType("audio/mpeg"_ns)) {
|
||||
supported += MediaCodecs::MP3;
|
||||
}
|
||||
if (pdm->SupportsMimeType("audio/opus"_ns)) {
|
||||
supported += MediaCodecs::Opus;
|
||||
}
|
||||
if (pdm->SupportsMimeType("audio/vorbis"_ns)) {
|
||||
supported += MediaCodecs::Vorbis;
|
||||
}
|
||||
if (pdm->SupportsMimeType("audio/flac"_ns)) {
|
||||
supported += MediaCodecs::Flac;
|
||||
}
|
||||
if (pdm->SupportsMimeType("audio/x-wav"_ns)) {
|
||||
supported += MediaCodecs::Wave;
|
||||
for (const auto& cd : MCSInfo::GetAllCodecDefinitions()) {
|
||||
supported += MCSInfo::GetDecodeMediaCodecsSupported(
|
||||
cd.codec, pdm->SupportsMimeType(nsCString(cd.mimeTypeString)));
|
||||
}
|
||||
return supported;
|
||||
};
|
||||
|
||||
static MediaCodecsSupported supported = calculate();
|
||||
if (aForceRefresh) {
|
||||
supported = calculate();
|
||||
}
|
||||
|
||||
return supported;
|
||||
}
|
||||
|
||||
/* static */
|
||||
bool PDMFactory::SupportsMimeType(const nsACString& aMimeType,
|
||||
const MediaCodecsSupported& aSupported,
|
||||
RemoteDecodeIn aLocation) {
|
||||
DecodeSupportSet PDMFactory::SupportsMimeType(
|
||||
const nsACString& aMimeType, const MediaCodecsSupported& aSupported,
|
||||
RemoteDecodeIn aLocation) {
|
||||
const bool videoSupport = aLocation != RemoteDecodeIn::UtilityProcess;
|
||||
const bool audioSupport = (aLocation == RemoteDecodeIn::UtilityProcess &&
|
||||
StaticPrefs::media_utility_process_enabled()) ||
|
||||
|
@ -813,45 +799,44 @@ bool PDMFactory::SupportsMimeType(const nsACString& aMimeType,
|
|||
|
||||
if (videoSupport) {
|
||||
if (MP4Decoder::IsH264(aMimeType)) {
|
||||
return aSupported.contains(MediaCodecs::H264);
|
||||
return MCSInfo::GetDecodeSupportSet(MediaCodec::H264, aSupported);
|
||||
}
|
||||
if (VPXDecoder::IsVP9(aMimeType)) {
|
||||
return aSupported.contains(MediaCodecs::VP9);
|
||||
return MCSInfo::GetDecodeSupportSet(MediaCodec::VP9, aSupported);
|
||||
}
|
||||
if (VPXDecoder::IsVP8(aMimeType)) {
|
||||
return aSupported.contains(MediaCodecs::VP8);
|
||||
return MCSInfo::GetDecodeSupportSet(MediaCodec::VP8, aSupported);
|
||||
}
|
||||
#ifdef MOZ_AV1
|
||||
if (AOMDecoder::IsAV1(aMimeType)) {
|
||||
return aSupported.contains(MediaCodecs::AV1);
|
||||
return MCSInfo::GetDecodeSupportSet(MediaCodec::AV1, aSupported);
|
||||
}
|
||||
#endif
|
||||
if (TheoraDecoder::IsTheora(aMimeType)) {
|
||||
return aSupported.contains(MediaCodecs::Theora);
|
||||
return MCSInfo::GetDecodeSupportSet(MediaCodec::Theora, aSupported);
|
||||
}
|
||||
}
|
||||
|
||||
if (audioSupport) {
|
||||
if (MP4Decoder::IsAAC(aMimeType)) {
|
||||
return aSupported.contains(MediaCodecs::AAC);
|
||||
return MCSInfo::GetDecodeSupportSet(MediaCodec::AAC, aSupported);
|
||||
}
|
||||
if (aMimeType.EqualsLiteral("audio/mpeg")) {
|
||||
return aSupported.contains(MediaCodecs::MP3);
|
||||
return MCSInfo::GetDecodeSupportSet(MediaCodec::MP3, aSupported);
|
||||
}
|
||||
if (OpusDataDecoder::IsOpus(aMimeType)) {
|
||||
return aSupported.contains(MediaCodecs::Opus);
|
||||
return MCSInfo::GetDecodeSupportSet(MediaCodec::Opus, aSupported);
|
||||
}
|
||||
if (VorbisDataDecoder::IsVorbis(aMimeType)) {
|
||||
return aSupported.contains(MediaCodecs::Vorbis);
|
||||
return MCSInfo::GetDecodeSupportSet(MediaCodec::Vorbis, aSupported);
|
||||
}
|
||||
if (aMimeType.EqualsLiteral("audio/flac")) {
|
||||
return aSupported.contains(MediaCodecs::Flac);
|
||||
return MCSInfo::GetDecodeSupportSet(MediaCodec::FLAC, aSupported);
|
||||
}
|
||||
if (WaveDataDecoder::IsWave(aMimeType)) {
|
||||
return aSupported.contains(MediaCodecs::Wave);
|
||||
return MCSInfo::GetDecodeSupportSet(MediaCodec::Wave, aSupported);
|
||||
}
|
||||
}
|
||||
return false;
|
||||
return DecodeSupport::Unsupported;
|
||||
}
|
||||
|
||||
/* static */
|
||||
|
|
|
@ -25,8 +25,6 @@ class CDMProxy;
|
|||
class MediaDataDecoder;
|
||||
class MediaResult;
|
||||
class StaticMutex;
|
||||
template <typename T>
|
||||
struct MaxEnumValue;
|
||||
struct CreateDecoderParams;
|
||||
struct CreateDecoderParamsForAsync;
|
||||
struct SupportDecoderParams;
|
||||
|
@ -45,9 +43,10 @@ class PDMFactory final {
|
|||
RefPtr<PDMCreateDecoderPromise> CreateDecoder(
|
||||
const CreateDecoderParams& aParams);
|
||||
|
||||
bool SupportsMimeType(const nsACString& aMimeType) const;
|
||||
bool Supports(const SupportDecoderParams& aParams,
|
||||
DecoderDoctorDiagnostics* aDiagnostics) const;
|
||||
media::DecodeSupportSet SupportsMimeType(const nsACString& aMimeType) const;
|
||||
media::DecodeSupportSet Supports(
|
||||
const SupportDecoderParams& aParams,
|
||||
DecoderDoctorDiagnostics* aDiagnostics) const;
|
||||
|
||||
// Creates a PlatformDecoderModule that uses a CDMProxy to decrypt or
|
||||
// decrypt-and-decode EME encrypted content. If the CDM only decrypts and
|
||||
|
@ -61,31 +60,10 @@ class PDMFactory final {
|
|||
static constexpr int kYUV422 = 2;
|
||||
static constexpr int kYUV444 = 3;
|
||||
|
||||
/*
|
||||
* All the codecs we support
|
||||
*/
|
||||
enum class MediaCodecs {
|
||||
H264,
|
||||
VP9,
|
||||
VP8,
|
||||
AV1,
|
||||
Theora,
|
||||
AAC,
|
||||
MP3,
|
||||
Opus,
|
||||
Vorbis,
|
||||
Flac,
|
||||
Wave,
|
||||
|
||||
SENTINEL,
|
||||
};
|
||||
|
||||
using MediaCodecsSupported = EnumSet<MediaCodecs>;
|
||||
|
||||
static MediaCodecsSupported Supported(bool aForceRefresh = false);
|
||||
static bool SupportsMimeType(const nsACString& aMimeType,
|
||||
const MediaCodecsSupported& aSupported,
|
||||
RemoteDecodeIn aLocation);
|
||||
static media::MediaCodecsSupported Supported(bool aForceRefresh = false);
|
||||
static media::DecodeSupportSet SupportsMimeType(
|
||||
const nsACString& aMimeType,
|
||||
const media::MediaCodecsSupported& aSupported, RemoteDecodeIn aLocation);
|
||||
|
||||
static bool AllDecodersAreRemote();
|
||||
|
||||
|
@ -129,14 +107,6 @@ class PDMFactory final {
|
|||
static void EnsureInit();
|
||||
};
|
||||
|
||||
// Used for IPDL serialization.
|
||||
// The 'value' have to be the biggest enum from MediaCodecs.
|
||||
template <>
|
||||
struct MaxEnumValue<PDMFactory::MediaCodecs> {
|
||||
static constexpr unsigned int value =
|
||||
static_cast<unsigned int>(PDMFactory::MediaCodecs::SENTINEL);
|
||||
};
|
||||
|
||||
} // namespace mozilla
|
||||
|
||||
#endif /* PDMFactory_h_ */
|
||||
|
|
|
@ -5,6 +5,7 @@
|
|||
#include "AndroidDataEncoder.h"
|
||||
|
||||
#include "AnnexB.h"
|
||||
#include "H264.h"
|
||||
#include "MediaData.h"
|
||||
#include "MediaInfo.h"
|
||||
#include "SimpleMap.h"
|
||||
|
@ -170,9 +171,9 @@ RefPtr<MediaDataEncoder::EncodePromise> AndroidDataEncoder<ConfigType>::Encode(
|
|||
|
||||
static jni::ByteBuffer::LocalRef ConvertI420ToNV12Buffer(
|
||||
RefPtr<const VideoData> aSample, RefPtr<MediaByteBuffer>& aYUVBuffer) {
|
||||
const PlanarYCbCrImage* image = aSample->mImage->AsPlanarYCbCrImage();
|
||||
const layers::PlanarYCbCrImage* image = aSample->mImage->AsPlanarYCbCrImage();
|
||||
MOZ_ASSERT(image);
|
||||
const PlanarYCbCrData* yuv = image->GetData();
|
||||
const layers::PlanarYCbCrData* yuv = image->GetData();
|
||||
auto ySize = yuv->YDataSize();
|
||||
auto cbcrSize = yuv->CbCrDataSize();
|
||||
size_t yLength = yuv->mYStride * ySize.height;
|
||||
|
|
|
@ -38,6 +38,7 @@ UNIFIED_SOURCES += [
|
|||
"agnostic/VPXDecoder.cpp",
|
||||
"agnostic/WAVDecoder.cpp",
|
||||
"AllocationPolicy.cpp",
|
||||
"MediaCodecsSupport.cpp",
|
||||
"PDMFactory.cpp",
|
||||
"PEMFactory.cpp",
|
||||
"PlatformDecoderModule.cpp",
|
||||
|
|
|
@ -335,9 +335,13 @@ media::DecodeSupportSet WMFDecoderModule::Supports(
|
|||
}
|
||||
|
||||
if (CanCreateMFTDecoder(type)) {
|
||||
// TODO: Note that we do not yet distinguish between SW/HW decode support.
|
||||
// Will be done in bug 1754239.
|
||||
return media::DecodeSupport::SoftwareDecode;
|
||||
if (StreamTypeIsVideo(type)) {
|
||||
return sDXVAEnabled ? media::DecodeSupport::HardwareDecode
|
||||
: media::DecodeSupport::SoftwareDecode;
|
||||
} else {
|
||||
// Audio only supports software decode
|
||||
return media::DecodeSupport::SoftwareDecode;
|
||||
}
|
||||
}
|
||||
|
||||
return media::DecodeSupport::Unsupported;
|
||||
|
@ -430,17 +434,13 @@ media::DecodeSupportSet WMFDecoderModule::SupportsMimeType(
|
|||
if (!trackInfo) {
|
||||
return media::DecodeSupport::Unsupported;
|
||||
}
|
||||
bool supports = Supports(SupportDecoderParams(*trackInfo), aDiagnostics) !=
|
||||
media::DecodeSupport::Unsupported;
|
||||
MOZ_LOG(sPDMLog, LogLevel::Debug,
|
||||
("WMF decoder %s requested type '%s'",
|
||||
supports ? "supports" : "rejects", aMimeType.BeginReading()));
|
||||
if (!supports) {
|
||||
return media::DecodeSupport::Unsupported;
|
||||
}
|
||||
// TODO: Note that we do not yet distinguish between SW/HW decode support.
|
||||
// Will be done in bug 1754239.
|
||||
return media::DecodeSupport::SoftwareDecode;
|
||||
auto supports = Supports(SupportDecoderParams(*trackInfo), aDiagnostics);
|
||||
MOZ_LOG(
|
||||
sPDMLog, LogLevel::Debug,
|
||||
("WMF decoder %s requested type '%s'",
|
||||
supports != media::DecodeSupport::Unsupported ? "supports" : "rejects",
|
||||
aMimeType.BeginReading()));
|
||||
return supports;
|
||||
}
|
||||
|
||||
} // namespace mozilla
|
||||
|
|
|
@ -170,7 +170,7 @@ steps we run before each video. We keep track of which test we are running with
|
|||
});
|
||||
}
|
||||
|
||||
SpecialPowers.pushPrefEnv({"set": [["media.video_stats.enabled", true]]},
|
||||
SpecialPowers.pushPrefEnv({"set": [["media.video_stats.enabled", true], ["gfx.core-animation.specialize-video", true]]},
|
||||
async function() {
|
||||
// Clear out existing telemetry in case previous tests were displaying
|
||||
// video.
|
||||
|
|
|
@ -265,7 +265,8 @@ void MediaDecodeTask::OnInitDemuxerCompleted() {
|
|||
UniquePtr<TrackInfo> audioInfo = mTrackDemuxer->GetInfo();
|
||||
// We actively ignore audio tracks that we know we can't play.
|
||||
if (audioInfo && audioInfo->IsValid() &&
|
||||
platform->SupportsMimeType(audioInfo->mMimeType)) {
|
||||
platform->SupportsMimeType(audioInfo->mMimeType) !=
|
||||
media::DecodeSupport::Unsupported) {
|
||||
mMediaInfo.mAudio = *audioInfo->GetAsAudioInfo();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -104,8 +104,9 @@ bool WebMDecoder::IsSupportedType(const MediaContainerType& aContainerType) {
|
|||
// color depth
|
||||
RefPtr<PDMFactory> platform = new PDMFactory();
|
||||
for (const auto& track : tracks) {
|
||||
if (!track || !platform->Supports(SupportDecoderParams(*track),
|
||||
nullptr /* diagnostic */)) {
|
||||
if (!track || platform->Supports(SupportDecoderParams(*track),
|
||||
nullptr /* diagnostic */) ==
|
||||
media::DecodeSupport::Unsupported) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -60,7 +60,7 @@ WebrtcVideoDecoder* MediaDataCodec::CreateDecoder(
|
|||
return nullptr;
|
||||
}
|
||||
RefPtr<PDMFactory> pdm = new PDMFactory();
|
||||
if (!pdm->SupportsMimeType(codec)) {
|
||||
if (pdm->SupportsMimeType(codec) == media::DecodeSupport::Unsupported) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
|
|
|
@ -102,19 +102,19 @@ bool WebTask::Run() {
|
|||
|
||||
error.WouldReportJSException();
|
||||
|
||||
#ifdef DEBUG
|
||||
Promise::PromiseState promiseState = mPromise->State();
|
||||
|
||||
// If the state is Rejected, it means the above Call triggers the
|
||||
// RunAbortAlgorithm method and rejected the promise
|
||||
MOZ_ASSERT_IF(promiseState != Promise::PromiseState::Pending,
|
||||
promiseState == Promise::PromiseState::Rejected);
|
||||
#endif
|
||||
|
||||
if (promiseState == Promise::PromiseState::Pending) {
|
||||
if (error.Failed()) {
|
||||
mPromise->MaybeReject(std::move(error));
|
||||
} else {
|
||||
mPromise->MaybeResolve(returnVal);
|
||||
}
|
||||
if (error.Failed()) {
|
||||
mPromise->MaybeReject(std::move(error));
|
||||
} else {
|
||||
mPromise->MaybeResolve(returnVal);
|
||||
}
|
||||
|
||||
MOZ_ASSERT(!isInList());
|
||||
|
|
|
@ -84,6 +84,7 @@ class gfxVarReceiver;
|
|||
_(UseEGL, bool, false) \
|
||||
_(DrmRenderDevice, nsCString, nsCString()) \
|
||||
_(UseDMABuf, bool, false) \
|
||||
_(CodecSupportInfo, nsCString, nsCString()) \
|
||||
_(WebRenderRequiresHardwareDriver, bool, false) \
|
||||
_(SupportsThreadsafeGL, bool, false) \
|
||||
_(OffscreenCanvasDomainAllowlist, nsCString, nsCString()) \
|
||||
|
|
|
@ -326,7 +326,7 @@ mozilla::ipc::IPCResult GPUChild::RecvBHRThreadHang(
|
|||
}
|
||||
|
||||
mozilla::ipc::IPCResult GPUChild::RecvUpdateMediaCodecsSupported(
|
||||
const PDMFactory::MediaCodecsSupported& aSupported) {
|
||||
const media::MediaCodecsSupported& aSupported) {
|
||||
dom::ContentParent::BroadcastMediaCodecsSupportedUpdate(
|
||||
RemoteDecodeIn::GpuProcess, aSupported);
|
||||
return IPC_OK();
|
||||
|
|
|
@ -79,7 +79,7 @@ class GPUChild final : public ipc::CrashReporterHelper<GeckoProcessType_GPU>,
|
|||
const nsCString& aMessage);
|
||||
mozilla::ipc::IPCResult RecvBHRThreadHang(const HangDetails& aDetails);
|
||||
mozilla::ipc::IPCResult RecvUpdateMediaCodecsSupported(
|
||||
const PDMFactory::MediaCodecsSupported& aSupported);
|
||||
const media::MediaCodecsSupported& aSupported);
|
||||
mozilla::ipc::IPCResult RecvFOGData(ByteBuf&& aBuf);
|
||||
|
||||
bool SendRequestMemoryReport(const uint32_t& aGeneration,
|
||||
|
|
|
@ -37,7 +37,7 @@ using mozilla::gfx::Feature from "gfxFeature.h";
|
|||
using mozilla::gfx::Fallback from "gfxFallback.h";
|
||||
using mozilla::layers::LayersId from "mozilla/layers/LayersTypes.h";
|
||||
using mozilla::layers::OverlayInfo from "mozilla/layers/OverlayInfo.h";
|
||||
using mozilla::PDMFactory::MediaCodecsSupported from "PDMFactory.h";
|
||||
using mozilla::media::MediaCodecsSupported from "MediaCodecsSupport.h";
|
||||
|
||||
namespace mozilla {
|
||||
namespace gfx {
|
||||
|
|
|
@ -154,6 +154,9 @@ class MockGfxInfo final : public nsIGfxInfo {
|
|||
NS_IMETHOD GetTargetFrameRate(uint32_t* aTargetFrameRate) override {
|
||||
return NS_ERROR_NOT_IMPLEMENTED;
|
||||
}
|
||||
NS_IMETHOD GetCodecSupportInfo(nsACString& aCodecSupportInfo) override {
|
||||
return NS_ERROR_NOT_IMPLEMENTED;
|
||||
}
|
||||
NS_IMETHOD GetD2DEnabled(bool* aD2DEnabled) override {
|
||||
return NS_ERROR_NOT_IMPLEMENTED;
|
||||
}
|
||||
|
|
|
@ -5,7 +5,7 @@
|
|||
|
||||
include protocol PRemoteDecoderManager;
|
||||
|
||||
using mozilla::PDMFactory::MediaCodecsSupported from "PDMFactory.h";
|
||||
using mozilla::media::MediaCodecsSupported from "MediaCodecsSupport.h";
|
||||
|
||||
namespace mozilla {
|
||||
|
||||
|
|
|
@ -40,7 +40,7 @@ RefPtr<UtilityAudioDecoderChild> UtilityAudioDecoderChild::GetSingleton() {
|
|||
|
||||
mozilla::ipc::IPCResult
|
||||
UtilityAudioDecoderChild::RecvUpdateMediaCodecsSupported(
|
||||
const PDMFactory::MediaCodecsSupported& aSupported) {
|
||||
const media::MediaCodecsSupported& aSupported) {
|
||||
dom::ContentParent::BroadcastMediaCodecsSupportedUpdate(
|
||||
RemoteDecodeIn::UtilityProcess, aSupported);
|
||||
return IPC_OK();
|
||||
|
|
|
@ -24,7 +24,7 @@ class UtilityAudioDecoderChild final : public PUtilityAudioDecoderChild {
|
|||
NS_INLINE_DECL_THREADSAFE_REFCOUNTING(UtilityAudioDecoderChild, override);
|
||||
|
||||
mozilla::ipc::IPCResult RecvUpdateMediaCodecsSupported(
|
||||
const PDMFactory::MediaCodecsSupported& aSupported);
|
||||
const media::MediaCodecsSupported& aSupported);
|
||||
|
||||
UtilityActorName GetActorName() { return UtilityActorName::AudioDecoder; }
|
||||
|
||||
|
|
|
@ -10,6 +10,7 @@
|
|||
#include "nsDebugImpl.h"
|
||||
|
||||
#include "mozilla/RemoteDecoderManagerParent.h"
|
||||
#include "PDMFactory.h"
|
||||
|
||||
#if defined(XP_WIN) && defined(MOZ_SANDBOX)
|
||||
# include "WMF.h"
|
||||
|
|
|
@ -1691,11 +1691,7 @@ static bool DisassembleNative(JSContext* cx, unsigned argc, Value* vp) {
|
|||
return false;
|
||||
}
|
||||
|
||||
const Value& v2 =
|
||||
fun->getExtendedSlot(FunctionExtended::WASM_INSTANCE_OBJ_SLOT);
|
||||
|
||||
WasmInstanceObject* instobj = &v2.toObject().as<WasmInstanceObject>();
|
||||
js::wasm::Instance& inst = instobj->instance();
|
||||
js::wasm::Instance& inst = fun->wasmInstance();
|
||||
const js::wasm::Code& code = inst.code();
|
||||
js::wasm::Tier tier = code.bestTier();
|
||||
|
||||
|
|
|
@ -175,6 +175,7 @@ const definedOpcodes =
|
|||
...(wasmExceptionsEnabled() ? [0x06, 0x07, 0x08, 0x09] : []),
|
||||
0x0b, 0x0c, 0x0d, 0x0e, 0x0f,
|
||||
0x10, 0x11,
|
||||
...(wasmFunctionReferencesEnabled() ? [0x14] : []),
|
||||
...(wasmExceptionsEnabled() ? [0x18, 0x19] : []),
|
||||
0x1a, 0x1b, 0x1c,
|
||||
0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26,
|
||||
|
|
|
@ -0,0 +1,89 @@
|
|||
// |jit-test| skip-if: !wasmFunctionReferencesEnabled()
|
||||
|
||||
let { plusOne } = wasmEvalText(`(module
|
||||
(; forward declaration so that ref.func works ;)
|
||||
(elem declare $plusOneRef)
|
||||
|
||||
(func $plusOneRef (param i32) (result i32)
|
||||
(i32.add
|
||||
local.get 0
|
||||
i32.const 1)
|
||||
)
|
||||
|
||||
(func (export "plusOne") (param i32) (result i32)
|
||||
local.get 0
|
||||
ref.func $plusOneRef
|
||||
call_ref
|
||||
)
|
||||
)`).exports;
|
||||
|
||||
assertEq(plusOne(3), 4);
|
||||
|
||||
// pass non-funcref type
|
||||
wasmFailValidateText(`(module
|
||||
(func (param $a i32)
|
||||
local.get $a
|
||||
call_ref
|
||||
)
|
||||
)`, /type mismatch: expression has type i32 but expected a reference type/);
|
||||
|
||||
wasmFailValidateText(`(module
|
||||
(func (param $a (ref extern))
|
||||
local.get $a
|
||||
call_ref
|
||||
)
|
||||
)`, /type mismatch: reference expected to be a subtype of funcref/);
|
||||
|
||||
// pass (non-subtype of) funcref
|
||||
wasmFailValidateText(`(module
|
||||
(func (param funcref)
|
||||
local.get 0
|
||||
call_ref
|
||||
)
|
||||
)`, /type mismatch: reference expected to be a subtype of funcref/);
|
||||
|
||||
// signature mismatch
|
||||
wasmFailValidateText(`(module
|
||||
(elem declare $plusOneRef)
|
||||
(func $plusOneRef (param f32) (result f32)
|
||||
(f32.add
|
||||
local.get 0
|
||||
f32.const 1.0)
|
||||
)
|
||||
|
||||
(func (export "plusOne") (param i32) (result i32)
|
||||
local.get 0
|
||||
ref.func $plusOneRef
|
||||
call_ref
|
||||
)
|
||||
)`, /type mismatch/);
|
||||
|
||||
// TODO fix call_ref is not allowed in dead code
|
||||
wasmFailValidateText(`(module
|
||||
(func (param $a i32)
|
||||
unreachable
|
||||
call_ref
|
||||
)
|
||||
)`, /gc instruction temporarily not allowed in dead code/);
|
||||
|
||||
// Cross-instance calls
|
||||
let { loadInt } = wasmEvalText(`(module
|
||||
(memory 1 1)
|
||||
(data (i32.const 0) "\\04\\00\\00\\00")
|
||||
(func (export "loadInt") (result i32)
|
||||
i32.const 0
|
||||
i32.load offset=0
|
||||
)
|
||||
)`).exports;
|
||||
|
||||
let { callLoadInt } = wasmEvalText(`(module
|
||||
(elem declare 0)
|
||||
(import "" "loadInt" (func (result i32)))
|
||||
(func (export "callLoadInt") (result i32)
|
||||
ref.func 0
|
||||
call_ref
|
||||
)
|
||||
)`, {"": { loadInt, }}).exports;
|
||||
|
||||
assertEq(loadInt(), 4);
|
||||
assertEq(callLoadInt(), 4);
|
|
@ -8072,6 +8072,8 @@ void CodeGenerator::visitWasmCall(LWasmCall* lir) {
|
|||
callBase->builtinMethodFailureMode());
|
||||
switchRealm = false;
|
||||
break;
|
||||
case wasm::CalleeDesc::FuncRef:
|
||||
MOZ_CRASH("NYI");
|
||||
}
|
||||
|
||||
// Note the assembler offset for the associated LSafePoint.
|
||||
|
|
|
@ -3929,6 +3929,43 @@ void MacroAssembler::wasmCallIndirect(const wasm::CallSiteDesc& desc,
|
|||
bind(&done);
|
||||
}
|
||||
|
||||
CodeOffset MacroAssembler::wasmCallRef(const wasm::CallSiteDesc& desc,
|
||||
const wasm::CalleeDesc& callee) {
|
||||
MOZ_ASSERT(callee.which() == wasm::CalleeDesc::FuncRef);
|
||||
const Register calleeScratch = WasmCallRefCallScratchReg0;
|
||||
const Register calleeFnObj = WasmCallRefReg;
|
||||
|
||||
storePtr(InstanceReg,
|
||||
Address(getStackPointer(), WasmCallerInstanceOffsetBeforeCall));
|
||||
|
||||
// Get InstanceReg from the function's WASM_INSTANCE_SLOT extended slot.
|
||||
size_t instanceSlotOffset = FunctionExtended::offsetOfExtendedSlot(
|
||||
FunctionExtended::WASM_INSTANCE_SLOT);
|
||||
loadPtr(Address(calleeFnObj, instanceSlotOffset), InstanceReg);
|
||||
storePtr(InstanceReg,
|
||||
Address(getStackPointer(), WasmCalleeInstanceOffsetBeforeCall));
|
||||
|
||||
loadWasmPinnedRegsFromInstance();
|
||||
switchToWasmInstanceRealm(WasmCallRefCallScratchReg0,
|
||||
WasmCallRefCallScratchReg1);
|
||||
|
||||
// Get funcUncheckedCallEntry() from the function's
|
||||
// WASM_FUNC_UNCHECKED_ENTRY_SLOT extended slot.
|
||||
size_t uncheckedEntrySlotOffset = FunctionExtended::offsetOfExtendedSlot(
|
||||
FunctionExtended::WASM_FUNC_UNCHECKED_ENTRY_SLOT);
|
||||
loadPtr(Address(calleeFnObj, uncheckedEntrySlotOffset), calleeScratch);
|
||||
|
||||
CodeOffset raOffset = call(desc, calleeScratch);
|
||||
|
||||
// Restore registers and realm and back to this caller's.
|
||||
loadPtr(Address(getStackPointer(), WasmCallerInstanceOffsetBeforeCall),
|
||||
InstanceReg);
|
||||
loadWasmPinnedRegsFromInstance();
|
||||
switchToWasmInstanceRealm(ABINonArgReturnReg0, ABINonArgReturnReg1);
|
||||
|
||||
return raOffset;
|
||||
}
|
||||
|
||||
void MacroAssembler::nopPatchableToCall(const wasm::CallSiteDesc& desc) {
|
||||
CodeOffset offset = nopPatchableToCall();
|
||||
append(desc, offset);
|
||||
|
|
|
@ -3741,6 +3741,11 @@ class MacroAssembler : public MacroAssemblerSpecific {
|
|||
mozilla::Maybe<uint32_t> tableSize,
|
||||
CodeOffset* fastCallOffset, CodeOffset* slowCallOffset);
|
||||
|
||||
// This function takes care of loading the callee's instance and address from
|
||||
// pinned reg.
|
||||
CodeOffset wasmCallRef(const wasm::CallSiteDesc& desc,
|
||||
const wasm::CalleeDesc& callee);
|
||||
|
||||
// WasmTableCallIndexReg must contain the index of the indirect call.
|
||||
// This is for asm.js calls only.
|
||||
CodeOffset asmCallIndirect(const wasm::CallSiteDesc& desc,
|
||||
|
|
|
@ -192,6 +192,11 @@ static constexpr Register WasmTableCallScratchReg1 = ABINonArgReg1;
|
|||
static constexpr Register WasmTableCallSigReg = ABINonArgReg2;
|
||||
static constexpr Register WasmTableCallIndexReg = ABINonArgReg3;
|
||||
|
||||
// Registers used for ref calls.
|
||||
static constexpr Register WasmCallRefCallScratchReg0 = ABINonArgReg0;
|
||||
static constexpr Register WasmCallRefCallScratchReg1 = ABINonArgReg1;
|
||||
static constexpr Register WasmCallRefReg = ABINonArgReg3;
|
||||
|
||||
// Register used as a scratch along the return path in the fast js -> wasm stub
|
||||
// code. This must not overlap ReturnReg, JSReturnOperand, or InstanceReg.
|
||||
// It must be a volatile register.
|
||||
|
|
|
@ -710,6 +710,11 @@ static constexpr Register WasmTableCallScratchReg1 = ABINonArgReg1;
|
|||
static constexpr Register WasmTableCallSigReg = ABINonArgReg2;
|
||||
static constexpr Register WasmTableCallIndexReg = ABINonArgReg3;
|
||||
|
||||
// Registers used for ref calls.
|
||||
static constexpr Register WasmCallRefCallScratchReg0 = ABINonArgReg0;
|
||||
static constexpr Register WasmCallRefCallScratchReg1 = ABINonArgReg1;
|
||||
static constexpr Register WasmCallRefReg = ABINonArgReg3;
|
||||
|
||||
// Register used as a scratch along the return path in the fast js -> wasm stub
|
||||
// code. This must not overlap ReturnReg, JSReturnOperand, or InstanceReg.
|
||||
// It must be a volatile register.
|
||||
|
|
|
@ -202,6 +202,11 @@ static constexpr Register WasmTableCallScratchReg1 = ABINonArgReg1;
|
|||
static constexpr Register WasmTableCallSigReg = ABINonArgReg2;
|
||||
static constexpr Register WasmTableCallIndexReg = ABINonArgReg3;
|
||||
|
||||
// Registers used for ref calls.
|
||||
static constexpr Register WasmCallRefCallScratchReg0 = ABINonArgReg0;
|
||||
static constexpr Register WasmCallRefCallScratchReg1 = ABINonArgReg1;
|
||||
static constexpr Register WasmCallRefReg = ABINonArgReg3;
|
||||
|
||||
// Register used as a scratch along the return path in the fast js -> wasm stub
|
||||
// code. This must not overlap ReturnReg, JSReturnOperand, or InstanceReg.
|
||||
// It must be a volatile register.
|
||||
|
|
|
@ -82,6 +82,11 @@ static constexpr Register WasmTableCallScratchReg1 = ABINonArgReg1;
|
|||
static constexpr Register WasmTableCallSigReg = ABINonArgReg2;
|
||||
static constexpr Register WasmTableCallIndexReg = ABINonArgReg3;
|
||||
|
||||
// Registers used for ref calls.
|
||||
static constexpr Register WasmCallRefCallScratchReg0 = ABINonArgReg0;
|
||||
static constexpr Register WasmCallRefCallScratchReg1 = ABINonArgReg1;
|
||||
static constexpr Register WasmCallRefReg = ABINonArgReg3;
|
||||
|
||||
// Register used as a scratch along the return path in the fast js -> wasm stub
|
||||
// code. This must not overlap ReturnReg, JSReturnOperand, or InstanceReg.
|
||||
// It must be a volatile register.
|
||||
|
|
|
@ -70,6 +70,11 @@ static constexpr Register WasmTableCallScratchReg1 = ABINonArgReg1;
|
|||
static constexpr Register WasmTableCallSigReg = ABINonArgReg2;
|
||||
static constexpr Register WasmTableCallIndexReg = ABINonArgReg3;
|
||||
|
||||
// Registers used for ref calls.
|
||||
static constexpr Register WasmCallRefCallScratchReg0 = ABINonArgReg0;
|
||||
static constexpr Register WasmCallRefCallScratchReg1 = ABINonArgReg1;
|
||||
static constexpr Register WasmCallRefReg = ABINonArgReg3;
|
||||
|
||||
// Register used as a scratch along the return path in the fast js -> wasm stub
|
||||
// code. This must not overlap ReturnReg, JSReturnOperand, or InstanceReg.
|
||||
// It must be a volatile register.
|
||||
|
|
|
@ -101,6 +101,9 @@ static constexpr Register WasmTableCallSigReg{Registers::invalid_reg};
|
|||
static constexpr Register WasmTableCallIndexReg{Registers::invalid_reg};
|
||||
static constexpr Register InstanceReg{Registers::invalid_reg};
|
||||
static constexpr Register WasmJitEntryReturnScratch{Registers::invalid_reg};
|
||||
static constexpr Register WasmCallRefCallScratchReg0{Registers::invalid_reg};
|
||||
static constexpr Register WasmCallRefCallScratchReg1{Registers::invalid_reg};
|
||||
static constexpr Register WasmCallRefReg{Registers::invalid_reg};
|
||||
|
||||
static constexpr uint32_t ABIStackAlignment = 4;
|
||||
static constexpr uint32_t CodeAlignment = 16;
|
||||
|
|
|
@ -111,6 +111,9 @@ static constexpr Register WasmTableCallSigReg{Registers::invalid_reg};
|
|||
static constexpr Register WasmTableCallIndexReg{Registers::invalid_reg};
|
||||
static constexpr Register InstanceReg{Registers::invalid_reg};
|
||||
static constexpr Register WasmJitEntryReturnScratch{Registers::invalid_reg};
|
||||
static constexpr Register WasmCallRefCallScratchReg0{Registers::invalid_reg};
|
||||
static constexpr Register WasmCallRefCallScratchReg1{Registers::invalid_reg};
|
||||
static constexpr Register WasmCallRefReg{Registers::invalid_reg};
|
||||
|
||||
static constexpr uint32_t ABIStackAlignment = 4;
|
||||
static constexpr uint32_t CodeAlignment = 16;
|
||||
|
|
|
@ -231,6 +231,11 @@ static constexpr Register WasmTableCallScratchReg1 = ABINonArgReg1;
|
|||
static constexpr Register WasmTableCallSigReg = ABINonArgReg2;
|
||||
static constexpr Register WasmTableCallIndexReg = ABINonArgReg3;
|
||||
|
||||
// Registers used for ref calls.
|
||||
static constexpr Register WasmCallRefCallScratchReg0 = ABINonArgReg0;
|
||||
static constexpr Register WasmCallRefCallScratchReg1 = ABINonArgReg1;
|
||||
static constexpr Register WasmCallRefReg = ABINonArgReg3;
|
||||
|
||||
// Register used as a scratch along the return path in the fast js -> wasm stub
|
||||
// code. This must not overlap ReturnReg, JSReturnOperand, or InstanceReg.
|
||||
// It must be a volatile register.
|
||||
|
|
|
@ -137,6 +137,11 @@ static constexpr Register WasmTableCallScratchReg1 = ABINonArgReg1;
|
|||
static constexpr Register WasmTableCallSigReg = ABINonArgReg2;
|
||||
static constexpr Register WasmTableCallIndexReg = ABINonArgReg3;
|
||||
|
||||
// Registers used for ref calls.
|
||||
static constexpr Register WasmCallRefCallScratchReg0 = ABINonArgReg0;
|
||||
static constexpr Register WasmCallRefCallScratchReg1 = ABINonArgReg1;
|
||||
static constexpr Register WasmCallRefReg = ABINonArgReg3;
|
||||
|
||||
// Register used as a scratch along the return path in the fast js -> wasm stub
|
||||
// code. This must not overlap ReturnReg, JSReturnOperand, or InstanceReg.
|
||||
// It must be a volatile register.
|
||||
|
|
|
@ -662,6 +662,16 @@ inline void JSFunction::trace(JSTracer* trc) {
|
|||
}
|
||||
}
|
||||
}
|
||||
// wasm/asm.js exported functions need to keep WasmInstantObject alive,
|
||||
// access it via WASM_INSTANCE_SLOT extended slot.
|
||||
if (isAsmJSNative() || isWasm()) {
|
||||
const Value& v = getExtendedSlot(FunctionExtended::WASM_INSTANCE_SLOT);
|
||||
if (!v.isUndefined()) {
|
||||
js::wasm::Instance* instance =
|
||||
static_cast<js::wasm::Instance*>(v.toPrivate());
|
||||
instance->trace(trc);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void fun_trace(JSTracer* trc, JSObject* obj) {
|
||||
|
|
|
@ -43,6 +43,11 @@ static constexpr std::string_view FunctionConstructorFinalBrace = "\n}";
|
|||
extern const JSClass FunctionClass;
|
||||
extern const JSClass ExtendedFunctionClass;
|
||||
|
||||
namespace wasm {
|
||||
|
||||
class Instance;
|
||||
|
||||
} // namespace wasm
|
||||
} // namespace js
|
||||
|
||||
class JSFunction : public js::NativeObject {
|
||||
|
@ -638,6 +643,7 @@ class JSFunction : public js::NativeObject {
|
|||
MOZ_ASSERT(isWasmWithJitEntry());
|
||||
return static_cast<void**>(nativeJitInfoOrInterpretedScript());
|
||||
}
|
||||
inline js::wasm::Instance& wasmInstance() const;
|
||||
|
||||
bool isDerivedClassConstructor() const;
|
||||
bool isSyntheticFunction() const;
|
||||
|
@ -810,9 +816,10 @@ class FunctionExtended : public JSFunction {
|
|||
// to be resolved eagerly.
|
||||
static const uint32_t BOUND_FUNCTION_LENGTH_SLOT = 1;
|
||||
|
||||
// Exported asm.js/wasm functions store their WasmInstanceObject in the
|
||||
// first slot.
|
||||
static const uint32_t WASM_INSTANCE_OBJ_SLOT = 0;
|
||||
// wasm/asm.js exported functions store a code pointer to their direct entry
|
||||
// point (see CodeRange::funcUncheckedCallEntry()) to support the call_ref
|
||||
// instruction.
|
||||
static const uint32_t WASM_FUNC_UNCHECKED_ENTRY_SLOT = 0;
|
||||
|
||||
// wasm/asm.js exported functions store the wasm::Instance pointer of their
|
||||
// instance.
|
||||
|
@ -875,6 +882,14 @@ inline const js::Value& JSFunction::getExtendedSlot(uint32_t which) const {
|
|||
return getFixedSlot(js::FunctionExtended::FirstExtendedSlot + which);
|
||||
}
|
||||
|
||||
inline js::wasm::Instance& JSFunction::wasmInstance() const {
|
||||
MOZ_ASSERT(isWasm() || isAsmJSNative());
|
||||
MOZ_ASSERT(
|
||||
!getExtendedSlot(js::FunctionExtended::WASM_INSTANCE_SLOT).isUndefined());
|
||||
return *static_cast<js::wasm::Instance*>(
|
||||
getExtendedSlot(js::FunctionExtended::WASM_INSTANCE_SLOT).toPrivate());
|
||||
}
|
||||
|
||||
namespace js {
|
||||
|
||||
JSString* FunctionToString(JSContext* cx, HandleFunction fun, bool isToSource);
|
||||
|
|
|
@ -944,6 +944,9 @@ struct BaseCompiler final {
|
|||
const Stk& indexVal, const FunctionCall& call,
|
||||
CodeOffset* fastCallOffset, CodeOffset* slowCallOffset);
|
||||
CodeOffset callImport(unsigned globalDataOffset, const FunctionCall& call);
|
||||
#ifdef ENABLE_WASM_FUNCTION_REFERENCES
|
||||
CodeOffset callRef(const Stk& calleeRef, const FunctionCall& call);
|
||||
#endif
|
||||
CodeOffset builtinCall(SymbolicAddress builtin, const FunctionCall& call);
|
||||
CodeOffset builtinInstanceMethodCall(const SymbolicAddressSignature& builtin,
|
||||
const ABIArg& instanceArg,
|
||||
|
@ -1553,6 +1556,7 @@ struct BaseCompiler final {
|
|||
#ifdef ENABLE_WASM_FUNCTION_REFERENCES
|
||||
[[nodiscard]] bool emitRefAsNonNull();
|
||||
[[nodiscard]] bool emitBrOnNull();
|
||||
[[nodiscard]] bool emitCallRef();
|
||||
#endif
|
||||
|
||||
[[nodiscard]] bool emitAtomicCmpXchg(ValType type, Scalar::Type viewType);
|
||||
|
|
|
@ -1607,6 +1607,17 @@ bool BaseCompiler::callIndirect(uint32_t funcTypeIndex, uint32_t tableIndex,
|
|||
return true;
|
||||
}
|
||||
|
||||
#ifdef ENABLE_WASM_FUNCTION_REFERENCES
|
||||
CodeOffset BaseCompiler::callRef(const Stk& calleeRef,
|
||||
const FunctionCall& call) {
|
||||
CallSiteDesc desc(call.lineOrBytecode, CallSiteDesc::FuncRef);
|
||||
CalleeDesc callee = CalleeDesc::wasmFuncRef();
|
||||
|
||||
loadRef(calleeRef, RegRef(WasmCallRefReg));
|
||||
return masm.wasmCallRef(desc, callee);
|
||||
}
|
||||
#endif
|
||||
|
||||
// Precondition: sync()
|
||||
|
||||
CodeOffset BaseCompiler::callImport(unsigned globalDataOffset,
|
||||
|
@ -4740,6 +4751,61 @@ bool BaseCompiler::emitCallIndirect() {
|
|||
return pushCallResults(baselineCall, resultType, results);
|
||||
}
|
||||
|
||||
#ifdef ENABLE_WASM_FUNCTION_REFERENCES
|
||||
bool BaseCompiler::emitCallRef() {
|
||||
uint32_t lineOrBytecode = readCallSiteLineOrBytecode();
|
||||
|
||||
const FuncType* funcType;
|
||||
Nothing unused_callee;
|
||||
BaseNothingVector unused_args{};
|
||||
if (!iter_.readCallRef(&funcType, &unused_callee, &unused_args)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (deadCode_) {
|
||||
return true;
|
||||
}
|
||||
|
||||
sync();
|
||||
|
||||
// Stack: ... arg1 .. argn callee
|
||||
|
||||
uint32_t numArgs = funcType->args().length() + 1;
|
||||
size_t stackArgBytes = stackConsumed(numArgs);
|
||||
|
||||
ResultType resultType(ResultType::Vector(funcType->results()));
|
||||
StackResultsLoc results;
|
||||
if (!pushStackResultsForCall(resultType, RegPtr(ABINonArgReg0), &results)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
FunctionCall baselineCall(lineOrBytecode);
|
||||
// State and realm are restored as needed by by callRef (really by
|
||||
// MacroAssembler::wasmCallRef).
|
||||
beginCall(baselineCall, UseABI::Wasm, RestoreRegisterStateAndRealm::False);
|
||||
|
||||
if (!emitCallArgs(funcType->args(), results, &baselineCall,
|
||||
CalleeOnStack::True)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const Stk& callee = peek(results.count());
|
||||
CodeOffset raOffset = callRef(callee, baselineCall);
|
||||
if (!createStackMap("emitCallRef", raOffset)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
popStackResultsAfterCall(results, stackArgBytes);
|
||||
|
||||
endCall(baselineCall, stackArgBytes);
|
||||
|
||||
popValueStackBy(numArgs);
|
||||
|
||||
captureCallResultRegisters(resultType);
|
||||
return pushCallResults(baselineCall, resultType, results);
|
||||
}
|
||||
#endif
|
||||
|
||||
void BaseCompiler::emitRound(RoundingMode roundingMode, ValType operandType) {
|
||||
if (operandType == ValType::F32) {
|
||||
RegF32 f0 = popF32();
|
||||
|
@ -8578,6 +8644,13 @@ bool BaseCompiler::emitBody() {
|
|||
CHECK_NEXT(emitCall());
|
||||
case uint16_t(Op::CallIndirect):
|
||||
CHECK_NEXT(emitCallIndirect());
|
||||
#ifdef ENABLE_WASM_FUNCTION_REFERENCES
|
||||
case uint16_t(Op::CallRef):
|
||||
if (!moduleEnv_.functionReferencesEnabled()) {
|
||||
return iter_.unrecognizedOpcode(&op);
|
||||
}
|
||||
CHECK_NEXT(emitCallRef());
|
||||
#endif
|
||||
|
||||
// Locals and globals
|
||||
case uint16_t(Op::LocalGet):
|
||||
|
|
|
@ -213,3 +213,8 @@ CalleeDesc CalleeDesc::builtinInstanceMethod(SymbolicAddress callee) {
|
|||
c.u.builtin_ = callee;
|
||||
return c;
|
||||
}
|
||||
CalleeDesc CalleeDesc::wasmFuncRef() {
|
||||
CalleeDesc c;
|
||||
c.which_ = FuncRef;
|
||||
return c;
|
||||
}
|
||||
|
|
|
@ -400,9 +400,9 @@ extern const CodeRange* LookupInSorted(const CodeRangeVector& codeRanges,
|
|||
// adds the function index of the callee.
|
||||
|
||||
class CallSiteDesc {
|
||||
static constexpr size_t LINE_OR_BYTECODE_BITS_SIZE = 29;
|
||||
static constexpr size_t LINE_OR_BYTECODE_BITS_SIZE = 28;
|
||||
uint32_t lineOrBytecode_ : LINE_OR_BYTECODE_BITS_SIZE;
|
||||
uint32_t kind_ : 3;
|
||||
uint32_t kind_ : 4;
|
||||
|
||||
WASM_CHECK_CACHEABLE_POD(lineOrBytecode_, kind_);
|
||||
|
||||
|
@ -415,6 +415,7 @@ class CallSiteDesc {
|
|||
Import, // wasm import call
|
||||
Indirect, // dynamic callee called via register, context on stack
|
||||
IndirectFast, // dynamically determined to be same-instance
|
||||
FuncRef, // call using direct function reference
|
||||
Symbolic, // call to a single symbolic callee
|
||||
EnterFrame, // call to a enter frame handler
|
||||
LeaveFrame, // call to a leave frame handler
|
||||
|
@ -433,11 +434,15 @@ class CallSiteDesc {
|
|||
Kind kind() const { return Kind(kind_); }
|
||||
bool isImportCall() const { return kind() == CallSiteDesc::Import; }
|
||||
bool isIndirectCall() const { return kind() == CallSiteDesc::Indirect; }
|
||||
bool isFuncRefCall() const { return kind() == CallSiteDesc::FuncRef; }
|
||||
bool mightBeCrossInstance() const {
|
||||
return isImportCall() || isIndirectCall();
|
||||
return isImportCall() || isIndirectCall() || isFuncRefCall();
|
||||
}
|
||||
};
|
||||
|
||||
static_assert(js::wasm::MaxFunctionBytes <=
|
||||
CallSiteDesc::MAX_LINE_OR_BYTECODE_VALUE);
|
||||
|
||||
WASM_DECLARE_CACHEABLE_POD(CallSiteDesc);
|
||||
|
||||
class CallSite : public CallSiteDesc {
|
||||
|
@ -633,7 +638,10 @@ class CalleeDesc {
|
|||
Builtin,
|
||||
|
||||
// Like Builtin, but automatically passes Instance* as first argument.
|
||||
BuiltinInstanceMethod
|
||||
BuiltinInstanceMethod,
|
||||
|
||||
// Calls a function reference.
|
||||
FuncRef,
|
||||
};
|
||||
|
||||
private:
|
||||
|
@ -666,6 +674,7 @@ class CalleeDesc {
|
|||
static CalleeDesc asmJSTable(const TableDesc& desc);
|
||||
static CalleeDesc builtin(SymbolicAddress callee);
|
||||
static CalleeDesc builtinInstanceMethod(SymbolicAddress callee);
|
||||
static CalleeDesc wasmFuncRef();
|
||||
Which which() const { return which_; }
|
||||
uint32_t funcIndex() const {
|
||||
MOZ_ASSERT(which_ == Func);
|
||||
|
|
|
@ -240,6 +240,7 @@ enum class Op {
|
|||
// Call operators
|
||||
Call = 0x10,
|
||||
CallIndirect = 0x11,
|
||||
CallRef = 0x14,
|
||||
|
||||
// Additional exception operators
|
||||
Delegate = 0x18,
|
||||
|
|
|
@ -487,6 +487,7 @@ bool ModuleGenerator::linkCallSites() {
|
|||
case CallSiteDesc::Breakpoint:
|
||||
case CallSiteDesc::EnterFrame:
|
||||
case CallSiteDesc::LeaveFrame:
|
||||
case CallSiteDesc::FuncRef:
|
||||
break;
|
||||
case CallSiteDesc::Func: {
|
||||
if (funcIsCompiled(target.funcIndex())) {
|
||||
|
|
|
@ -2488,11 +2488,16 @@ bool WasmInstanceObject::getExportedFunction(
|
|||
}
|
||||
}
|
||||
|
||||
fun->setExtendedSlot(FunctionExtended::WASM_INSTANCE_OBJ_SLOT,
|
||||
ObjectValue(*instanceObj));
|
||||
|
||||
fun->setExtendedSlot(FunctionExtended::WASM_INSTANCE_SLOT,
|
||||
PrivateValue((void*)&instance));
|
||||
PrivateValue(const_cast<Instance*>(&instance)));
|
||||
|
||||
const CodeTier& codeTier =
|
||||
instance.code().codeTier(instance.code().bestTier());
|
||||
const CodeRange& codeRange = codeTier.metadata().codeRange(funcExport);
|
||||
|
||||
fun->setExtendedSlot(FunctionExtended::WASM_FUNC_UNCHECKED_ENTRY_SLOT,
|
||||
PrivateValue(codeTier.segment().base() +
|
||||
codeRange.funcUncheckedCallEntry()));
|
||||
|
||||
if (!instanceObj->exports().putNew(funcIndex, fun)) {
|
||||
ReportOutOfMemory(cx);
|
||||
|
@ -2565,20 +2570,15 @@ bool wasm::IsWasmExportedFunction(JSFunction* fun) {
|
|||
}
|
||||
|
||||
Instance& wasm::ExportedFunctionToInstance(JSFunction* fun) {
|
||||
return ExportedFunctionToInstanceObject(fun)->instance();
|
||||
return fun->wasmInstance();
|
||||
}
|
||||
|
||||
WasmInstanceObject* wasm::ExportedFunctionToInstanceObject(JSFunction* fun) {
|
||||
MOZ_ASSERT(fun->kind() == FunctionFlags::Wasm ||
|
||||
fun->kind() == FunctionFlags::AsmJS);
|
||||
const Value& v =
|
||||
fun->getExtendedSlot(FunctionExtended::WASM_INSTANCE_OBJ_SLOT);
|
||||
return &v.toObject().as<WasmInstanceObject>();
|
||||
return fun->wasmInstance().object();
|
||||
}
|
||||
|
||||
uint32_t wasm::ExportedFunctionToFuncIndex(JSFunction* fun) {
|
||||
Instance& instance = ExportedFunctionToInstanceObject(fun)->instance();
|
||||
return instance.code().getFuncIndex(fun);
|
||||
return fun->wasmInstance().code().getFuncIndex(fun);
|
||||
}
|
||||
|
||||
// ============================================================================
|
||||
|
|
|
@ -252,6 +252,10 @@ OpKind wasm::Classify(OpBytes op) {
|
|||
return OpKind::Call;
|
||||
case Op::CallIndirect:
|
||||
return OpKind::CallIndirect;
|
||||
# ifdef ENABLE_WASM_FUNCTION_REFERENCES
|
||||
case Op::CallRef:
|
||||
return OpKind::CallRef;
|
||||
# endif
|
||||
case Op::Return:
|
||||
case Op::Limit:
|
||||
// Accept Limit, for use in decoding the end of a function after the body.
|
||||
|
|
|
@ -146,6 +146,9 @@ enum class OpKind {
|
|||
TeeGlobal,
|
||||
Call,
|
||||
CallIndirect,
|
||||
# ifdef ENABLE_WASM_FUNCTION_REFERENCES
|
||||
CallRef,
|
||||
# endif
|
||||
OldCallDirect,
|
||||
OldCallIndirect,
|
||||
Return,
|
||||
|
@ -364,6 +367,7 @@ class MOZ_STACK_CLASS OpIter : private Policy {
|
|||
template <typename ValTypeSpanT>
|
||||
[[nodiscard]] bool popWithTypes(ValTypeSpanT expected, ValueVector* values);
|
||||
[[nodiscard]] bool popWithRefType(Value* value, StackType* type);
|
||||
[[nodiscard]] bool popWithFuncType(Value* value, FuncType** funcType);
|
||||
[[nodiscard]] bool popWithRttType(Value* rtt, uint32_t* rttTypeIndex,
|
||||
uint32_t* rttDepth);
|
||||
[[nodiscard]] bool popWithRttType(Value* rtt, uint32_t rttTypeIndex,
|
||||
|
@ -562,6 +566,10 @@ class MOZ_STACK_CLASS OpIter : private Policy {
|
|||
[[nodiscard]] bool readCallIndirect(uint32_t* funcTypeIndex,
|
||||
uint32_t* tableIndex, Value* callee,
|
||||
ValueVector* argValues);
|
||||
#ifdef ENABLE_WASM_FUNCTION_REFERENCES
|
||||
[[nodiscard]] bool readCallRef(const FuncType** funcType, Value* callee,
|
||||
ValueVector* argValues);
|
||||
#endif
|
||||
[[nodiscard]] bool readOldCallDirect(uint32_t numFuncImports,
|
||||
uint32_t* funcTypeIndex,
|
||||
ValueVector* argValues);
|
||||
|
@ -905,6 +913,35 @@ inline bool OpIter<Policy>::popWithRefType(Value* value, StackType* type) {
|
|||
return fail(error.get());
|
||||
}
|
||||
|
||||
// This function pops exactly one value from the stack, checking that it is a
|
||||
// subtype of the function type.
|
||||
template <typename Policy>
|
||||
inline bool OpIter<Policy>::popWithFuncType(Value* value, FuncType** funcType) {
|
||||
StackType ty;
|
||||
if (!popWithRefType(value, &ty)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (ty.isBottom()) {
|
||||
return fail("gc instruction temporarily not allowed in dead code");
|
||||
}
|
||||
|
||||
if (!ty.valType().isTypeIndex() ||
|
||||
!env_.types->isFuncType(ty.valType().typeIndex())) {
|
||||
return fail("type mismatch: reference expected to be a subtype of funcref");
|
||||
}
|
||||
|
||||
*funcType = &env_.types->funcType(ty.valType().typeIndex());
|
||||
|
||||
#ifdef WASM_PRIVATE_REFTYPES
|
||||
if ((*funcType)->exposesTypeIndex()) {
|
||||
return fail("cannot expose indexed reference type");
|
||||
}
|
||||
#endif
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
// This function pops exactly one value from the stack, checking that it is an
|
||||
// rtt type with any type index or depth value.
|
||||
template <typename Policy>
|
||||
|
@ -2213,6 +2250,15 @@ inline bool OpIter<Policy>::readRefFunc(uint32_t* funcIndex) {
|
|||
return fail(
|
||||
"function index is not declared in a section before the code section");
|
||||
}
|
||||
|
||||
#ifdef ENABLE_WASM_FUNCTION_REFERENCES
|
||||
// When function references enabled, push type index on the stack, e.g. for
|
||||
// validation of the call_ref instruction.
|
||||
if (env_.functionReferencesEnabled()) {
|
||||
const uint32_t typeIndex = env_.funcs[*funcIndex].typeIndex;
|
||||
return push(RefType::fromTypeIndex(typeIndex, false));
|
||||
}
|
||||
#endif
|
||||
return push(RefType::func());
|
||||
}
|
||||
|
||||
|
@ -2374,6 +2420,27 @@ inline bool OpIter<Policy>::readCallIndirect(uint32_t* funcTypeIndex,
|
|||
return push(ResultType::Vector(funcType.results()));
|
||||
}
|
||||
|
||||
#ifdef ENABLE_WASM_FUNCTION_REFERENCES
|
||||
template <typename Policy>
|
||||
inline bool OpIter<Policy>::readCallRef(const FuncType** funcType,
|
||||
Value* callee, ValueVector* argValues) {
|
||||
MOZ_ASSERT(Classify(op_) == OpKind::CallRef);
|
||||
|
||||
FuncType* funcType_;
|
||||
if (!popWithFuncType(callee, &funcType_)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
*funcType = funcType_;
|
||||
|
||||
if (!popCallArgs(funcType_->args(), argValues)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return push(ResultType::Vector(funcType_->results()));
|
||||
}
|
||||
#endif
|
||||
|
||||
template <typename Policy>
|
||||
inline bool OpIter<Policy>::readOldCallDirect(uint32_t numFuncImports,
|
||||
uint32_t* funcTypeIndex,
|
||||
|
|
|
@ -309,6 +309,12 @@ TypeResult TypeContext::isRefSubtypeOf(RefType subType, RefType superType,
|
|||
return TypeResult::True;
|
||||
}
|
||||
|
||||
// The ref T <: funcref when T = func-type rule
|
||||
if (subType.isTypeIndex() && types_[subType.typeIndex()].isFuncType() &&
|
||||
superType.isFunc()) {
|
||||
return TypeResult::True;
|
||||
}
|
||||
|
||||
// Type-index references can be subtypes
|
||||
if (subType.isTypeIndex() && superType.isTypeIndex()) {
|
||||
return isTypeIndexSubtypeOf(subType.typeIndex(), superType.typeIndex(),
|
||||
|
|
|
@ -223,6 +223,16 @@ static bool DecodeFunctionBodyExprs(const ModuleEnvironment& env,
|
|||
CHECK(iter.readCallIndirect(&unusedIndex, &unusedIndex2, ¬hing,
|
||||
&unusedArgs));
|
||||
}
|
||||
#ifdef ENABLE_WASM_FUNCTION_REFERENCES
|
||||
case uint16_t(Op::CallRef): {
|
||||
if (!env.functionReferencesEnabled()) {
|
||||
return iter.unrecognizedOpcode(&op);
|
||||
}
|
||||
const FuncType* unusedType;
|
||||
NothingVector unusedArgs{};
|
||||
CHECK(iter.readCallRef(&unusedType, ¬hing, &unusedArgs));
|
||||
}
|
||||
#endif
|
||||
case uint16_t(Op::I32Const): {
|
||||
int32_t unused;
|
||||
CHECK(iter.readI32Const(&unused));
|
||||
|
|
|
@ -4,7 +4,7 @@
|
|||
|
||||
"use strict";
|
||||
|
||||
var EXPORTED_SYMBOLS = ["deserialize", "serialize"];
|
||||
var EXPORTED_SYMBOLS = ["deserialize", "serialize", "stringify"];
|
||||
|
||||
const { XPCOMUtils } = ChromeUtils.importESModule(
|
||||
"resource://gre/modules/XPCOMUtils.sys.mjs"
|
||||
|
@ -418,8 +418,32 @@ function serialize(
|
|||
}
|
||||
|
||||
lazy.logger.warn(
|
||||
`Unsupported type: ${type} for remote value: ${value.toString()}`
|
||||
`Unsupported type: ${type} for remote value: ${stringify(value)}`
|
||||
);
|
||||
|
||||
return undefined;
|
||||
}
|
||||
|
||||
/**
|
||||
* Safely stringify a value.
|
||||
*
|
||||
* @param {Object} value
|
||||
* Value of any type to be stringified.
|
||||
*
|
||||
* @returns {String} String representation of the value.
|
||||
*/
|
||||
function stringify(obj) {
|
||||
let text;
|
||||
try {
|
||||
text =
|
||||
obj !== null && typeof obj === "object" ? obj.toString() : String(obj);
|
||||
} catch (e) {
|
||||
// The error-case will also be handled in `finally {}`.
|
||||
} finally {
|
||||
if (typeof text != "string") {
|
||||
text = Object.prototype.toString.apply(obj);
|
||||
}
|
||||
}
|
||||
|
||||
return text;
|
||||
}
|
||||
|
|
Некоторые файлы не были показаны из-за слишком большого количества измененных файлов Показать больше
Загрузка…
Ссылка в новой задаче