diff --git a/browser/actors/moz.build b/browser/actors/moz.build
index 87ba6ef0c2a81..34f274a453e73 100644
--- a/browser/actors/moz.build
+++ b/browser/actors/moz.build
@@ -23,7 +23,7 @@ with Files("PageStyleChild.jsm"):
BUG_COMPONENT = ("Firefox", "Menus")
with Files("PluginChild.jsm"):
- BUG_COMPONENT = ("Core", "Plug-ins")
+ BUG_COMPONENT = ("Core", "Audio/Video: GMP")
with Files("ScreenshotsComponentChild.jsm"):
BUG_COMPONENT = ("Firefox", "Screenshots")
diff --git a/browser/base/content/browser-box.inc.xhtml b/browser/base/content/browser-box.inc.xhtml
index 2c8edad944b52..ae5b1979921a9 100644
--- a/browser/base/content/browser-box.inc.xhtml
+++ b/browser/base/content/browser-box.inc.xhtml
@@ -5,20 +5,16 @@
diff --git a/browser/base/content/browser.css b/browser/base/content/browser.css
index ec0a85c6dcabb..7dc9b08af57a1 100644
--- a/browser/base/content/browser.css
+++ b/browser/base/content/browser.css
@@ -32,8 +32,21 @@ body {
min-width: -moz-fit-content;
}
-/* Prevent shrinking the page content to 0 height and width */
+/* We set large flex on both containers to allow the devtools toolbox to
+ * set a flex value itself. We don't want the toolbox to actually take up free
+ * space, but we do want it to collapse when the window shrinks, and with
+ * flex: 0 it can't.
+ *
+ * When the toolbox is on the bottom it's a sibling of browserStack, and when
+ * it's on the side it's a sibling of browserContainer.
+ */
+.browserContainer {
+ -moz-box-flex: 10000;
+}
+
.browserStack {
+ -moz-box-flex: 10000;
+ /* Prevent shrinking the page content to 0 height and width */
min-height: 25px;
min-width: 25px;
}
diff --git a/browser/base/content/moz.build b/browser/base/content/moz.build
index 84cd0211ec0dc..9ed93b95e353b 100644
--- a/browser/base/content/moz.build
+++ b/browser/base/content/moz.build
@@ -56,7 +56,7 @@ with Files("test/permissions/**"):
BUG_COMPONENT = ("Firefox", "Site Permissions")
with Files("test/plugins/**"):
- BUG_COMPONENT = ("Core", "Plug-ins")
+ BUG_COMPONENT = ("Core", "Audio/Video: GMP")
with Files("test/popupNotifications/**"):
BUG_COMPONENT = ("Toolkit", "Notifications and Alerts")
diff --git a/browser/base/content/navigator-toolbox.inc.xhtml b/browser/base/content/navigator-toolbox.inc.xhtml
index 0bc1ec3603d0e..3873365655f40 100644
--- a/browser/base/content/navigator-toolbox.inc.xhtml
+++ b/browser/base/content/navigator-toolbox.inc.xhtml
@@ -155,7 +155,7 @@
-
@@ -654,7 +654,6 @@
class="chromeclass-toolbar-additional"
data-l10n-id="navbar-search"
align="center"
- flex="175"
persist="width">
diff --git a/browser/base/content/pageinfo/pageInfo.js b/browser/base/content/pageinfo/pageInfo.js
index 86adb7b7fb6c6..f94caf043cecf 100644
--- a/browser/base/content/pageinfo/pageInfo.js
+++ b/browser/base/content/pageinfo/pageInfo.js
@@ -833,17 +833,17 @@ function onImageSelect() {
previewBox.collapsed = true;
mediaSaveBox.collapsed = true;
splitter.collapsed = true;
- tree.flex = 1;
+ tree.setAttribute("flex", "1");
} else if (count > 1) {
splitter.collapsed = true;
previewBox.collapsed = true;
mediaSaveBox.collapsed = false;
- tree.flex = 1;
+ tree.setAttribute("flex", "1");
} else {
mediaSaveBox.collapsed = true;
splitter.collapsed = false;
previewBox.collapsed = false;
- tree.flex = 0;
+ tree.setAttribute("flex", "0");
makePreview(getSelectedRows(tree)[0]);
}
}
diff --git a/browser/base/content/pageinfo/pageInfo.xhtml b/browser/base/content/pageinfo/pageInfo.xhtml
index 4fe7f49091805..7cf284a384832 100644
--- a/browser/base/content/pageinfo/pageInfo.xhtml
+++ b/browser/base/content/pageinfo/pageInfo.xhtml
@@ -152,11 +152,11 @@
@@ -172,23 +172,23 @@
-
-
-
-
-
diff --git a/browser/base/content/tabbrowser.js b/browser/base/content/tabbrowser.js
index 8989f91c60809..898fe40a36244 100644
--- a/browser/base/content/tabbrowser.js
+++ b/browser/base/content/tabbrowser.js
@@ -2161,22 +2161,14 @@
let notificationbox = document.createXULElement("notificationbox");
notificationbox.setAttribute("notificationside", "top");
- // We set large flex on both containers to allow the devtools toolbox to
- // set a flex attribute. We don't want the toolbox to actually take up free
- // space, but we do want it to collapse when the window shrinks, and with
- // flex=0 it can't. When the toolbox is on the bottom it's a sibling of
- // browserStack, and when it's on the side it's a sibling of
- // browserContainer.
let stack = document.createXULElement("stack");
stack.className = "browserStack";
stack.appendChild(b);
- stack.setAttribute("flex", "10000");
let browserContainer = document.createXULElement("vbox");
browserContainer.className = "browserContainer";
browserContainer.appendChild(notificationbox);
browserContainer.appendChild(stack);
- browserContainer.setAttribute("flex", "10000");
let browserSidebarContainer = document.createXULElement("hbox");
browserSidebarContainer.className = "browserSidebarContainer";
diff --git a/browser/components/BrowserGlue.jsm b/browser/components/BrowserGlue.jsm
index 7775f66a56b76..d727eefd18ac5 100644
--- a/browser/components/BrowserGlue.jsm
+++ b/browser/components/BrowserGlue.jsm
@@ -4130,50 +4130,6 @@ BrowserGlue.prototype = {
lazy.UrlbarPrefs.migrateResultGroups();
}
- if (currentUIVersion < 119 && AppConstants.NIGHTLY_BUILD) {
- // Uninstall outdated monochromatic themes for the following UI versions:
- // 118: Uninstall prototype monochromatic purple theme.
- // 119 (bug 1732957): Uninstall themes with old IDs.
- const themeIdsToMigrate = [
- "firefox-monochromatic-purple@mozilla.org",
- "firefox-lush-soft@mozilla.org",
- "firefox-lush-balanced@mozilla.org",
- "firefox-lush-bold@mozilla.org",
- "firefox-abstract-soft@mozilla.org",
- "firefox-abstract-balanced@mozilla.org",
- "firefox-abstract-bold@mozilla.org",
- "firefox-elemental-soft@mozilla.org",
- "firefox-elemental-balanced@mozilla.org",
- "firefox-elemental-bold@mozilla.org",
- "firefox-cheers-soft@mozilla.org",
- "firefox-cheers-balanced@mozilla.org",
- "firefox-cheers-bold@mozilla.org",
- "firefox-graffiti-soft@mozilla.org",
- "firefox-graffiti-balanced@mozilla.org",
- "firefox-graffiti-bold@mozilla.org",
- "firefox-foto-soft@mozilla.org",
- "firefox-foto-balanced@mozilla.org",
- "firefox-foto-bold@mozilla.org",
- ];
- try {
- for (let id of themeIdsToMigrate) {
- lazy.AddonManager.getAddonByID(id).then(addon => {
- if (!addon) {
- // Either the addon wasn't installed, or the call to getAddonByID failed.
- return;
- }
- addon.uninstall().catch(Cu.reportError);
- }, Cu.reportError);
- }
- } catch (error) {
- Cu.reportError(
- "Could not access the AddonManager to upgrade the profile. This is most " +
- "likely because the upgrader is being run from an xpcshell test where " +
- "the AddonManager is not initialized."
- );
- }
- }
-
if (currentUIVersion < 120) {
// Migrate old titlebar bool pref to new int-based one.
const oldPref = "browser.tabs.drawInTitlebar";
diff --git a/browser/components/firefoxview/featureCallout.mjs b/browser/components/firefoxview/featureCallout.mjs
index 274f650cda285..eaa8bab8af061 100644
--- a/browser/components/firefoxview/featureCallout.mjs
+++ b/browser/components/firefoxview/featureCallout.mjs
@@ -247,6 +247,12 @@ function _positionCallout() {
function clearPosition() {
positions.forEach(position => {
container.style[position] = "unset";
+ if (container.classList.contains(`arrow-${position}`)) {
+ container.classList.remove(`arrow-${position}`);
+ }
+ if (container.classList.contains(`arrow-inline-${position}`)) {
+ container.classList.remove(`arrow-inline-${position}`);
+ }
});
}
@@ -270,6 +276,7 @@ function _positionCallout() {
containerTop
)}px`;
centerHorizontally(container, parentEl);
+ container.classList.add("arrow-top");
},
// Point to an element below the callout
bottom() {
@@ -277,6 +284,7 @@ function _positionCallout() {
getOffset(parentEl).top - container.clientHeight + margin;
container.style.top = `${Math.max(containerTop, 0)}px`;
centerHorizontally(container, parentEl);
+ container.classList.add("arrow-bottom");
},
// Point to an element to the right of the callout
left() {
@@ -286,6 +294,7 @@ function _positionCallout() {
containerLeft
)}px`;
container.style.top = `${getOffset(parentEl).top}px`;
+ container.classList.add("arrow-inline-start");
},
// Point to an element to the left of the callout
right() {
@@ -293,11 +302,16 @@ function _positionCallout() {
getOffset(parentEl).left - container.offsetWidth + margin;
container.style.left = `${Math.max(containerLeft, margin)}px`;
container.style.top = `${getOffset(parentEl).top}px`;
+ container.classList.add("arrow-inline-end");
},
};
clearPosition(container);
+ if (!container.classList.contains("callout-arrow")) {
+ container.classList.add("callout-arrow");
+ }
+
if (["start", "end"].includes(arrowPosition)) {
if (RTL) {
positioners[arrowPosition === "start" ? "right" : "left"]();
diff --git a/browser/components/firefoxview/tests/browser/browser_feature_callout.js b/browser/components/firefoxview/tests/browser/browser_feature_callout.js
index b9248cc0d602a..fa8ab12261b5e 100644
--- a/browser/components/firefoxview/tests/browser/browser_feature_callout.js
+++ b/browser/components/firefoxview/tests/browser/browser_feature_callout.js
@@ -415,3 +415,62 @@ add_task(async function feature_callout_only_highlights_existing_elements() {
}
);
});
+
+add_task(async function feature_callout_arrow_class_exists() {
+ await SpecialPowers.pushPrefEnv({
+ set: [
+ ["browser.firefox-view.feature-tour", "{}"],
+ ["intl.l10n.pseudo", ""],
+ ],
+ });
+
+ await BrowserTestUtils.withNewTab(
+ {
+ gBrowser,
+ url: "about:firefoxview",
+ },
+ async browser => {
+ const { document } = browser.contentWindow;
+
+ await waitForCalloutRender(document);
+ await waitForCalloutPositioned(document);
+
+ const arrowParent = document.querySelector(".callout-arrow.arrow-top");
+ ok(arrowParent, "Arrow class exists on parent container");
+ }
+ );
+});
+
+add_task(async function feature_callout_arrow_is_not_flipped_on_ltr() {
+ await SpecialPowers.pushPrefEnv({
+ set: [
+ ["browser.firefox-view.feature-tour", "{}"],
+ ["intl.l10n.pseudo", ""],
+ ],
+ });
+
+ await BrowserTestUtils.withNewTab(
+ {
+ gBrowser,
+ url: "about:firefoxview",
+ },
+ async browser => {
+ const { document } = browser.contentWindow;
+
+ await waitForCalloutRender(document);
+ await waitForCalloutPositioned(document);
+ // Advance to third screen
+ clickPrimaryButton(document);
+ await waitForCalloutScreen(document, ".FEATURE_CALLOUT_2");
+ clickPrimaryButton(document);
+ await waitForCalloutScreen(document, ".FEATURE_CALLOUT_3");
+ let arrowParent = document.querySelector(
+ ".callout-arrow.arrow-inline-end"
+ );
+ ok(
+ arrowParent,
+ "Feature Callout arrow parent has arrow-end class when arrow direction is set to 'end'"
+ );
+ }
+ );
+});
diff --git a/browser/components/newtab/aboutwelcome/content/aboutwelcome.css b/browser/components/newtab/aboutwelcome/content/aboutwelcome.css
index 8927b37378b5a..4c3a87fb9a985 100644
--- a/browser/components/newtab/aboutwelcome/content/aboutwelcome.css
+++ b/browser/components/newtab/aboutwelcome/content/aboutwelcome.css
@@ -37,6 +37,9 @@ input {
.onboardingContainer.featureCallout, .onboardingContainer.featureCallout .outer-wrapper {
height: auto;
}
+.onboardingContainer.featureCallout .screen:dir(rtl) {
+ transform: none;
+}
.onboardingContainer.featureCallout .screen[pos=callout] {
z-index: 2147483646;
height: fit-content;
@@ -106,6 +109,57 @@ input {
.onboardingContainer.featureCallout .screen[pos=callout] .action-buttons .secondary-cta {
float: inline-end;
}
+.onboardingContainer.featureCallout.callout-arrow::before, .onboardingContainer.featureCallout.callout-arrow::after {
+ content: "";
+ position: absolute;
+ width: 24px;
+ height: 24px;
+ transform: rotate(45deg);
+}
+.onboardingContainer.featureCallout.callout-arrow:dir(rtl)::before, .onboardingContainer.featureCallout.callout-arrow:dir(rtl)::after {
+ transform: rotate(315deg);
+}
+.onboardingContainer.featureCallout.callout-arrow::before {
+ background-color: var(--in-content-page-background);
+ z-index: 2147483647;
+}
+.onboardingContainer.featureCallout.callout-arrow::after {
+ background: transparent;
+ box-shadow: 0 2px 6px rgba(0, 0, 0, 0.15);
+ z-index: -1;
+}
+.onboardingContainer.featureCallout.arrow-top::before, .onboardingContainer.featureCallout.arrow-top::after {
+ top: -12px;
+ inset-inline-start: calc(50% - 12px);
+}
+.onboardingContainer.featureCallout.arrow-top::before {
+ border-top: 1px solid #CFCFD8;
+ border-inline-start: 1px solid #CFCFD8;
+}
+.onboardingContainer.featureCallout.arrow-bottom::before, .onboardingContainer.featureCallout.arrow-bottom::after {
+ top: calc(100% - 12px);
+ inset-inline-start: calc(50% - 12px);
+}
+.onboardingContainer.featureCallout.arrow-bottom::before {
+ border-inline-end: 1px solid #CFCFD8;
+ border-bottom: 1px solid #CFCFD8;
+}
+.onboardingContainer.featureCallout.arrow-inline-end::before, .onboardingContainer.featureCallout.arrow-inline-end::after {
+ top: calc(50% - 12px);
+ inset-inline-start: calc(100% - 12px);
+}
+.onboardingContainer.featureCallout.arrow-inline-end::before {
+ border-top: 1px solid #CFCFD8;
+ border-inline-end: 1px solid #CFCFD8;
+}
+.onboardingContainer.featureCallout.arrow-inline-start::before, .onboardingContainer.featureCallout.arrow-inline-start::after {
+ top: calc(50% - 12px);
+ inset-inline-start: -12px;
+}
+.onboardingContainer.featureCallout.arrow-inline-start::before {
+ border-bottom: 1px solid #CFCFD8;
+ border-inline-start: 1px solid #CFCFD8;
+}
html {
height: 100%;
diff --git a/browser/components/newtab/content-src/styles/_feature-callout.scss b/browser/components/newtab/content-src/styles/_feature-callout.scss
index d391b4f453389..efad07a61f717 100644
--- a/browser/components/newtab/content-src/styles/_feature-callout.scss
+++ b/browser/components/newtab/content-src/styles/_feature-callout.scss
@@ -5,11 +5,17 @@ $max-z-index: 2147483647;
&,
& .outer-wrapper {
+ // auto height to allow for arrow positioning based on height
height: auto;
}
// sass-lint:disable no-color-literals
.screen {
+ // override transform in about:welcome
+ &:dir(rtl) {
+ transform: none;
+ }
+
&[pos='callout'] {
// Max value for z-index
z-index: $max-z-index - 1;
@@ -98,4 +104,88 @@ $max-z-index: 2147483647;
}
}
}
+
+ $arrow-size: 24px;
+ $arrow-inset-size: math.div($arrow-size, 2);
+ $arrow-border: 1px solid #CFCFD8;
+
+ // applied to all callout arrow foreground and background
+ &.callout-arrow::before,
+ &.callout-arrow::after {
+ content: '';
+ position: absolute;
+ width: $arrow-size;
+ height: $arrow-size;
+ transform: rotate(45deg);
+ }
+
+ &.callout-arrow:dir(rtl)::before,
+ &.callout-arrow:dir(rtl)::after {
+ transform: rotate(315deg);
+ }
+
+ // color for all arrow foreground
+ &.callout-arrow::before {
+ background-color: var(--in-content-page-background);
+ z-index: $max-z-index;
+ }
+
+ // styles for all arrow backgrounds
+ &.callout-arrow::after {
+ background: transparent;
+ box-shadow: 0 2px 6px rgba(0, 0, 0, 0.15);
+ z-index: -1;
+ }
+
+ // up arrow positioning
+ &.arrow-top::before,
+ &.arrow-top::after {
+ top: -$arrow-inset-size;
+ inset-inline-start: calc(50% - $arrow-inset-size);
+ }
+
+ // up arrow foreground
+ &.arrow-top::before {
+ border-top: $arrow-border;
+ border-inline-start: $arrow-border;
+ }
+
+ // down arrow positioning
+ &.arrow-bottom::before,
+ &.arrow-bottom::after {
+ top: calc(100% - $arrow-inset-size);
+ inset-inline-start: calc(50% - $arrow-inset-size);
+ }
+
+ // down arrow foreground
+ &.arrow-bottom::before {
+ border-inline-end: $arrow-border;
+ border-bottom: $arrow-border;
+ }
+
+ // end arrow positioning
+ &.arrow-inline-end::before,
+ &.arrow-inline-end::after {
+ top: calc(50% - $arrow-inset-size);
+ inset-inline-start: calc(100% - $arrow-inset-size);
+ }
+
+ // end arrow foreground
+ &.arrow-inline-end::before {
+ border-top: $arrow-border;
+ border-inline-end: $arrow-border;
+ }
+
+ // start arrow positioning
+ &.arrow-inline-start::before,
+ &.arrow-inline-start::after {
+ top: calc(50% - $arrow-inset-size);
+ inset-inline-start: -$arrow-inset-size;
+ }
+
+ // start arrow foreground
+ &.arrow-inline-start::before {
+ border-bottom: $arrow-border;
+ border-inline-start: $arrow-border;
+ }
}
diff --git a/browser/components/places/content/places.xhtml b/browser/components/places/content/places.xhtml
index e19a580298283..bfcfa36243338 100644
--- a/browser/components/places/content/places.xhtml
+++ b/browser/components/places/content/places.xhtml
@@ -341,7 +341,7 @@
-
+
-
-
-
-
-
-
-
@@ -397,12 +397,12 @@
-
+
-
+
#include editBookmarkPanel.inc.xhtml
diff --git a/browser/components/preferences/dialogs/blocklists.xhtml b/browser/components/preferences/dialogs/blocklists.xhtml
index 06948335f4568..47d467c13e000 100644
--- a/browser/components/preferences/dialogs/blocklists.xhtml
+++ b/browser/components/preferences/dialogs/blocklists.xhtml
@@ -41,9 +41,9 @@
hidecolumnpicker="true"
onselect="gBlocklistManager.onListSelected();">
-
-
diff --git a/browser/components/preferences/dialogs/permissions.js b/browser/components/preferences/dialogs/permissions.js
index bbab64e570617..b4a1c5f58ca9e 100644
--- a/browser/components/preferences/dialogs/permissions.js
+++ b/browser/components/preferences/dialogs/permissions.js
@@ -354,7 +354,7 @@ var gPermissionManager = {
let richlistitem = document.createXULElement("richlistitem");
richlistitem.setAttribute("origin", permission.origin);
let row = document.createXULElement("hbox");
- row.setAttribute("flex", "1");
+ row.setAttribute("style", "-moz-box-flex: 1");
let hbox = document.createXULElement("hbox");
let website = document.createXULElement("label");
@@ -363,7 +363,7 @@ var gPermissionManager = {
website.setAttribute("value", permission.origin);
hbox.setAttribute("width", "0");
hbox.setAttribute("class", "website-name");
- hbox.setAttribute("flex", "3");
+ hbox.setAttribute("style", "-moz-box-flex: 3");
hbox.appendChild(website);
row.appendChild(hbox);
@@ -378,7 +378,7 @@ var gPermissionManager = {
);
hbox.setAttribute("width", "0");
hbox.setAttribute("class", "website-name");
- hbox.setAttribute("flex", "1");
+ hbox.setAttribute("style", "-moz-box-flex: 1");
hbox.appendChild(capability);
row.appendChild(hbox);
}
diff --git a/browser/components/preferences/dialogs/permissions.xhtml b/browser/components/preferences/dialogs/permissions.xhtml
index 34ee97300a3bd..1d51e6fe67f63 100644
--- a/browser/components/preferences/dialogs/permissions.xhtml
+++ b/browser/components/preferences/dialogs/permissions.xhtml
@@ -58,9 +58,9 @@
-
-
diff --git a/browser/components/preferences/dialogs/siteDataSettings.js b/browser/components/preferences/dialogs/siteDataSettings.js
index 2b512156486c2..500001aa3abe3 100644
--- a/browser/components/preferences/dialogs/siteDataSettings.js
+++ b/browser/components/preferences/dialogs/siteDataSettings.js
@@ -41,7 +41,7 @@ let gSiteDataSettings = {
function addColumnItem(l10n, flexWidth, tooltipText) {
let box = document.createXULElement("hbox");
box.className = "item-box";
- box.setAttribute("flex", flexWidth);
+ box.setAttribute("style", `-moz-box-flex: ${flexWidth}`);
let label = document.createXULElement("label");
label.setAttribute("crop", "end");
if (l10n) {
diff --git a/browser/components/preferences/dialogs/siteDataSettings.xhtml b/browser/components/preferences/dialogs/siteDataSettings.xhtml
index c20b76d5c090d..aeacde9009273 100644
--- a/browser/components/preferences/dialogs/siteDataSettings.xhtml
+++ b/browser/components/preferences/dialogs/siteDataSettings.xhtml
@@ -42,11 +42,11 @@
-
-
+
+
-
-
+
+
diff --git a/browser/components/preferences/dialogs/sitePermissions.js b/browser/components/preferences/dialogs/sitePermissions.js
index 8c684f6f501ad..1d2c14fd4d751 100644
--- a/browser/components/preferences/dialogs/sitePermissions.js
+++ b/browser/components/preferences/dialogs/sitePermissions.js
@@ -351,18 +351,18 @@ var gSitePermissionsManager = {
let richlistitem = document.createXULElement("richlistitem");
richlistitem.setAttribute("origin", permission.origin);
let row = document.createXULElement("hbox");
- row.setAttribute("flex", "1");
+ row.setAttribute("style", "-moz-box-flex: 1");
let hbox = document.createXULElement("hbox");
let website = document.createXULElement("label");
website.setAttribute("value", permission.origin);
website.setAttribute("width", width);
hbox.setAttribute("class", "website-name");
- hbox.setAttribute("flex", "3");
+ hbox.setAttribute("style", "-moz-box-flex: 1");
hbox.appendChild(website);
let menulist = document.createXULElement("menulist");
- menulist.setAttribute("flex", "1");
+ menulist.setAttribute("style", "-moz-box-flex: 1");
menulist.setAttribute("width", width);
menulist.setAttribute("class", "website-status");
let states = SitePermissions.getAvailableStates(permission.type);
diff --git a/browser/components/preferences/dialogs/sitePermissions.xhtml b/browser/components/preferences/dialogs/sitePermissions.xhtml
index e99758098cb9a..038b17eb9957e 100644
--- a/browser/components/preferences/dialogs/sitePermissions.xhtml
+++ b/browser/components/preferences/dialogs/sitePermissions.xhtml
@@ -49,9 +49,9 @@
-
-
diff --git a/browser/themes/shared/sidebar.css b/browser/themes/shared/sidebar.css
index f8cfde28fd4e8..fb879bc73fced 100644
--- a/browser/themes/shared/sidebar.css
+++ b/browser/themes/shared/sidebar.css
@@ -18,6 +18,16 @@
border-bottom: 1px solid var(--sidebar-border-color);
}
+#sidebar-spacer {
+ /* To ensure the button doesn't expand unnecessarily for short labels, the
+ spacer should significantly out-flex the button. */
+ -moz-box-flex: 1000;
+}
+
+#sidebar {
+ -moz-box-flex: 1;
+}
+
@media not (-moz-platform: linux) {
/* We don't let the splitter overlap the sidebar on Linux since the sidebar's
scrollbar is too narrow on Linux. */
@@ -55,6 +65,7 @@
margin: 0;
padding: 0;
padding-inline: 8px 4px;
+ -moz-box-flex: 1;
}
#sidebar-switcher-arrow {
@@ -76,6 +87,7 @@
border-radius: var(--toolbarbutton-border-radius);
border: 1px solid transparent;
padding: 2px 4px;
+ -moz-box-flex: 1;
}
#sidebar-switcher-target:hover {
diff --git a/browser/themes/shared/urlbar-searchbar.css b/browser/themes/shared/urlbar-searchbar.css
index e406f8d26d2c3..dff34ccbacf89 100644
--- a/browser/themes/shared/urlbar-searchbar.css
+++ b/browser/themes/shared/urlbar-searchbar.css
@@ -265,6 +265,7 @@
#urlbar-container {
-moz-box-align: center;
+ -moz-box-flex: 400;
}
#urlbar-search-splitter {
@@ -638,6 +639,7 @@
#search-container {
min-width: 125px;
+ -moz-box-flex: 175;
}
#search-container[cui-areatype="menu-panel"] > #searchbar:-moz-lwtheme {
diff --git a/devtools/client/framework/test/browser_toolbox_screenshot_tool.js b/devtools/client/framework/test/browser_toolbox_screenshot_tool.js
index 07c3b0b647987..62cf3e6f30b35 100644
--- a/devtools/client/framework/test/browser_toolbox_screenshot_tool.js
+++ b/devtools/client/framework/test/browser_toolbox_screenshot_tool.js
@@ -35,7 +35,7 @@ add_task(async function() {
toolbox.doc.querySelector("#command-button-screenshot").click();
const filePath = await onScreenshotDownloaded;
- ok(filePath, "The screenshot was taken");
+ ok(!!filePath, "The screenshot was taken");
info("Create an image using the downloaded file as source");
const image = new Image();
@@ -78,7 +78,49 @@ add_task(async function() {
`The warning message is rendered as expected (${message})`
);
- //Remove the downloaded screenshot file
+ // Remove the downloaded screenshot file
await IOUtils.remove(filePath);
+
+ info(
+ "Check that taking a screenshot in a private window doesn't appear in the non-private window"
+ );
+ const privateWindow = await BrowserTestUtils.openNewBrowserWindow({
+ private: true,
+ });
+ ok(PrivateBrowsingUtils.isWindowPrivate(privateWindow), "window is private");
+ const privateBrowser = privateWindow.gBrowser;
+ privateBrowser.selectedTab = BrowserTestUtils.addTab(
+ privateBrowser,
+ TEST_URL
+ );
+
+ info("private tab opened");
+ ok(
+ PrivateBrowsingUtils.isBrowserPrivate(privateBrowser.selectedBrowser),
+ "tab window is private"
+ );
+
+ const privateToolbox = await gDevTools.showToolboxForTab(
+ privateBrowser.selectedTab
+ );
+
+ const onPrivateScreenshotDownloaded = waitUntilScreenshot({
+ isWindowPrivate: true,
+ });
+ privateToolbox.doc.querySelector("#command-button-screenshot").click();
+ const privateScreenshotFilePath = await onPrivateScreenshotDownloaded;
+ ok(
+ !!privateScreenshotFilePath,
+ "The screenshot was taken in the private window"
+ );
+
+ // Remove the downloaded screenshot file
+ await IOUtils.remove(privateScreenshotFilePath);
+
+ // cleanup the downloads
await resetDownloads();
+
+ const closePromise = BrowserTestUtils.windowClosed(privateWindow);
+ privateWindow.BrowserTryToCloseWindow();
+ await closePromise;
});
diff --git a/devtools/client/framework/toolbox-hosts.js b/devtools/client/framework/toolbox-hosts.js
index b26253bde6f49..d0a31c5fbba23 100644
--- a/devtools/client/framework/toolbox-hosts.js
+++ b/devtools/client/framework/toolbox-hosts.js
@@ -420,7 +420,7 @@ function focusTab(tab) {
function createDevToolsFrame(doc, className) {
const frame = doc.createXULElement("browser");
frame.setAttribute("type", "content");
- frame.flex = 1; // Required to be able to shrink when the window shrinks
+ frame.setAttribute("flex", "1"); // Required to be able to shrink when the window shrinks
frame.className = className;
const inXULDocument = doc.documentElement.namespaceURI === XUL_NS;
diff --git a/devtools/client/framework/toolbox.xhtml b/devtools/client/framework/toolbox.xhtml
index 773221b76edf5..f5c83d05c0772 100644
--- a/devtools/client/framework/toolbox.xhtml
+++ b/devtools/client/framework/toolbox.xhtml
@@ -23,19 +23,15 @@
-
+
-
-
+
-
+
diff --git a/devtools/client/shared/screenshot.js b/devtools/client/shared/screenshot.js
index 28ed63195c337..fd4a9ac635718 100644
--- a/devtools/client/shared/screenshot.js
+++ b/devtools/client/shared/screenshot.js
@@ -10,6 +10,11 @@ const Services = require("Services");
loader.lazyImporter(this, "Downloads", "resource://gre/modules/Downloads.jsm");
loader.lazyImporter(this, "FileUtils", "resource://gre/modules/FileUtils.jsm");
+loader.lazyImporter(
+ this,
+ "PrivateBrowsingUtils",
+ "resource://gre/modules/PrivateBrowsingUtils.jsm"
+);
const STRINGS_URI = "devtools/shared/locales/screenshot.properties";
const L10N = new LocalizationHelper(STRINGS_URI);
@@ -232,7 +237,7 @@ function saveScreenshot(window, args = {}, value) {
}
simulateCameraShutter(window);
- return save(args, value);
+ return save(window, args, value);
}
/**
@@ -253,16 +258,20 @@ function simulateCameraShutter(window) {
/**
* Save the captured screenshot to one of several destinations.
*
+ * @param object window
+ * The DevTools Client window.
+ *
* @param object args
* The original args with which the screenshot was called.
*
* @param object image
* The image object that was sent from the server.
*
+ *
* @return string[]
* Response messages from processing the screenshot.
*/
-async function save(args, image) {
+async function save(window, args, image) {
const fileNeeded = args.filename || !args.clipboard || args.file;
const results = [];
@@ -272,7 +281,7 @@ async function save(args, image) {
}
if (fileNeeded) {
- const result = await saveToFile(image);
+ const result = await saveToFile(window, image);
results.push(result);
}
return results;
@@ -326,13 +335,16 @@ function saveToClipboard(base64URI) {
* Save the screenshot data to disk, returning a promise which is resolved on
* completion.
*
+ * @param object window
+ * The DevTools Client window.
+ *
* @param object image
* The image object that was sent from the server.
*
* @return string
* Response message from processing the screenshot.
*/
-async function saveToFile(image) {
+async function saveToFile(window, image) {
let filename = image.filename;
// Guard against missing image data.
@@ -355,13 +367,22 @@ async function saveToFile(image) {
: PathUtils.joinRelative(downloadsDir, filename);
}
- const sourceURI = Services.io.newURI(image.data);
const targetFile = new FileUtils.File(filename);
// Create download and track its progress.
try {
const download = await Downloads.createDownload({
- source: sourceURI,
+ source: {
+ url: image.data,
+ // Here we want to know if the window in which the screenshot is taken is private.
+ // We have a ChromeWindow when this is called from Browser Console (:screenshot) and
+ // RDM (screenshot button).
+ isPrivate: window.isChromeWindow
+ ? PrivateBrowsingUtils.isWindowPrivate(window)
+ : PrivateBrowsingUtils.isBrowserPrivate(
+ window.browsingContext.embedderElement
+ ),
+ },
target: targetFile,
});
const list = await Downloads.getList(Downloads.ALL);
diff --git a/devtools/client/shared/sourceeditor/editor.js b/devtools/client/shared/sourceeditor/editor.js
index a3c939f32511b..b2a801e9abce6 100644
--- a/devtools/client/shared/sourceeditor/editor.js
+++ b/devtools/client/shared/sourceeditor/editor.js
@@ -327,7 +327,7 @@ Editor.prototype = {
env = el.ownerDocument.createElementNS(el.namespaceURI, "iframe");
if (el.namespaceURI === XUL_NS) {
- env.flex = 1;
+ env.setAttribute("flex", "1");
}
}
diff --git a/devtools/client/shared/test/shared-head.js b/devtools/client/shared/test/shared-head.js
index 143851b466201..b56a3f05c6ae1 100644
--- a/devtools/client/shared/test/shared-head.js
+++ b/devtools/client/shared/test/shared-head.js
@@ -1516,8 +1516,13 @@ function colorAt(image, x, y) {
let allDownloads = [];
/**
* Returns a Promise that resolves when a new screenshot is available in the download folder.
+ *
+ * @param {Object} [options]
+ * @param {Boolean} options.isWindowPrivate: Set to true if the window from which the screenshot
+ * is taken is a private window. This will ensure that we check that the
+ * screenshot appears in the private window, not the non-private one (See Bug 1783373)
*/
-async function waitUntilScreenshot() {
+async function waitUntilScreenshot({ isWindowPrivate = false } = {}) {
const { Downloads } = require("resource://gre/modules/Downloads.jsm");
const list = await Downloads.getList(Downloads.ALL);
@@ -1529,6 +1534,14 @@ async function waitUntilScreenshot() {
return;
}
+ is(
+ !!download.source.isPrivate,
+ isWindowPrivate,
+ `The download occured in the expected${
+ isWindowPrivate ? " private" : ""
+ } window`
+ );
+
allDownloads.push(download);
resolve(download.target.path);
list.removeView(view);
@@ -1545,10 +1558,10 @@ async function waitUntilScreenshot() {
async function resetDownloads() {
info("Reset downloads");
const { Downloads } = require("resource://gre/modules/Downloads.jsm");
- const publicList = await Downloads.getList(Downloads.PUBLIC);
- const downloads = await publicList.getAll();
+ const downloadList = await Downloads.getList(Downloads.ALL);
+ const downloads = await downloadList.getAll();
for (const download of downloads) {
- publicList.remove(download);
+ downloadList.remove(download);
await download.finalize(true);
}
allDownloads = [];
diff --git a/devtools/client/themes/toolbox.css b/devtools/client/themes/toolbox.css
index aef36ab72ec2f..0b98f25935fe6 100644
--- a/devtools/client/themes/toolbox.css
+++ b/devtools/client/themes/toolbox.css
@@ -521,6 +521,11 @@
-moz-user-focus: ignore;
}
+#toolbox-panel-webconsole {
+ min-height: 75px;
+ -moz-box-flex: 1;
+}
+
/**
* Enrure that selected toolbox panel's contents are keyboard accessible as they
* are explicitly made not to be when hidden (default).
@@ -529,11 +534,21 @@
-moz-user-focus: normal;
}
-/* Let the component gain focus when a click hits an empty area */
#toolbox-container {
+ -moz-box-flex: 1;
+ /* Let the component gain focus when a click hits an empty area */
-moz-user-focus: normal;
}
+#toolbox-deck {
+ min-height: 75px;
+ /* Set large flex to allow the toolbox-panel-webconsole to have a
+ height set to a small value without flexing to fill up extra
+ space. There must be a flex on both to ensure that the console
+ panel itself is sized properly */
+ -moz-box-flex: 10000;
+}
+
#toolbox-container:focus {
outline: 0;
}
diff --git a/dom/canvas/OffscreenCanvasDisplayHelper.cpp b/dom/canvas/OffscreenCanvasDisplayHelper.cpp
index b2bbcd433d1d9..b77e914bf6b8b 100644
--- a/dom/canvas/OffscreenCanvasDisplayHelper.cpp
+++ b/dom/canvas/OffscreenCanvasDisplayHelper.cpp
@@ -53,15 +53,17 @@ layers::CompositableHandle OffscreenCanvasDisplayHelper::GetCompositableHandle()
void OffscreenCanvasDisplayHelper::UpdateContext(
CanvasContextType aType, const Maybe& aChildId) {
- MutexAutoLock lock(mMutex);
-
+ RefPtr imageContainer;
if (aType != CanvasContextType::WebGPU) {
- mImageContainer = MakeRefPtr(
+ imageContainer = MakeRefPtr(
layers::ImageContainer::ASYNCHRONOUS);
}
+ MutexAutoLock lock(mMutex);
+
mType = aType;
mContextChildId = aChildId;
+ mImageContainer = std::move(imageContainer);
if (aChildId) {
mContextManagerId = Some(gfx::CanvasManagerChild::Get()->Id());
@@ -125,15 +127,39 @@ bool OffscreenCanvasDisplayHelper::CommitFrameToCompositor(
return false;
}
- if (mData.mDoPaintCallbacks) {
- aContext->OnBeforePaintTransaction();
- }
-
+ bool paintCallbacks = mData.mDoPaintCallbacks;
RefPtr image;
RefPtr surface;
+ Maybe desc;
+
+ {
+ MutexAutoUnlock unlock(mMutex);
+ if (paintCallbacks) {
+ aContext->OnBeforePaintTransaction();
+ }
+
+ desc = aContext->PresentFrontBuffer(nullptr, aTextureType);
+ if (!desc) {
+ surface =
+ aContext->GetFrontBufferSnapshot(/* requireAlphaPremult */ false);
+ if (surface && surface->GetType() == gfx::SurfaceType::WEBGL) {
+ // Ensure we can map in the surface. If we get a SourceSurfaceWebgl
+ // surface, then it may not be backed by raw pixels yet. We need to map
+ // it on the owning thread rather than the ImageBridge thread.
+ gfx::DataSourceSurface::ScopedMap map(
+ static_cast(surface.get()),
+ gfx::DataSourceSurface::READ);
+ if (!map.IsMapped()) {
+ surface = nullptr;
+ }
+ }
+ }
+
+ if (paintCallbacks) {
+ aContext->OnDidPaintTransaction();
+ }
+ }
- Maybe desc =
- aContext->PresentFrontBuffer(nullptr, aTextureType);
if (desc) {
if (desc->type() ==
layers::SurfaceDescriptor::TSurfaceDescriptorRemoteTexture) {
@@ -150,30 +176,10 @@ bool OffscreenCanvasDisplayHelper::CommitFrameToCompositor(
texture, gfx::IntRect(gfx::IntPoint(0, 0), mData.mSize));
}
}
- } else {
- surface = aContext->GetFrontBufferSnapshot(/* requireAlphaPremult */ false);
- if (surface) {
- bool usable = true;
- if (surface->GetType() == gfx::SurfaceType::WEBGL) {
- // Ensure we can map in the surface. If we get a SourceSurfaceWebgl
- // surface, then it may not be backed by raw pixels yet. We need to map
- // it on the owning thread rather than the ImageBridge thread.
- gfx::DataSourceSurface::ScopedMap map(
- static_cast(surface.get()),
- gfx::DataSourceSurface::READ);
- usable = map.IsMapped();
- }
-
- if (usable) {
- auto surfaceImage = MakeRefPtr(surface);
- surfaceImage->SetTextureFlags(flags);
- image = surfaceImage;
- }
- }
- }
-
- if (mData.mDoPaintCallbacks) {
- aContext->OnDidPaintTransaction();
+ } else if (surface) {
+ auto surfaceImage = MakeRefPtr(surface);
+ surfaceImage->SetTextureFlags(flags);
+ image = surfaceImage;
}
if (image) {
@@ -195,8 +201,6 @@ bool OffscreenCanvasDisplayHelper::CommitFrameToCompositor(
}
void OffscreenCanvasDisplayHelper::MaybeQueueInvalidateElement() {
- mMutex.AssertCurrentThreadOwns();
-
if (!mPendingInvalidate) {
mPendingInvalidate = true;
NS_DispatchToMainThread(NS_NewRunnableFunction(
diff --git a/dom/canvas/OffscreenCanvasDisplayHelper.h b/dom/canvas/OffscreenCanvasDisplayHelper.h
index c28f9676b911a..2991f11b4eb8e 100644
--- a/dom/canvas/OffscreenCanvasDisplayHelper.h
+++ b/dom/canvas/OffscreenCanvasDisplayHelper.h
@@ -57,7 +57,7 @@ class OffscreenCanvasDisplayHelper final {
private:
~OffscreenCanvasDisplayHelper();
- void MaybeQueueInvalidateElement();
+ void MaybeQueueInvalidateElement() MOZ_REQUIRES(mMutex);
void InvalidateElement();
bool TransformSurface(const gfx::DataSourceSurface::ScopedMap& aSrcMap,
@@ -65,18 +65,19 @@ class OffscreenCanvasDisplayHelper final {
gfx::SurfaceFormat aFormat, const gfx::IntSize& aSize,
bool aNeedsPremult, gl::OriginPos aOriginPos) const;
- mutable Mutex mMutex MOZ_UNANNOTATED;
- HTMLCanvasElement* MOZ_NON_OWNING_REF mCanvasElement;
- RefPtr mImageContainer;
- RefPtr mFrontBufferSurface;
-
- OffscreenCanvasDisplayData mData;
- CanvasContextType mType = CanvasContextType::NoContext;
- Maybe mContextManagerId;
- Maybe mContextChildId;
- mozilla::layers::ImageContainer::ProducerID mImageProducerID;
- mozilla::layers::ImageContainer::FrameID mLastFrameID = 0;
- bool mPendingInvalidate = false;
+ mutable Mutex mMutex;
+ HTMLCanvasElement* MOZ_NON_OWNING_REF mCanvasElement MOZ_GUARDED_BY(mMutex);
+ RefPtr mImageContainer MOZ_GUARDED_BY(mMutex);
+ RefPtr mFrontBufferSurface MOZ_GUARDED_BY(mMutex);
+
+ OffscreenCanvasDisplayData mData MOZ_GUARDED_BY(mMutex);
+ CanvasContextType mType MOZ_GUARDED_BY(mMutex) = CanvasContextType::NoContext;
+ Maybe mContextManagerId MOZ_GUARDED_BY(mMutex);
+ Maybe mContextChildId MOZ_GUARDED_BY(mMutex);
+ const mozilla::layers::ImageContainer::ProducerID mImageProducerID;
+ mozilla::layers::ImageContainer::FrameID mLastFrameID MOZ_GUARDED_BY(mMutex) =
+ 0;
+ bool mPendingInvalidate MOZ_GUARDED_BY(mMutex) = false;
};
} // namespace mozilla::dom
diff --git a/dom/chrome-webidl/IOUtils.webidl b/dom/chrome-webidl/IOUtils.webidl
index a01e41ddff2e8..27dbd2b10dc4e 100644
--- a/dom/chrome-webidl/IOUtils.webidl
+++ b/dom/chrome-webidl/IOUtils.webidl
@@ -338,8 +338,20 @@ namespace IOUtils {
[Exposed=Window]
partial namespace IOUtils {
+ /**
+ * The async shutdown client for the profile-before-change shutdown phase.
+ */
[Throws]
readonly attribute any profileBeforeChange;
+
+ /**
+ * The async shutdown client for the profile-before-change-telemetry shutdown
+ * phase.
+ *
+ * ONLY telemetry should register blockers on this client.
+ */
+ [Throws]
+ readonly attribute any sendTelemetry;
};
[Exposed=Worker]
diff --git a/dom/media/MediaDevices.cpp b/dom/media/MediaDevices.cpp
index a74a709d24550..c500bab26b69c 100644
--- a/dom/media/MediaDevices.cpp
+++ b/dom/media/MediaDevices.cpp
@@ -6,7 +6,7 @@
#include "AudioDeviceInfo.h"
#include "MediaEngine.h"
-#include "MediaEngineDefault.h"
+#include "MediaEngineFake.h"
#include "mozilla/dom/BrowsingContext.h"
#include "mozilla/dom/Document.h"
#include "mozilla/dom/FeaturePolicyUtils.h"
@@ -226,7 +226,7 @@ RefPtr MediaDevices::FilterExposedDevices(
bool resistFingerprinting = nsContentUtils::ShouldResistFingerprinting(doc);
if (resistFingerprinting) {
- RefPtr fakeEngine = new MediaEngineDefault();
+ RefPtr fakeEngine = new MediaEngineFake();
fakeEngine->EnumerateDevices(MediaSourceEnum::Microphone,
MediaSinkEnum::Other, exposed);
fakeEngine->EnumerateDevices(MediaSourceEnum::Camera, MediaSinkEnum::Other,
diff --git a/dom/media/MediaManager.cpp b/dom/media/MediaManager.cpp
index 1d519a83021f0..e74289fd64b92 100644
--- a/dom/media/MediaManager.cpp
+++ b/dom/media/MediaManager.cpp
@@ -61,7 +61,7 @@
#include "pk11pub.h"
/* Using WebRTC backend on Desktops (Mac, Windows, Linux), otherwise default */
-#include "MediaEngineDefault.h"
+#include "MediaEngineFake.h"
#include "MediaEngineSource.h"
#if defined(MOZ_WEBRTC)
# include "MediaEngineWebRTC.h"
@@ -1829,7 +1829,7 @@ RefPtr MediaManager::EnumerateRawDevices(
// Only enumerate what's asked for, and only fake cams and mics.
RefPtr fakeBackend, realBackend;
if (hasFakeCams || hasFakeMics) {
- fakeBackend = new MediaEngineDefault();
+ fakeBackend = new MediaEngineFake();
}
if (realDeviceRequested) {
MediaManager* manager = MediaManager::GetIfExists();
@@ -3058,7 +3058,7 @@ MediaEngine* MediaManager::GetBackend() {
#if defined(MOZ_WEBRTC)
mBackend = new MediaEngineWebRTC();
#else
- mBackend = new MediaEngineDefault();
+ mBackend = new MediaEngineFake();
#endif
mDeviceListChangeListener = mBackend->DeviceListChangeEvent().Connect(
AbstractThread::MainThread(), this, &MediaManager::DeviceListChanged);
diff --git a/dom/media/gtest/TestGroupId.cpp b/dom/media/gtest/TestGroupId.cpp
index d3417c1ac733e..07c219fa992ea 100644
--- a/dom/media/gtest/TestGroupId.cpp
+++ b/dom/media/gtest/TestGroupId.cpp
@@ -11,7 +11,7 @@
#include "mozilla/Attributes.h"
#include "mozilla/UniquePtr.h"
#include "nsTArray.h"
-#include "webrtc/MediaEngineDefault.h"
+#include "webrtc/MediaEngineFake.h"
using ::testing::Return;
using namespace mozilla;
@@ -36,14 +36,14 @@ RefPtr MakeAudioDeviceInfo(const nsAString& aName,
RefPtr MakeCameraDevice(const nsString& aName,
const nsString& aGroupId) {
- return new MediaDevice(new MediaEngineDefault(), dom::MediaSourceEnum::Camera,
+ return new MediaDevice(new MediaEngineFake(), dom::MediaSourceEnum::Camera,
aName, u""_ns, aGroupId, MediaDevice::IsScary::No);
}
RefPtr MakeMicDevice(const nsString& aName,
const nsString& aGroupId) {
return new MediaDevice(
- new MediaEngineDefault(),
+ new MediaEngineFake(),
MakeAudioDeviceInfo(aName, aGroupId, AudioDeviceInfo::TYPE_INPUT),
u""_ns);
}
@@ -51,7 +51,7 @@ RefPtr MakeMicDevice(const nsString& aName,
RefPtr MakeSpeakerDevice(const nsString& aName,
const nsString& aGroupId) {
return new MediaDevice(
- new MediaEngineDefault(),
+ new MediaEngineFake(),
MakeAudioDeviceInfo(aName, aGroupId, AudioDeviceInfo::TYPE_OUTPUT),
u"ID"_ns);
}
diff --git a/dom/media/webrtc/MediaEngineDefault.cpp b/dom/media/webrtc/MediaEngineFake.cpp
similarity index 87%
rename from dom/media/webrtc/MediaEngineDefault.cpp
rename to dom/media/webrtc/MediaEngineFake.cpp
index 34bbe9b1c0695..6922ca3cc7427 100644
--- a/dom/media/webrtc/MediaEngineDefault.cpp
+++ b/dom/media/webrtc/MediaEngineFake.cpp
@@ -2,7 +2,7 @@
* 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 "MediaEngineDefault.h"
+#include "MediaEngineFake.h"
#include "AudioSegment.h"
#include "DOMMediaStream.h"
@@ -49,7 +49,7 @@ using dom::MediaTrackConstraints;
using dom::MediaTrackSettings;
using dom::VideoFacingModeEnum;
-static nsString DefaultVideoName() {
+static nsString FakeVideoName() {
// For the purpose of testing we allow to change the name of the fake device
// by pref.
nsAutoString cameraNameFromPref;
@@ -76,11 +76,11 @@ static nsString DefaultVideoName() {
}
/**
- * Default video source.
+ * Fake video source.
*/
-class MediaEngineDefaultVideoSource : public MediaEngineSource {
+class MediaEngineFakeVideoSource : public MediaEngineSource {
public:
- MediaEngineDefaultVideoSource();
+ MediaEngineFakeVideoSource();
static nsString GetGroupId();
@@ -104,7 +104,7 @@ class MediaEngineDefaultVideoSource : public MediaEngineSource {
bool IsFake() const override { return true; }
protected:
- ~MediaEngineDefaultVideoSource() = default;
+ ~MediaEngineFakeVideoSource() = default;
/**
* Called by mTimer when it's time to generate a new frame.
@@ -129,7 +129,7 @@ class MediaEngineDefaultVideoSource : public MediaEngineSource {
const RefPtr> mSettings;
};
-MediaEngineDefaultVideoSource::MediaEngineDefaultVideoSource()
+MediaEngineFakeVideoSource::MediaEngineFakeVideoSource()
: mTimer(nullptr),
mSettings(MakeAndAddRef>()) {
mSettings->mWidth.Construct(
@@ -143,11 +143,11 @@ MediaEngineDefaultVideoSource::MediaEngineDefaultVideoSource()
.value));
}
-nsString MediaEngineDefaultVideoSource::GetGroupId() {
- return u"Default Video Group"_ns;
+nsString MediaEngineFakeVideoSource::GetGroupId() {
+ return u"Fake Video Group"_ns;
}
-uint32_t MediaEngineDefaultVideoSource::GetBestFitnessDistance(
+uint32_t MediaEngineFakeVideoSource::GetBestFitnessDistance(
const nsTArray& aConstraintSets) const {
AssertIsOnOwningThread();
@@ -176,13 +176,13 @@ uint32_t MediaEngineDefaultVideoSource::GetBestFitnessDistance(
return uint32_t(std::min(distance, uint64_t(UINT32_MAX)));
}
-void MediaEngineDefaultVideoSource::GetSettings(
+void MediaEngineFakeVideoSource::GetSettings(
MediaTrackSettings& aOutSettings) const {
MOZ_ASSERT(NS_IsMainThread());
aOutSettings = *mSettings;
}
-nsresult MediaEngineDefaultVideoSource::Allocate(
+nsresult MediaEngineFakeVideoSource::Allocate(
const MediaTrackConstraints& aConstraints, const MediaEnginePrefs& aPrefs,
uint64_t aWindowID, const char** aOutBadConstraint) {
AssertIsOnOwningThread();
@@ -228,7 +228,7 @@ nsresult MediaEngineDefaultVideoSource::Allocate(
return NS_OK;
}
-nsresult MediaEngineDefaultVideoSource::Deallocate() {
+nsresult MediaEngineFakeVideoSource::Deallocate() {
AssertIsOnOwningThread();
MOZ_ASSERT(!mImage);
@@ -273,8 +273,8 @@ static void ReleaseFrame(layers::PlanarYCbCrData& aData) {
free(aData.mYChannel);
}
-void MediaEngineDefaultVideoSource::SetTrack(
- const RefPtr& aTrack, const PrincipalHandle& aPrincipal) {
+void MediaEngineFakeVideoSource::SetTrack(const RefPtr& aTrack,
+ const PrincipalHandle& aPrincipal) {
AssertIsOnOwningThread();
MOZ_ASSERT(mState == kAllocated);
@@ -285,7 +285,7 @@ void MediaEngineDefaultVideoSource::SetTrack(
mPrincipalHandle = aPrincipal;
}
-nsresult MediaEngineDefaultVideoSource::Start() {
+nsresult MediaEngineFakeVideoSource::Start() {
AssertIsOnOwningThread();
MOZ_ASSERT(mState == kAllocated || mState == kStopped);
@@ -312,18 +312,18 @@ nsresult MediaEngineDefaultVideoSource::Start() {
#endif
mTimer->InitWithNamedFuncCallback(
[](nsITimer* aTimer, void* aClosure) {
- RefPtr source =
- static_cast(aClosure);
+ RefPtr source =
+ static_cast(aClosure);
source->GenerateFrame();
},
this, interval, nsITimer::TYPE_REPEATING_SLACK,
- "MediaEngineDefaultVideoSource::GenerateFrame");
+ "MediaEngineFakeVideoSource::GenerateFrame");
mState = kStarted;
return NS_OK;
}
-nsresult MediaEngineDefaultVideoSource::Stop() {
+nsresult MediaEngineFakeVideoSource::Stop() {
AssertIsOnOwningThread();
if (mState == kStopped || mState == kAllocated) {
@@ -342,13 +342,13 @@ nsresult MediaEngineDefaultVideoSource::Stop() {
return NS_OK;
}
-nsresult MediaEngineDefaultVideoSource::Reconfigure(
+nsresult MediaEngineFakeVideoSource::Reconfigure(
const MediaTrackConstraints& aConstraints, const MediaEnginePrefs& aPrefs,
const char** aOutBadConstraint) {
return NS_OK;
}
-void MediaEngineDefaultVideoSource::GenerateFrame() {
+void MediaEngineFakeVideoSource::GenerateFrame() {
AssertIsOnOwningThread();
// Update the target color
@@ -429,11 +429,11 @@ class AudioSourcePullListener : public MediaTrackListener {
};
/**
- * Default audio source.
+ * Fake audio source.
*/
-class MediaEngineDefaultAudioSource : public MediaEngineSource {
+class MediaEngineFakeAudioSource : public MediaEngineSource {
public:
- MediaEngineDefaultAudioSource() = default;
+ MediaEngineFakeAudioSource() = default;
static nsString GetUUID();
static nsString GetGroupId();
@@ -455,7 +455,7 @@ class MediaEngineDefaultAudioSource : public MediaEngineSource {
void GetSettings(dom::MediaTrackSettings& aOutSettings) const override;
protected:
- ~MediaEngineDefaultAudioSource() = default;
+ ~MediaEngineFakeAudioSource() = default;
// Current state of this source.
MediaEngineSourceState mState = kReleased;
@@ -465,15 +465,15 @@ class MediaEngineDefaultAudioSource : public MediaEngineSource {
RefPtr mPullListener;
};
-nsString MediaEngineDefaultAudioSource::GetUUID() {
+nsString MediaEngineFakeAudioSource::GetUUID() {
return u"B7CBD7C1-53EF-42F9-8353-73F61C70C092"_ns;
}
-nsString MediaEngineDefaultAudioSource::GetGroupId() {
- return u"Default Audio Group"_ns;
+nsString MediaEngineFakeAudioSource::GetGroupId() {
+ return u"Fake Audio Group"_ns;
}
-void MediaEngineDefaultAudioSource::GetSettings(
+void MediaEngineFakeAudioSource::GetSettings(
MediaTrackSettings& aOutSettings) const {
MOZ_ASSERT(NS_IsMainThread());
aOutSettings.mAutoGainControl.Construct(false);
@@ -482,7 +482,7 @@ void MediaEngineDefaultAudioSource::GetSettings(
aOutSettings.mChannelCount.Construct(1);
}
-nsresult MediaEngineDefaultAudioSource::Allocate(
+nsresult MediaEngineFakeAudioSource::Allocate(
const MediaTrackConstraints& aConstraints, const MediaEnginePrefs& aPrefs,
uint64_t aWindowID, const char** aOutBadConstraint) {
AssertIsOnOwningThread();
@@ -495,7 +495,7 @@ nsresult MediaEngineDefaultAudioSource::Allocate(
return NS_OK;
}
-nsresult MediaEngineDefaultAudioSource::Deallocate() {
+nsresult MediaEngineFakeAudioSource::Deallocate() {
AssertIsOnOwningThread();
MOZ_ASSERT(mState == kStopped || mState == kAllocated);
@@ -509,8 +509,8 @@ nsresult MediaEngineDefaultAudioSource::Deallocate() {
return NS_OK;
}
-void MediaEngineDefaultAudioSource::SetTrack(
- const RefPtr& aTrack, const PrincipalHandle& aPrincipal) {
+void MediaEngineFakeAudioSource::SetTrack(const RefPtr& aTrack,
+ const PrincipalHandle& aPrincipal) {
AssertIsOnOwningThread();
MOZ_ASSERT(mState == kAllocated);
@@ -521,7 +521,7 @@ void MediaEngineDefaultAudioSource::SetTrack(
mPrincipalHandle = aPrincipal;
}
-nsresult MediaEngineDefaultAudioSource::Start() {
+nsresult MediaEngineFakeAudioSource::Start() {
AssertIsOnOwningThread();
if (mState == kStarted) {
@@ -550,7 +550,7 @@ nsresult MediaEngineDefaultAudioSource::Start() {
return NS_OK;
}
-nsresult MediaEngineDefaultAudioSource::Stop() {
+nsresult MediaEngineFakeAudioSource::Stop() {
AssertIsOnOwningThread();
if (mState == kStopped || mState == kAllocated) {
@@ -570,7 +570,7 @@ nsresult MediaEngineDefaultAudioSource::Stop() {
return NS_OK;
}
-nsresult MediaEngineDefaultAudioSource::Reconfigure(
+nsresult MediaEngineFakeAudioSource::Reconfigure(
const MediaTrackConstraints& aConstraints, const MediaEnginePrefs& aPrefs,
const char** aOutBadConstraint) {
return NS_OK;
@@ -594,10 +594,10 @@ void AudioSourcePullListener::NotifyPull(MediaTrackGraph* aGraph,
mTrack->AppendData(&segment);
}
-MediaEngineDefault::MediaEngineDefault() = default;
-MediaEngineDefault::~MediaEngineDefault() = default;
+MediaEngineFake::MediaEngineFake() = default;
+MediaEngineFake::~MediaEngineFake() = default;
-void MediaEngineDefault::EnumerateDevices(
+void MediaEngineFake::EnumerateDevices(
MediaSourceEnum aMediaSource, MediaSinkEnum aMediaSink,
nsTArray>* aDevices) {
AssertIsOnOwningThread();
@@ -609,17 +609,17 @@ void MediaEngineDefault::EnumerateDevices(
switch (aMediaSource) {
case MediaSourceEnum::Camera: {
- nsString name = DefaultVideoName();
+ nsString name = FakeVideoName();
aDevices->EmplaceBack(new MediaDevice(
this, aMediaSource, name, /*aRawId=*/name,
- MediaEngineDefaultVideoSource::GetGroupId(), IsScary::No));
+ MediaEngineFakeVideoSource::GetGroupId(), IsScary::No));
return;
}
case MediaSourceEnum::Microphone:
aDevices->EmplaceBack(new MediaDevice(
this, aMediaSource, u"Default Audio Device"_ns,
- MediaEngineDefaultAudioSource::GetUUID(),
- MediaEngineDefaultAudioSource::GetGroupId(), IsScary::No));
+ MediaEngineFakeAudioSource::GetUUID(),
+ MediaEngineFakeAudioSource::GetGroupId(), IsScary::No));
return;
default:
MOZ_ASSERT_UNREACHABLE("Unsupported source type");
@@ -627,14 +627,14 @@ void MediaEngineDefault::EnumerateDevices(
}
}
-RefPtr MediaEngineDefault::CreateSource(
+RefPtr MediaEngineFake::CreateSource(
const MediaDevice* aMediaDevice) {
MOZ_ASSERT(aMediaDevice->mEngine == this);
switch (aMediaDevice->mMediaSource) {
case MediaSourceEnum::Camera:
- return new MediaEngineDefaultVideoSource();
+ return new MediaEngineFakeVideoSource();
case MediaSourceEnum::Microphone:
- return new MediaEngineDefaultAudioSource();
+ return new MediaEngineFakeAudioSource();
default:
MOZ_ASSERT_UNREACHABLE("Unsupported source type");
return nullptr;
diff --git a/dom/media/webrtc/MediaEngineDefault.h b/dom/media/webrtc/MediaEngineFake.h
similarity index 76%
rename from dom/media/webrtc/MediaEngineDefault.h
rename to dom/media/webrtc/MediaEngineFake.h
index 46cf03b080d2c..611529a067ac0 100644
--- a/dom/media/webrtc/MediaEngineDefault.h
+++ b/dom/media/webrtc/MediaEngineFake.h
@@ -2,8 +2,8 @@
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
* You can obtain one at http://mozilla.org/MPL/2.0/. */
-#ifndef MEDIAENGINEDEFAULT_H_
-#define MEDIAENGINEDEFAULT_H_
+#ifndef MEDIAENGINEFAKE_H_
+#define MEDIAENGINEFAKE_H_
#include "nsTArrayForwardDeclare.h"
#include "MediaEngine.h"
@@ -14,11 +14,11 @@ template
class MediaEventProducer;
/**
- * The default implementation of the MediaEngine interface.
+ * The fake implementation of the MediaEngine interface.
*/
-class MediaEngineDefault : public MediaEngine {
+class MediaEngineFake : public MediaEngine {
public:
- MediaEngineDefault();
+ MediaEngineFake();
void EnumerateDevices(dom::MediaSourceEnum, MediaSinkEnum,
nsTArray>*) override;
@@ -31,10 +31,10 @@ class MediaEngineDefault : public MediaEngine {
bool IsFake() const override { return true; }
private:
- ~MediaEngineDefault();
+ ~MediaEngineFake();
MediaEventProducer mDeviceListChangeEvent;
};
} // namespace mozilla
-#endif /* NSMEDIAENGINEDEFAULT_H_ */
+#endif /* NSMEDIAENGINEFAKE_H_ */
diff --git a/dom/media/webrtc/moz.build b/dom/media/webrtc/moz.build
index a8ca8e5519d9f..ddf9321b58ea5 100644
--- a/dom/media/webrtc/moz.build
+++ b/dom/media/webrtc/moz.build
@@ -30,7 +30,7 @@ with Files("third_party_build/**"):
EXPORTS += [
"CubebDeviceEnumerator.h",
"MediaEngine.h",
- "MediaEngineDefault.h",
+ "MediaEngineFake.h",
"MediaEnginePrefs.h",
"MediaEngineSource.h",
"MediaTrackConstraints.h",
@@ -96,7 +96,7 @@ PREPROCESSED_IPDL_SOURCES += [
]
UNIFIED_SOURCES += [
- "MediaEngineDefault.cpp",
+ "MediaEngineFake.cpp",
"MediaEngineSource.cpp",
"MediaTrackConstraints.cpp",
"PeerIdentity.cpp",
diff --git a/dom/media/webrtc/tests/mochitests/test_peerConnection_trackDisabling.html b/dom/media/webrtc/tests/mochitests/test_peerConnection_trackDisabling.html
index 58a30aa18e15a..73323cf007ba2 100644
--- a/dom/media/webrtc/tests/mochitests/test_peerConnection_trackDisabling.html
+++ b/dom/media/webrtc/tests/mochitests/test_peerConnection_trackDisabling.html
@@ -45,7 +45,7 @@
const remoteVideo = test.pcRemote.remoteMediaElements
.find(e => e instanceof HTMLVideoElement);
// We check a pixel somewhere away from the top left corner since
- // MediaEngineDefault puts semi-transparent time indicators there.
+ // MediaEngineFake puts semi-transparent time indicators there.
const offsetX = 50;
const offsetY = 50;
const threshold = 128;
diff --git a/dom/media/webrtc/tests/mochitests/test_peerConnection_trackDisabling_clones.html b/dom/media/webrtc/tests/mochitests/test_peerConnection_trackDisabling_clones.html
index 460f578cad46e..ae7647fa1a3f2 100644
--- a/dom/media/webrtc/tests/mochitests/test_peerConnection_trackDisabling_clones.html
+++ b/dom/media/webrtc/tests/mochitests/test_peerConnection_trackDisabling_clones.html
@@ -65,7 +65,7 @@
.find(e => e instanceof HTMLVideoElement);
// We check a pixel somewhere away from the top left corner since
- // MediaEngineDefault puts semi-transparent time indicators there.
+ // MediaEngineFake puts semi-transparent time indicators there.
const offsetX = 50;
const offsetY = 50;
const threshold = 128;
diff --git a/dom/moz.build b/dom/moz.build
index f26f9203d2870..0cfc381066a5c 100644
--- a/dom/moz.build
+++ b/dom/moz.build
@@ -8,9 +8,6 @@
with Files("**"):
BUG_COMPONENT = ("Core", "DOM: Core & HTML")
-with Files("plugins/**"):
- BUG_COMPONENT = ("Core", "Plug-ins")
-
JAR_MANIFESTS += ["jar.mn"]
interfaces = [
diff --git a/dom/system/IOUtils.cpp b/dom/system/IOUtils.cpp
index 08a68800c2779..f81bd4079b143 100644
--- a/dom/system/IOUtils.cpp
+++ b/dom/system/IOUtils.cpp
@@ -1999,8 +1999,35 @@ Result IOUtils::DelMacXAttrSync(nsIFile* aFile,
void IOUtils::GetProfileBeforeChange(GlobalObject& aGlobal,
JS::MutableHandle aClient,
ErrorResult& aRv) {
+ return GetShutdownClient(aGlobal, aClient, aRv,
+ ShutdownPhase::ProfileBeforeChange);
+}
+
+/* static */
+void IOUtils::GetSendTelemetry(GlobalObject& aGlobal,
+ JS::MutableHandle aClient,
+ ErrorResult& aRv) {
+ return GetShutdownClient(aGlobal, aClient, aRv, ShutdownPhase::SendTelemetry);
+}
+
+/**
+ * Assert that the given phase has a shutdown client exposed by IOUtils
+ *
+ * There is no shutdown client exposed for XpcomWillShutdown.
+ */
+static void AssertHasShutdownClient(const IOUtils::ShutdownPhase aPhase) {
+ MOZ_RELEASE_ASSERT(aPhase >= IOUtils::ShutdownPhase::ProfileBeforeChange &&
+ aPhase < IOUtils::ShutdownPhase::XpcomWillShutdown);
+}
+
+/* static */
+void IOUtils::GetShutdownClient(GlobalObject& aGlobal,
+ JS::MutableHandle aClient,
+ ErrorResult& aRv,
+ const IOUtils::ShutdownPhase aPhase) {
MOZ_RELEASE_ASSERT(XRE_IsParentProcess());
MOZ_RELEASE_ASSERT(NS_IsMainThread());
+ AssertHasShutdownClient(aPhase);
if (auto state = GetState()) {
MOZ_RELEASE_ASSERT(state.ref()->mBlockerStatus !=
@@ -2013,7 +2040,7 @@ void IOUtils::GetProfileBeforeChange(GlobalObject& aGlobal,
MOZ_RELEASE_ASSERT(state.ref()->mBlockerStatus ==
ShutdownBlockerStatus::Initialized);
- auto result = state.ref()->mEventQueue->GetProfileBeforeChangeClient();
+ auto result = state.ref()->mEventQueue->GetShutdownClient(aPhase);
if (result.isErr()) {
aRv.ThrowAbortError("IOUtils: could not get shutdown client");
return;
@@ -2081,36 +2108,122 @@ void IOUtils::State::SetShutdownHooks() {
nsresult IOUtils::EventQueue::SetShutdownHooks() {
MOZ_RELEASE_ASSERT(NS_IsMainThread());
+ constexpr static auto STACK = u"IOUtils::EventQueue::SetShutdownHooks"_ns;
+ constexpr static auto FILE = NS_LITERAL_STRING_FROM_CSTRING(__FILE__);
+
nsCOMPtr svc = services::GetAsyncShutdownService();
if (!svc) {
return NS_ERROR_NOT_AVAILABLE;
}
- nsCOMPtr blocker = new IOUtilsShutdownBlocker(
- IOUtilsShutdownBlocker::Phase::ProfileBeforeChange);
+ nsCOMPtr profileBeforeChangeBlocker;
+
+ // Create a shutdown blocker for the profile-before-change phase.
+ {
+ profileBeforeChangeBlocker =
+ new IOUtilsShutdownBlocker(ShutdownPhase::ProfileBeforeChange);
+
+ nsCOMPtr globalClient;
+ MOZ_TRY(svc->GetProfileBeforeChange(getter_AddRefs(globalClient)));
+ MOZ_RELEASE_ASSERT(globalClient);
+
+ MOZ_TRY(globalClient->AddBlocker(profileBeforeChangeBlocker, FILE, __LINE__,
+ STACK));
+ }
+
+ // Create the shutdown barrier for profile-before-change so that consumers can
+ // register shutdown blockers.
+ //
+ // The blocker we just created will wait for all clients registered on this
+ // barrier to finish.
+ {
+ nsCOMPtr barrier;
+
+ // It is okay for this to fail. The created shutdown blocker won't await
+ // anything and shutdown will proceed.
+ MOZ_TRY(svc->MakeBarrier(
+ u"IOUtils: waiting for profileBeforeChange IO to complete"_ns,
+ getter_AddRefs(barrier)));
+ MOZ_RELEASE_ASSERT(barrier);
+
+ mBarriers[ShutdownPhase::ProfileBeforeChange] = std::move(barrier);
+ }
+
+ // Create a shutdown blocker for the profile-before-change-telemetry phase.
+ nsCOMPtr sendTelemetryBlocker;
+ {
+ sendTelemetryBlocker =
+ new IOUtilsShutdownBlocker(ShutdownPhase::SendTelemetry);
+
+ nsCOMPtr globalClient;
+ MOZ_TRY(svc->GetSendTelemetry(getter_AddRefs(globalClient)));
+ MOZ_RELEASE_ASSERT(globalClient);
+
+ MOZ_TRY(
+ globalClient->AddBlocker(sendTelemetryBlocker, FILE, __LINE__, STACK));
+ }
+
+ // Create the shutdown barrier for profile-before-change-telemetry so that
+ // consumers can register shutdown blockers.
+ //
+ // The blocker we just created will wait for all clients registered on this
+ // barrier to finish.
+ {
+ nsCOMPtr barrier;
+
+ MOZ_TRY(svc->MakeBarrier(
+ u"IOUtils: waiting for sendTelemetry IO to complete"_ns,
+ getter_AddRefs(barrier)));
+ MOZ_RELEASE_ASSERT(barrier);
+
+ // Add a blocker on the previous shutdown phase.
+ nsCOMPtr client;
+ MOZ_TRY(barrier->GetClient(getter_AddRefs(client)));
+
+ MOZ_TRY(
+ client->AddBlocker(profileBeforeChangeBlocker, FILE, __LINE__, STACK));
+
+ mBarriers[ShutdownPhase::SendTelemetry] = std::move(barrier);
+ }
+
+ // Create a shutdown blocker for the xpcom-will-shutdown phase.
+ {
+ nsCOMPtr globalClient;
+ MOZ_TRY(svc->GetXpcomWillShutdown(getter_AddRefs(globalClient)));
+ MOZ_RELEASE_ASSERT(globalClient);
- nsCOMPtr profileBeforeChange;
- MOZ_TRY(svc->GetProfileBeforeChange(getter_AddRefs(profileBeforeChange)));
- MOZ_RELEASE_ASSERT(profileBeforeChange);
+ nsCOMPtr blocker =
+ new IOUtilsShutdownBlocker(ShutdownPhase::XpcomWillShutdown);
+ MOZ_TRY(globalClient->AddBlocker(
+ blocker, FILE, __LINE__, u"IOUtils::EventQueue::SetShutdownHooks"_ns));
+ }
- MOZ_TRY(profileBeforeChange->AddBlocker(
- blocker, NS_LITERAL_STRING_FROM_CSTRING(__FILE__), __LINE__,
- u"IOUtils::EventQueue::SetShutdownHooks"_ns));
+ // Create a shutdown barrier for the xpcom-will-shutdown phase.
+ //
+ // The blocker we just created will wait for all clients registered on this
+ // barrier to finish.
+ //
+ // The only client registered on this barrier should be a blocker for the
+ // previous phase. This is to ensure that all shutdown IO happens when
+ // shutdown phases do not happen (e.g., in xpcshell tests where
+ // profile-before-change does not occur).
+ {
+ nsCOMPtr barrier;
+
+ MOZ_TRY(svc->MakeBarrier(
+ u"IOUtils: waiting for xpcomWillShutdown IO to complete"_ns,
+ getter_AddRefs(barrier)));
+ MOZ_RELEASE_ASSERT(barrier);
- nsCOMPtr xpcomWillShutdown;
- MOZ_TRY(svc->GetXpcomWillShutdown(getter_AddRefs(xpcomWillShutdown)));
- MOZ_RELEASE_ASSERT(xpcomWillShutdown);
+ // Add a blocker on the previous shutdown phase.
+ nsCOMPtr client;
+ MOZ_TRY(barrier->GetClient(getter_AddRefs(client)));
- blocker = new IOUtilsShutdownBlocker(
- IOUtilsShutdownBlocker::Phase::XpcomWillShutdown);
- MOZ_TRY(xpcomWillShutdown->AddBlocker(
- blocker, NS_LITERAL_STRING_FROM_CSTRING(__FILE__), __LINE__,
- u"IOUtils::EventQueue::SetShutdownHooks"_ns));
+ client->AddBlocker(sendTelemetryBlocker, FILE, __LINE__,
+ u"IOUtils::EventQueue::SetShutdownHooks"_ns);
- MOZ_TRY(svc->MakeBarrier(
- u"IOUtils: waiting for profileBeforeChange IO to complete"_ns,
- getter_AddRefs(mProfileBeforeChangeBarrier)));
- MOZ_RELEASE_ASSERT(mProfileBeforeChangeBarrier);
+ mBarriers[ShutdownPhase::XpcomWillShutdown] = std::move(barrier);
+ }
return NS_OK;
}
@@ -2135,25 +2248,27 @@ RefPtr> IOUtils::EventQueue::Dispatch(Fn aFunc) {
return promise;
};
-Result, nsresult>
-IOUtils::EventQueue::GetProfileBeforeChangeClient() {
- if (!mProfileBeforeChangeBarrier) {
+Result, nsresult>
+IOUtils::EventQueue::GetShutdownBarrier(const IOUtils::ShutdownPhase aPhase) {
+ if (!mBarriers[aPhase]) {
return Err(NS_ERROR_NOT_AVAILABLE);
}
- nsCOMPtr profileBeforeChange;
- MOZ_TRY(mProfileBeforeChangeBarrier->GetClient(
- getter_AddRefs(profileBeforeChange)));
- return profileBeforeChange.forget();
+ return do_AddRef(mBarriers[aPhase]);
}
-Result, nsresult>
-IOUtils::EventQueue::GetProfileBeforeChangeBarrier() {
- if (!mProfileBeforeChangeBarrier) {
+Result, nsresult>
+IOUtils::EventQueue::GetShutdownClient(const IOUtils::ShutdownPhase aPhase) {
+ AssertHasShutdownClient(aPhase);
+
+ if (!mBarriers[aPhase]) {
return Err(NS_ERROR_NOT_AVAILABLE);
}
- return do_AddRef(mProfileBeforeChangeBarrier);
+ nsCOMPtr client;
+ MOZ_TRY(mBarriers[aPhase]->GetClient(getter_AddRefs(client)));
+
+ return do_AddRef(client);
}
/* static */
@@ -2247,27 +2362,16 @@ NS_IMPL_ISUPPORTS(IOUtilsShutdownBlocker, nsIAsyncShutdownBlocker,
NS_IMETHODIMP IOUtilsShutdownBlocker::GetName(nsAString& aName) {
aName = u"IOUtils Blocker ("_ns;
-
- switch (mPhase) {
- case Phase::ProfileBeforeChange:
- aName.Append(u"profile-before-change"_ns);
- break;
-
- case Phase::XpcomWillShutdown:
- aName.Append(u"xpcom-will-shutdown"_ns);
- break;
-
- default:
- MOZ_CRASH("Unknown shutdown phase");
- }
-
+ aName.Append(PHASE_NAMES[mPhase]);
aName.Append(')');
+
return NS_OK;
}
NS_IMETHODIMP IOUtilsShutdownBlocker::BlockShutdown(
nsIAsyncShutdownClient* aBarrierClient) {
using EventQueueStatus = IOUtils::EventQueueStatus;
+ using ShutdownPhase = IOUtils::ShutdownPhase;
MOZ_RELEASE_ASSERT(NS_IsMainThread());
@@ -2276,10 +2380,10 @@ NS_IMETHODIMP IOUtilsShutdownBlocker::BlockShutdown(
{
auto state = IOUtils::sState.Lock();
if (state->mQueueStatus == EventQueueStatus::Shutdown) {
- // If the blocker for profile-before-change has already run, then the
- // event queue is already torn down and we have nothing to do.
+ // If the previous blockers have already run, then the event queue is
+ // already torn down and we have nothing to do.
- MOZ_RELEASE_ASSERT(mPhase == Phase::XpcomWillShutdown);
+ MOZ_RELEASE_ASSERT(mPhase == ShutdownPhase::XpcomWillShutdown);
MOZ_RELEASE_ASSERT(!state->mEventQueue);
Unused << NS_WARN_IF(NS_FAILED(aBarrierClient->RemoveBlocker(this)));
@@ -2292,8 +2396,7 @@ NS_IMETHODIMP IOUtilsShutdownBlocker::BlockShutdown(
mParentClient = aBarrierClient;
- barrier =
- state->mEventQueue->GetProfileBeforeChangeBarrier().unwrapOr(nullptr);
+ barrier = state->mEventQueue->GetShutdownBarrier(mPhase).unwrapOr(nullptr);
}
// We cannot barrier->Wait() while holding the mutex because it will lead to
@@ -2312,34 +2415,63 @@ NS_IMETHODIMP IOUtilsShutdownBlocker::BlockShutdown(
NS_IMETHODIMP IOUtilsShutdownBlocker::Done() {
using EventQueueStatus = IOUtils::EventQueueStatus;
+ using ShutdownPhase = IOUtils::ShutdownPhase;
MOZ_RELEASE_ASSERT(NS_IsMainThread());
- auto state = IOUtils::sState.Lock();
- MOZ_RELEASE_ASSERT(state->mEventQueue);
+ bool didFlush = false;
- // This method is called once we have served all shutdown clients. Now we
- // flush the remaining IO queue and forbid additional IO requests.
- state->mEventQueue->Dispatch([]() { return Ok{}; })
- ->Then(GetMainThreadSerialEventTarget(), __func__,
- [self = RefPtr(this)]() {
- if (self->mParentClient) {
- Unused << NS_WARN_IF(
- NS_FAILED(self->mParentClient->RemoveBlocker(self)));
- self->mParentClient = nullptr;
+ {
+ auto state = IOUtils::sState.Lock();
+
+ if (state->mEventQueue) {
+ MOZ_RELEASE_ASSERT(state->mQueueStatus == EventQueueStatus::Initialized);
+
+ // This method is called once we have served all shutdown clients. Now we
+ // flush the remaining IO queue. This ensures any straggling IO that was
+ // not part of the shutdown blocker finishes before we move to the next
+ // phase.
+ state->mEventQueue->Dispatch([]() { return Ok{}; })
+ ->Then(GetMainThreadSerialEventTarget(), __func__,
+ [self = RefPtr(this)]() { self->OnFlush(); });
+
+ // And if we're the last shutdown phase to allow IO, disable the event
+ // queue to disallow further IO requests.
+ if (mPhase >= LAST_IO_PHASE) {
+ state->mQueueStatus = EventQueueStatus::Shutdown;
+ }
- auto state = IOUtils::sState.Lock();
- MOZ_RELEASE_ASSERT(state->mEventQueue);
- state->mEventQueue = nullptr;
- }
- });
+ didFlush = true;
+ }
+ }
- MOZ_RELEASE_ASSERT(state->mQueueStatus == EventQueueStatus::Initialized);
- state->mQueueStatus = EventQueueStatus::Shutdown;
+ // If we have already shut down the event loop, then call OnFlush to stop
+ // blocking our parent shutdown client.
+ if (!didFlush) {
+ MOZ_RELEASE_ASSERT(mPhase == ShutdownPhase::XpcomWillShutdown);
+ OnFlush();
+ }
return NS_OK;
}
+void IOUtilsShutdownBlocker::OnFlush() {
+ if (mParentClient) {
+ (void)NS_WARN_IF(NS_FAILED(mParentClient->RemoveBlocker(this)));
+ mParentClient = nullptr;
+
+ // If we are past the last shutdown phase that allows IO,
+ // we can shutdown the event queue here because no additional IO requests
+ // will be allowed (see |Done()|).
+ if (mPhase >= LAST_IO_PHASE) {
+ auto state = IOUtils::sState.Lock();
+ if (state->mEventQueue) {
+ state->mEventQueue = nullptr;
+ }
+ }
+ }
+}
+
NS_IMETHODIMP IOUtilsShutdownBlocker::GetState(nsIPropertyBag** aState) {
return NS_OK;
}
diff --git a/dom/system/IOUtils.h b/dom/system/IOUtils.h
index cb0a28d34a57e..514a20878f6c3 100644
--- a/dom/system/IOUtils.h
+++ b/dom/system/IOUtils.h
@@ -60,6 +60,17 @@ class IOUtils final {
public:
class IOError;
+ enum class ShutdownPhase : uint8_t {
+ ProfileBeforeChange,
+ SendTelemetry,
+ XpcomWillShutdown,
+ Count,
+ };
+
+ template
+ using PhaseArray =
+ EnumeratedArray;
+
static already_AddRefed Read(GlobalObject& aGlobal,
const nsAString& aPath,
const ReadOptions& aOptions,
@@ -201,6 +212,9 @@ class IOUtils final {
JS::MutableHandle,
ErrorResult& aRv);
+ static void GetSendTelemetry(GlobalObject& aGlobal,
+ JS::MutableHandle, ErrorResult& aRv);
+
static RefPtr OpenFileForSyncReading(GlobalObject& aGlobal,
const nsAString& aPath,
ErrorResult& aRv);
@@ -506,6 +520,10 @@ class IOUtils final {
const nsCString& aAttr);
#endif
+ static void GetShutdownClient(GlobalObject& aGlobal,
+ JS::MutableHandle aClient,
+ ErrorResult& aRv, const ShutdownPhase aPhase);
+
enum class EventQueueStatus {
Uninitialized,
Initialized,
@@ -583,17 +601,16 @@ class IOUtils::EventQueue final {
template
RefPtr> Dispatch(Fn aFunc);
- Result, nsresult>
- GetProfileBeforeChangeClient();
-
Result, nsresult>
- GetProfileBeforeChangeBarrier();
+ GetShutdownBarrier(const ShutdownPhase aPhase);
+ Result, nsresult> GetShutdownClient(
+ const ShutdownPhase aPhase);
private:
nsresult SetShutdownHooks();
nsCOMPtr mBackgroundEventTarget;
- nsCOMPtr mProfileBeforeChangeBarrier;
+ IOUtils::PhaseArray> mBarriers;
};
/**
@@ -713,18 +730,28 @@ class IOUtilsShutdownBlocker : public nsIAsyncShutdownBlocker,
NS_DECL_NSIASYNCSHUTDOWNBLOCKER
NS_DECL_NSIASYNCSHUTDOWNCOMPLETIONCALLBACK
- enum Phase {
- ProfileBeforeChange,
- XpcomWillShutdown,
- };
-
- explicit IOUtilsShutdownBlocker(Phase aPhase) : mPhase(aPhase) {}
+ explicit IOUtilsShutdownBlocker(const IOUtils::ShutdownPhase aPhase)
+ : mPhase(aPhase) {}
private:
virtual ~IOUtilsShutdownBlocker() = default;
- Phase mPhase;
- RefPtr mParentClient;
+ /**
+ * Called on the main thread after the event queue has been flushed.
+ */
+ void OnFlush();
+
+ static constexpr IOUtils::PhaseArray PHASE_NAMES{
+ u"profile-before-change",
+ u"profile-before-change-telemetry",
+ u"xpcom-will-shutdown",
+ };
+
+ // The last shutdown phase before we should shut down the event loop.
+ static constexpr auto LAST_IO_PHASE = IOUtils::ShutdownPhase::SendTelemetry;
+
+ IOUtils::ShutdownPhase mPhase;
+ nsCOMPtr mParentClient;
};
/**
diff --git a/dom/tests/mochitest/general/test_interfaces.js b/dom/tests/mochitest/general/test_interfaces.js
index 34d1f08ddd44f..e34567c64de3c 100644
--- a/dom/tests/mochitest/general/test_interfaces.js
+++ b/dom/tests/mochitest/general/test_interfaces.js
@@ -1405,6 +1405,12 @@ let interfaceNamesInGlobalScope = [
// IMPORTANT: Do not change this list without review from a DOM peer!
{ name: "alert", insecureContext: true },
// IMPORTANT: Do not change this list without review from a DOM peer!
+ {
+ name: "applicationCache",
+ insecureContext: false,
+ disabled: isEarlyBetaOrEarlier,
+ },
+ // IMPORTANT: Do not change this list without review from a DOM peer!
{ name: "atob", insecureContext: true },
// IMPORTANT: Do not change this list without review from a DOM peer!
{ name: "blur", insecureContext: true },
diff --git a/dom/webidl/XULElement.webidl b/dom/webidl/XULElement.webidl
index a2911f838e312..ececa60104098 100644
--- a/dom/webidl/XULElement.webidl
+++ b/dom/webidl/XULElement.webidl
@@ -11,10 +11,6 @@ interface XULControllers;
interface XULElement : Element {
[HTMLConstructor] constructor();
- // Layout properties
- [SetterThrows]
- attribute DOMString flex;
-
// Properties for hiding elements.
attribute boolean hidden;
attribute boolean collapsed;
diff --git a/dom/webidl/moz.build b/dom/webidl/moz.build
index 619972c4c2285..f25d3124bc07d 100644
--- a/dom/webidl/moz.build
+++ b/dom/webidl/moz.build
@@ -106,9 +106,6 @@ with Files("DelayNode.webidl"):
with Files("DynamicsCompressorNode.webidl"):
BUG_COMPONENT = ("Core", "Web Audio")
-with Files("FakePluginTagInit.webidl"):
- BUG_COMPONENT = ("Core", "Plug-ins")
-
with Files("FeaturePolicy.webidl"):
BUG_COMPONENT = ("Core", "DOM: Security")
diff --git a/dom/xml/crashtests/382636-4.xhtml b/dom/xml/crashtests/382636-4.xhtml
index 9907973c00576..e1c65b9030458 100644
--- a/dom/xml/crashtests/382636-4.xhtml
+++ b/dom/xml/crashtests/382636-4.xhtml
@@ -1,5 +1,5 @@
-
+
diff --git a/dom/xslt/tests/XSLTMark/XSLTMark.xhtml b/dom/xslt/tests/XSLTMark/XSLTMark.xhtml
index 152704460670b..0be9a10fb66c1 100644
--- a/dom/xslt/tests/XSLTMark/XSLTMark.xhtml
+++ b/dom/xslt/tests/XSLTMark/XSLTMark.xhtml
@@ -40,14 +40,14 @@
-
-
-
+
+
+
-
-
+
+
-
-
+
+
diff --git a/dom/xul/XULTooltipElement.cpp b/dom/xul/XULTooltipElement.cpp
index c604a5a639b4f..f18ab92304d5c 100644
--- a/dom/xul/XULTooltipElement.cpp
+++ b/dom/xul/XULTooltipElement.cpp
@@ -40,7 +40,6 @@ nsresult XULTooltipElement::Init() {
NS_ENSURE_SUCCESS(rv, rv);
description->SetAttr(kNameSpaceID_None, nsGkAtoms::_class,
u"tooltip-label"_ns, false);
- description->SetAttr(kNameSpaceID_None, nsGkAtoms::flex, u"true"_ns, false);
ErrorResult error;
AppendChild(*description, error);
diff --git a/dom/xul/nsXULElement.h b/dom/xul/nsXULElement.h
index b6d0aaab2bbcf..63514f4e656cb 100644
--- a/dom/xul/nsXULElement.h
+++ b/dom/xul/nsXULElement.h
@@ -398,10 +398,6 @@ class nsXULElement : public nsStyledElement {
}
// WebIDL API
- void GetFlex(DOMString& aValue) const { GetXULAttr(nsGkAtoms::flex, aValue); }
- void SetFlex(const nsAString& aValue, mozilla::ErrorResult& rv) {
- SetXULAttr(nsGkAtoms::flex, aValue, rv);
- }
bool Hidden() const { return BoolAttrIsTrue(nsGkAtoms::hidden); }
void SetHidden(bool aHidden) { SetXULBoolAttr(nsGkAtoms::hidden, aHidden); }
bool Collapsed() const { return BoolAttrIsTrue(nsGkAtoms::collapsed); }
diff --git a/gfx/thebes/gfxPlatform.cpp b/gfx/thebes/gfxPlatform.cpp
index f842f28c3fcfa..b71bb793e88b7 100644
--- a/gfx/thebes/gfxPlatform.cpp
+++ b/gfx/thebes/gfxPlatform.cpp
@@ -1800,29 +1800,48 @@ bool gfxPlatform::UseGraphiteShaping() {
}
bool gfxPlatform::IsFontFormatSupported(
- StyleFontFaceSourceFormatKeyword aHint) {
- switch (aHint) {
+ StyleFontFaceSourceFormatKeyword aFormatHint,
+ StyleFontFaceSourceTechFlags aTechFlags) {
+ // By default, font resources are assumed to be supported; but if the format
+ // hint or technology flags explicitly indicate something we don't support,
+ // then return false.
+ switch (aFormatHint) {
case StyleFontFaceSourceFormatKeyword::None:
- return true;
+ break;
case StyleFontFaceSourceFormatKeyword::Collection:
return false;
case StyleFontFaceSourceFormatKeyword::Opentype:
case StyleFontFaceSourceFormatKeyword::Truetype:
- return true;
+ break;
case StyleFontFaceSourceFormatKeyword::EmbeddedOpentype:
return false;
case StyleFontFaceSourceFormatKeyword::Svg:
return false;
case StyleFontFaceSourceFormatKeyword::Woff:
- return true;
+ break;
case StyleFontFaceSourceFormatKeyword::Woff2:
- return true;
+ break;
case StyleFontFaceSourceFormatKeyword::Unknown:
return false;
default:
MOZ_ASSERT_UNREACHABLE("bad format hint!");
return false;
}
+ StyleFontFaceSourceTechFlags unsupportedTechnologies =
+ StyleFontFaceSourceTechFlags::INCREMENTAL |
+ StyleFontFaceSourceTechFlags::PALETTES |
+ StyleFontFaceSourceTechFlags::COLOR_COLRV1 |
+ StyleFontFaceSourceTechFlags::COLOR_SBIX;
+ if (!StaticPrefs::gfx_downloadable_fonts_keep_color_bitmaps()) {
+ unsupportedTechnologies |= StyleFontFaceSourceTechFlags::COLOR_CBDT;
+ }
+ if (!StaticPrefs::layout_css_font_variations_enabled()) {
+ unsupportedTechnologies |= StyleFontFaceSourceTechFlags::VARIATIONS;
+ }
+ if (aTechFlags & unsupportedTechnologies) {
+ return false;
+ }
+ return true;
}
gfxFontGroup* gfxPlatform::CreateFontGroup(
diff --git a/gfx/thebes/gfxPlatform.h b/gfx/thebes/gfxPlatform.h
index 378853ca8a7b8..2aded032b1916 100644
--- a/gfx/thebes/gfxPlatform.h
+++ b/gfx/thebes/gfxPlatform.h
@@ -498,7 +498,8 @@ class gfxPlatform : public mozilla::layers::MemoryPressureListener {
// Default implementation checks for "common" formats that we support across
// all platforms, but individual platform implementations may override.
virtual bool IsFontFormatSupported(
- mozilla::StyleFontFaceSourceFormatKeyword aFormatHint);
+ mozilla::StyleFontFaceSourceFormatKeyword aFormatHint,
+ mozilla::StyleFontFaceSourceTechFlags aTechFlags);
virtual bool DidRenderingDeviceReset(
DeviceResetReason* aResetReason = nullptr) {
diff --git a/gfx/thebes/gfxUserFontSet.cpp b/gfx/thebes/gfxUserFontSet.cpp
index 7d641a1fc4ad4..d6ad888190cb6 100644
--- a/gfx/thebes/gfxUserFontSet.cpp
+++ b/gfx/thebes/gfxUserFontSet.cpp
@@ -231,6 +231,7 @@ void gfxUserFontEntry::StoreUserFontData(gfxFontEntry* aFontEntry,
break;
}
userFontData->mPrivate = aPrivate;
+ userFontData->mTechFlags = src.mTechFlags;
userFontData->mFormatHint = src.mFormatHint;
userFontData->mRealName = aOriginalName;
if (aMetadata) {
@@ -469,7 +470,7 @@ void gfxUserFontEntry::DoLoadNextSrc(bool aForceAsync) {
// src url ==> start the load process
else if (currSrc.mSourceType == gfxFontFaceSrc::eSourceType_URL) {
if (gfxPlatform::GetPlatform()->IsFontFormatSupported(
- currSrc.mFormatHint)) {
+ currSrc.mFormatHint, currSrc.mTechFlags)) {
if (ServoStyleSet* set = gfxFontUtils::CurrentServoStyleSet()) {
// Only support style worker threads synchronously getting
// entries from the font cache when it's not a data: URI
diff --git a/gfx/thebes/gfxUserFontSet.h b/gfx/thebes/gfxUserFontSet.h
index fef0b2e775ab4..3e01371d90414 100644
--- a/gfx/thebes/gfxUserFontSet.h
+++ b/gfx/thebes/gfxUserFontSet.h
@@ -77,6 +77,9 @@ struct gfxFontFaceSrc {
// if url, whether to use the origin principal or not
bool mUseOriginPrincipal = false;
+ // Required font technologies.
+ mozilla::StyleFontFaceSourceTechFlags mTechFlags;
+
// Format hint, if any was specified.
mozilla::StyleFontFaceSourceFormatKeyword mFormatHint;
@@ -113,7 +116,7 @@ inline bool operator==(const gfxFontFaceSrc& a, const gfxFontFaceSrc& b) {
return false;
}
bool equals;
- return a.mFormatHint == b.mFormatHint &&
+ return a.mFormatHint == b.mFormatHint && a.mTechFlags == b.mTechFlags &&
(a.mURI == b.mURI || a.mURI->Equals(b.mURI)) &&
NS_SUCCEEDED(a.mReferrerInfo->Equals(b.mReferrerInfo, &equals)) &&
equals;
@@ -136,6 +139,7 @@ class gfxUserFontData {
gfxUserFontData()
: mSrcIndex(0),
mMetaOrigLen(0),
+ mTechFlags(mozilla::StyleFontFaceSourceTechFlags::Empty()),
mFormatHint(mozilla::StyleFontFaceSourceFormatKeyword::None),
mCompression(kUnknownCompression),
mPrivate(false),
@@ -152,6 +156,7 @@ class gfxUserFontData {
nsCString mRealName; // original fullname from the font resource
uint32_t mSrcIndex; // index in the rule's source list
uint32_t mMetaOrigLen; // length needed to decompress metadata
+ mozilla::StyleFontFaceSourceTechFlags mTechFlags; // required font tech
mozilla::StyleFontFaceSourceFormatKeyword
mFormatHint; // format hint for the source used, if any
uint8_t mCompression; // compression type
diff --git a/image/encoders/webp/nsWebPEncoder.cpp b/image/encoders/webp/nsWebPEncoder.cpp
index 38c4f2ce4c288..6fdbf89bca64b 100644
--- a/image/encoders/webp/nsWebPEncoder.cpp
+++ b/image/encoders/webp/nsWebPEncoder.cpp
@@ -104,11 +104,15 @@ nsWebPEncoder::InitFromData(const uint8_t* aData,
size_t size = 0;
if (aInputFormat == INPUT_FORMAT_RGB) {
- size =
- WebPEncodeRGB(aData, aWidth, aHeight, aStride, quality, &mImageBuffer);
+ size = quality == 100 ? WebPEncodeLosslessRGB(aData, aWidth, aHeight,
+ aStride, &mImageBuffer)
+ : WebPEncodeRGB(aData, aWidth, aHeight, aStride,
+ quality, &mImageBuffer);
} else if (aInputFormat == INPUT_FORMAT_RGBA) {
- size =
- WebPEncodeRGBA(aData, aWidth, aHeight, aStride, quality, &mImageBuffer);
+ size = quality == 100 ? WebPEncodeLosslessRGBA(aData, aWidth, aHeight,
+ aStride, &mImageBuffer)
+ : WebPEncodeRGBA(aData, aWidth, aHeight, aStride,
+ quality, &mImageBuffer);
} else if (aInputFormat == INPUT_FORMAT_HOSTARGB) {
UniquePtr aDest = MakeUnique(aStride * aHeight);
@@ -135,8 +139,10 @@ nsWebPEncoder::InitFromData(const uint8_t* aData,
}
}
- size = WebPEncodeRGBA(aDest.get(), aWidth, aHeight, aStride, quality,
- &mImageBuffer);
+ size = quality == 100 ? WebPEncodeLosslessRGBA(aDest.get(), aWidth, aHeight,
+ aStride, &mImageBuffer)
+ : WebPEncodeRGBA(aDest.get(), aWidth, aHeight,
+ aStride, quality, &mImageBuffer);
}
mFinished = true;
diff --git a/image/test/reftest/encoders-lossless/reftest.list b/image/test/reftest/encoders-lossless/reftest.list
index 6cd96d0de2e54..fe8fbc5bc56f3 100644
--- a/image/test/reftest/encoders-lossless/reftest.list
+++ b/image/test/reftest/encoders-lossless/reftest.list
@@ -157,3 +157,19 @@ HTTP == size-32x32.png encoder.html?img=size-32x32.png&mime=image/vnd.microsoft.
HTTP == size-33x33.png encoder.html?img=size-33x33.png&mime=image/vnd.microsoft.icon&options=-moz-parse-options%3Aformat%3Dpng
HTTP == size-256x256.png encoder.html?img=size-256x256.png&mime=image/vnd.microsoft.icon&options=-moz-parse-options%3Aformat%3Dpng
+# WEBP
+HTTP == size-1x1.png encoder.html?img=size-1x1.png&mime=image/webp&options=1
+HTTP == size-2x2.png encoder.html?img=size-2x2.png&mime=image/webp&options=1
+HTTP == size-3x3.png encoder.html?img=size-3x3.png&mime=image/webp&options=1
+HTTP == size-4x4.png encoder.html?img=size-4x4.png&mime=image/webp&options=1
+HTTP == size-5x5.png encoder.html?img=size-5x5.png&mime=image/webp&options=1
+HTTP == size-6x6.png encoder.html?img=size-6x6.png&mime=image/webp&options=1
+HTTP == size-7x7.png encoder.html?img=size-7x7.png&mime=image/webp&options=1
+HTTP == size-8x8.png encoder.html?img=size-8x8.png&mime=image/webp&options=1
+HTTP == size-9x9.png encoder.html?img=size-9x9.png&mime=image/webp&options=1
+HTTP == size-15x15.png encoder.html?img=size-15x15.png&mime=image/webp&options=1
+HTTP == size-16x16.png encoder.html?img=size-16x16.png&mime=image/webp&options=1
+HTTP == size-17x17.png encoder.html?img=size-17x17.png&mime=image/webp&options=1
+HTTP == size-31x31.png encoder.html?img=size-31x31.png&mime=image/webp&options=1
+HTTP == size-32x32.png encoder.html?img=size-32x32.png&mime=image/webp&options=1
+HTTP == size-33x33.png encoder.html?img=size-33x33.png&mime=image/webp&options=1
diff --git a/js/src/jit-test/tests/wasm/function-references/nnl-test.js b/js/src/jit-test/tests/wasm/function-references/nnl-test.js
new file mode 100644
index 0000000000000..0036a7b30d4c5
--- /dev/null
+++ b/js/src/jit-test/tests/wasm/function-references/nnl-test.js
@@ -0,0 +1,172 @@
+// |jit-test| skip-if: !wasmFunctionReferencesEnabled()
+
+// Generates combinations of different block types and operations for
+// non-defaultable locals (local.set / .tee / .get).
+// See the function references specification on the updated algorithm
+// for validating non-null references in locals.
+
+const KINDS = [
+ "block",
+ "loop",
+ "try",
+ "catch",
+ "if",
+ "else",
+]
+const INITIALIZED = [
+ "nowhere",
+ "outer",
+ "inner",
+ "outer-tee",
+ "inner-tee",
+];
+const USED = [
+ "outer",
+ "inner",
+ "after-inner",
+ "after-outer",
+];
+
+function generateBlock(kind, contents) {
+ switch (kind) {
+ case "block": {
+ return `block\n${contents}end\n`
+ }
+ case "loop": {
+ return `loop\n${contents}end\n`
+ }
+ case "try": {
+ return `try\n${contents}end\n`
+ }
+ case "catch": {
+ return `try\ncatch_all\n${contents}end\n`
+ }
+ case "if": {
+ return `i32.const 0\nif\n${contents}end\n`
+ }
+ case "else": {
+ return `i32.const 0\nif\nelse\n${contents}end\n`
+ }
+ }
+}
+
+// Generate a variation of the module below:
+//
+// (func
+// (block
+// $outer
+// (block
+// $inner
+// )
+// $after-inner
+// )
+// $after-outer
+// )
+//
+// Where a local is used and initialized at different points depending on the
+// parameters. The block kinds of the inner and outer block may also be
+// customized.
+function generateModule(outerBlockKind, innerBlockKind, initializedWhere, usedWhere) {
+ const INITIALIZE_STMT = '(local.set 0 ref.func 0)\n';
+ const INITIALIZE_STMT2 = '(drop (local.tee 0 ref.func 0))\n';
+ const USE_STMT = '(drop local.get 0)\n';
+
+ // inner block
+ let innerBlockContents = '';
+ if (initializedWhere === 'inner') {
+ innerBlockContents += INITIALIZE_STMT;
+ } else if (initializedWhere === 'inner-tee') {
+ innerBlockContents += INITIALIZE_STMT2;
+ }
+ if (usedWhere === 'inner') {
+ innerBlockContents += USE_STMT;
+ }
+ let innerBlock = generateBlock(innerBlockKind, innerBlockContents);
+
+ // outer block
+ let outerBlockContents = '';
+ if (initializedWhere === 'outer') {
+ outerBlockContents += INITIALIZE_STMT;
+ } else if (initializedWhere === 'outer-tee') {
+ outerBlockContents += INITIALIZE_STMT2;
+ }
+ if (usedWhere === 'outer') {
+ outerBlockContents += USE_STMT;
+ }
+ outerBlockContents += innerBlock;
+ if (usedWhere === 'after-inner') {
+ outerBlockContents += USE_STMT;
+ }
+ let outerBlock = generateBlock(outerBlockKind, outerBlockContents);
+
+ // after outer block
+ let afterOuterBlock = '';
+ if (usedWhere === 'after-outer') {
+ afterOuterBlock += USE_STMT;
+ }
+
+ return `(module
+ (type $t (func))
+ (func (export "test")
+ (local (ref $t))
+${outerBlock}${afterOuterBlock} )
+)`;
+}
+
+const LOGGING = false;
+
+for (let outer of KINDS) {
+ for (let inner of KINDS) {
+ for (let initialized of INITIALIZED) {
+ for (let used of USED) {
+ let text = generateModule(outer, inner, initialized, used);
+
+ let expectPass;
+ switch (initialized) {
+ case "outer":
+ case "outer-tee": {
+ // Defining the local in the outer block makes it valid
+ // in the outer block, the inner block, and after the
+ // inner block
+ expectPass = used !== "after-outer";
+ break;
+ }
+ case "inner":
+ case "inner-tee": {
+ // Defining the local in the inner block makes it valid
+ // in the inner block
+ //
+ // NOTE: an extension to typing could make this valid
+ // after the inner block in some cases
+ expectPass = used === "inner";
+ break;
+ }
+ case "nowhere": {
+ // Not defining the local makes it always invalid to
+ // use
+ expectPass = false;
+ break;
+ }
+ }
+
+ if (LOGGING) {
+ console.log();
+ console.log(`TEST: outer=${outer}, inner=${inner}, initialized=${initialized}, used=${used}`);
+ console.log(expectPass ? "EXPECT PASS" : "EXPECT FAIL");
+ console.log(text);
+ }
+
+ let binary = wasmTextToBinary(text);
+ assertEq(WebAssembly.validate(binary), expectPass);
+ if (!expectPass) {
+ // Check if the error message is right.
+ try {
+ new WebAssembly.Module(binary);
+ } catch (ex) {
+ assertEq(true, /local\.get read from unset local/.test(ex.message));
+ }
+ }
+ }
+ }
+ }
+}
diff --git a/js/src/jit-test/tests/wasm/function-references/non-nullable.js b/js/src/jit-test/tests/wasm/function-references/non-nullable.js
index 1cec41001165c..71c69a63d9345 100644
--- a/js/src/jit-test/tests/wasm/function-references/non-nullable.js
+++ b/js/src/jit-test/tests/wasm/function-references/non-nullable.js
@@ -20,10 +20,65 @@ wasmFailValidateText(`(module
)
)`, /expression has type externref but expected \(ref extern\)/);
-// cannot have non-defaultable local
-wasmFailValidateText(`(module
+// can have non-defaultable local, but not use/get if unset.
+wasmValidateText(`(module
(func (local (ref extern)))
-)`, /cannot have a non-defaultable local/);
+)`);
+wasmFailValidateText(`(module
+ (func (local (ref extern))
+ local.get 0
+ drop
+ )
+)`, /local\.get read from unset local/);
+wasmFailValidateText(`(module
+ (func
+ (local (ref extern))
+ unreachable
+ block
+ local.get 0
+ drop
+ end
+ )
+)`, /local\.get read from unset local/);
+wasmFailValidateText(`(module
+ (func (param funcref) (result funcref) (local (ref func))
+ block
+ local.get 0
+ ref.as_non_null
+ local.set 1
+ end
+ local.get 1
+ )
+)`, /local\.get read from unset local/);
+wasmValidateText(`(module
+ (func (param $r (ref extern))
+ (local $var (ref extern))
+ local.get $r
+ ref.as_non_null
+ local.set $var
+ block block block
+ local.get $var
+ drop
+ end end end
+ )
+ (func
+ (param (ref null func) (ref null func) (ref func))
+ (result funcref)
+ (local (ref func) i32 (ref func) (ref null func))
+ local.get 0
+ ref.as_non_null
+ local.tee 3
+ block
+ local.get 6
+ ref.as_non_null
+ local.set 5
+ end
+ local.get 2
+ drop
+ local.tee 5
+ )
+)`);
+
// exported funcs can't take null in non-nullable params
let {a} = wasmEvalText(`(module
diff --git a/js/src/jit/CacheIR.cpp b/js/src/jit/CacheIR.cpp
index 9af9ac31be496..1e0634eb53608 100644
--- a/js/src/jit/CacheIR.cpp
+++ b/js/src/jit/CacheIR.cpp
@@ -9471,12 +9471,10 @@ AttachDecision CallIRGenerator::tryAttachWasmCall(HandleFunction calleeFunc) {
auto bestTier = inst.code().bestTier();
const wasm::FuncExport& funcExport =
inst.metadata(bestTier).lookupFuncExport(funcIndex);
+ const wasm::FuncType& sig = inst.metadata().getFuncExportType(funcExport);
MOZ_ASSERT(!IsInsideNursery(inst.object()));
- MOZ_ASSERT(funcExport.canHaveJitEntry(),
- "Function should allow a Wasm JitEntry");
-
- const wasm::FuncType& sig = funcExport.funcType();
+ MOZ_ASSERT(sig.canHaveJitEntry(), "Function should allow a Wasm JitEntry");
// If there are too many arguments, don't optimize (we won't be able to store
// the arguments in the LIR node).
diff --git a/js/src/jit/CodeGenerator.cpp b/js/src/jit/CodeGenerator.cpp
index f2d688f0ed945..a24d42457454f 100644
--- a/js/src/jit/CodeGenerator.cpp
+++ b/js/src/jit/CodeGenerator.cpp
@@ -86,6 +86,7 @@
#include "jit/TemplateObject-inl.h"
#include "jit/VMFunctionList-inl.h"
#include "vm/JSScript-inl.h"
+#include "wasm/WasmInstance-inl.h"
using namespace js;
using namespace js::jit;
@@ -17000,8 +17001,10 @@ void CodeGenerator::emitIonToWasmCallBase(LIonToWasmCallBase* lir) {
return;
}
- const wasm::FuncExport& funcExport = lir->mir()->funcExport();
- const wasm::FuncType& sig = funcExport.funcType();
+ MIonToWasmCall* mir = lir->mir();
+ const wasm::FuncExport& funcExport = mir->funcExport();
+ const wasm::FuncType& sig =
+ mir->instance()->metadata().getFuncExportType(funcExport);
WasmABIArgGenerator abi;
for (size_t i = 0; i < lir->numOperands(); i++) {
diff --git a/js/src/jit/MIR.cpp b/js/src/jit/MIR.cpp
index 752c073c93370..4e945910b079e 100644
--- a/js/src/jit/MIR.cpp
+++ b/js/src/jit/MIR.cpp
@@ -37,6 +37,7 @@
#include "wasm/WasmCode.h"
#include "vm/JSAtom-inl.h"
+#include "wasm/WasmInstance-inl.h"
using namespace js;
using namespace js::jit;
@@ -6733,7 +6734,9 @@ AliasSet MMapObjectGetValueVMCall::getAliasSet() const {
MIonToWasmCall* MIonToWasmCall::New(TempAllocator& alloc,
WasmInstanceObject* instanceObj,
const wasm::FuncExport& funcExport) {
- const wasm::ValTypeVector& results = funcExport.funcType().results();
+ const wasm::FuncType& funcType =
+ instanceObj->instance().metadata().getFuncExportType(funcExport);
+ const wasm::ValTypeVector& results = funcType.results();
MIRType resultType = MIRType::Value;
// At the JS boundary some wasm types must be represented as a Value, and in
// addition a void return requires an Undefined value.
@@ -6744,7 +6747,7 @@ MIonToWasmCall* MIonToWasmCall::New(TempAllocator& alloc,
}
auto* ins = new (alloc) MIonToWasmCall(instanceObj, resultType, funcExport);
- if (!ins->init(alloc, funcExport.funcType().args().length())) {
+ if (!ins->init(alloc, funcType.args().length())) {
return nullptr;
}
return ins;
@@ -6752,8 +6755,9 @@ MIonToWasmCall* MIonToWasmCall::New(TempAllocator& alloc,
#ifdef DEBUG
bool MIonToWasmCall::isConsistentFloat32Use(MUse* use) const {
- return funcExport_.funcType().args()[use->index()].kind() ==
- wasm::ValType::F32;
+ const wasm::FuncType& funcType =
+ instance()->metadata().getFuncExportType(funcExport_);
+ return funcType.args()[use->index()].kind() == wasm::ValType::F32;
}
#endif
diff --git a/js/src/jit/MIR.h b/js/src/jit/MIR.h
index 52dfb2e2ed496..a6bcbc5c96cd8 100644
--- a/js/src/jit/MIR.h
+++ b/js/src/jit/MIR.h
@@ -10694,6 +10694,7 @@ class MIonToWasmCall final : public MVariadicInstruction,
void initArg(size_t i, MDefinition* arg) { initOperand(i, arg); }
WasmInstanceObject* instanceObject() const { return instanceObj_; }
+ wasm::Instance* instance() const { return &instanceObj_->instance(); }
const wasm::FuncExport& funcExport() const { return funcExport_; }
bool possiblyCalls() const override { return true; }
#ifdef DEBUG
diff --git a/js/src/jit/WarpCacheIRTranspiler.cpp b/js/src/jit/WarpCacheIRTranspiler.cpp
index 31993b2a0dc29..1128463f153b2 100644
--- a/js/src/jit/WarpCacheIRTranspiler.cpp
+++ b/js/src/jit/WarpCacheIRTranspiler.cpp
@@ -32,6 +32,7 @@
#include "gc/ObjectKind-inl.h"
#include "vm/NativeObject-inl.h"
+#include "wasm/WasmInstance-inl.h"
using namespace js;
using namespace js::jit;
@@ -4931,8 +4932,10 @@ bool WarpCacheIRTranspiler::emitCallWasmFunction(ObjOperandId calleeId,
static_cast(callInfo_->argc()));
#endif
JSObject* instanceObject = tenuredObjectStubField(instanceOffset);
+ auto* wasmInstanceObj = &instanceObject->as();
const wasm::FuncExport* funcExport = wasmFuncExportField(funcExportOffset);
- const wasm::FuncType& sig = funcExport->funcType();
+ const wasm::FuncType& sig =
+ wasmInstanceObj->instance().metadata().getFuncExportType(*funcExport);
if (!updateCallInfo(callee, flags)) {
return false;
@@ -4944,7 +4947,6 @@ bool WarpCacheIRTranspiler::emitCallWasmFunction(ObjOperandId calleeId,
MOZ_ASSERT(callInfo_->argFormat() == CallInfo::ArgFormat::Standard);
- auto* wasmInstanceObj = &instanceObject->as();
auto* call = MIonToWasmCall::New(alloc(), wasmInstanceObj, *funcExport);
if (!call) {
return false;
diff --git a/js/src/wasm/WasmBaselineCompile.cpp b/js/src/wasm/WasmBaselineCompile.cpp
index 89258ac1e9c7a..e4349f540444f 100644
--- a/js/src/wasm/WasmBaselineCompile.cpp
+++ b/js/src/wasm/WasmBaselineCompile.cpp
@@ -8392,7 +8392,7 @@ bool BaseCompiler::emitBody() {
MOZ_ASSERT(stackMapGenerator_.framePushedAtEntryToBody.isSome());
- if (!iter_.startFunction(func_.index)) {
+ if (!iter_.startFunction(func_.index, locals_)) {
return false;
}
diff --git a/js/src/wasm/WasmBuiltins.cpp b/js/src/wasm/WasmBuiltins.cpp
index 2cc819a9ea849..ee275e9f9fec4 100644
--- a/js/src/wasm/WasmBuiltins.cpp
+++ b/js/src/wasm/WasmBuiltins.cpp
@@ -778,10 +778,11 @@ static int32_t CoerceInPlace_JitEntry(int funcExportIndex, Instance* instance,
const Code& code = instance->code();
const FuncExport& fe =
code.metadata(code.stableTier()).funcExports[funcExportIndex];
+ const FuncType& funcType = code.metadata().getFuncExportType(fe);
- for (size_t i = 0; i < fe.funcType().args().length(); i++) {
+ for (size_t i = 0; i < funcType.args().length(); i++) {
HandleValue arg = HandleValue::fromMarkedLocation(&argv[i]);
- switch (fe.funcType().args()[i].kind()) {
+ switch (funcType.args()[i].kind()) {
case ValType::I32: {
int32_t i32;
if (!ToInt32(cx, arg, &i32)) {
@@ -813,7 +814,7 @@ static int32_t CoerceInPlace_JitEntry(int funcExportIndex, Instance* instance,
break;
}
case ValType::Ref: {
- switch (fe.funcType().args()[i].refTypeKind()) {
+ switch (funcType.args()[i].refTypeKind()) {
case RefType::Extern:
// Leave Object and Null alone, we will unbox inline. All we need
// to do is convert other values to an Object representation.
diff --git a/js/src/wasm/WasmCode.cpp b/js/src/wasm/WasmCode.cpp
index 4e27c6851874c..d8ab3f832f6b5 100644
--- a/js/src/wasm/WasmCode.cpp
+++ b/js/src/wasm/WasmCode.cpp
@@ -363,14 +363,6 @@ const CodeRange* ModuleSegment::lookupRange(const void* pc) const {
return codeTier().lookupRange(pc);
}
-size_t FuncExport::sizeOfExcludingThis(MallocSizeOf mallocSizeOf) const {
- return funcType_.sizeOfExcludingThis(mallocSizeOf);
-}
-
-size_t FuncImport::sizeOfExcludingThis(MallocSizeOf mallocSizeOf) const {
- return funcType_.sizeOfExcludingThis(mallocSizeOf);
-}
-
size_t CacheableChars::sizeOfExcludingThis(MallocSizeOf mallocSizeOf) const {
return mallocSizeOf(get());
}
@@ -381,8 +373,8 @@ size_t MetadataTier::sizeOfExcludingThis(MallocSizeOf mallocSizeOf) const {
callSites.sizeOfExcludingThis(mallocSizeOf) +
tryNotes.sizeOfExcludingThis(mallocSizeOf) +
trapSites.sizeOfExcludingThis(mallocSizeOf) +
- SizeOfVectorExcludingThis(funcImports, mallocSizeOf) +
- SizeOfVectorExcludingThis(funcExports, mallocSizeOf);
+ funcImports.sizeOfExcludingThis(mallocSizeOf) +
+ funcExports.sizeOfExcludingThis(mallocSizeOf);
}
UniqueLazyStubSegment LazyStubSegment::create(const CodeTier& codeTier,
@@ -405,7 +397,7 @@ bool LazyStubSegment::hasSpace(size_t bytes) const {
return bytes <= length() && usedBytes_ <= length() - bytes;
}
-bool LazyStubSegment::addStubs(size_t codeLength,
+bool LazyStubSegment::addStubs(const Metadata& metadata, size_t codeLength,
const Uint32Vector& funcExportIndices,
const FuncExportVector& funcExports,
const CodeRangeVector& codeRanges,
@@ -425,6 +417,8 @@ bool LazyStubSegment::addStubs(size_t codeLength,
size_t i = 0;
for (uint32_t funcExportIndex : funcExportIndices) {
+ const FuncExport& fe = funcExports[funcExportIndex];
+ const FuncType& funcType = metadata.getFuncExportType(fe);
const CodeRange& interpRange = codeRanges[i];
MOZ_ASSERT(interpRange.isInterpEntry());
MOZ_ASSERT(interpRange.funcIndex() ==
@@ -434,7 +428,7 @@ bool LazyStubSegment::addStubs(size_t codeLength,
codeRanges_.back().offsetBy(offsetInSegment);
i++;
- if (!funcExports[funcExportIndex].canHaveJitEntry()) {
+ if (!funcType.canHaveJitEntry()) {
continue;
}
@@ -494,6 +488,7 @@ static void PadCodeForSingleStub(MacroAssembler& masm) {
static constexpr unsigned LAZY_STUB_LIFO_DEFAULT_CHUNK_SIZE = 8 * 1024;
bool LazyStubTier::createManyEntryStubs(const Uint32Vector& funcExportIndices,
+ const Metadata& metadata,
const CodeTier& codeTier,
size_t* stubSegmentIndex) {
MOZ_ASSERT(funcExportIndices.length());
@@ -507,21 +502,22 @@ bool LazyStubTier::createManyEntryStubs(const Uint32Vector& funcExportIndices,
PadCodeForSingleStub(masm);
}
- const MetadataTier& metadata = codeTier.metadata();
- const FuncExportVector& funcExports = metadata.funcExports;
+ const MetadataTier& metadataTier = codeTier.metadata();
+ const FuncExportVector& funcExports = metadataTier.funcExports;
uint8_t* moduleSegmentBase = codeTier.segment().base();
CodeRangeVector codeRanges;
DebugOnly numExpectedRanges = 0;
for (uint32_t funcExportIndex : funcExportIndices) {
const FuncExport& fe = funcExports[funcExportIndex];
+ const FuncType& funcType = metadata.getFuncExportType(fe);
// Exports that don't support a jit entry get only the interp entry.
- numExpectedRanges += (fe.canHaveJitEntry() ? 2 : 1);
+ numExpectedRanges += (funcType.canHaveJitEntry() ? 2 : 1);
void* calleePtr =
- moduleSegmentBase + metadata.codeRange(fe).funcUncheckedCallEntry();
+ moduleSegmentBase + metadataTier.codeRange(fe).funcUncheckedCallEntry();
Maybe callee;
callee.emplace(calleePtr, ImmPtr::NoCheckToken());
- if (!GenerateEntryStubs(masm, funcExportIndex, fe, callee,
+ if (!GenerateEntryStubs(masm, funcExportIndex, fe, funcType, callee,
/* asmjs */ false, &codeRanges)) {
return false;
}
@@ -561,8 +557,8 @@ bool LazyStubTier::createManyEntryStubs(const Uint32Vector& funcExportIndices,
size_t interpRangeIndex;
uint8_t* codePtr = nullptr;
- if (!segment->addStubs(codeLength, funcExportIndices, funcExports, codeRanges,
- &codePtr, &interpRangeIndex)) {
+ if (!segment->addStubs(metadata, codeLength, funcExportIndices, funcExports,
+ codeRanges, &codePtr, &interpRangeIndex)) {
return false;
}
@@ -585,6 +581,7 @@ bool LazyStubTier::createManyEntryStubs(const Uint32Vector& funcExportIndices,
for (uint32_t funcExportIndex : funcExportIndices) {
const FuncExport& fe = funcExports[funcExportIndex];
+ const FuncType& funcType = metadata.getFuncExportType(fe);
DebugOnly cr = segment->codeRanges()[interpRangeIndex];
MOZ_ASSERT(cr.value.isInterpEntry());
@@ -605,13 +602,14 @@ bool LazyStubTier::createManyEntryStubs(const Uint32Vector& funcExportIndices,
exports_.insert(exports_.begin() + exportIndex, std::move(lazyExport)));
// Exports that don't support a jit entry get only the interp entry.
- interpRangeIndex += (fe.canHaveJitEntry() ? 2 : 1);
+ interpRangeIndex += (funcType.canHaveJitEntry() ? 2 : 1);
}
return true;
}
bool LazyStubTier::createOneEntryStub(uint32_t funcExportIndex,
+ const Metadata& metadata,
const CodeTier& codeTier) {
Uint32Vector funcExportIndexes;
if (!funcExportIndexes.append(funcExportIndex)) {
@@ -619,15 +617,19 @@ bool LazyStubTier::createOneEntryStub(uint32_t funcExportIndex,
}
size_t stubSegmentIndex;
- if (!createManyEntryStubs(funcExportIndexes, codeTier, &stubSegmentIndex)) {
+ if (!createManyEntryStubs(funcExportIndexes, metadata, codeTier,
+ &stubSegmentIndex)) {
return false;
}
const UniqueLazyStubSegment& segment = stubSegments_[stubSegmentIndex];
const CodeRangeVector& codeRanges = segment->codeRanges();
+ const FuncExport& fe = codeTier.metadata().funcExports[funcExportIndex];
+ const FuncType& funcType = metadata.getFuncExportType(fe);
+
// Exports that don't support a jit entry get only the interp entry.
- if (!codeTier.metadata().funcExports[funcExportIndex].canHaveJitEntry()) {
+ if (!funcType.canHaveJitEntry()) {
MOZ_ASSERT(codeRanges.length() >= 1);
MOZ_ASSERT(codeRanges.back().isInterpEntry());
return true;
@@ -644,6 +646,7 @@ bool LazyStubTier::createOneEntryStub(uint32_t funcExportIndex,
}
bool LazyStubTier::createTier2(const Uint32Vector& funcExportIndices,
+ const Metadata& metadata,
const CodeTier& codeTier,
Maybe* outStubSegmentIndex) {
if (!funcExportIndices.length()) {
@@ -651,7 +654,8 @@ bool LazyStubTier::createTier2(const Uint32Vector& funcExportIndices,
}
size_t stubSegmentIndex;
- if (!createManyEntryStubs(funcExportIndices, codeTier, &stubSegmentIndex)) {
+ if (!createManyEntryStubs(funcExportIndices, metadata, codeTier,
+ &stubSegmentIndex)) {
return false;
}
@@ -707,46 +711,9 @@ void LazyStubTier::addSizeOfMisc(MallocSizeOf mallocSizeOf, size_t* code,
}
}
-bool MetadataTier::clone(const MetadataTier& src) {
- if (!funcToCodeRange.appendAll(src.funcToCodeRange)) {
- return false;
- }
- if (!codeRanges.appendAll(src.codeRanges)) {
- return false;
- }
- if (!callSites.appendAll(src.callSites)) {
- return false;
- }
- if (!tryNotes.appendAll(src.tryNotes)) {
- return false;
- }
-
- for (Trap trap : MakeEnumeratedRange(Trap::Limit)) {
- if (!trapSites[trap].appendAll(src.trapSites[trap])) {
- return false;
- }
- }
-
- if (!funcImports.resize(src.funcImports.length())) {
- return false;
- }
- for (size_t i = 0; i < src.funcImports.length(); i++) {
- funcImports[i].clone(src.funcImports[i]);
- }
-
- if (!funcExports.resize(src.funcExports.length())) {
- return false;
- }
- for (size_t i = 0; i < src.funcExports.length(); i++) {
- funcExports[i].clone(src.funcExports[i]);
- }
-
- return true;
-}
-
size_t Metadata::sizeOfExcludingThis(MallocSizeOf mallocSizeOf) const {
return SizeOfVectorExcludingThis(types, mallocSizeOf) +
- typesRenumbering.sizeOfExcludingThis(mallocSizeOf) +
+ typeIds.sizeOfExcludingThis(mallocSizeOf) +
globals.sizeOfExcludingThis(mallocSizeOf) +
tables.sizeOfExcludingThis(mallocSizeOf) +
tags.sizeOfExcludingThis(mallocSizeOf) +
diff --git a/js/src/wasm/WasmCode.h b/js/src/wasm/WasmCode.h
index 1881be3f626af..618f452c62d9c 100644
--- a/js/src/wasm/WasmCode.h
+++ b/js/src/wasm/WasmCode.h
@@ -251,56 +251,40 @@ extern void StaticallyUnlink(uint8_t* base, const LinkData& linkData);
// function definition index.
class FuncExport {
- public:
- struct CacheablePod {
- uint32_t funcIndex_;
- uint32_t eagerInterpEntryOffset_; // Machine code offset
- bool hasEagerStubs_;
-
- WASM_CHECK_CACHEABLE_POD(funcIndex_, eagerInterpEntryOffset_,
- hasEagerStubs_);
- };
+ uint32_t typeIndex_;
+ uint32_t funcIndex_;
+ uint32_t eagerInterpEntryOffset_; // Machine code offset
+ bool hasEagerStubs_;
- private:
- FuncType funcType_;
- MOZ_INIT_OUTSIDE_CTOR CacheablePod pod;
+ WASM_CHECK_CACHEABLE_POD(typeIndex_, funcIndex_, eagerInterpEntryOffset_,
+ hasEagerStubs_);
public:
FuncExport() = default;
- explicit FuncExport(FuncType&& funcType, uint32_t funcIndex,
- bool hasEagerStubs)
- : funcType_(std::move(funcType)) {
- pod.funcIndex_ = funcIndex;
- pod.eagerInterpEntryOffset_ = UINT32_MAX;
- pod.hasEagerStubs_ = hasEagerStubs;
+ explicit FuncExport(uint32_t typeIndex, uint32_t funcIndex,
+ bool hasEagerStubs) {
+ typeIndex_ = typeIndex;
+ funcIndex_ = funcIndex;
+ eagerInterpEntryOffset_ = UINT32_MAX;
+ hasEagerStubs_ = hasEagerStubs;
}
void initEagerInterpEntryOffset(uint32_t entryOffset) {
- MOZ_ASSERT(pod.eagerInterpEntryOffset_ == UINT32_MAX);
+ MOZ_ASSERT(eagerInterpEntryOffset_ == UINT32_MAX);
MOZ_ASSERT(hasEagerStubs());
- pod.eagerInterpEntryOffset_ = entryOffset;
+ eagerInterpEntryOffset_ = entryOffset;
}
- bool hasEagerStubs() const { return pod.hasEagerStubs_; }
- const FuncType& funcType() const { return funcType_; }
- uint32_t funcIndex() const { return pod.funcIndex_; }
+ bool hasEagerStubs() const { return hasEagerStubs_; }
+ uint32_t typeIndex() const { return typeIndex_; }
+ uint32_t funcIndex() const { return funcIndex_; }
uint32_t eagerInterpEntryOffset() const {
- MOZ_ASSERT(pod.eagerInterpEntryOffset_ != UINT32_MAX);
+ MOZ_ASSERT(eagerInterpEntryOffset_ != UINT32_MAX);
MOZ_ASSERT(hasEagerStubs());
- return pod.eagerInterpEntryOffset_;
- }
-
- bool canHaveJitEntry() const { return funcType_.canHaveJitEntry(); }
-
- bool clone(const FuncExport& src) {
- mozilla::PodAssign(&pod, &src.pod);
- return funcType_.clone(src.funcType_);
+ return eagerInterpEntryOffset_;
}
-
- size_t sizeOfExcludingThis(mozilla::MallocSizeOf mallocSizeOf) const;
- WASM_DECLARE_FRIEND_SERIALIZE(FuncExport);
};
-WASM_DECLARE_CACHEABLE_POD(FuncExport::CacheablePod);
+WASM_DECLARE_CACHEABLE_POD(FuncExport);
using FuncExportVector = Vector;
@@ -311,56 +295,45 @@ using FuncExportVector = Vector;
// dynamically patched at runtime.
class FuncImport {
- public:
- struct CacheablePod {
- uint32_t instanceOffset_;
- uint32_t interpExitCodeOffset_; // Machine code offset
- uint32_t jitExitCodeOffset_; // Machine code offset
-
- WASM_CHECK_CACHEABLE_POD(instanceOffset_, interpExitCodeOffset_,
- jitExitCodeOffset_);
- };
-
private:
- FuncType funcType_;
- CacheablePod pod = {};
+ uint32_t typeIndex_;
+ uint32_t instanceOffset_;
+ uint32_t interpExitCodeOffset_; // Machine code offset
+ uint32_t jitExitCodeOffset_; // Machine code offset
+
+ WASM_CHECK_CACHEABLE_POD(typeIndex_, instanceOffset_, interpExitCodeOffset_,
+ jitExitCodeOffset_);
public:
- FuncImport() = default;
+ FuncImport()
+ : typeIndex_(0),
+ instanceOffset_(0),
+ interpExitCodeOffset_(0),
+ jitExitCodeOffset_(0) {}
- FuncImport(FuncType&& funcType, uint32_t instanceOffset)
- : funcType_(std::move(funcType)) {
- pod.instanceOffset_ = instanceOffset;
- pod.interpExitCodeOffset_ = 0;
- pod.jitExitCodeOffset_ = 0;
+ FuncImport(uint32_t typeIndex, uint32_t instanceOffset) {
+ typeIndex_ = typeIndex;
+ instanceOffset_ = instanceOffset;
+ interpExitCodeOffset_ = 0;
+ jitExitCodeOffset_ = 0;
}
void initInterpExitOffset(uint32_t off) {
- MOZ_ASSERT(!pod.interpExitCodeOffset_);
- pod.interpExitCodeOffset_ = off;
+ MOZ_ASSERT(!interpExitCodeOffset_);
+ interpExitCodeOffset_ = off;
}
void initJitExitOffset(uint32_t off) {
- MOZ_ASSERT(!pod.jitExitCodeOffset_);
- pod.jitExitCodeOffset_ = off;
- }
-
- const FuncType& funcType() const { return funcType_; }
- uint32_t instanceOffset() const { return pod.instanceOffset_; }
- uint32_t interpExitCodeOffset() const { return pod.interpExitCodeOffset_; }
- uint32_t jitExitCodeOffset() const { return pod.jitExitCodeOffset_; }
-
- bool clone(const FuncImport& src) {
- mozilla::PodAssign(&pod, &src.pod);
- return funcType_.clone(src.funcType_);
+ MOZ_ASSERT(!jitExitCodeOffset_);
+ jitExitCodeOffset_ = off;
}
- bool canHaveJitExit() const { return funcType_.canHaveJitExit(); }
-
- size_t sizeOfExcludingThis(mozilla::MallocSizeOf mallocSizeOf) const;
- WASM_DECLARE_FRIEND_SERIALIZE(FuncImport);
+ uint32_t typeIndex() const { return typeIndex_; }
+ uint32_t instanceOffset() const { return instanceOffset_; }
+ uint32_t interpExitCodeOffset() const { return interpExitCodeOffset_; }
+ uint32_t jitExitCodeOffset() const { return jitExitCodeOffset_; }
};
-WASM_DECLARE_CACHEABLE_POD(FuncImport::CacheablePod)
+WASM_DECLARE_CACHEABLE_POD(FuncImport)
using FuncImportVector = Vector;
@@ -399,12 +372,10 @@ struct MetadataCacheablePod {
WASM_DECLARE_CACHEABLE_POD(MetadataCacheablePod)
typedef uint8_t ModuleHash[8];
-using FuncArgTypesVector = Vector;
-using FuncReturnTypesVector = Vector;
struct Metadata : public ShareableBase, public MetadataCacheablePod {
- TypeDefWithIdVector types;
- RenumberVector typesRenumbering;
+ TypeDefVector types;
+ TypeIdDescVector typeIds;
GlobalDescVector globals;
TableDescVector tables;
TagDescVector tags;
@@ -420,8 +391,7 @@ struct Metadata : public ShareableBase, public MetadataCacheablePod {
// Debug-enabled code is not serialized.
bool debugEnabled;
- FuncArgTypesVector debugFuncArgTypes;
- FuncReturnTypesVector debugFuncReturnTypes;
+ Uint32Vector debugFuncTypeIndices;
ModuleHash debugHash;
explicit Metadata(ModuleKind kind = ModuleKind::Wasm)
@@ -436,12 +406,18 @@ struct Metadata : public ShareableBase, public MetadataCacheablePod {
return memory.isSome() && memory->isShared();
}
- // Invariant: The result of getFuncResultType can only be used as long as
- // MetaData is live, because the returned ResultType may encode a pointer to
- // debugFuncReturnTypes.
- ResultType getFuncResultType(uint32_t funcIndex) const {
- return ResultType::Vector(debugFuncReturnTypes[funcIndex]);
- };
+ const FuncType& getFuncImportType(const FuncImport& funcImport) const {
+ return types[funcImport.typeIndex()].funcType();
+ }
+ const FuncType& getFuncExportType(const FuncExport& funcExport) const {
+ return types[funcExport.typeIndex()].funcType();
+ }
+
+ size_t debugNumFuncs() const { return debugFuncTypeIndices.length(); }
+ const FuncType& debugFuncType(uint32_t funcIndex) const {
+ MOZ_ASSERT(debugEnabled);
+ return types[debugFuncTypeIndices[funcIndex]].funcType();
+ }
// AsmJSMetadata derives Metadata iff isAsmJS(). Mostly this distinction is
// encapsulated within AsmJS.cpp, but the additional virtual functions allow
@@ -508,8 +484,6 @@ struct MetadataTier {
return codeRanges[funcToCodeRange[funcExport.funcIndex()]];
}
- bool clone(const MetadataTier& src);
-
size_t sizeOfExcludingThis(mozilla::MallocSizeOf mallocSizeOf) const;
};
@@ -543,7 +517,7 @@ class LazyStubSegment : public CodeSegment {
}
bool hasSpace(size_t bytes) const;
- [[nodiscard]] bool addStubs(size_t codeLength,
+ [[nodiscard]] bool addStubs(const Metadata& metadata, size_t codeLength,
const Uint32Vector& funcExportIndices,
const FuncExportVector& funcExports,
const CodeRangeVector& codeRanges,
@@ -587,6 +561,7 @@ class LazyStubTier {
size_t lastStubSegmentIndex_;
[[nodiscard]] bool createManyEntryStubs(const Uint32Vector& funcExportIndices,
+ const Metadata& metadata,
const CodeTier& codeTier,
size_t* stubSegmentIndex);
@@ -596,6 +571,7 @@ class LazyStubTier {
// Creates one lazy stub for the exported function, for which the jit entry
// will be set to the lazily-generated one.
[[nodiscard]] bool createOneEntryStub(uint32_t funcExportIndex,
+ const Metadata& metadata,
const CodeTier& codeTier);
bool entryStubsEmpty() const { return stubSegments_.empty(); }
@@ -610,6 +586,7 @@ class LazyStubTier {
// setJitEntries() is actually called, after the Code owner has committed
// tier2.
[[nodiscard]] bool createTier2(const Uint32Vector& funcExportIndices,
+ const Metadata& metadata,
const CodeTier& codeTier,
Maybe* stubSegmentIndex);
void setJitEntries(const Maybe& stubSegmentIndex, const Code& code);
diff --git a/js/src/wasm/WasmDebug.cpp b/js/src/wasm/WasmDebug.cpp
index 60fc8a366af9c..9185e62deb13a 100644
--- a/js/src/wasm/WasmDebug.cpp
+++ b/js/src/wasm/WasmDebug.cpp
@@ -318,7 +318,7 @@ void DebugState::adjustEnterAndLeaveFrameTrapsState(JSContext* cx,
}
MOZ_RELEASE_ASSERT(&instance->metadata() == &metadata());
- uint32_t numFuncs = metadata().debugFuncReturnTypes.length();
+ uint32_t numFuncs = metadata().debugNumFuncs();
if (enabled) {
MOZ_ASSERT(enterAndLeaveFrameTrapsCounter_ > 0);
for (uint32_t funcIdx = 0; funcIdx < numFuncs; funcIdx++) {
@@ -372,8 +372,9 @@ void DebugState::ensureEnterFrameTrapsState(JSContext* cx, Instance* instance,
bool DebugState::debugGetLocalTypes(uint32_t funcIndex, ValTypeVector* locals,
size_t* argsLength,
StackResults* stackResults) {
- const ValTypeVector& args = metadata().debugFuncArgTypes[funcIndex];
- const ValTypeVector& results = metadata().debugFuncReturnTypes[funcIndex];
+ const FuncType& funcType = metadata().debugFuncType(funcIndex);
+ const ValTypeVector& args = funcType.args();
+ const ValTypeVector& results = funcType.results();
ResultType resultType(ResultType::Vector(results));
*argsLength = args.length();
*stackResults = ABIResultIter::HasStackResults(resultType)
diff --git a/js/src/wasm/WasmDebug.h b/js/src/wasm/WasmDebug.h
index 41f15354a8933..35c8a838ed4ac 100644
--- a/js/src/wasm/WasmDebug.h
+++ b/js/src/wasm/WasmDebug.h
@@ -150,12 +150,6 @@ class DebugState {
ValTypeVector* locals,
size_t* argsLength,
StackResults* stackResults);
- // Invariant: the result of getDebugResultType can only be used as long as
- // code_->metadata() is live. See MetaData::getFuncResultType for more
- // information.
- ResultType debugGetResultType(uint32_t funcIndex) const {
- return metadata().getFuncResultType(funcIndex);
- }
[[nodiscard]] bool getGlobal(Instance& instance, uint32_t globalIndex,
MutableHandleValue vp);
diff --git a/js/src/wasm/WasmDebugFrame.cpp b/js/src/wasm/WasmDebugFrame.cpp
index 4dcef877b9a05..2c93e750bc952 100644
--- a/js/src/wasm/WasmDebugFrame.cpp
+++ b/js/src/wasm/WasmDebugFrame.cpp
@@ -26,6 +26,7 @@
#include "wasm/WasmStubs.h"
#include "vm/NativeObject-inl.h"
+#include "wasm/WasmInstance-inl.h"
using namespace js;
using namespace js::jit;
@@ -134,7 +135,8 @@ bool DebugFrame::updateReturnJSValue(JSContext* cx) {
MutableHandleValue::fromMarkedLocation(&cachedReturnJSValue_);
rval.setUndefined();
flags_.hasCachedReturnJSValue = true;
- ResultType resultType = instance()->debug().debugGetResultType(funcIndex());
+ ResultType resultType = ResultType::Vector(
+ instance()->metadata().debugFuncType(funcIndex()).results());
Maybe stackResultsLoc;
if (ABIResultIter::HasStackResults(resultType)) {
stackResultsLoc = Some(static_cast(stackResultsPointer_));
diff --git a/js/src/wasm/WasmExprType.h b/js/src/wasm/WasmExprType.h
index 89b276d1d623e..506159ce7d78f 100644
--- a/js/src/wasm/WasmExprType.h
+++ b/js/src/wasm/WasmExprType.h
@@ -37,6 +37,8 @@ class TaggedValue {
PointerKind2 = 3
};
using PackedRepr = uintptr_t;
+ static_assert(std::is_same(),
+ "can use pointer tagging with PackedTypeCode");
private:
PackedRepr bits_;
@@ -89,10 +91,6 @@ class TaggedValue {
}
};
-static_assert(
- std::is_same::PackedRepr, PackedTypeCode::PackedRepr>(),
- "can use pointer tagging with PackedTypeCode");
-
// ResultType represents the WebAssembly spec's `resulttype`. Semantically, a
// result type is just a vec(valtype). For effiency, though, the ResultType
// value is packed into a word, with separate encodings for these 3 cases:
diff --git a/js/src/wasm/WasmGenerator.cpp b/js/src/wasm/WasmGenerator.cpp
index 85bda4bfe9130..3c2cc80e806bc 100644
--- a/js/src/wasm/WasmGenerator.cpp
+++ b/js/src/wasm/WasmGenerator.cpp
@@ -241,12 +241,8 @@ bool ModuleGenerator::init(Metadata* maybeAsmJSMetadata) {
moduleEnv_->funcImportGlobalDataOffsets[i] = globalDataOffset;
- FuncType copy;
- if (!copy.clone(*moduleEnv_->funcs[i].type)) {
- return false;
- }
- if (!metadataTier_->funcImports.emplaceBack(std::move(copy),
- globalDataOffset)) {
+ if (!metadataTier_->funcImports.emplaceBack(
+ FuncImport(moduleEnv_->funcs[i].typeIndex, globalDataOffset))) {
return false;
}
}
@@ -265,15 +261,31 @@ bool ModuleGenerator::init(Metadata* maybeAsmJSMetadata) {
}
}
+ // Copy type definitions to metadata
+ if (!metadata_->types.resize(moduleEnv_->types->length())) {
+ return false;
+ }
+
+ for (uint32_t i = 0; i < moduleEnv_->types->length(); i++) {
+ const TypeDef& typeDef = (*moduleEnv_->types)[i];
+ if (!metadata_->types[i].clone(typeDef)) {
+ return false;
+ }
+ }
+
+ // Generate type id's for all types. This will be either an immediate that
+ // can be generated, or a slot in global data to load.
+ if (!metadata_->typeIds.resize(moduleEnv_->types->length())) {
+ return false;
+ }
+
+ // asm.js requires no signature checks to be emitted as every table only
+ // stores the same type of func, and so we leave each type id as 'none'.
if (!isAsmJS()) {
- // Copy type definitions to metadata that are required at runtime,
- // allocating global data so that codegen can find the type id's at
- // runtime.
- for (uint32_t typeIndex = 0; typeIndex < moduleEnv_->types->length();
- typeIndex++) {
- const TypeDef& typeDef = (*moduleEnv_->types)[typeIndex];
- TypeIdDesc& typeId = moduleEnv_->typeIds[typeIndex];
+ for (uint32_t i = 0; i < moduleEnv_->types->length(); i++) {
+ const TypeDef& typeDef = (*moduleEnv_->types)[i];
+ TypeIdDesc typeId;
if (TypeIdDesc::isGlobal(typeDef)) {
uint32_t globalDataOffset;
if (!allocateGlobalBytes(sizeof(void*), sizeof(void*),
@@ -282,45 +294,12 @@ bool ModuleGenerator::init(Metadata* maybeAsmJSMetadata) {
}
typeId = TypeIdDesc::global(typeDef, globalDataOffset);
-
- TypeDef copy;
- if (!copy.clone(typeDef)) {
- return false;
- }
-
- if (!metadata_->types.emplaceBack(std::move(copy), typeId)) {
- return false;
- }
} else {
typeId = TypeIdDesc::immediate(typeDef);
}
- }
-
- // If we allow type indices, then we need to rewrite the index space to
- // account for types that are omitted from metadata, such as function
- // types that fit in an immediate.
- if (moduleEnv_->functionReferencesEnabled()) {
- // Do a linear pass to create a map from src index to dest index.
- RenumberVector renumbering;
- if (!renumbering.reserve(moduleEnv_->types->length())) {
- return false;
- }
- for (uint32_t srcIndex = 0, destIndex = 0;
- srcIndex < moduleEnv_->types->length(); srcIndex++) {
- const TypeDef& typeDef = (*moduleEnv_->types)[srcIndex];
- if (!TypeIdDesc::isGlobal(typeDef)) {
- renumbering.infallibleAppend(UINT32_MAX);
- continue;
- }
- MOZ_ASSERT(renumbering.length() == srcIndex);
- renumbering.infallibleAppend(destIndex++);
- }
- // Apply the renumbering
- for (TypeDefWithId& typeDef : metadata_->types) {
- typeDef.renumber(renumbering);
- }
- metadata_->typesRenumbering = std::move(renumbering);
+ moduleEnv_->typeIds[i] = typeId;
+ metadata_->typeIds[i] = typeId;
}
}
@@ -366,12 +345,8 @@ bool ModuleGenerator::init(Metadata* maybeAsmJSMetadata) {
continue;
}
- FuncType funcType;
- if (!funcType.clone(*func.type)) {
- return false;
- }
- metadataTier_->funcExports.infallibleEmplaceBack(std::move(funcType),
- funcIndex, func.isEager());
+ metadataTier_->funcExports.infallibleEmplaceBack(
+ FuncExport(func.typeIndex, funcIndex, func.isEager()));
}
// Determine whether parallel or sequential compilation is to be used and
@@ -1099,21 +1074,11 @@ SharedMetadata ModuleGenerator::finishMetadata(const Bytes& bytecode) {
metadata_->debugEnabled = true;
const size_t numFuncs = moduleEnv_->funcs.length();
- if (!metadata_->debugFuncArgTypes.resize(numFuncs)) {
- return nullptr;
- }
- if (!metadata_->debugFuncReturnTypes.resize(numFuncs)) {
+ if (!metadata_->debugFuncTypeIndices.resize(numFuncs)) {
return nullptr;
}
for (size_t i = 0; i < numFuncs; i++) {
- if (!metadata_->debugFuncArgTypes[i].appendAll(
- moduleEnv_->funcs[i].type->args())) {
- return nullptr;
- }
- if (!metadata_->debugFuncReturnTypes[i].appendAll(
- moduleEnv_->funcs[i].type->results())) {
- return nullptr;
- }
+ metadata_->debugFuncTypeIndices[i] = moduleEnv_->funcs[i].typeIndex;
}
static_assert(sizeof(ModuleHash) <= sizeof(mozilla::SHA1Sum::Hash),
diff --git a/js/src/wasm/WasmInstance.cpp b/js/src/wasm/WasmInstance.cpp
index 931634dbfb307..900abd09bf62f 100644
--- a/js/src/wasm/WasmInstance.cpp
+++ b/js/src/wasm/WasmInstance.cpp
@@ -236,14 +236,15 @@ bool Instance::callImport(JSContext* cx, uint32_t funcImportIndex,
Tier tier = code().bestTier();
const FuncImport& fi = metadata(tier).funcImports[funcImportIndex];
+ const FuncType& funcType = metadata().getFuncImportType(fi);
- ArgTypeVector argTypes(fi.funcType());
+ ArgTypeVector argTypes(funcType);
InvokeArgs args(cx);
if (!args.init(cx, argTypes.lengthWithoutStackResults())) {
return false;
}
- if (fi.funcType().hasUnexposableArgOrRet()) {
+ if (funcType.hasUnexposableArgOrRet()) {
JS_ReportErrorNumberUTF8(cx, GetErrorMessage, nullptr,
JSMSG_WASM_BAD_VAL_TYPE);
return false;
@@ -258,7 +259,7 @@ bool Instance::callImport(JSContext* cx, uint32_t funcImportIndex,
continue;
}
size_t naturalIndex = argTypes.naturalIndex(i);
- ValType type = fi.funcType().args()[naturalIndex];
+ ValType type = funcType.args()[naturalIndex];
MutableHandleValue argValue = args[naturalIndex];
if (!ToJSValue(cx, rawArgLoc, type, argValue)) {
return false;
@@ -276,8 +277,7 @@ bool Instance::callImport(JSContext* cx, uint32_t funcImportIndex,
return false;
}
- if (!UnpackResults(cx, fi.funcType().results(), stackResultPointer, argv,
- &rval)) {
+ if (!UnpackResults(cx, funcType.results(), stackResultPointer, argv, &rval)) {
return false;
}
@@ -306,7 +306,7 @@ bool Instance::callImport(JSContext* cx, uint32_t funcImportIndex,
}
// Skip if the function does not have a signature that allows for a JIT exit.
- if (!fi.canHaveJitExit()) {
+ if (!funcType.canHaveJitExit()) {
return true;
}
@@ -1359,6 +1359,7 @@ bool Instance::init(JSContext* cx, const JSFunctionVector& funcImports,
for (size_t i = 0; i < metadata(callerTier).funcImports.length(); i++) {
JSFunction* f = funcImports[i];
const FuncImport& fi = metadata(callerTier).funcImports[i];
+ const FuncType& funcType = metadata().getFuncImportType(fi);
FuncImportInstanceData& import = funcImportInstanceData(fi);
import.fun = f;
if (!isAsmJS() && IsWasmExportedFunction(f)) {
@@ -1372,7 +1373,7 @@ bool Instance::init(JSContext* cx, const JSFunctionVector& funcImports,
import.realm = f->realm();
import.code = calleeInstance.codeBase(calleeTier) +
codeRange.funcUncheckedCallEntry();
- } else if (void* thunk = MaybeGetBuiltinThunk(f, fi.funcType())) {
+ } else if (void* thunk = MaybeGetBuiltinThunk(f, funcType)) {
import.instance = this;
import.realm = f->realm();
import.code = thunk;
@@ -1403,7 +1404,7 @@ bool Instance::init(JSContext* cx, const JSFunctionVector& funcImports,
// Add debug filtering table.
if (metadata().debugEnabled) {
- size_t numFuncs = metadata().debugFuncReturnTypes.length();
+ size_t numFuncs = metadata().debugNumFuncs();
size_t numWords = std::max((numFuncs + 31) / 32, 1);
debugFilter_ = (uint32_t*)js_calloc(numWords, sizeof(uint32_t));
if (!debugFilter_) {
@@ -1430,14 +1431,16 @@ bool Instance::init(JSContext* cx, const JSFunctionVector& funcImports,
if (GcAvailable(cx)) {
// Transfer and allocate type objects for the struct types in the module
MutableTypeContext tycx = js_new();
- if (!tycx || !tycx->cloneDerived(metadata().types)) {
+ if (!tycx || !tycx->clone(metadata().types)) {
return false;
}
for (uint32_t typeIndex = 0; typeIndex < metadata().types.length();
typeIndex++) {
- const TypeDefWithId& typeDef = metadata().types[typeIndex];
- if (!typeDef.isStructType() && !typeDef.isArrayType()) {
+ const TypeDef& typeDef = metadata().types[typeIndex];
+ const TypeIdDesc& typeId = metadata().typeIds[typeIndex];
+ if (!typeId.isGlobal() ||
+ (!typeDef.isStructType() && !typeDef.isArrayType())) {
continue;
}
@@ -1449,7 +1452,7 @@ bool Instance::init(JSContext* cx, const JSFunctionVector& funcImports,
// We do not need to use a barrier here because RttValue is always
// tenured
MOZ_ASSERT(rttValue.get()->isTenured());
- *((GCPtr*)addressOfTypeId(typeDef.id)) = rttValue;
+ *((GCPtr*)addressOfTypeId(typeId)) = rttValue;
hasGcTypes_ = true;
}
}
@@ -1460,7 +1463,13 @@ bool Instance::init(JSContext* cx, const JSFunctionVector& funcImports,
ExclusiveData::Guard lockedFuncTypeIdSet =
funcTypeIdSet.lock();
- for (const TypeDefWithId& typeDef : metadata().types) {
+ for (uint32_t typeIndex = 0; typeIndex < metadata().types.length();
+ typeIndex++) {
+ const TypeDef& typeDef = metadata().types[typeIndex];
+ const TypeIdDesc& typeId = metadata().typeIds[typeIndex];
+ if (!typeId.isGlobal()) {
+ continue;
+ }
switch (typeDef.kind()) {
case TypeDefKind::Func: {
const FuncType& funcType = typeDef.funcType();
@@ -1469,7 +1478,7 @@ bool Instance::init(JSContext* cx, const JSFunctionVector& funcImports,
&funcTypeId)) {
return false;
}
- *addressOfTypeId(typeDef.id) = funcTypeId;
+ *addressOfTypeId(typeId) = funcTypeId;
break;
}
case TypeDefKind::Struct:
@@ -1561,12 +1570,15 @@ Instance::~Instance() {
ExclusiveData::Guard lockedFuncTypeIdSet =
funcTypeIdSet.lock();
- for (const TypeDefWithId& typeDef : metadata().types) {
- if (!typeDef.isFuncType()) {
+ for (uint32_t typeIndex = 0; typeIndex < metadata().types.length();
+ typeIndex++) {
+ const TypeDef& typeDef = metadata().types[typeIndex];
+ const TypeIdDesc& typeId = metadata().typeIds[typeIndex];
+ if (!typeDef.isFuncType() || !typeId.isGlobal()) {
continue;
}
const FuncType& funcType = typeDef.funcType();
- if (const void* funcTypeId = *addressOfTypeId(typeDef.id)) {
+ if (const void* funcTypeId = *addressOfTypeId(typeId)) {
lockedFuncTypeIdSet->deallocateFuncTypeId(funcType, funcTypeId);
}
}
@@ -1662,11 +1674,15 @@ void Instance::tracePrivate(JSTracer* trc) {
TraceNullableEdge(trc, &memory_, "wasm buffer");
#ifdef ENABLE_WASM_GC
if (hasGcTypes_) {
- for (const TypeDefWithId& typeDef : metadata().types) {
- if (!typeDef.isStructType() && !typeDef.isArrayType()) {
+ for (uint32_t typeIndex = 0; typeIndex < metadata().types.length();
+ typeIndex++) {
+ const TypeDef& typeDef = metadata().types[typeIndex];
+ const TypeIdDesc& typeId = metadata().typeIds[typeIndex];
+ if (!typeId.isGlobal() ||
+ (!typeDef.isStructType() && !typeDef.isArrayType())) {
continue;
}
- TraceNullableEdge(trc, ((GCPtr*)addressOfTypeId(typeDef.id)),
+ TraceNullableEdge(trc, ((GCPtr*)addressOfTypeId(typeId)),
"wasm rtt value");
}
}
@@ -1847,9 +1863,10 @@ static bool EnsureEntryStubs(const Instance& instance, uint32_t funcIndex,
// The best tier might have changed after we've taken the lock.
Tier prevTier = tier;
tier = instance.code().bestTier();
+ const Metadata& metadata = instance.metadata();
const CodeTier& codeTier = instance.code(tier);
if (tier == prevTier) {
- if (!stubs->createOneEntryStub(funcExportIndex, codeTier)) {
+ if (!stubs->createOneEntryStub(funcExportIndex, metadata, codeTier)) {
return false;
}
@@ -1865,7 +1882,7 @@ static bool EnsureEntryStubs(const Instance& instance, uint32_t funcIndex,
// shouldn't have made one in the second tier.
MOZ_ASSERT(!stubs2->hasEntryStub(fe.funcIndex()));
- if (!stubs2->createOneEntryStub(funcExportIndex, codeTier)) {
+ if (!stubs2->createOneEntryStub(funcExportIndex, metadata, codeTier)) {
return false;
}
@@ -1883,12 +1900,14 @@ static bool GetInterpEntryAndEnsureStubs(JSContext* cx, Instance& instance,
return false;
}
+ *funcType = &instance.metadata().getFuncExportType(*funcExport);
+
#ifdef DEBUG
// EnsureEntryStubs() has ensured proper jit-entry stubs have been created and
// installed in funcIndex's JumpTable entry, so check against the presence of
// the provisional lazy stub. See also
// WasmInstanceObject::getExportedFunction().
- if (!funcExport->hasEagerStubs() && funcExport->canHaveJitEntry()) {
+ if (!funcExport->hasEagerStubs() && (*funcType)->canHaveJitEntry()) {
if (!EnsureBuiltinThunksInitialized()) {
return false;
}
@@ -1899,8 +1918,6 @@ static bool GetInterpEntryAndEnsureStubs(JSContext* cx, Instance& instance,
MOZ_ASSERT(*callee.wasmJitEntry() != provisionalLazyJitEntryStub);
}
#endif
-
- *funcType = &funcExport->funcType();
return true;
}
diff --git a/js/src/wasm/WasmIonCompile.cpp b/js/src/wasm/WasmIonCompile.cpp
index 2b5704571b523..910e2d93918b1 100644
--- a/js/src/wasm/WasmIonCompile.cpp
+++ b/js/src/wasm/WasmIonCompile.cpp
@@ -5833,7 +5833,7 @@ static bool EmitIntrinsic(FunctionCompiler& f) {
}
static bool EmitBodyExprs(FunctionCompiler& f) {
- if (!f.iter().startFunction(f.funcIndex())) {
+ if (!f.iter().startFunction(f.funcIndex(), f.locals())) {
return false;
}
diff --git a/js/src/wasm/WasmJS.cpp b/js/src/wasm/WasmJS.cpp
index 5851e20d1d93f..0cfc6a11fd88e 100644
--- a/js/src/wasm/WasmJS.cpp
+++ b/js/src/wasm/WasmJS.cpp
@@ -1360,8 +1360,9 @@ bool WasmModuleObject::imports(JSContext* cx, unsigned argc, Value* vp) {
switch (import.kind) {
case DefinitionKind::Function: {
size_t funcIndex = numFuncImport++;
- typeObj = FuncTypeToObject(
- cx, metadataTier.funcImports[funcIndex].funcType());
+ const FuncType& funcType =
+ metadata.getFuncImportType(metadataTier.funcImports[funcIndex]);
+ typeObj = FuncTypeToObject(cx, funcType);
break;
}
case DefinitionKind::Table: {
@@ -1469,7 +1470,8 @@ bool WasmModuleObject::exports(JSContext* cx, unsigned argc, Value* vp) {
switch (exp.kind()) {
case DefinitionKind::Function: {
const FuncExport& fe = metadataTier.lookupFuncExport(exp.funcIndex());
- typeObj = FuncTypeToObject(cx, fe.funcType());
+ const FuncType& funcType = metadata.getFuncExportType(fe);
+ typeObj = FuncTypeToObject(cx, funcType);
break;
}
case DefinitionKind::Table: {
@@ -2308,7 +2310,8 @@ bool WasmInstanceObject::getExportedFunction(
const Instance& instance = instanceObj->instance();
const FuncExport& funcExport =
instance.metadata(instance.code().bestTier()).lookupFuncExport(funcIndex);
- unsigned numArgs = funcExport.funcType().args().length();
+ const FuncType& funcType = instance.metadata().getFuncExportType(funcExport);
+ unsigned numArgs = funcType.args().length();
if (instance.isAsmJS()) {
// asm.js needs to act like a normal JS function which means having the
@@ -2350,7 +2353,7 @@ bool WasmInstanceObject::getExportedFunction(
// separate 4kb code page. Most eagerly-accessed functions are not called,
// so use a shared, provisional (and slow) lazy stub as JitEntry and wait
// until Instance::callExport() to create the fast entry stubs.
- if (funcExport.canHaveJitEntry()) {
+ if (funcType.canHaveJitEntry()) {
if (!funcExport.hasEagerStubs()) {
if (!EnsureBuiltinThunksInitialized()) {
return false;
@@ -4143,10 +4146,10 @@ bool WasmFunctionTypeImpl(JSContext* cx, const CallArgs& args) {
cx, ExportedFunctionToInstanceObject(function));
uint32_t funcIndex = ExportedFunctionToFuncIndex(function);
Instance& instance = instanceObj->instance();
- const FuncType& ft = instance.metadata(instance.code().bestTier())
- .lookupFuncExport(funcIndex)
- .funcType();
- RootedObject typeObj(cx, FuncTypeToObject(cx, ft));
+ const FuncExport& fe =
+ instance.metadata(instance.code().bestTier()).lookupFuncExport(funcIndex);
+ const FuncType& funcType = instance.metadata().getFuncExportType(fe);
+ RootedObject typeObj(cx, FuncTypeToObject(cx, funcType));
if (!typeObj) {
return false;
}
diff --git a/js/src/wasm/WasmModule.cpp b/js/src/wasm/WasmModule.cpp
index e2d0fb86855ff..f6df8bc62ea20 100644
--- a/js/src/wasm/WasmModule.cpp
+++ b/js/src/wasm/WasmModule.cpp
@@ -209,7 +209,8 @@ bool Module::finishTier2(const LinkData& linkData2,
}
Maybe stub2Index;
- if (!stubs2->createTier2(funcExportIndices, *borrowedTier2, &stub2Index)) {
+ if (!stubs2->createTier2(funcExportIndices, metadata(), *borrowedTier2,
+ &stub2Index)) {
return false;
}
@@ -547,10 +548,12 @@ bool Module::instantiateFunctions(JSContext* cx,
Instance& instance = ExportedFunctionToInstance(f);
Tier otherTier = instance.code().stableTier();
- const FuncExport& funcExport =
- instance.metadata(otherTier).lookupFuncExport(funcIndex);
+ const FuncType& exportFuncType = instance.metadata().getFuncExportType(
+ instance.metadata(otherTier).lookupFuncExport(funcIndex));
+ const FuncType& importFuncType =
+ metadata().getFuncImportType(metadata(tier).funcImports[i]);
- if (funcExport.funcType() != metadata(tier).funcImports[i].funcType()) {
+ if (exportFuncType != importFuncType) {
const Import& import = FindImportFunction(imports_, i);
UniqueChars importModuleName = import.module.toQuotedString(cx);
UniqueChars importFieldName = import.field.toQuotedString(cx);
diff --git a/js/src/wasm/WasmOpIter.cpp b/js/src/wasm/WasmOpIter.cpp
index c3554664a461f..b34c8f7bf7623 100644
--- a/js/src/wasm/WasmOpIter.cpp
+++ b/js/src/wasm/WasmOpIter.cpp
@@ -783,4 +783,45 @@ OpKind wasm::Classify(OpBytes op) {
# undef WASM_GC_OP
# undef WASM_REF_OP
-#endif
+#endif // DEBUG
+
+bool UnsetLocalsState::init(const ValTypeVector& locals, size_t numParams) {
+ MOZ_ASSERT(setLocalsStack_.empty());
+
+ // Find the first and total count of non-defaultable locals.
+ size_t firstNonDefaultable = UINT32_MAX;
+ size_t countNonDefaultable = 0;
+ for (size_t i = numParams; i < locals.length(); i++) {
+ if (!locals[i].isDefaultable()) {
+ firstNonDefaultable = std::min(i, firstNonDefaultable);
+ countNonDefaultable++;
+ }
+ }
+ firstNonDefaultLocal_ = firstNonDefaultable;
+ if (countNonDefaultable == 0) {
+ // No locals to track, saving CPU cycles.
+ MOZ_ASSERT(firstNonDefaultable == UINT32_MAX);
+ return true;
+ }
+
+ // setLocalsStack_ cannot be deeper than amount of non-defaultable locals.
+ if (!setLocalsStack_.reserve(countNonDefaultable)) {
+ return false;
+ }
+
+ // Allocate a bitmap for locals starting at the first non-defaultable local.
+ size_t bitmapSize =
+ ((locals.length() - firstNonDefaultable) + (WordBits - 1)) / WordBits;
+ if (!unsetLocals_.resize(bitmapSize)) {
+ return false;
+ }
+ memset(unsetLocals_.begin(), 0, bitmapSize * WordSize);
+ for (size_t i = firstNonDefaultable; i < locals.length(); i++) {
+ if (!locals[i].isDefaultable()) {
+ size_t localUnsetIndex = i - firstNonDefaultable;
+ unsetLocals_[localUnsetIndex / WordBits] |=
+ 1 << (localUnsetIndex % WordBits);
+ }
+ }
+ return true;
+}
diff --git a/js/src/wasm/WasmOpIter.h b/js/src/wasm/WasmOpIter.h
index 75922d69b55e5..511f40feabdc8 100644
--- a/js/src/wasm/WasmOpIter.h
+++ b/js/src/wasm/WasmOpIter.h
@@ -279,6 +279,74 @@ class ControlStackEntry {
}
};
+// Track state of the non-defaultable locals. Every time such local is
+// initialized, the stack will record at what depth and which local was set.
+// On a block end, the "unset" state will be rolled back to how it was before
+// the block started.
+//
+// It is very likely only a few functions will have non-defaultable locals and
+// very few locals will be non-defaultable. This class is optimized to be fast
+// for this common case.
+class UnsetLocalsState {
+ struct SetLocalEntry {
+ uint32_t depth;
+ uint32_t localUnsetIndex;
+ SetLocalEntry(uint32_t depth_, uint32_t localUnsetIndex_)
+ : depth(depth_), localUnsetIndex(localUnsetIndex_) {}
+ };
+ using SetLocalsStack = Vector;
+ using UnsetLocals = Vector;
+
+ static constexpr size_t WordSize = 4;
+ static constexpr size_t WordBits = WordSize * 8;
+
+ // Bit array of "unset" function locals. Stores only unset states of the
+ // locals that are declared after the first non-defaultable local.
+ UnsetLocals unsetLocals_;
+ // Stack of "set" operations. Contains pair where the first field is a depth,
+ // and the second field is local id (offset by firstNonDefaultLocal_).
+ SetLocalsStack setLocalsStack_;
+ uint32_t firstNonDefaultLocal_;
+
+ public:
+ [[nodiscard]] bool init(const ValTypeVector& locals, size_t numParams);
+
+ inline bool isUnset(uint32_t id) const {
+ if (MOZ_LIKELY(id < firstNonDefaultLocal_)) {
+ return false;
+ }
+ uint32_t localUnsetIndex = id - firstNonDefaultLocal_;
+ return unsetLocals_[localUnsetIndex / WordBits] &
+ (1 << (localUnsetIndex % WordBits));
+ }
+
+ inline void set(uint32_t id, uint32_t depth) {
+ MOZ_ASSERT(isUnset(id));
+ MOZ_ASSERT(id >= firstNonDefaultLocal_ &&
+ (id - firstNonDefaultLocal_) / WordSize < unsetLocals_.length());
+ uint32_t localUnsetIndex = id - firstNonDefaultLocal_;
+ unsetLocals_[localUnsetIndex / WordBits] ^= 1
+ << (localUnsetIndex % WordBits);
+ // The setLocalsStack_ is reserved upfront in the UnsetLocalsState::init.
+ // A SetLocalEntry will be pushed only once per local.
+ setLocalsStack_.infallibleEmplaceBack(depth, localUnsetIndex);
+ }
+
+ inline void resetToBlock(uint32_t controlDepth) {
+ while (MOZ_UNLIKELY(setLocalsStack_.length() > 0) &&
+ setLocalsStack_.back().depth > controlDepth) {
+ uint32_t localUnsetIndex = setLocalsStack_.back().localUnsetIndex;
+ MOZ_ASSERT(!(unsetLocals_[localUnsetIndex / WordBits] &
+ (1 << (localUnsetIndex % WordBits))));
+ unsetLocals_[localUnsetIndex / WordBits] |=
+ 1 << (localUnsetIndex % WordBits);
+ setLocalsStack_.popBack();
+ }
+ }
+
+ int empty() const { return setLocalsStack_.empty(); }
+};
+
template
class TypeAndValueT {
// Use a Pair to optimize away empty Value.
@@ -327,6 +395,7 @@ class MOZ_STACK_CLASS OpIter : private Policy {
TypeAndValueStack valueStack_;
TypeAndValueStack elseParamStack_;
ControlStack controlStack_;
+ UnsetLocalsState unsetLocals_;
#ifdef DEBUG
OpBytes op_;
@@ -474,7 +543,8 @@ class MOZ_STACK_CLASS OpIter : private Policy {
// Initialization and termination
- [[nodiscard]] bool startFunction(uint32_t funcIndex);
+ [[nodiscard]] bool startFunction(uint32_t funcIndex,
+ const ValTypeVector& locals);
[[nodiscard]] bool endFunction(const uint8_t* bodyEnd);
[[nodiscard]] bool startInitExpr(ValType expected);
@@ -1164,13 +1234,20 @@ inline void OpIter::peekOp(OpBytes* op) {
}
template
-inline bool OpIter::startFunction(uint32_t funcIndex) {
+inline bool OpIter::startFunction(uint32_t funcIndex,
+ const ValTypeVector& locals) {
MOZ_ASSERT(kind_ == OpIter::Func);
MOZ_ASSERT(elseParamStack_.empty());
MOZ_ASSERT(valueStack_.empty());
MOZ_ASSERT(controlStack_.empty());
MOZ_ASSERT(op_.b0 == uint16_t(Op::Limit));
BlockType type = BlockType::FuncResults(*env_.funcs[funcIndex].type);
+
+ size_t numArgs = env_.funcs[funcIndex].type->args().length();
+ if (!unsetLocals_.init(locals, numArgs)) {
+ return false;
+ }
+
return pushControl(LabelKind::Body, type);
}
@@ -1184,6 +1261,7 @@ inline bool OpIter::endFunction(const uint8_t* bodyEnd) {
return fail("unbalanced function body control flow");
}
MOZ_ASSERT(elseParamStack_.empty());
+ MOZ_ASSERT(unsetLocals_.empty());
#ifdef DEBUG
op_ = OpBytes(Op::Limit);
@@ -1357,6 +1435,7 @@ inline void OpIter::popEnd() {
MOZ_ASSERT(Classify(op_) == OpKind::End);
controlStack_.popBack();
+ unsetLocals_.resetToBlock(controlStack_.length());
}
template
@@ -2041,6 +2120,10 @@ inline bool OpIter::readGetLocal(const ValTypeVector& locals,
return fail("local.get index out of range");
}
+ if (unsetLocals_.isUnset(*id)) {
+ return fail("local.get read from unset local");
+ }
+
return push(locals[*id]);
}
@@ -2057,6 +2140,10 @@ inline bool OpIter::readSetLocal(const ValTypeVector& locals,
return fail("local.set index out of range");
}
+ if (unsetLocals_.isUnset(*id)) {
+ unsetLocals_.set(*id, controlStackDepth());
+ }
+
return popWithType(locals[*id], value);
}
@@ -2073,6 +2160,10 @@ inline bool OpIter::readTeeLocal(const ValTypeVector& locals,
return fail("local.set index out of range");
}
+ if (unsetLocals_.isUnset(*id)) {
+ unsetLocals_.set(*id, controlStackDepth());
+ }
+
ValueVector single;
if (!popThenPushType(ResultType::Single(locals[*id]), &single)) {
return false;
diff --git a/js/src/wasm/WasmSerialize.cpp b/js/src/wasm/WasmSerialize.cpp
index 3ab47a3f49d58..9cd75d7c216d8 100644
--- a/js/src/wasm/WasmSerialize.cpp
+++ b/js/src/wasm/WasmSerialize.cpp
@@ -436,7 +436,7 @@ CoderResult CodeInitExpr(Coder& coder, CoderArg item) {
template
CoderResult CodeFuncType(Coder& coder, CoderArg item) {
- WASM_VERIFY_SERIALIZATION_FOR_SIZE(wasm::FuncType, 336);
+ WASM_VERIFY_SERIALIZATION_FOR_SIZE(wasm::FuncType, 208);
MOZ_TRY(CodePodVector(coder, &item->results_));
MOZ_TRY(CodePodVector(coder, &item->args_));
return Ok();
@@ -453,7 +453,7 @@ CoderResult CodeStructType(Coder& coder,
template
CoderResult CodeTypeDef(Coder& coder, CoderArg item) {
- WASM_VERIFY_SERIALIZATION_FOR_SIZE(wasm::TypeDef, 344);
+ WASM_VERIFY_SERIALIZATION_FOR_SIZE(wasm::TypeDef, 216);
// TypeDef is a tagged union that begins with kind = None. This implies that
// we must manually initialize the variant that we decode.
if constexpr (mode == MODE_DECODE) {
@@ -488,15 +488,6 @@ CoderResult CodeTypeDef(Coder& coder, CoderArg item) {
return Ok();
}
-template
-CoderResult CodeTypeDefWithId(Coder& coder,
- CoderArg item) {
- WASM_VERIFY_SERIALIZATION_FOR_SIZE(wasm::TypeDefWithId, 360);
- MOZ_TRY(CodeTypeDef(coder, item));
- MOZ_TRY(CodePod(coder, &item->id));
- return Ok();
-}
-
// WasmModuleTypes.h
template
@@ -532,7 +523,7 @@ CoderResult CodeGlobalDesc(Coder& coder,
template
CoderResult CodeTagType(Coder& coder, CoderArg item) {
- WASM_VERIFY_SERIALIZATION_FOR_SIZE(wasm::TagType, 224);
+ WASM_VERIFY_SERIALIZATION_FOR_SIZE(wasm::TagType, 160);
MOZ_TRY(CodePodVector(coder, &item->argTypes_));
MOZ_TRY(CodePodVector(coder, &item->argOffsets_));
MOZ_TRY(CodePod(coder, &item->size_));
@@ -696,24 +687,6 @@ CoderResult CodeStackMaps(Coder& coder,
// WasmCode.h
-template
-CoderResult CodeFuncExport(Coder& coder,
- CoderArg item) {
- WASM_VERIFY_SERIALIZATION_FOR_SIZE(wasm::FuncExport, 352);
- MOZ_TRY(CodeFuncType(coder, &item->funcType_));
- MOZ_TRY(CodePod(coder, &item->pod));
- return Ok();
-}
-
-template
-CoderResult CodeFuncImport(Coder& coder,
- CoderArg item) {
- WASM_VERIFY_SERIALIZATION_FOR_SIZE(wasm::FuncImport, 352);
- MOZ_TRY(CodeFuncType(coder, &item->funcType_));
- MOZ_TRY(CodePod(coder, &item->pod));
- return Ok();
-}
-
template
CoderResult CodeSymbolicLinkArray(
Coder& coder,
@@ -809,10 +782,8 @@ CoderResult CodeMetadataTier(Coder& coder,
MOZ_TRY(CodePodVector(coder, &item->codeRanges));
MOZ_TRY(CodePodVector(coder, &item->callSites));
MOZ_TRY(CodeTrapSiteVectorArray(coder, &item->trapSites));
- MOZ_TRY((CodeVector>(
- coder, &item->funcImports)));
- MOZ_TRY((CodeVector>(
- coder, &item->funcExports)));
+ MOZ_TRY(CodePodVector(coder, &item->funcImports));
+ MOZ_TRY(CodePodVector(coder, &item->funcExports));
MOZ_TRY(CodeStackMaps(coder, &item->stackMaps, codeStart));
MOZ_TRY(CodePodVector(coder, &item->tryNotes));
return Ok();
@@ -821,18 +792,16 @@ CoderResult CodeMetadataTier(Coder& coder,
template
CoderResult CodeMetadata(Coder& coder,
CoderArg item) {
- WASM_VERIFY_SERIALIZATION_FOR_SIZE(wasm::Metadata, 472);
+ WASM_VERIFY_SERIALIZATION_FOR_SIZE(wasm::Metadata, 464);
if constexpr (mode == MODE_ENCODE) {
- MOZ_ASSERT(!item->debugEnabled && item->debugFuncArgTypes.empty() &&
- item->debugFuncReturnTypes.empty());
+ MOZ_ASSERT(!item->debugEnabled && item->debugFuncTypeIndices.empty());
MOZ_ASSERT(!item->isAsmJS());
}
MOZ_TRY(Magic(coder, Marker::Metadata));
MOZ_TRY(CodePod(coder, &item->pod()));
- MOZ_TRY((CodeVector>(
- coder, &item->types)));
- MOZ_TRY(CodePodVector(coder, &item->typesRenumbering));
+ MOZ_TRY((CodeVector>(coder, &item->types)));
+ MOZ_TRY((CodePodVector(coder, &item->typeIds)));
MOZ_TRY((CodeVector>(
coder, &item->globals)));
MOZ_TRY(CodePodVector(coder, &item->tables));
@@ -844,8 +813,7 @@ CoderResult CodeMetadata(Coder& coder,
if constexpr (mode == MODE_DECODE) {
item->debugEnabled = false;
- item->debugFuncArgTypes.clear();
- item->debugFuncReturnTypes.clear();
+ item->debugFuncTypeIndices.clear();
}
return Ok();
diff --git a/js/src/wasm/WasmStubs.cpp b/js/src/wasm/WasmStubs.cpp
index 316ec01eceaeb..f7811c51e88ee 100644
--- a/js/src/wasm/WasmStubs.cpp
+++ b/js/src/wasm/WasmStubs.cpp
@@ -32,6 +32,7 @@
#include "wasm/WasmInstance.h"
#include "jit/MacroAssembler-inl.h"
+#include "wasm/WasmInstance-inl.h"
using namespace js;
using namespace js::jit;
@@ -363,13 +364,14 @@ static void Move64(MacroAssembler& masm, const Address& src,
}
static void SetupABIArguments(MacroAssembler& masm, const FuncExport& fe,
- Register argv, Register scratch) {
+ const FuncType& funcType, Register argv,
+ Register scratch) {
// Copy parameters out of argv and into the registers/stack-slots specified by
// the wasm ABI.
//
// SetupABIArguments are only used for C++ -> wasm calls through callExport(),
// and V128 and Ref types (other than externref) are not currently allowed.
- ArgTypeVector args(fe.funcType());
+ ArgTypeVector args(funcType);
for (WasmABIArgIter iter(args); !iter.done(); iter++) {
unsigned argOffset = iter.index() * sizeof(ExportArg);
Address src(argv, argOffset);
@@ -488,8 +490,8 @@ static void SetupABIArguments(MacroAssembler& masm, const FuncExport& fe,
}
static void StoreRegisterResult(MacroAssembler& masm, const FuncExport& fe,
- Register loc) {
- ResultType results = ResultType::Vector(fe.funcType().results());
+ const FuncType& funcType, Register loc) {
+ ResultType results = ResultType::Vector(funcType.results());
DebugOnly sawRegisterResult = false;
for (ABIResultIter iter(results); !iter.done(); iter.next()) {
const ABIResult& result = iter.cur();
@@ -717,6 +719,7 @@ static void BoxValueIntoAnyref(MacroAssembler& masm, ValueOperand src,
// function has an ABI derived from its specific signature, so this function
// must map from the ABI of ExportFuncPtr to the export's signature's ABI.
static bool GenerateInterpEntry(MacroAssembler& masm, const FuncExport& fe,
+ const FuncType& funcType,
const Maybe& funcPtr,
Offsets* offsets) {
AutoCreatedBy acb(masm, "GenerateInterpEntry");
@@ -808,11 +811,11 @@ static bool GenerateInterpEntry(MacroAssembler& masm, const FuncExport& fe,
// Reserve stack space for the wasm call.
unsigned argDecrement =
StackDecrementForCall(WasmStackAlignment, masm.framePushed(),
- StackArgBytesForWasmABI(fe.funcType()));
+ StackArgBytesForWasmABI(funcType));
masm.reserveStack(argDecrement);
// Copy parameters out of argv and into the wasm ABI registers/stack-slots.
- SetupABIArguments(masm, fe, argv, scratch);
+ SetupABIArguments(masm, fe, funcType, argv, scratch);
// Setup wasm register state. Ensure the frame pointer passed by the C++
// caller doesn't have the ExitFPTag bit set to not confuse frame iterators.
@@ -850,7 +853,7 @@ static bool GenerateInterpEntry(MacroAssembler& masm, const FuncExport& fe,
// Store the register result, if any, in argv[0].
// No widening is required, as the value leaves ReturnReg.
- StoreRegisterResult(masm, fe, argv);
+ StoreRegisterResult(masm, fe, funcType, argv);
// After the ReturnReg is stored into argv[0] but before fp is clobbered by
// the PopRegsInMask(NonVolatileRegs) below, set the return value based on
@@ -986,7 +989,8 @@ static void GenerateBigIntInitialization(MacroAssembler& masm,
// The JIT code we return to assumes it is correct.
static bool GenerateJitEntry(MacroAssembler& masm, size_t funcExportIndex,
- const FuncExport& fe, const Maybe& funcPtr,
+ const FuncExport& fe, const FuncType& funcType,
+ const Maybe& funcPtr,
CallableOffsets* offsets) {
AutoCreatedBy acb(masm, "GenerateJitEntry");
@@ -1005,7 +1009,7 @@ static bool GenerateJitEntry(MacroAssembler& masm, size_t funcExportIndex,
MOZ_ASSERT(masm.framePushed() == 0);
- unsigned normalBytesNeeded = StackArgBytesForWasmABI(fe.funcType());
+ unsigned normalBytesNeeded = StackArgBytesForWasmABI(funcType);
MIRTypeVector coerceArgTypes;
MOZ_ALWAYS_TRUE(coerceArgTypes.append(MIRType::Int32));
@@ -1028,7 +1032,7 @@ static bool GenerateJitEntry(MacroAssembler& masm, size_t funcExportIndex,
GenerateJitEntryLoadInstance(masm);
- if (fe.funcType().hasUnexposableArgOrRet()) {
+ if (funcType.hasUnexposableArgOrRet()) {
CallSymbolicAddress(masm, !fe.hasEagerStubs(),
SymbolicAddress::ReportV128JSCall);
GenerateJitEntryThrow(masm, frameSize);
@@ -1049,12 +1053,12 @@ static bool GenerateJitEntry(MacroAssembler& masm, size_t funcExportIndex,
// conversions are ordered in the way we expect them to happen the most.
// - the second loop will unbox the arguments into the right registers.
Label oolCall;
- for (size_t i = 0; i < fe.funcType().args().length(); i++) {
+ for (size_t i = 0; i < funcType.args().length(); i++) {
Address jitArgAddr(FramePointer, JitFrameLayout::offsetOfActualArg(i));
masm.loadValue(jitArgAddr, scratchV);
Label next;
- switch (fe.funcType().args()[i].kind()) {
+ switch (funcType.args()[i].kind()) {
case ValType::I32: {
ScratchTagScope tag(masm, scratchV);
masm.splitTagForTest(scratchV, tag);
@@ -1163,7 +1167,7 @@ static bool GenerateJitEntry(MacroAssembler& masm, size_t funcExportIndex,
break;
}
case ValType::Ref: {
- switch (fe.funcType().args()[i].refTypeKind()) {
+ switch (funcType.args()[i].refTypeKind()) {
case RefType::Extern: {
ScratchTagScope tag(masm, scratchV);
masm.splitTagForTest(scratchV, tag);
@@ -1201,7 +1205,7 @@ static bool GenerateJitEntry(MacroAssembler& masm, size_t funcExportIndex,
masm.bind(&rejoinBeforeCall);
// Convert all the expected values to unboxed values on the stack.
- ArgTypeVector args(fe.funcType());
+ ArgTypeVector args(funcType);
for (WasmABIArgIter iter(args); !iter.done(); iter++) {
Address argv(FramePointer, JitFrameLayout::offsetOfActualArg(iter.index()));
bool isStackArg = iter->kind() == ABIArg::Stack;
@@ -1300,7 +1304,7 @@ static bool GenerateJitEntry(MacroAssembler& masm, size_t funcExportIndex,
fe.funcIndex());
// Store the return value in the JSReturnOperand.
- const ValTypeVector& results = fe.funcType().results();
+ const ValTypeVector& results = funcType.results();
if (results.length() == 0) {
GenPrintf(DebugChannel::Function, masm, "void");
masm.moveValue(UndefinedValue(), JSReturnOperand);
@@ -1375,7 +1379,7 @@ static bool GenerateJitEntry(MacroAssembler& masm, size_t funcExportIndex,
MOZ_ASSERT(masm.framePushed() == 0);
// Generate an OOL call to the C++ conversion path.
- if (fe.funcType().args().length()) {
+ if (funcType.args().length()) {
masm.bind(&oolCall);
masm.setFramePushed(frameSize);
@@ -1436,6 +1440,8 @@ void wasm::GenerateDirectCallFromJit(MacroAssembler& masm, const FuncExport& fe,
Register scratch, uint32_t* callOffset) {
MOZ_ASSERT(!IsCompilingWasm());
+ const FuncType& funcType = inst.metadata().getFuncExportType(fe);
+
size_t framePushedAtStart = masm.framePushed();
// Note, if code here pushes a reference value into the frame for its own
@@ -1455,7 +1461,7 @@ void wasm::GenerateDirectCallFromJit(MacroAssembler& masm, const FuncExport& fe,
masm.enterFakeExitFrame(scratch, scratch, ExitFrameType::DirectWasmJitCall);
// Move stack arguments to their final locations.
- unsigned bytesNeeded = StackArgBytesForWasmABI(fe.funcType());
+ unsigned bytesNeeded = StackArgBytesForWasmABI(funcType);
bytesNeeded = StackDecrementForCall(WasmStackAlignment, masm.framePushed(),
bytesNeeded);
if (bytesNeeded) {
@@ -1465,7 +1471,7 @@ void wasm::GenerateDirectCallFromJit(MacroAssembler& masm, const FuncExport& fe,
GenPrintf(DebugChannel::Function, masm, "wasm-function[%d]; arguments ",
fe.funcIndex());
- ArgTypeVector args(fe.funcType());
+ ArgTypeVector args(funcType);
for (WasmABIArgIter iter(args); !iter.done(); iter++) {
MOZ_ASSERT_IF(iter->kind() == ABIArg::GPR, iter->gpr() != scratch);
MOZ_ASSERT_IF(iter->kind() == ABIArg::GPR, iter->gpr() != FramePointer);
@@ -1602,7 +1608,7 @@ void wasm::GenerateDirectCallFromJit(MacroAssembler& masm, const FuncExport& fe,
// Store the return value in the appropriate place.
GenPrintf(DebugChannel::Function, masm, "wasm-function[%d]; returns ",
fe.funcIndex());
- const ValTypeVector& results = fe.funcType().results();
+ const ValTypeVector& results = funcType.results();
if (results.length() == 0) {
masm.moveValue(UndefinedValue(), JSReturnOperand);
GenPrintf(DebugChannel::Function, masm, "void");
@@ -1950,7 +1956,9 @@ static void FillArgumentArrayForJitExit(MacroAssembler& masm, Register instance,
// - normal entries, so that, if the import is re-exported, an entry stub can
// be generated and called without any special cases
static bool GenerateImportFunction(jit::MacroAssembler& masm,
- const FuncImport& fi, TypeIdDesc funcTypeId,
+ const FuncImport& fi,
+ const FuncType& funcType,
+ TypeIdDesc funcTypeId,
FuncOffsets* offsets) {
AutoCreatedBy acb(masm, "wasm::GenerateImportFunction");
@@ -1963,7 +1971,7 @@ static bool GenerateImportFunction(jit::MacroAssembler& masm,
unsigned framePushed = StackDecrementForCall(
WasmStackAlignment,
sizeof(Frame), // pushed by prologue
- StackArgBytesForWasmABI(fi.funcType()) + sizeOfInstanceSlot);
+ StackArgBytesForWasmABI(funcType) + sizeOfInstanceSlot);
masm.wasmReserveStackChecked(framePushed, BytecodeOffset(0));
MOZ_ASSERT(masm.framePushed() == framePushed);
@@ -1980,7 +1988,7 @@ static bool GenerateImportFunction(jit::MacroAssembler& masm,
// WasmABIArgIter accounts for both the ShadowStackSpace and the instance
// fields of FrameWithInstances.
unsigned offsetFromFPToCallerStackArgs = sizeof(Frame);
- ArgTypeVector args(fi.funcType());
+ ArgTypeVector args(funcType);
for (WasmABIArgIter i(args); !i.done(); i++) {
if (i->kind() != ABIArg::Stack) {
continue;
@@ -2024,10 +2032,11 @@ bool wasm::GenerateImportFunctions(const ModuleEnvironment& env,
for (uint32_t funcIndex = 0; funcIndex < imports.length(); funcIndex++) {
const FuncImport& fi = imports[funcIndex];
+ const FuncType& funcType = *env.funcs[funcIndex].type;
+ TypeIdDesc funcTypeId = *env.funcs[funcIndex].typeId;
FuncOffsets offsets;
- if (!GenerateImportFunction(masm, fi, *env.funcs[funcIndex].typeId,
- &offsets)) {
+ if (!GenerateImportFunction(masm, fi, funcType, funcTypeId, &offsets)) {
return false;
}
if (!code->codeRanges.emplaceBack(funcIndex, /* bytecodeOffset = */ 0,
@@ -2048,6 +2057,7 @@ bool wasm::GenerateImportFunctions(const ModuleEnvironment& env,
// signature of the import and calls into an appropriate callImport C++
// function, having boxed all the ABI arguments into a homogeneous Value array.
static bool GenerateImportInterpExit(MacroAssembler& masm, const FuncImport& fi,
+ const FuncType& funcType,
uint32_t funcImportIndex,
Label* throwLabel,
CallableOffsets* offsets) {
@@ -2079,7 +2089,7 @@ static bool GenerateImportInterpExit(MacroAssembler& masm, const FuncImport& fi,
unsigned argOffset =
AlignBytes(StackArgBytesForNativeABI(invokeArgTypes), sizeof(double));
// The abiArgCount includes a stack result pointer argument if needed.
- unsigned abiArgCount = ArgTypeVector(fi.funcType()).lengthWithStackResults();
+ unsigned abiArgCount = ArgTypeVector(funcType).lengthWithStackResults();
unsigned argBytes = std::max(1, abiArgCount) * sizeof(Value);
unsigned framePushed =
StackDecrementForCall(ABIStackAlignment,
@@ -2091,8 +2101,8 @@ static bool GenerateImportInterpExit(MacroAssembler& masm, const FuncImport& fi,
// Fill the argument array.
Register scratch = ABINonArgReturnReg0;
- FillArgumentArrayForInterpExit(masm, funcImportIndex, fi.funcType(),
- argOffset, scratch);
+ FillArgumentArrayForInterpExit(masm, funcImportIndex, funcType, argOffset,
+ scratch);
// Prepare the arguments for the call to Instance::callImport_*.
ABIArgMIRTypeIter i(invokeArgTypes);
@@ -2142,7 +2152,7 @@ static bool GenerateImportInterpExit(MacroAssembler& masm, const FuncImport& fi,
masm.call(SymbolicAddress::CallImport_General);
masm.branchTest32(Assembler::Zero, ReturnReg, ReturnReg, throwLabel);
- ResultType resultType = ResultType::Vector(fi.funcType().results());
+ ResultType resultType = ResultType::Vector(funcType.results());
ValType registerResultType;
for (ABIResultIter iter(resultType); !iter.done(); iter.next()) {
if (iter.cur().inRegister()) {
@@ -2229,6 +2239,7 @@ static bool GenerateImportInterpExit(MacroAssembler& masm, const FuncImport& fi,
// signature of the import and calls into a compatible JIT function,
// having boxed all the ABI arguments into the JIT stack frame layout.
static bool GenerateImportJitExit(MacroAssembler& masm, const FuncImport& fi,
+ const FuncType& funcType,
unsigned funcImportIndex, Label* throwLabel,
CallableOffsets* offsets) {
AutoCreatedBy acb(masm, "GenerateImportJitExit");
@@ -2250,7 +2261,7 @@ static bool GenerateImportJitExit(MacroAssembler& masm, const FuncImport& fi,
const unsigned sizeOfPreFrame =
WasmToJSJitFrameLayout::Size() - sizeOfRetAddrAndFP;
const unsigned sizeOfThisAndArgs =
- (1 + fi.funcType().args().length()) * sizeof(Value);
+ (1 + funcType.args().length()) * sizeof(Value);
const unsigned totalJitFrameBytes = sizeOfRetAddrAndFP + sizeOfPreFrame +
sizeOfThisAndArgs + sizeOfInstanceSlot;
const unsigned jitFramePushed =
@@ -2262,7 +2273,7 @@ static bool GenerateImportJitExit(MacroAssembler& masm, const FuncImport& fi,
GenerateJitExitPrologue(masm, jitFramePushed, offsets);
// 1. Descriptor.
- unsigned argc = fi.funcType().args().length();
+ unsigned argc = funcType.args().length();
size_t argOffset = 0;
uint32_t descriptor =
MakeFrameDescriptorForJitCall(FrameType::WasmToJSJit, argc);
@@ -2283,9 +2294,9 @@ static bool GenerateImportJitExit(MacroAssembler& masm, const FuncImport& fi,
// 4. Fill the arguments.
Register scratch = ABINonArgReturnReg1; // Repeatedly clobbered
Register scratch2 = ABINonArgReturnReg0; // Reused as callee below
- FillArgumentArrayForJitExit(masm, InstanceReg, funcImportIndex, fi.funcType(),
+ FillArgumentArrayForJitExit(masm, InstanceReg, funcImportIndex, funcType,
argOffset, scratch, scratch2, throwLabel);
- argOffset += fi.funcType().args().length() * sizeof(Value);
+ argOffset += funcType.args().length() * sizeof(Value);
MOZ_ASSERT(argOffset == sizeOfThisAndArgs + sizeOfPreFrame);
// Preserve instance because the JIT callee clobbers it.
@@ -2307,7 +2318,7 @@ static bool GenerateImportJitExit(MacroAssembler& masm, const FuncImport& fi,
masm.loadFunctionArgCount(callee, scratch);
Label rectify;
- masm.branch32(Assembler::Above, scratch, Imm32(fi.funcType().args().length()),
+ masm.branch32(Assembler::Above, scratch, Imm32(funcType.args().length()),
&rectify);
// 6. If we haven't rectified arguments, load callee executable entry point.
@@ -2361,7 +2372,7 @@ static bool GenerateImportJitExit(MacroAssembler& masm, const FuncImport& fi,
funcImportIndex);
Label oolConvert;
- const ValTypeVector& results = fi.funcType().results();
+ const ValTypeVector& results = funcType.results();
if (results.length() == 0) {
GenPrintf(DebugChannel::Import, masm, "void");
} else {
@@ -2463,7 +2474,7 @@ static bool GenerateImportJitExit(MacroAssembler& masm, const FuncImport& fi,
// Call coercion function. Note that right after the call, the value of
// FP is correct because FP is non-volatile in the native ABI.
AssertStackAlignment(masm, ABIStackAlignment);
- const ValTypeVector& results = fi.funcType().results();
+ const ValTypeVector& results = funcType.results();
if (results.length() > 0) {
// NOTE that once there can be more than one result and we can box some of
// the results (as we must for AnyRef), pointer and already-boxed results
@@ -2956,13 +2967,14 @@ static bool GenerateDebugTrapStub(MacroAssembler& masm, Label* throwLabel,
}
bool wasm::GenerateEntryStubs(MacroAssembler& masm, size_t funcExportIndex,
- const FuncExport& fe, const Maybe& callee,
- bool isAsmJS, CodeRangeVector* codeRanges) {
+ const FuncExport& fe, const FuncType& funcType,
+ const Maybe& callee, bool isAsmJS,
+ CodeRangeVector* codeRanges) {
MOZ_ASSERT(!callee == fe.hasEagerStubs());
MOZ_ASSERT_IF(isAsmJS, fe.hasEagerStubs());
Offsets offsets;
- if (!GenerateInterpEntry(masm, fe, callee, &offsets)) {
+ if (!GenerateInterpEntry(masm, fe, funcType, callee, &offsets)) {
return false;
}
if (!codeRanges->emplaceBack(CodeRange::InterpEntry, fe.funcIndex(),
@@ -2970,12 +2982,13 @@ bool wasm::GenerateEntryStubs(MacroAssembler& masm, size_t funcExportIndex,
return false;
}
- if (isAsmJS || !fe.canHaveJitEntry()) {
+ if (isAsmJS || !funcType.canHaveJitEntry()) {
return true;
}
CallableOffsets jitOffsets;
- if (!GenerateJitEntry(masm, funcExportIndex, fe, callee, &jitOffsets)) {
+ if (!GenerateJitEntry(masm, funcExportIndex, fe, funcType, callee,
+ &jitOffsets)) {
return false;
}
if (!codeRanges->emplaceBack(CodeRange::JitEntry, fe.funcIndex(),
@@ -3044,9 +3057,10 @@ bool wasm::GenerateStubs(const ModuleEnvironment& env,
for (uint32_t funcIndex = 0; funcIndex < imports.length(); funcIndex++) {
const FuncImport& fi = imports[funcIndex];
+ const FuncType& funcType = *env.funcs[funcIndex].type;
CallableOffsets interpOffsets;
- if (!GenerateImportInterpExit(masm, fi, funcIndex, &throwLabel,
+ if (!GenerateImportInterpExit(masm, fi, funcType, funcIndex, &throwLabel,
&interpOffsets)) {
return false;
}
@@ -3057,12 +3071,13 @@ bool wasm::GenerateStubs(const ModuleEnvironment& env,
// Skip if the function does not have a signature that allows for a JIT
// exit.
- if (!fi.canHaveJitExit()) {
+ if (!funcType.canHaveJitExit()) {
continue;
}
CallableOffsets jitOffsets;
- if (!GenerateImportJitExit(masm, fi, funcIndex, &throwLabel, &jitOffsets)) {
+ if (!GenerateImportJitExit(masm, fi, funcType, funcIndex, &throwLabel,
+ &jitOffsets)) {
return false;
}
if (!code->codeRanges.emplaceBack(CodeRange::ImportJitExit, funcIndex,
@@ -3076,10 +3091,11 @@ bool wasm::GenerateStubs(const ModuleEnvironment& env,
Maybe noAbsolute;
for (size_t i = 0; i < exports.length(); i++) {
const FuncExport& fe = exports[i];
+ const FuncType& funcType = (*env.types)[fe.typeIndex()].funcType();
if (!fe.hasEagerStubs()) {
continue;
}
- if (!GenerateEntryStubs(masm, i, fe, noAbsolute, env.isAsmJS(),
+ if (!GenerateEntryStubs(masm, i, fe, funcType, noAbsolute, env.isAsmJS(),
&code->codeRanges)) {
return false;
}
diff --git a/js/src/wasm/WasmStubs.h b/js/src/wasm/WasmStubs.h
index 7c584dcd14496..6a6622fdf2dd0 100644
--- a/js/src/wasm/WasmStubs.h
+++ b/js/src/wasm/WasmStubs.h
@@ -261,6 +261,7 @@ extern bool GenerateStubs(const ModuleEnvironment& env,
extern bool GenerateEntryStubs(jit::MacroAssembler& masm,
size_t funcExportIndex, const FuncExport& fe,
+ const FuncType& funcType,
const Maybe& callee, bool isAsmJS,
CodeRangeVector* codeRanges);
diff --git a/js/src/wasm/WasmTypeDecls.h b/js/src/wasm/WasmTypeDecls.h
index 56661eaba7c5e..777be0a7c4e00 100644
--- a/js/src/wasm/WasmTypeDecls.h
+++ b/js/src/wasm/WasmTypeDecls.h
@@ -100,7 +100,6 @@ using UniqueConstBytes = UniquePtr;
using UTF8Bytes = Vector;
using InstanceVector = Vector;
using UniqueCharsVector = Vector;
-using RenumberVector = Vector;
} // namespace wasm
} // namespace js
diff --git a/js/src/wasm/WasmTypeDef.h b/js/src/wasm/WasmTypeDef.h
index 37d5f56281344..349be0aeb7602 100644
--- a/js/src/wasm/WasmTypeDef.h
+++ b/js/src/wasm/WasmTypeDef.h
@@ -101,15 +101,6 @@ class FuncType {
return args_.appendAll(src.args_) && results_.appendAll(src.results_);
}
- void renumber(const RenumberVector& renumbering) {
- for (auto& arg : args_) {
- arg.renumber(renumbering);
- }
- for (auto& result : results_) {
- result.renumber(renumbering);
- }
- }
-
ValType arg(unsigned i) const { return args_[i]; }
const ValTypeVector& args() const { return args_; }
ValType result(unsigned i) const { return results_[i]; }
@@ -223,12 +214,6 @@ class StructType {
return true;
}
- void renumber(const RenumberVector& renumbering) {
- for (auto& field : fields_) {
- field.type.renumber(renumbering);
- }
- }
-
bool isDefaultable() const {
for (auto& field : fields_) {
if (!field.type.isDefaultable()) {
@@ -286,10 +271,6 @@ class ArrayType {
return true;
}
- void renumber(const RenumberVector& renumbering) {
- elementType_.renumber(renumbering);
- }
-
bool isDefaultable() const { return elementType_.isDefaultable(); }
size_t sizeOfExcludingThis(mozilla::MallocSizeOf mallocSizeOf) const;
@@ -441,31 +422,12 @@ class TypeDef {
return arrayType_;
}
- void renumber(const RenumberVector& renumbering) {
- switch (kind_) {
- case TypeDefKind::Func:
- funcType_.renumber(renumbering);
- break;
- case TypeDefKind::Struct:
- structType_.renumber(renumbering);
- break;
- case TypeDefKind::Array:
- arrayType_.renumber(renumbering);
- break;
- case TypeDefKind::None:
- break;
- }
- }
-
size_t sizeOfExcludingThis(mozilla::MallocSizeOf mallocSizeOf) const;
WASM_DECLARE_FRIEND_SERIALIZE(TypeDef);
};
using TypeDefVector = Vector;
-template
-using DerivedTypeDefVector = Vector;
-
// A type cache maintains a cache of equivalence and subtype relations between
// wasm types. This is required for the computation of equivalence and subtyping
// on recursive types.
@@ -545,8 +507,7 @@ class TypeContext : public AtomicRefCounted {
TypeContext(const FeatureArgs& features, TypeDefVector&& types)
: features_(features), types_(std::move(types)) {}
- template
- [[nodiscard]] bool cloneDerived(const DerivedTypeDefVector& source) {
+ [[nodiscard]] bool clone(const TypeDefVector& source) {
MOZ_ASSERT(types_.length() == 0);
if (!types_.resize(source.length())) {
return false;
@@ -773,20 +734,6 @@ class TypeHandle {
// is given a unique value and no canonicalization is done (which would require
// hash-consing of infinite-trees), this is not yet spec compliant.
//
-// # wasm::Metadata and renumbering
-//
-// Once module compilation is finished, types are transfered to wasm::Metadata
-// for use during runtime. Only non-immediate function and GC types are
-// transfered, creating a new index space for types. Types are 'renumbered' to
-// account for this. The map from source type index to renumbered type index is
-// transferred with wasm::Metadata and used for constant expressions that have
-// encoded source type indices. All other uses of type indices, such as in
-// function, global, and table types are renumbered.
-//
-// The renumbering map itself is an array from source type index to renumbered
-// type index. For types that are immediates, the renumbered type index is
-// UINT32_MAX.
-//
// # wasm::Instance and the global type context
//
// As GC objects (aka TypedObject) may outlive the module they are created in,
diff --git a/js/src/wasm/WasmValType.h b/js/src/wasm/WasmValType.h
index fb32e1ee8c663..74c9d3b9ca147 100644
--- a/js/src/wasm/WasmValType.h
+++ b/js/src/wasm/WasmValType.h
@@ -36,25 +36,18 @@ using mozilla::Maybe;
// A PackedTypeCode represents any value type in an compact POD format.
union PackedTypeCode {
public:
- using PackedRepr = uintptr_t;
+ using PackedRepr = uint32_t;
private:
-#ifdef JS_64BIT
- static constexpr size_t TypeCodeBits = 8;
- static constexpr size_t TypeIndexBits = 21;
- static constexpr size_t NullableBits = 1;
- static constexpr size_t PointerTagBits = 2;
-#else
static constexpr size_t TypeCodeBits = 8;
- static constexpr size_t TypeIndexBits = 14;
+ static constexpr size_t TypeIndexBits = 20;
static constexpr size_t NullableBits = 1;
static constexpr size_t PointerTagBits = 2;
-#endif
static_assert(TypeCodeBits + TypeIndexBits + NullableBits + PointerTagBits <=
(sizeof(PackedRepr) * 8),
"enough bits");
- static_assert(MaxTypeIndex < (1 << TypeIndexBits), "enough bits");
+ static_assert(MaxTypes < (1 << TypeIndexBits), "enough bits");
PackedRepr bits_;
struct {
@@ -171,7 +164,7 @@ union PackedTypeCode {
WASM_DECLARE_CACHEABLE_POD(PackedTypeCode);
-static_assert(sizeof(PackedTypeCode) == sizeof(uintptr_t), "packed");
+static_assert(sizeof(PackedTypeCode) == sizeof(uint32_t), "packed");
static_assert(std::is_pod_v,
"must be POD to be simply serialized/deserialized");
@@ -519,16 +512,6 @@ class PackedType : public T {
return RefType(tc_).kind();
}
- void renumber(const RenumberVector& renumbering) {
- if (!isTypeIndex()) {
- return;
- }
-
- uint32_t newIndex = renumbering[typeIndex()];
- MOZ_ASSERT(newIndex != UINT32_MAX);
- *this = RefType::fromTypeIndex(newIndex, isNullable());
- }
-
// Some types are encoded as JS::Value when they escape from Wasm (when passed
// as parameters to imports or returned from exports). For ExternRef the
// Value encoding is pretty much a requirement. For other types it's a choice
diff --git a/js/src/wasm/WasmValidate.cpp b/js/src/wasm/WasmValidate.cpp
index 503d5c935ec32..6477c084f77ae 100644
--- a/js/src/wasm/WasmValidate.cpp
+++ b/js/src/wasm/WasmValidate.cpp
@@ -110,10 +110,6 @@ bool wasm::DecodeLocalEntries(Decoder& d, const TypeContext& types,
return false;
}
- if (!type.isDefaultable()) {
- return d.fail("cannot have a non-defaultable local");
- }
-
if (!locals->appendN(type, count)) {
return false;
}
@@ -178,7 +174,7 @@ static bool DecodeFunctionBodyExprs(const ModuleEnvironment& env,
const uint8_t* bodyEnd, Decoder* d) {
ValidatingOpIter iter(env, *d);
- if (!iter.startFunction(funcIndex)) {
+ if (!iter.startFunction(funcIndex, locals)) {
return false;
}
diff --git a/layout/generic/nsGridContainerFrame.cpp b/layout/generic/nsGridContainerFrame.cpp
index 497e67a3db276..469e1813f280c 100644
--- a/layout/generic/nsGridContainerFrame.cpp
+++ b/layout/generic/nsGridContainerFrame.cpp
@@ -8596,14 +8596,24 @@ void nsGridContainerFrame::Reflow(nsPresContext* aPresContext,
grid.mGridColEnd = subgrid->mGridColEnd;
grid.mGridRowEnd = subgrid->mGridRowEnd;
}
- gridReflowInput.CalculateTrackSizes(grid, computedSize,
- SizingConstraint::NoConstraint);
// XXX Technically incorrect: 'contain-intrinsic-block-size: none' is
// treated as 0, ignoring our row sizes, when really we should use them but
// *they* should be computed as if we had no children. To be fixed in bug
// 1488878.
- if (Maybe containBSize =
- aReflowInput.mFrame->ContainIntrinsicBSize()) {
+ const Maybe containBSize =
+ aReflowInput.mFrame->ContainIntrinsicBSize();
+ const nscoord trackSizingBSize = [&] {
+ // This clamping only applies to auto sizes.
+ if (containBSize && computedBSize == NS_UNCONSTRAINEDSIZE) {
+ return NS_CSS_MINMAX(*containBSize, aReflowInput.ComputedMinBSize(),
+ aReflowInput.ComputedMaxBSize());
+ }
+ return computedBSize;
+ }();
+ const LogicalSize containLogicalSize(wm, computedISize, trackSizingBSize);
+ gridReflowInput.CalculateTrackSizes(grid, containLogicalSize,
+ SizingConstraint::NoConstraint);
+ if (containBSize) {
bSize = *containBSize;
} else {
if (IsMasonry(eLogicalAxisBlock)) {
diff --git a/layout/generic/nsIFrame.cpp b/layout/generic/nsIFrame.cpp
index 14a79d9b2d436..bacd22e974361 100644
--- a/layout/generic/nsIFrame.cpp
+++ b/layout/generic/nsIFrame.cpp
@@ -10603,10 +10603,10 @@ nsSize nsIFrame::GetXULMaxSize(nsBoxLayoutState& aState) {
return size;
}
-nscoord nsIFrame::GetXULFlex() {
+int32_t nsIFrame::GetXULFlex() {
nsBoxLayoutMetrics* metrics = BoxMetrics();
if (XULNeedsRecalc(metrics->mFlex)) {
- nsIFrame::AddXULFlex(this, metrics->mFlex);
+ metrics->mFlex = nsIFrame::ComputeXULFlex(this);
}
return metrics->mFlex;
diff --git a/layout/generic/nsIFrame.h b/layout/generic/nsIFrame.h
index 9901f7c011471..cc9c84887ec1f 100644
--- a/layout/generic/nsIFrame.h
+++ b/layout/generic/nsIFrame.h
@@ -4265,7 +4265,7 @@ class nsIFrame : public nsQueryFrame {
*/
virtual nsSize GetXULMaxSize(nsBoxLayoutState& aBoxLayoutState);
- virtual nscoord GetXULFlex();
+ virtual int32_t GetXULFlex();
virtual nscoord GetXULBoxAscent(nsBoxLayoutState& aBoxLayoutState);
virtual bool IsXULCollapsed();
// This does not alter the overflow area. If the caller is changing
@@ -4310,7 +4310,7 @@ class nsIFrame : public nsQueryFrame {
bool& aHeightSet);
static bool AddXULMaxSize(nsIFrame* aBox, nsSize& aSize, bool& aWidth,
bool& aHeightSet);
- static bool AddXULFlex(nsIFrame* aBox, nscoord& aFlex);
+ static int32_t ComputeXULFlex(nsIFrame* aBox);
void AddXULBorderAndPadding(nsSize& aSize);
diff --git a/layout/reftests/box/flex-emulation-1-notref.xhtml b/layout/reftests/box/flex-emulation-1-ref.xhtml
similarity index 69%
rename from layout/reftests/box/flex-emulation-1-notref.xhtml
rename to layout/reftests/box/flex-emulation-1-ref.xhtml
index b99433f69edcf..31603dcf40b32 100644
--- a/layout/reftests/box/flex-emulation-1-notref.xhtml
+++ b/layout/reftests/box/flex-emulation-1-ref.xhtml
@@ -6,7 +6,6 @@
+
+
+
+ X
+
+
+
+