Merge autoland to mozilla-central. a=merge

This commit is contained in:
Narcis Beleuzu 2023-10-17 06:56:59 +03:00
commit e0488c86ff
695 changed files with 37927 additions and 13133 deletions

29
Cargo.lock generated
View file

@ -351,6 +351,10 @@ dependencies = [
name = "autocfg"
version = "1.1.0"
[[package]]
name = "backtrace"
version = "0.3.999"
[[package]]
name = "base64"
version = "0.13.999"
@ -1693,9 +1697,9 @@ checksum = "cda653ca797810c02f7ca4b804b40b8b95ae046eb989d356bce17919a8c25499"
[[package]]
name = "flate2"
version = "1.0.25"
version = "1.0.26"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a8a2db397cb1c8772f31494cb8917e48cd1e64f0fa7efac59fbd741a0a8ce841"
checksum = "3b9429470923de8e8cbd4d2dc513535400b4b3fef0319fb5c4e1f520a7bef743"
dependencies = [
"crc32fast",
"miniz_oxide",
@ -3485,9 +3489,9 @@ checksum = "68354c5c6bd36d73ff3feceb05efa59b6acb7626617f4962be322a825e61f79a"
[[package]]
name = "miniz_oxide"
version = "0.6.2"
version = "0.7.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b275950c28b37e794e8c55d88aeb5e139d0ce23fdbbeda68f8d7174abdf9e8fa"
checksum = "e7810e0be55b428ada41041c41f32c9f1a42817901b4ccf45fa3d4b6561e74c7"
dependencies = [
"adler",
]
@ -4026,9 +4030,9 @@ dependencies = [
[[package]]
name = "object"
version = "0.30.3"
version = "0.32.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ea86265d3d3dcb6a27fc51bd29a4bf387fae9d2986b823079d4986af253eb439"
checksum = "77ac5bbd07aea88c60a577a1ce218075ffd59208b2d7ca97adf9bfc5aeb21ebe"
dependencies = [
"memchr",
]
@ -5096,9 +5100,9 @@ dependencies = [
[[package]]
name = "socket2"
version = "0.4.7"
version = "0.4.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "02e2d2db9033d13a1567121ddd7a095ee144db4e1ca1b1bda3419bc0da294ebd"
checksum = "64a4a911eed85daf18834cfaa86a79b7d266ff93ff5ba14005426219480ed662"
dependencies = [
"libc",
"winapi",
@ -5544,18 +5548,19 @@ dependencies = [
[[package]]
name = "tokio"
version = "1.17.0"
version = "1.29.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2af73ac49756f3f7c01172e34a23e5d0216f6c32333757c2c61feb2bbff5a5ee"
checksum = "532826ff75199d5833b9d2c5fe410f29235e25704ee5f0ef599fb51c21f4a4da"
dependencies = [
"autocfg",
"backtrace",
"bytes",
"libc",
"memchr",
"mio 0.8.8",
"num_cpus",
"pin-project-lite",
"socket2",
"winapi",
"windows-sys",
]
[[package]]

View file

@ -132,6 +132,11 @@ base64 = { path = "build/rust/base64" }
# Patch wasi 0.10 to 0.11
wasi = { path = "build/rust/wasi" }
# tokio 0.29.0 includes an experimental "tracing" feature which requires
# backtrace ^0.3.58 and the `tokio_unstable` flag. We don't use it, and nothing
# else we do use requires backtrace, so dummy it out for now.
backtrace = { path = "build/rust/backtrace" }
# Patch bindgen 0.63, 0.64 and 0.66 to 0.68
bindgen_0_63 = { package = "bindgen", path = "build/rust/bindgen-0.63" }
bindgen_0_64 = { package = "bindgen", path = "build/rust/bindgen-0.64" }

View file

@ -2184,10 +2184,9 @@ pref("browser.migrate.opera-gx.enabled", true);
pref("browser.migrate.safari.enabled", true);
pref("browser.migrate.vivaldi.enabled", true);
pref("browser.migrate.content-modal.enabled", true);
pref("browser.migrate.content-modal.import-all.enabled", true);
// Values can be: "default", "autoclose", "standalone", "legacy", "embedded".
// Values can be: "default", "autoclose", "standalone", "embedded".
pref("browser.migrate.content-modal.about-welcome-behavior", "embedded");
// The maximum age of history entries we'll import, in days.

View file

@ -689,25 +689,23 @@
</panelview>
<panelview id="reset-pbm-panel" class="PanelUI-subView">
<hbox id="reset-pbm-panel-container">
<hbox id="reset-pbm-panel-header">
<description data-l10n-id="reset-pbm-panel-heading"/>
</hbox>
<description id="reset-pbm-panel-description" data-l10n-id="reset-pbm-panel-description"/>
<checkbox id="reset-pbm-panel-checkbox" data-l10n-id="reset-pbm-panel-always-ask-checkbox"/>
<html:moz-button-group id="reset-pbm-panel-footer" class="panel-footer">
<button id="reset-pbm-panel-cancel-button"
class="panel-footer-button"
data-l10n-id="reset-pbm-panel-cancel-button"
oncommand="ResetPBMPanel.onCancel(this)"></button>
<button slot="primary"
id="reset-pbm-panel-confirm-button"
class="panel-footer-button"
data-l10n-id="reset-pbm-panel-confirm-button"
oncommand="ResetPBMPanel.onConfirm(this)"></button>
</html:moz-button-group>
<hbox id="reset-pbm-panel-header">
<description data-l10n-id="reset-pbm-panel-heading"/>
</hbox>
<description id="reset-pbm-panel-description" data-l10n-id="reset-pbm-panel-description"/>
<checkbox id="reset-pbm-panel-checkbox" data-l10n-id="reset-pbm-panel-always-ask-checkbox"/>
<html:moz-button-group id="reset-pbm-panel-footer" class="panel-footer">
<button id="reset-pbm-panel-cancel-button"
class="panel-footer-button"
data-l10n-id="reset-pbm-panel-cancel-button"
oncommand="ResetPBMPanel.onCancel(this)"></button>
<button slot="primary"
id="reset-pbm-panel-confirm-button"
class="panel-footer-button"
data-l10n-id="reset-pbm-panel-confirm-button"
oncommand="ResetPBMPanel.onConfirm(this)"></button>
</html:moz-button-group>
</panelview>
</html:template>

View file

@ -4,24 +4,20 @@
"use strict";
add_setup(async () => {
// Load the initial tab at example.com. This makes it so that if
// we're using the new migration wizard, we'll load the about:preferences
// page in a new tab rather than overtaking the initial one. This
// makes it easier to be consistent with closing and opening
// behaviours between the two kinds of migration wizards.
// Load the initial tab at example.com. This makes it so that when the
// about:preferences hosting the migration wizard opens, we'll load
// the about:preferences page in a new tab rather than overtaking the
// initial one. This makes it easier to be more explicit when cleaning
// up because we can just remove the opened tab.
let browser = gBrowser.selectedBrowser;
BrowserTestUtils.startLoadingURIString(browser, "https://example.com");
await BrowserTestUtils.browserLoaded(browser);
});
add_task(async function file_menu_import_wizard() {
// We can't call this code directly or our JS execution will get blocked on Windows/Linux where
// the dialog is modal.
executeSoon(() =>
document.getElementById("menu_importFromAnotherBrowser").doCommand()
);
let wizard = await BrowserTestUtils.waitForMigrationWizard(window);
ok(wizard, "Migrator window opened");
await BrowserTestUtils.closeMigrationWizard(wizard);
let wizardTabPromise = BrowserTestUtils.waitForMigrationWizard(window);
document.getElementById("menu_importFromAnotherBrowser").doCommand();
let wizardTab = await wizardTabPromise;
ok(wizardTab, "Migration wizard tab opened");
BrowserTestUtils.removeTab(wizardTab);
});

View file

@ -27,7 +27,6 @@ skip-if = [
["browser_permission_delegate_geo.js"]
["browser_permissions.js"]
skip-if = ["a11y_checks"] # Bug 1858037 to investigate intermittent a11y_checks results (fails on Try, passes on Autoland)
["browser_permissions_delegate_vibrate.js"]
support-files = ["empty.html"]
@ -41,7 +40,6 @@ support-files = ["dummy.js"]
["browser_reservedkey.js"]
["browser_site_scoped_permissions.js"]
skip-if = ["a11y_checks"] # Bug 1858037 to investigate intermittent a11y_checks results (fails on Try, passes on Autoland)
["browser_temporary_permissions.js"]
support-files = ["../webrtc/get_user_media.html"]

View file

@ -60,7 +60,16 @@ add_task(async function testMainViewVisible() {
PermissionTestUtils.remove(gBrowser.currentURI, "camera");
// We intentionally turn off a11y_checks, because the following function
// is expected to click a toolbar button that may be already hidden
// with "display:none;". The permissions panel anchor is hidden because
// the last permission was removed, however we force opening the panel
// anyways in order to test that the list has been properly emptied:
AccessibilityUtils.setEnv({
mustHaveAccessibleRule: false,
});
await openPermissionPopup();
AccessibilityUtils.resetEnv();
testPermListHasEntries(false);

View file

@ -46,7 +46,16 @@ add_task(async function testSiteScopedPermissionSubdomainAffectsBaseDomain() {
Services.perms.removeFromPrincipal(subdomainPrincipal, id);
// We intentionally turn off a11y_checks, because the following function
// is expected to click a toolbar button that may be already hidden
// with "display:none;". The permissions panel anchor is hidden because
// the last permission was removed, however we force opening the panel
// anyways in order to test that the list has been properly emptied:
AccessibilityUtils.setEnv({
mustHaveAccessibleRule: false,
});
await openPermissionPopup();
AccessibilityUtils.resetEnv();
listEntryCount = permissionsList.querySelectorAll(
".permission-popup-permission-item-3rdPartyStorage"

View file

@ -73,6 +73,11 @@ var gExceptionPaths = [
// CSS files are referenced inside JS in an html template
"chrome://browser/content/aboutlogins/components/",
// These files are for the old migration wizard which will be removed
// shortly.
"chrome://browser/content/migration/migration.xhtml",
"chrome://browser/content/migration/migration.js",
];
// These are not part of the omni.ja file, so we find them only when running

View file

@ -108,9 +108,9 @@ add_task(async function test_no_logins_class() {
// End the test now for Linux since the link is hidden.
return;
}
let wizard = await wizardPromise;
Assert.ok(wizard, "Migrator window opened");
await BrowserTestUtils.closeMigrationWizard(wizard);
let wizardTab = await wizardPromise;
Assert.ok(wizardTab, "Migrator wizard tab opened");
await BrowserTestUtils.removeTab(wizardTab);
});
add_task(

View file

@ -25,7 +25,7 @@ add_setup(async function () {
});
add_task(async function test_open_import() {
let promiseImportWindow = BrowserTestUtils.waitForMigrationWizard(window);
let promiseWizardTab = BrowserTestUtils.waitForMigrationWizard(window);
let browser = gBrowser.selectedBrowser;
await BrowserTestUtils.synthesizeMouseAtCenter("menu-button", {}, browser);
@ -44,9 +44,9 @@ add_task(async function test_open_import() {
}
await BrowserTestUtils.synthesizeMouseAtCenter(getImportItem, {}, browser);
info("waiting for Import to get opened");
let importWindow = await promiseImportWindow;
Assert.ok(true, "Import opened");
info("waiting for migration wizard to open");
let wizardTab = await promiseWizardTab;
Assert.ok(wizardTab, "Migration wizard opened");
// First event is for opening about:logins
await LoginTestUtils.telemetry.waitForEventCount(2);
@ -56,5 +56,5 @@ add_task(async function test_open_import() {
{ process: "content" }
);
await BrowserTestUtils.closeMigrationWizard(importWindow);
await BrowserTestUtils.removeTab(wizardTab);
});

View file

@ -515,11 +515,15 @@ export class FxviewTabRow extends MozLitElement {
backgroundImage: `url(${this.getImageUrl(this.favicon, this.url)})`,
})}
></span>
<span class="fxview-tab-row-title" id="fxview-tab-row-title">
<span
class="fxview-tab-row-title text-truncated-ellipsis"
id="fxview-tab-row-title"
dir="auto"
>
${title}
</span>
<span
class="fxview-tab-row-url"
class="fxview-tab-row-url text-truncated-ellipsis"
id="fxview-tab-row-url"
?hidden=${this.compact}
>

View file

@ -77,19 +77,14 @@
}
.fxview-tab-row-title {
text-overflow: ellipsis;
white-space: nowrap;
text-align: match-parent;
}
.fxview-tab-row-url {
color: var(--text-color-deemphasized);
word-break: break-word;
text-decoration-line: underline;
}
.fxview-tab-row-title,
.fxview-tab-row-url {
overflow: hidden;
direction: ltr;
text-align: match-parent;
}
.fxview-tab-row-date,

View file

@ -31,7 +31,6 @@ var gMigrators = null;
var gFileMigrators = null;
var gProfileStartup = null;
var gL10n = null;
var gHasOpenedLegacyWizard = false;
let gForceExitSpinResolve = false;
let gKeepUndoData = false;
@ -552,8 +551,8 @@ class MigrationUtils {
}
/**
* Show the migration wizard. On mac, this may just focus the wizard if it's
* already running, in which case aOpener and aOptions are ignored.
* Show the migration wizard in about:preferences, or if there is not an existing
* browser window open, in a new top-level dialog window.
*
* NB: If you add new consumers, please add a migration entry point constant to
* MIGRATION_ENTRYPOINTS and supply that entrypoint with the entrypoint property
@ -577,10 +576,9 @@ class MigrationUtils {
* @param {string} [aOptions.profileId]
* An identifier for the profile to use when migrating.
* @returns {Promise<undefined>}
* If the new content-modal migration dialog is enabled and an
* about:preferences tab can be opened, this will resolve when
* If an about:preferences tab can be opened, this will resolve when
* that tab has been switched to. Otherwise, this will resolve
* just after opening the dialog window.
* just after opening the top-level dialog window.
*/
showMigrationWizard(aOpener, aOptions) {
// When migration is kicked off from about:welcome, there are
@ -597,10 +595,6 @@ class MigrationUtils {
// The migration wizard will open in a new top-level content
// window.
//
// "legacy":
// The legacy migration wizard will open, even if the new migration
// wizard is enabled by default.
//
// "default" / other
// The user will be directed to the migration wizard in
// about:preferences. The tab will not close once the
@ -610,92 +604,59 @@ class MigrationUtils {
"default"
);
let aboutWelcomeLegacyBehavior =
aboutWelcomeBehavior == "legacy" &&
aOptions.entrypoint == this.MIGRATION_ENTRYPOINTS.NEWTAB;
let entrypoint = aOptions.entrypoint || this.MIGRATION_ENTRYPOINTS.UNKNOWN;
Services.telemetry
.getHistogramById("FX_MIGRATION_ENTRY_POINT_CATEGORICAL")
.add(entrypoint);
if (
Services.prefs.getBoolPref(
"browser.migrate.content-modal.enabled",
false
) &&
!aboutWelcomeLegacyBehavior
) {
let entrypoint =
aOptions.entrypoint || this.MIGRATION_ENTRYPOINTS.UNKNOWN;
Services.telemetry
.getHistogramById("FX_MIGRATION_ENTRY_POINT_CATEGORICAL")
.add(entrypoint);
let openStandaloneWindow = blocking => {
let features = "dialog,centerscreen,resizable=no";
let openStandaloneWindow = blocking => {
let features = "dialog,centerscreen,resizable=no";
if (blocking) {
features += ",modal";
}
if (blocking) {
features += ",modal";
Services.ww.openWindow(
aOpener,
"chrome://browser/content/migration/migration-dialog-window.html",
"_blank",
features,
{
options: aOptions,
}
);
return Promise.resolve();
};
Services.ww.openWindow(
aOpener,
"chrome://browser/content/migration/migration-dialog-window.html",
"_blank",
features,
{
options: aOptions,
}
if (aOptions.isStartupMigration) {
// Record that the uninstaller requested a profile refresh
if (Services.env.get("MOZ_UNINSTALLER_PROFILE_REFRESH")) {
Services.env.set("MOZ_UNINSTALLER_PROFILE_REFRESH", "");
Services.telemetry.scalarSet(
"migration.uninstaller_profile_refresh",
true
);
return Promise.resolve();
};
if (aOptions.isStartupMigration) {
// Record that the uninstaller requested a profile refresh
if (Services.env.get("MOZ_UNINSTALLER_PROFILE_REFRESH")) {
Services.env.set("MOZ_UNINSTALLER_PROFILE_REFRESH", "");
Services.telemetry.scalarSet(
"migration.uninstaller_profile_refresh",
true
);
}
openStandaloneWindow(true /* blocking */);
return Promise.resolve();
}
if (aOpener?.openPreferences) {
if (aOptions.entrypoint == this.MIGRATION_ENTRYPOINTS.NEWTAB) {
if (aboutWelcomeBehavior == "autoclose") {
return aOpener.openPreferences("general-migrate-autoclose");
} else if (aboutWelcomeBehavior == "standalone") {
openStandaloneWindow(false /* blocking */);
return Promise.resolve();
}
}
return aOpener.openPreferences("general-migrate");
}
// If somehow we failed to open about:preferences, fall back to opening
// the top-level window.
openStandaloneWindow(false /* blocking */);
openStandaloneWindow(true /* blocking */);
return Promise.resolve();
}
// Legacy migration dialog
if (!gHasOpenedLegacyWizard) {
gHasOpenedLegacyWizard = true;
aOptions.openedTime = Cu.now();
if (aOpener?.openPreferences) {
if (aOptions.entrypoint == this.MIGRATION_ENTRYPOINTS.NEWTAB) {
if (aboutWelcomeBehavior == "autoclose") {
return aOpener.openPreferences("general-migrate-autoclose");
} else if (aboutWelcomeBehavior == "standalone") {
openStandaloneWindow(false /* blocking */);
return Promise.resolve();
}
}
return aOpener.openPreferences("general-migrate");
}
const DIALOG_URL = "chrome://browser/content/migration/migration.xhtml";
let features = "chrome,dialog,modal,centerscreen,titlebar,resizable=no";
if (AppConstants.platform == "macosx" && !this.isStartupMigration) {
let win = Services.wm.getMostRecentWindow("Browser:MigrationWizard");
if (win) {
win.focus();
return Promise.resolve();
}
// On mac, the migration wiazrd should only be modal in the case of
// startup-migration.
features = "centerscreen,chrome,resizable=no";
}
Services.ww.openWindow(aOpener, DIALOG_URL, "_blank", features, aOptions);
// If somehow we failed to open about:preferences, fall back to opening
// the top-level window.
openStandaloneWindow(false /* blocking */);
return Promise.resolve();
}

View file

@ -4,11 +4,6 @@ Migration Wizard Reference
The migration wizard is the piece of UI that allows users to migrate from other browsers to Firefox.
.. note::
Firefox has had a legacy migration wizard for many years, and this has historically been a top-level XUL dialog window. **This documentation is not for the legacy migration wizard**, but is instead for an in-progress replacement migration wizard.
The new migration wizard can be enabled by setting ``browser.migrate.content-modal.enabled`` to ``true``.
The migration wizard can be embedded in the following contexts:
1. In a top level stand-alone dialog window

View file

@ -1,7 +1,6 @@
[DEFAULT]
head = "head.js"
prefs = [
"browser.migrate.content-modal.enabled=true",
"browser.migrate.internal-testing.enabled=true",
"dom.window.sizeToContent.enabled=true",
]

View file

@ -3,58 +3,27 @@
"use strict";
const CONTENT_MODAL_ENABLED_PREF = "browser.migrate.content-modal.enabled";
const HISTOGRAM_ID = "FX_MIGRATION_ENTRY_POINT_CATEGORICAL";
const LEGACY_HISTOGRAM_ID = "FX_MIGRATION_ENTRY_POINT";
async function showThenCloseMigrationWizardViaEntrypoint(entrypoint) {
let openedPromise = BrowserTestUtils.waitForMigrationWizard(window);
// On some platforms, this call blocks, so in order to let the test proceed, we
// run it on the next tick of the event loop.
executeSoon(() => {
MigrationUtils.showMigrationWizard(window, {
entrypoint,
});
MigrationUtils.showMigrationWizard(window, {
entrypoint,
});
let wizard = await openedPromise;
Assert.ok(wizard, "Migration wizard opened.");
let wizardTab = await openedPromise;
Assert.ok(wizardTab, "Migration wizard opened.");
if (!Services.prefs.getBoolPref(CONTENT_MODAL_ENABLED_PREF)) {
// This scalar gets written to asynchronously. The old wizard will be going
// away soon, so we'll just poll for it to be set rather than doing anything
// fancier.
await BrowserTestUtils.waitForCondition(() => {
// We should make sure that the migration.time_to_produce_legacy_migrator_list
// scalar was set, since we know that at least one legacy migration wizard has
// been opened.
let scalars = TelemetryTestUtils.getProcessScalars(
"parent",
false,
false
);
if (!scalars["migration.time_to_produce_legacy_migrator_list"]) {
return false;
}
Assert.ok(
scalars["migration.time_to_produce_legacy_migrator_list"] > 0,
"Non-zero scalar value recorded for migration.time_to_produce_migrator_list"
);
return true;
});
}
await BrowserTestUtils.closeMigrationWizard(wizard);
await BrowserTestUtils.removeTab(wizardTab);
}
add_setup(async () => {
// Load the initial tab at example.com. This makes it so that if
// we're using the new migration wizard, we'll load the about:preferences
// page in a new tab rather than overtaking the initial one. This
// makes it easier to be consistent with closing and opening
// behaviours between the two kinds of migration wizards.
// when we load the wizard in about:preferences, we'll load the
// about:preferences page in a new tab rather than overtaking the
// initial one. This makes cleanup of the wizard more explicit, since
// we can just close the tab.
let browser = gBrowser.selectedBrowser;
BrowserTestUtils.startLoadingURIString(browser, "https://example.com");
await BrowserTestUtils.browserLoaded(browser);
@ -62,76 +31,42 @@ add_setup(async () => {
/**
* Tests that the entrypoint passed to MigrationUtils.showMigrationWizard gets
* written to both the FX_MIGRATION_ENTRY_POINT_CATEGORICAL histogram, as well
* as the legacy FX_MIGRATION_ENTRY_POINT histogram (but only if using the old
* wizard window).
* written to both the FX_MIGRATION_ENTRY_POINT_CATEGORICAL histogram.
*/
add_task(async function test_legacy_wizard() {
for (let contentModalEnabled of [true, false]) {
info("Testing with content modal enabled: " + contentModalEnabled);
await SpecialPowers.pushPrefEnv({
set: [[CONTENT_MODAL_ENABLED_PREF, contentModalEnabled]],
});
add_task(async function test_entrypoints() {
let histogram = TelemetryTestUtils.getAndClearHistogram(HISTOGRAM_ID);
let histogram = TelemetryTestUtils.getAndClearHistogram(HISTOGRAM_ID);
let legacyHistogram =
TelemetryTestUtils.getAndClearHistogram(LEGACY_HISTOGRAM_ID);
// Let's arbitrarily pick the "Bookmarks" entrypoint, and make sure this
// is recorded.
await showThenCloseMigrationWizardViaEntrypoint(
MigrationUtils.MIGRATION_ENTRYPOINTS.BOOKMARKS
);
let entrypointId = MigrationUtils.getLegacyMigrationEntrypoint(
MigrationUtils.MIGRATION_ENTRYPOINTS.BOOKMARKS
);
TelemetryTestUtils.assertHistogram(histogram, entrypointId, 1);
if (!contentModalEnabled) {
TelemetryTestUtils.assertHistogram(legacyHistogram, entrypointId, 1);
}
histogram = TelemetryTestUtils.getAndClearHistogram(HISTOGRAM_ID);
legacyHistogram =
TelemetryTestUtils.getAndClearHistogram(LEGACY_HISTOGRAM_ID);
// Now let's pick the "Preferences" entrypoint, and make sure this
// is recorded.
await showThenCloseMigrationWizardViaEntrypoint(
MigrationUtils.MIGRATION_ENTRYPOINTS.PREFERENCES
);
entrypointId = MigrationUtils.getLegacyMigrationEntrypoint(
MigrationUtils.MIGRATION_ENTRYPOINTS.PREFERENCES
);
TelemetryTestUtils.assertHistogram(histogram, entrypointId, 1);
if (!contentModalEnabled) {
TelemetryTestUtils.assertHistogram(legacyHistogram, entrypointId, 1);
}
histogram = TelemetryTestUtils.getAndClearHistogram(HISTOGRAM_ID);
legacyHistogram =
TelemetryTestUtils.getAndClearHistogram(LEGACY_HISTOGRAM_ID);
// Finally, check the fallback by passing in something invalid as an entrypoint.
await showThenCloseMigrationWizardViaEntrypoint(undefined);
entrypointId = MigrationUtils.getLegacyMigrationEntrypoint(
MigrationUtils.MIGRATION_ENTRYPOINTS.UNKNOWN
);
TelemetryTestUtils.assertHistogram(histogram, entrypointId, 1);
if (!contentModalEnabled) {
TelemetryTestUtils.assertHistogram(legacyHistogram, entrypointId, 1);
}
}
// We should make sure that the migration.time_to_produce_legacy_migrator_list
// scalar was set, since we know that at least one legacy migration wizard has
// been opened.
let scalars = TelemetryTestUtils.getProcessScalars("parent", false, false);
Assert.ok(
scalars["migration.time_to_produce_legacy_migrator_list"] > 0,
"Non-zero scalar value recorded for migration.time_to_produce_migrator_list"
// Let's arbitrarily pick the "Bookmarks" entrypoint, and make sure this
// is recorded.
await showThenCloseMigrationWizardViaEntrypoint(
MigrationUtils.MIGRATION_ENTRYPOINTS.BOOKMARKS
);
let entrypointId = MigrationUtils.getLegacyMigrationEntrypoint(
MigrationUtils.MIGRATION_ENTRYPOINTS.BOOKMARKS
);
TelemetryTestUtils.assertHistogram(histogram, entrypointId, 1);
histogram = TelemetryTestUtils.getAndClearHistogram(HISTOGRAM_ID);
// Now let's pick the "Preferences" entrypoint, and make sure this
// is recorded.
await showThenCloseMigrationWizardViaEntrypoint(
MigrationUtils.MIGRATION_ENTRYPOINTS.PREFERENCES
);
entrypointId = MigrationUtils.getLegacyMigrationEntrypoint(
MigrationUtils.MIGRATION_ENTRYPOINTS.PREFERENCES
);
TelemetryTestUtils.assertHistogram(histogram, entrypointId, 1);
histogram = TelemetryTestUtils.getAndClearHistogram(HISTOGRAM_ID);
// Finally, check the fallback by passing in something invalid as an entrypoint.
await showThenCloseMigrationWizardViaEntrypoint(undefined);
entrypointId = MigrationUtils.getLegacyMigrationEntrypoint(
MigrationUtils.MIGRATION_ENTRYPOINTS.UNKNOWN
);
TelemetryTestUtils.assertHistogram(histogram, entrypointId, 1);
});

View file

@ -15,12 +15,6 @@ const IMPORT_SCREEN = {
},
};
const FORCE_LEGACY =
Services.prefs.getCharPref(
"browser.migrate.content-modal.about-welcome-behavior",
"default"
) === "legacy";
add_task(async function test_wait_import_modal() {
await setAboutWelcomeMultiStage(
JSON.stringify([IMPORT_SCREEN, { id: "AW_NEXT", content: {} }])
@ -38,10 +32,7 @@ add_task(async function test_wait_import_modal() {
["main.AW_NEXT"]
);
const wizardPromise = BrowserTestUtils.waitForMigrationWizard(
window,
FORCE_LEGACY
);
const wizardPromise = BrowserTestUtils.waitForMigrationWizard(window);
const prefsTab = await BrowserTestUtils.openNewForegroundTab(
gBrowser,
"about:preferences"
@ -59,7 +50,7 @@ add_task(async function test_wait_import_modal() {
["main.AW_NEXT"]
);
await BrowserTestUtils.closeMigrationWizard(wizard, FORCE_LEGACY);
await BrowserTestUtils.removeTab(wizard);
await test_screen_content(
browser,
@ -86,10 +77,7 @@ add_task(async function test_wait_import_spotlight() {
});
const [win] = await spotlightPromise;
const wizardPromise = BrowserTestUtils.waitForMigrationWizard(
window,
FORCE_LEGACY
);
const wizardPromise = BrowserTestUtils.waitForMigrationWizard(window);
const prefsTab = await BrowserTestUtils.openNewForegroundTab(
gBrowser,
"about:preferences"
@ -99,7 +87,7 @@ add_task(async function test_wait_import_spotlight() {
.click();
const wizard = await wizardPromise;
await BrowserTestUtils.closeMigrationWizard(wizard, FORCE_LEGACY);
await BrowserTestUtils.removeTab(wizard);
// cleanup
BrowserTestUtils.removeTab(prefsTab);

View file

@ -1049,7 +1049,7 @@ var gMainPane = {
const supportedLanguages =
await TranslationsParent.getSupportedLanguages();
const languageList =
TranslationsState.getLanguageList(supportedLanguages);
TranslationsParent.getLanguageList(supportedLanguages);
const downloadPhases = await TranslationsState.createDownloadPhases(
languageList
);
@ -1067,38 +1067,6 @@ var gMainPane = {
);
}
/**
* Create a unique list of languages, sorted by the display name.
*
* @param {Object} supportedLanguages
* @returns {Array<{ langTag: string, displayName: string}}
*/
static getLanguageList(supportedLanguages) {
const displayNames = new Map();
for (const languages of [
supportedLanguages.fromLanguages,
supportedLanguages.toLanguages,
]) {
for (const { langTag, displayName } of languages) {
displayNames.set(langTag, displayName);
}
}
let appLangTag = new Intl.Locale(Services.locale.appLocaleAsBCP47)
.language;
// Don't offer to download the app's language.
displayNames.delete(appLangTag);
// Sort the list of languages by the display names.
return [...displayNames.entries()]
.map(([langTag, displayName]) => ({
langTag,
displayName,
}))
.sort((a, b) => a.displayName.localeCompare(b.displayName));
}
/**
* Determine the download phase of each language file.
*
@ -2164,22 +2132,14 @@ var gMainPane = {
},
onMigrationButtonCommand(command) {
// When browser.migrate.content-modal.enabled is enabled by default,
// the event handler can just call showMigrationWizardDialog directly,
// but for now, we delegate to MigrationUtils to open the native modal
// in case that's the dialog we're still using.
//
// Enabling the pref by default will be part of bug 1822156.
const browser = window.docShell.chromeEventHandler;
const browserWindow = browser.ownerGlobal;
// Even though we're going to be showing the migration wizard here in
// about:preferences, we'll delegate the call to
// `MigrationUtils.showMigrationWizard`, as this will allow us to
// properly measure entering the dialog from the PREFERENCES entrypoint.
const browserWindow = window.browsingContext.topChromeWindow;
// showMigrationWizard blocks on some platforms. We'll dispatch the request
// to open to a runnable on the main thread so that we don't have to block
// this function call.
Services.tm.dispatchToMainThread(() => {
MigrationUtils.showMigrationWizard(browserWindow, {
entrypoint: MigrationUtils.MIGRATION_ENTRYPOINTS.PREFERENCES,
});
MigrationUtils.showMigrationWizard(browserWindow, {
entrypoint: MigrationUtils.MIGRATION_ENTRYPOINTS.PREFERENCES,
});
},

View file

@ -15,33 +15,6 @@ add_task(async function test_open_migration_wizard() {
async function (browser) {
let button = browser.contentDocument.getElementById(BUTTON_ID);
// First, we'll test the legacy Migration Wizard.
await SpecialPowers.pushPrefEnv({
set: [["browser.migrate.content-modal.enabled", false]],
});
let migrationWizardWindow = BrowserTestUtils.domWindowOpenedAndLoaded(
null,
win => {
let type = win.document.documentElement.getAttribute("windowtype");
if (type == "Browser:MigrationWizard") {
Assert.ok(true, "Saw legacy Migration Wizard window open.");
return true;
}
return false;
}
);
button.click();
let win = await migrationWizardWindow;
await BrowserTestUtils.closeWindow(win);
// Next, we'll test the new Migration Wizard.
await SpecialPowers.pushPrefEnv({
set: [["browser.migrate.content-modal.enabled", true]],
});
let wizardReady = BrowserTestUtils.waitForEvent(
browser.contentWindow,
"MigrationWizard:Ready"

View file

@ -51,24 +51,22 @@ add_task(async function test_cookie_banners_promo() {
});
await ASRouter.onPrefChange();
const { win, tab } = await openTabAndWaitForRender();
const sandbox = sinon.createSandbox();
const expectedUrl = Services.urlFormatter.formatURL(
"https://support.mozilla.org/1/firefox/%VERSION%/%OS%/%LOCALE%/cookie-banner-reduction"
);
let tabOpened = new Promise(resolve => {
win.gBrowser.tabContainer.addEventListener(
"TabOpen",
event => {
let newTab = event.target;
let newBrowser = newTab.linkedBrowser;
let result = newTab;
BrowserTestUtils.waitForDocLoadAndStopIt(expectedUrl, newBrowser).then(
() => resolve(result)
);
},
{ once: true }
);
const { win, tab } = await openTabAndWaitForRender();
let triedToOpenTab = new Promise(resolve => {
sandbox.stub(win, "openLinkIn").callsFake((url, where) => {
is(url, expectedUrl, "The link should open the expected URL");
is(
where,
"tabshifted",
"The link should open the expected URL in a new foreground tab"
);
resolve();
});
});
await SpecialPowers.spawn(tab, [promoImgSrc], async function (imgSrc) {
@ -77,10 +75,11 @@ add_task(async function test_cookie_banners_promo() {
);
ok(promoImage?.src === imgSrc, "Cookie banner reduction promo is shown");
let linkEl = content.document.getElementById("private-browsing-promo-link");
EventUtils.synthesizeClick(linkEl);
linkEl.click();
});
await tabOpened;
await triedToOpenTab;
sandbox.restore();
ok(true, "The link was clicked and the new tab opened");

View file

@ -645,7 +645,7 @@
"win64-aarch64-devedition",
"win64-devedition"
],
"revision": "a2ff61a05d98f6ac590af31e20b9584f3fc1ce59"
"revision": "10247f877325c5d3e2e6421c9fc6f832aecb14c3"
},
"fur": {
"pin": false,
@ -663,7 +663,7 @@
"win64-aarch64-devedition",
"win64-devedition"
],
"revision": "7c858ed3cdb39ba09620131778166de4c5e8c4af"
"revision": "81fe64e956bf23f74da866bcdd4465407a6e91db"
},
"fy-NL": {
"pin": false,
@ -753,7 +753,7 @@
"win64-aarch64-devedition",
"win64-devedition"
],
"revision": "0fa7f55ed6790d99b8af40e2dfafd02a4f422991"
"revision": "ea5321945f68d7fb674802ad2793cd49deee54f7"
},
"gu-IN": {
"pin": false,
@ -789,7 +789,7 @@
"win64-aarch64-devedition",
"win64-devedition"
],
"revision": "21494472c9e94f59dbdf3b64a3e860914784bd53"
"revision": "d7ec09a12940e974bf6611377752f2985331adac"
},
"hi-IN": {
"pin": false,
@ -843,7 +843,7 @@
"win64-aarch64-devedition",
"win64-devedition"
],
"revision": "41292dabca672049b111529a6e43d6d334e70b74"
"revision": "2b9fc1dc146113f573fd6d6311de23b044c6d092"
},
"hu": {
"pin": false,
@ -861,7 +861,7 @@
"win64-aarch64-devedition",
"win64-devedition"
],
"revision": "6e2b118c75b97b54a45d7e728393084a30c40394"
"revision": "6a7128933727b0c11038544c74ce9f5d8ac8d0b3"
},
"hy-AM": {
"pin": false,
@ -915,7 +915,7 @@
"win64-aarch64-devedition",
"win64-devedition"
],
"revision": "278ffacad10965be4e0b110b37339a6e90fa3d12"
"revision": "c9aae1b862b4b239c56cb12fdbc885da88fa25b0"
},
"id": {
"pin": false,
@ -969,7 +969,7 @@
"win64-aarch64-devedition",
"win64-devedition"
],
"revision": "382ca60beb3d4245a903913cd35945792af5a3ce"
"revision": "9650cb3d6b2dcb127f9a7af9142306d37a5dc3c2"
},
"ja": {
"pin": false,
@ -1101,7 +1101,7 @@
"win64-aarch64-devedition",
"win64-devedition"
],
"revision": "bc3e11d81961f39ed881baf680a246e6ae814baa"
"revision": "962df49c492ee3a57137736546545a910267f95e"
},
"lij": {
"pin": false,
@ -1353,7 +1353,7 @@
"win64-aarch64-devedition",
"win64-devedition"
],
"revision": "197abc4fac9566518e45a7c3ed42603ec82b9928"
"revision": "b04979cfb0d8be4dc6be3bc8d9fb33aaefab7dab"
},
"oc": {
"pin": false,
@ -1425,7 +1425,7 @@
"win64-aarch64-devedition",
"win64-devedition"
],
"revision": "e7fb94ac0808d5ba8ee356506798dc1f0aa763d6"
"revision": "4ae9f56d370edd85133699018ac0ceec858175f3"
},
"pt-PT": {
"pin": false,
@ -1605,7 +1605,7 @@
"win64-aarch64-devedition",
"win64-devedition"
],
"revision": "f84cd60575f1414124959f8edfdcbca42eda6e74"
"revision": "fe258f24a71e6584396d3ffcd6a9b7501a78b95f"
},
"skr": {
"pin": false,
@ -2001,6 +2001,6 @@
"win64-aarch64-devedition",
"win64-devedition"
],
"revision": "01eac9796780092fdd37978e29a5cd1788aef160"
"revision": "c3cbc202fb4b5ed2d6436dcab28f4c9aca2e1e87"
}
}

View file

@ -31,7 +31,7 @@ export const ThemeVariableMap = [
},
],
[
"--lwt-tab-text",
"--tab-selected-textcolor",
{
lwtProperty: "tab_text",
},

View file

@ -23,23 +23,6 @@
position: relative;
}
/*
This is a workaround for Bug 1482157
-moz-default-appearance: toolbox; makes the macOS sheets attached to the
element's bottom border. We cannot put this property on the toolbox itself as
it cancels all backgrounds that are there, so we set it on the toolbox bottom
border.
*/
#navigator-toolbox::after {
content: "";
display: flex;
appearance: auto;
-moz-default-appearance: toolbox;
height: 1px;
margin-top: -1px;
opacity: 0.001;
}
/** Begin titlebar **/
#titlebar {

View file

@ -54,6 +54,7 @@
--input-border-color: light-dark(color-mix(in srgb, currentColor 41%, transparent), #8f8f9d);
--tab-selected-bgcolor: light-dark(rgb(255, 255, 255), rgb(66, 65, 77));
--tab-selected-textcolor: light-dark(rgb(21, 20, 26), rgb(251, 251, 254));
--tab-icon-overlay-stroke: light-dark(rgb(255, 255, 255), rgb(66, 65, 77));
--tab-icon-overlay-fill: light-dark(rgb(91, 91, 102), rgb(251, 251, 254));
--tab-attention-icon-color: light-dark(rgb(42, 195, 162), rgb(84, 255, 189));

View file

@ -163,6 +163,7 @@
#navigator-toolbox {
appearance: none;
/* Toolbar / content area border */
border-bottom: 1px solid var(--chrome-content-separator-color);

View file

@ -434,12 +434,12 @@ panelview[id^=PanelUI-webext-] {
width: 22.5em;
}
#customizationui-widget-multiview panelview:not([extension]) {
:where(#customizationui-widget-multiview) panelview:not([extension]) {
min-width: var(--menu-panel-width);
max-width: 35em;
}
#customizationui-widget-multiview #appMenu-libraryView,
#appMenu-libraryView,
#pageActionPanel panelview,
#widget-overflow panelview {
min-width: var(--menu-panel-width-wide);
@ -1987,12 +1987,7 @@ panelview:not([mainview]) #PanelUI-whatsNew-title {
#reset-pbm-panel {
max-width: var(--menu-panel-width-wide);
}
#reset-pbm-panel-container {
padding: 16px 16px 0;
display: flex;
flex-direction: column;
gap: 8px;
}

View file

@ -445,8 +445,9 @@
background-repeat: repeat-x;
}
.tabbrowser-tab:is([selected], [multiselected]):-moz-lwtheme {
color: var(--lwt-tab-text, var(--toolbar-color));
#TabsToolbar #firefox-view-button[open] > .toolbarbutton-icon,
.tabbrowser-tab:is([selected], [multiselected]) {
color: var(--tab-selected-textcolor, var(--toolbar-color));
}
@media (prefers-contrast) {

View file

@ -357,20 +357,26 @@
/* URL bar and page action buttons */
/* The background can be very dark and if the add-on uses a black-ish svg it
will be barely visible. In the future we should have a standardized SVG
solution we can apply to add-on icons, for now we can only try to make them
visible through some filtering tricks */
:root[lwt-toolbar-field-brighttext] #urlbar:not([focused="true"]) .urlbar-addon-page-action[style*=".svg"] > .urlbar-icon,
:root[lwt-toolbar-field-focus-brighttext] #urlbar[focused="true"] .urlbar-addon-page-action[style*=".svg"] > .urlbar-icon {
filter: grayscale(100%) brightness(20%) invert();
}
@media (prefers-color-scheme: dark) {
/* As above, but for the default theme in dark mode, which suffers from the same issue */
:root:not(:-moz-lwtheme) .urlbar-addon-page-action[style*=".svg"] > .urlbar-icon {
/*
* The background can be very dark and if the add-on uses a black-ish svg it
* will be barely visible. For now we try to make them more visible through
* some filtering tricks.
*
* TODO(emilio): Evaluate removing this. SVGs can use prefers-color-scheme to
* determine the right color-scheme to use, see bug 1779457.
*/
.urlbar-addon-page-action[style*=".svg"] > .urlbar-icon {
:root[lwt-toolbar-field="dark"] #urlbar:not([focused="true"]) &,
:root[lwt-toolbar-field-focus="dark"] #urlbar[focused="true"] & {
filter: grayscale(100%) brightness(20%) invert();
}
/* As above, but for the default theme in dark mode, which suffers from the same issue */
@media (prefers-color-scheme: dark) {
&:not(:-moz-lwtheme) {
filter: grayscale(100%) brightness(20%) invert();
}
}
}
#userContext-icons,

View file

@ -30,21 +30,29 @@
}
}
@media not (prefers-contrast) {
/* Use a different color in inactive windows. */
#toolbar-menubar:not(:-moz-lwtheme):-moz-window-inactive {
color: ThreeDShadow;
}
@media (-moz-windows-accent-color-in-titlebar) {
:root[tabsintitlebar] {
@media (-moz-windows-accent-color-in-tabs) {
--toolbox-non-lwt-bgcolor: ActiveCaption;
--toolbox-non-lwt-textcolor: CaptionText;
--toolbox-non-lwt-bgcolor-inactive: InactiveCaption;
--toolbox-non-lwt-textcolor-inactive: InactiveCaptionText;
}
@media (-moz-windows-accent-color-in-titlebar) {
:root[sizemode=normal][tabsintitlebar] #navigator-toolbox {
&[sizemode="normal"] #navigator-toolbox {
border-top: .5px solid ActiveBorder;
&:-moz-window-inactive {
border-top-color: InactiveBorder;
}
}
}
}
@media not (prefers-contrast) {
/* Use a different color in inactive windows. */
#toolbar-menubar:not(:-moz-lwtheme):-moz-window-inactive {
color: ThreeDShadow;
}
:root[tabsintitlebar] .tab-label-container:-moz-window-inactive {
/* Calculated to match the opacity change of Windows Explorer

View file

@ -0,0 +1,8 @@
[package]
name = "backtrace"
version = "0.3.999"
edition = "2018"
license = "MPL-2.0"
[lib]
path = "lib.rs"

View file

View file

@ -713,8 +713,8 @@ EventSourceImpl::OnStartRequest(nsIRequest* aRequest) {
if (NS_FAILED(status)) {
// EventSource::OnStopRequest will evaluate if it shall either reestablish
// or fail the connection
return NS_ERROR_ABORT;
// or fail the connection, based on the status.
return status;
}
uint32_t httpStatus;

View file

@ -147,3 +147,5 @@ skip-if = ["verify"]
["browser_user_input_handling_delay_reload_ticks.js"]
["browser_xml_toggle.js"]
["browser_event_source_reconnect_after_disconnect.js"]

View file

@ -0,0 +1,108 @@
var { HttpServer } = ChromeUtils.importESModule(
"resource://testing-common/httpd.sys.mjs"
);
function eventSourcePageHandler(metadata, response) {
response.setStatusLine(metadata.httpVersion, 200, "OK");
response.setHeader("Content-Type", "text/html", false);
// An HTML page that sets up the EventSource
let pageContent = `
<html>
<body>
<script>
let es = new EventSource("/eventstream");
es.onopen = function() {
console.log("send es_open");
window.dispatchEvent(new CustomEvent('es-open'));
};
es.onmessage = function(err) {
console.log("send es_message");
window.dispatchEvent(new CustomEvent('es-message'));
};
es.onerror = function(err) {
console.log("send es_error");
window.dispatchEvent(new CustomEvent('es-error'));
};
</script>
</body>
</html>`;
response.write(pageContent);
}
const server = new HttpServer();
server.start(-1);
const SERVER_PORT = server.identity.primaryPort;
registerCleanupFunction(async () => {
await server.stop();
});
function eventStreamHandler(metadata, response) {
response.setStatusLine(metadata.httpVersion, 200, "OK");
response.setHeader("Content-Type", "text/event-stream", false);
response.write("retry: 500\n");
}
server.registerPathHandler("/page", eventSourcePageHandler);
server.registerPathHandler("/eventstream", eventStreamHandler);
add_task(async function testReconnectAfterDisconnect() {
info("Connect to the server to retrieve the EventSource doc");
let tab = await BrowserTestUtils.openNewForegroundTab(
gBrowser,
`http://localhost:${server.identity.primaryPort}/page`
);
let browser = tab.linkedBrowser;
// Define a function to handle events in the content process
async function contentEventHandler() {
return new Promise(resolve => {
content.addEventListener(
"es-open",
function () {
resolve("es-open");
},
{ once: true }
);
content.addEventListener(
"es-error",
function () {
resolve("es-error");
},
{ once: true }
);
});
}
// Execute the event handler in the content process
let eventType = await SpecialPowers.spawn(browser, [], contentEventHandler);
Assert.equal(eventType, "es-open", "EventSource opened successfully");
// Stop the server; we expect an error
await server.stop();
eventType = await SpecialPowers.spawn(browser, [], contentEventHandler);
Assert.equal(
eventType,
"es-error",
"EventSource encountered an error after server close"
);
// Restart the server; the eventSource should automatically reconnect
server.start(SERVER_PORT);
eventType = await SpecialPowers.spawn(browser, [], contentEventHandler);
Assert.equal(
eventType,
"es-open",
"EventSource opened successfully after server restart"
);
BrowserTestUtils.removeTab(tab);
});

View file

@ -80,6 +80,18 @@ void ImageBitmapRenderingContext::TransferFromImageBitmap(
return;
}
// Note that this is reentrant and will call back into SetDimensions.
if (mCanvasElement) {
mCanvasElement->SetSize(mImage->GetSize(), aRv);
} else if (mOffscreenCanvas) {
mOffscreenCanvas->SetSize(mImage->GetSize(), aRv);
}
if (NS_WARN_IF(aRv.Failed())) {
mImage = nullptr;
return;
}
if (aImageBitmap->IsWriteOnly()) {
if (mCanvasElement) {
mCanvasElement->SetWriteOnly();
@ -96,6 +108,16 @@ NS_IMETHODIMP
ImageBitmapRenderingContext::SetDimensions(int32_t aWidth, int32_t aHeight) {
mWidth = aWidth;
mHeight = aHeight;
if (mOffscreenCanvas) {
OffscreenCanvasDisplayData data;
data.mSize = {mWidth, mHeight};
data.mIsOpaque = GetIsOpaque();
data.mIsAlphaPremult = true;
data.mDoPaintCallbacks = false;
mOffscreenCanvas->UpdateDisplayData(data);
}
return NS_OK;
}

View file

@ -147,6 +147,23 @@ void OffscreenCanvas::SetHeight(uint32_t aHeight, ErrorResult& aRv) {
CanvasAttrChanged();
}
void OffscreenCanvas::SetSize(const nsIntSize& aSize, ErrorResult& aRv) {
if (mNeutered) {
aRv.ThrowInvalidStateError(
"Cannot set dimensions of placeholder canvas transferred to worker.");
return;
}
if (NS_WARN_IF(aSize.IsEmpty())) {
aRv.ThrowRangeError("OffscreenCanvas size is empty, must be non-empty.");
return;
}
mWidth = aSize.width;
mHeight = aSize.height;
CanvasAttrChanged();
}
void OffscreenCanvas::GetContext(
JSContext* aCx, const OffscreenRenderingContextId& aContextId,
JS::Handle<JS::Value> aContextOptions,

View file

@ -150,6 +150,8 @@ class OffscreenCanvas final : public DOMEventTargetHelper,
bool MayNeuter() const { return !mNeutered && !mCurrentContext; }
void SetSize(const nsIntSize& aSize, ErrorResult& aRv);
nsIPrincipal* GetExpandedReader() const { return mExpandedReader; }
void SetWriteOnly(RefPtr<nsIPrincipal>&& aExpandedReader);

View file

@ -43,11 +43,11 @@ function runTest(canvasWidth, canvasHeight, nextTest) {
ctx.fillStyle = "#00FF00";
ctx.fillRect(0, 0, canvasWidth, canvasHeight);
var canvasRef = createCanvas(90, 90);
var canvasRef = createCanvas(canvasWidth, canvasHeight);
var ctx = canvasRef.getContext("2d");
// Clear with black transparent first
ctx.fillStyle = "rgba(0, 0, 0, 0)";
ctx.fillRect(0, 0, 90, 90);
ctx.fillRect(0, 0, canvasWidth, canvasHeight);
ctx.fillStyle = "#00FF00";
ctx.fillRect(0, 0, canvasWidth, canvasHeight);
@ -89,52 +89,58 @@ function runTest(canvasWidth, canvasHeight, nextTest) {
});
}
function scaleTest() {
var canvas1 = createCanvas(64, 64);
var ctx = canvas1.getContext("2d");
function createSolidGreenBitmap(width, height) {
const canvas = createCanvas(width, height);
const ctx = canvas.getContext("2d");
ctx.fillStyle = "#00FF00";
ctx.fillRect(0, 0, 64, 64);
ctx.fillRect(0, 0, width, height);
const bitmap = createImageBitmap(canvas);
document.body.removeChild(canvas);
return bitmap;
}
var canvas2 = createCanvas(64, 64);
var ctx2 = canvas2.getContext("2d");
ctx2.fillStyle = "#00FF00";
ctx2.fillRect(0, 0, 64, 64);
async function scaleTestCase(name, refWidth, refHeight, testWidth, testHeight, canvasWidth, canvasHeight) {
const refBitmap = await createSolidGreenBitmap(refWidth, refHeight);
const refCanvas = createCanvas(refWidth, refHeight);
const refCtx = refCanvas.getContext("bitmaprenderer");
refCtx.transferFromImageBitmap(refBitmap);
is(refCanvas.width, refWidth, name + ": refCanvas width " + refCanvas.width + " is ref " + refWidth);
is(refCanvas.height, refHeight, name + ": refCanvas height " + refCanvas.height + " is ref " + refHeight);
const refSnapshot = await snapshotWindow(window);
document.body.removeChild(refCanvas);
var p1 = createImageBitmap(canvas1);
var p2 = createImageBitmap(canvas2);
Promise.all([p1, p2]).then(async function(bitmaps) {
document.body.removeChild(canvas1);
document.body.removeChild(canvas2);
const bitmap = await createSolidGreenBitmap(testWidth, testHeight);
const canvas = createCanvas(canvasWidth, canvasHeight);
const ctx = canvas.getContext("bitmaprenderer");
ctx.transferFromImageBitmap(bitmap);
is(canvas.width, testWidth, name + ": canvas width " + canvas.width + " is bitmap " + testWidth);
is(canvas.height, testHeight, name + ": canvas height " + canvas.height + " is bitmap " + testHeight);
if (refWidth !== testWidth) {
canvas.width = refWidth;
is(canvas.width, refWidth, name + ": canvas width " + canvas.width + " is ref " + refWidth);
}
if (refHeight !== testHeight) {
canvas.height = refHeight;
is(canvas.height, refHeight, name + ": canvas height " + canvas.height + " is ref " + refHeight);
}
const snapshot = await snapshotWindow(window);
document.body.removeChild(canvas);
// Create a large canvas then shrink.
var canvas3 = createCanvas(128, 128);
var ctx3 = canvas3.getContext("bitmaprenderer");
ctx3.transferFromImageBitmap(bitmaps[0]);
var snapshotLargeRef = await snapshotWindow(window);
const results = compareSnapshots(snapshot, refSnapshot, true);
ok(results[0], name + ": screenshots should be the same");
return Promise.resolve();
}
canvas3.width = 32;
canvas3.height = 32;
var snapshotSmall = await snapshotWindow(window);
document.body.removeChild(canvas3);
// Create a small canvas then grow.
var canvas4 = createCanvas(32, 32);
var ctx4 = canvas4.getContext("bitmaprenderer");
ctx4.transferFromImageBitmap(bitmaps[1]);
var snapshotSmallRef = await snapshotWindow(window);
canvas4.width = 128;
canvas4.height = 128;
var snapshotLarge = await snapshotWindow(window);
document.body.removeChild(canvas4);
var resultsLarge = compareSnapshots(snapshotLarge, snapshotLargeRef, true);
ok(resultsLarge[0], "Screenshots should be the same");
var resultsSmall = compareSnapshots(snapshotSmall, snapshotSmallRef, true);
ok(resultsSmall[0], "Screenshots should be the same");
runTestOnWorker();
});
async function scaleTest() {
await scaleTestCase("grow_unscaled", 64, 64, 64, 64, 32, 32); // Canvas grows, no scaling.
await scaleTestCase("grow_downscaled", 64, 64, 128, 128, 32, 32); // Canvas grows, scales down.
await scaleTestCase("grow_upscaled", 64, 64, 32, 32, 16, 16); // Canvas grows, scales up.
await scaleTestCase("same_downscaled", 64, 64, 128, 128, 128, 128); // Canvas unchanged, scales down.
await scaleTestCase("same_upscaled", 64, 64, 32, 32, 32, 32); // Canvas unchanged, scales up.
await scaleTestCase("shrink_unscaled", 64, 64, 64, 64, 128, 128); // Canvas shrinks, no scaling.
await scaleTestCase("shrink_downscaled", 64, 64, 128, 128, 256, 256); // Canvas shrinks, scales down.
await scaleTestCase("shrink_upscaled", 64, 64, 32, 32, 256, 256); // Canvas shrinks, scales up.
runTestOnWorker();
}
function runTestOnWorker() {

View file

@ -1104,6 +1104,26 @@ void HTMLCanvasElement::SetHeight(uint32_t aHeight, ErrorResult& aRv) {
SetUnsignedIntAttr(nsGkAtoms::height, aHeight, DEFAULT_CANVAS_HEIGHT, aRv);
}
void HTMLCanvasElement::SetSize(const nsIntSize& aSize, ErrorResult& aRv) {
if (mOffscreenCanvas) {
aRv.ThrowInvalidStateError(
"Cannot set width of placeholder canvas transferred to "
"OffscreenCanvas.");
return;
}
if (NS_WARN_IF(aSize.IsEmpty())) {
aRv.ThrowRangeError("Canvas size is empty, must be non-empty.");
return;
}
SetUnsignedIntAttr(nsGkAtoms::width, aSize.width, DEFAULT_CANVAS_WIDTH, aRv);
MOZ_ASSERT(!aRv.Failed());
SetUnsignedIntAttr(nsGkAtoms::height, aSize.height, DEFAULT_CANVAS_HEIGHT,
aRv);
MOZ_ASSERT(!aRv.Failed());
}
void HTMLCanvasElement::FlushOffscreenCanvas() {
if (mOffscreenDisplay) {
mOffscreenDisplay->FlushForDisplay();

View file

@ -175,6 +175,11 @@ class HTMLCanvasElement final : public nsGenericHTMLElement,
*/
nsIntSize GetSize();
/**
* Set the size in pixels of this canvas element.
*/
void SetSize(const nsIntSize& aSize, ErrorResult& aRv);
/**
* Determine whether the canvas is write-only.
*/

View file

@ -486,6 +486,8 @@ class MediaTrack : public mozilla::LinkedListElement<MediaTrack> {
*/
virtual void DecrementSuspendCount();
class ControlMessageInterface;
protected:
// Called on graph thread either during destroy handling or before handing
// graph control to the main thread to release tracks.
@ -1174,6 +1176,31 @@ class MediaTrackGraph {
const TrackRate mSampleRate;
};
/**
* This represents a message run on the graph thread to modify track or graph
* state. These are passed from main thread to graph thread through
* AppendMessage(), or scheduled on the graph thread with
* RunMessageAfterProcessing().
*/
class MediaTrack::ControlMessageInterface {
public:
MOZ_COUNTED_DEFAULT_CTOR(ControlMessageInterface)
// All these run on the graph thread unless the graph has been forced to
// shut down.
MOZ_COUNTED_DTOR_VIRTUAL(ControlMessageInterface)
// Do the action of this message on the MediaTrackGraph thread. Any actions
// affecting graph processing should take effect at mProcessedTime.
// All track data for times < mProcessedTime has already been
// computed.
virtual void Run() = 0;
// RunDuringShutdown() is only relevant to messages generated on the main
// thread (for AppendMessage()).
// When we're shutting down the application, most messages are ignored but
// some cleanup messages should still be processed (on the main thread).
// This must not add new control messages to the graph.
virtual void RunDuringShutdown() {}
};
} // namespace mozilla
#endif /* MOZILLA_MEDIATRACKGRAPH_H_ */

View file

@ -75,31 +75,17 @@ struct TrackUpdate {
* RunMessageAfterProcessing(). A ControlMessage
* always has a weak reference to a particular affected track.
*/
class ControlMessage {
class ControlMessage : public MediaTrack::ControlMessageInterface {
public:
explicit ControlMessage(MediaTrack* aTrack) : mTrack(aTrack) {
MOZ_COUNT_CTOR(ControlMessage);
}
// All these run on the graph thread
MOZ_COUNTED_DTOR_VIRTUAL(ControlMessage)
// Do the action of this message on the MediaTrackGraph thread. Any actions
// affecting graph processing should take effect at mProcessedTime.
// All track data for times < mProcessedTime has already been
// computed.
virtual void Run() = 0;
// RunDuringShutdown() is only relevant to messages generated on the main
// thread (for AppendMessage()).
// When we're shutting down the application, most messages are ignored but
// some cleanup messages should still be processed (on the main thread).
// This must not add new control messages to the graph.
virtual void RunDuringShutdown() {}
explicit ControlMessage(MediaTrack* aTrack) : mTrack(aTrack) {}
MediaTrack* GetTrack() { return mTrack; }
protected:
// We do not hold a reference to mTrack. The graph will be holding a reference
// to the track until the Destroy message is processed. The last message
// referencing a track is the Destroy message for that track.
MediaTrack* mTrack;
MediaTrack* const mTrack;
};
class MessageBlock {
@ -123,6 +109,8 @@ class MediaTrackGraphImpl : public MediaTrackGraph,
public nsITimerCallback,
public nsINamed {
public:
using ControlMessageInterface = MediaTrack::ControlMessageInterface;
NS_DECL_THREADSAFE_ISUPPORTS
NS_DECL_NSIMEMORYREPORTER
NS_DECL_NSITHREADOBSERVER

View file

@ -380,7 +380,7 @@ static void UpdateCodecSpecificInfo(webrtc::CodecSpecificInfo& aInfo,
vp9.temporal_idx = webrtc::kNoTemporalIdx;
vp9.temporal_up_switch = false;
vp9.num_spatial_layers = 1;
vp9.end_of_picture = true;
aInfo.end_of_picture = true;
vp9.gof_idx = webrtc::kNoGofIdx;
vp9.width[0] = aSize.width;
vp9.height[0] = aSize.height;

View file

@ -495,7 +495,7 @@ FilenameTypeAndDetails nsContentSecurityUtils::FilenameToFilenameType(
return FilenameTypeAndDetails(kOther, Nothing());
}
#ifdef NIGHTLY_BUILD
#if defined(EARLY_BETA_OR_EARLIER)
// Crash String must be safe from a telemetry point of view.
// This will be ensured when this function is used.
void PossiblyCrash(const char* aPrefSuffix, const char* aUnsafeCrashString,
@ -1541,7 +1541,7 @@ bool nsContentSecurityUtils::ValidateScriptFilename(JSContext* cx,
: "(None)",
"Blocking a script load %s from file %s");
MOZ_CRASH_UNSAFE_PRINTF("%s", crashString.get());
#elif defined(NIGHTLY_BUILD)
#elif defined(EARLY_BETA_OR_EARLIER)
// Cause a crash (if we've never crashed before and we can ensure we won't do
// it again.)
// The details in the second arg, passed to UNSAFE_PRINTF, are also included

View file

@ -127,6 +127,8 @@ already_AddRefed<Buffer> Buffer::Create(Device* aDevice, RawId aDeviceId,
buffer->SetMapped(0, aDesc.mSize, writable);
}
aDevice->TrackBuffer(buffer.get());
return buffer.forget();
}
@ -144,9 +146,12 @@ void Buffer::Drop() {
}
mMapped.reset();
if (mValid && !GetDevice().IsLost()) {
if (mValid && GetDevice().IsBridgeAlive()) {
GetDevice().GetBridge()->SendBufferDrop(mId);
}
GetDevice().UntrackBuffer(this);
mValid = false;
}

View file

@ -69,14 +69,6 @@ void Device::Cleanup() {
if (mBridge) {
mBridge->UnregisterDevice(mId);
}
// Cycle collection may have disconnected the promise object.
if (mLostPromise && mLostPromise->PromiseObj() != nullptr) {
auto info = MakeRefPtr<DeviceLostInfo>(GetParentObject(),
dom::GPUDeviceLostReason::Destroyed,
u"Device destroyed"_ns);
mLostPromise->MaybeResolve(info);
}
}
void Device::CleanupUnregisteredInParent() {
@ -86,19 +78,29 @@ void Device::CleanupUnregisteredInParent() {
mValid = false;
}
bool Device::IsLost() const { return !mBridge || !mBridge->CanSend(); }
bool Device::IsLost() const {
return !mBridge || !mBridge->CanSend() ||
(mLostPromise &&
(mLostPromise->State() != dom::Promise::PromiseState::Pending));
}
bool Device::IsBridgeAlive() const { return mBridge && mBridge->CanSend(); }
// Generate an error on the Device timeline for this device.
//
// aMessage is interpreted as UTF-8.
void Device::GenerateValidationError(const nsCString& aMessage) {
if (IsLost()) {
if (!IsBridgeAlive()) {
return; // Just drop it?
}
mBridge->SendGenerateError(Some(mId), dom::GPUErrorFilter::Validation,
aMessage);
}
void Device::TrackBuffer(Buffer* aBuffer) { mTrackedBuffers.Insert(aBuffer); }
void Device::UntrackBuffer(Buffer* aBuffer) { mTrackedBuffers.Remove(aBuffer); }
void Device::GetLabel(nsAString& aValue) const { aValue = mLabel; }
void Device::SetLabel(const nsAString& aLabel) { mLabel = aLabel; }
@ -114,6 +116,32 @@ dom::Promise* Device::GetLost(ErrorResult& aRv) {
return mLostPromise;
}
void Device::ResolveLost(Maybe<dom::GPUDeviceLostReason> aReason,
const nsAString& aMessage) {
ErrorResult rv;
dom::Promise* lostPromise = GetLost(rv);
if (!lostPromise) {
// Promise doesn't exist? Maybe out of memory.
return;
}
if (lostPromise->State() != dom::Promise::PromiseState::Pending) {
// lostPromise was already resolved or rejected.
return;
}
if (!lostPromise->PromiseObj()) {
// The underlying JS object is gone.
return;
}
RefPtr<DeviceLostInfo> info;
if (aReason.isSome()) {
info = MakeRefPtr<DeviceLostInfo>(GetParentObject(), *aReason, aMessage);
} else {
info = MakeRefPtr<DeviceLostInfo>(GetParentObject(), aMessage);
}
lostPromise->MaybeResolve(info);
}
already_AddRefed<Buffer> Device::CreateBuffer(
const dom::GPUBufferDescriptor& aDesc, ErrorResult& aRv) {
return Buffer::Create(this, mId, aDesc, aRv);
@ -334,11 +362,25 @@ bool Device::CheckNewWarning(const nsACString& aMessage) {
}
void Device::Destroy() {
// TODO
if (IsLost()) {
return;
}
// Unmap all buffers from this device, as specified by
// https://gpuweb.github.io/gpuweb/#dom-gpudevice-destroy.
dom::AutoJSAPI jsapi;
if (jsapi.Init(GetOwnerGlobal())) {
IgnoredErrorResult rv;
for (const auto& buffer : mTrackedBuffers) {
buffer->Unmap(jsapi.cx(), rv);
}
}
mBridge->SendDeviceDestroy(mId);
}
void Device::PushErrorScope(const dom::GPUErrorFilter& aFilter) {
if (IsLost()) {
if (!IsBridgeAlive()) {
return;
}
mBridge->SendDevicePushErrorScope(mId, aFilter);
@ -357,7 +399,7 @@ already_AddRefed<dom::Promise> Device::PopErrorScope(ErrorResult& aRv) {
return nullptr;
}
if (IsLost()) {
if (!IsBridgeAlive()) {
WebGPUChild::JsWarning(
GetOwnerGlobal(),
"popErrorScope resolving to null because device is already lost."_ns);

View file

@ -45,6 +45,7 @@ class Promise;
template <typename T>
class Sequence;
class GPUBufferOrGPUTexture;
enum class GPUDeviceLostReason : uint8_t;
enum class GPUErrorFilter : uint8_t;
enum class GPUFeatureName : uint8_t;
class GPULogCallback;
@ -102,8 +103,11 @@ class Device final : public DOMEventTargetHelper, public SupportsWeakPtr {
void CleanupUnregisteredInParent();
void GenerateValidationError(const nsCString& aMessage);
void TrackBuffer(Buffer* aBuffer);
void UntrackBuffer(Buffer* aBuffer);
bool IsLost() const;
bool IsBridgeAlive() const;
private:
~Device();
@ -115,12 +119,14 @@ class Device final : public DOMEventTargetHelper, public SupportsWeakPtr {
RefPtr<dom::Promise> mLostPromise;
RefPtr<Queue> mQueue;
nsTHashSet<nsCString> mKnownWarnings;
nsTHashSet<Buffer*> mTrackedBuffers;
public:
void GetLabel(nsAString& aValue) const;
void SetLabel(const nsAString& aLabel);
dom::Promise* GetLost(ErrorResult& aRv);
dom::Promise* MaybeGetLost() const { return mLostPromise; }
void ResolveLost(Maybe<dom::GPUDeviceLostReason> aReason,
const nsAString& aMessage);
const RefPtr<SupportedFeatures>& Features() const { return mFeatures; }
const RefPtr<SupportedLimits>& Limits() const { return mLimits; }

View file

@ -13,6 +13,7 @@
#include "mozilla/dom/Promise.h"
#include "mozilla/gfx/CanvasManagerChild.h"
#include "mozilla/gfx/gfxVars.h"
#include "mozilla/StaticPrefs_dom.h"
#include <optional>
#include <string_view>
@ -25,6 +26,18 @@ static inline nsDependentCString ToCString(const std::string_view s) {
return {s.data(), s.length()};
}
/* static */ bool Instance::PrefEnabled(JSContext* aCx, JSObject* aObj) {
if (!StaticPrefs::dom_webgpu_enabled()) {
return false;
}
if (NS_IsMainThread()) {
return true;
}
return StaticPrefs::dom_webgpu_workers_enabled();
}
/*static*/
already_AddRefed<Instance> Instance::Create(nsIGlobalObject* aOwner) {
RefPtr<Instance> result = new Instance(aOwner);

View file

@ -32,6 +32,8 @@ class Instance final : public nsWrapperCache {
nsIGlobalObject* GetParentObject() const { return mOwner; }
static bool PrefEnabled(JSContext* aCx, JSObject* aObj);
static already_AddRefed<Instance> Create(nsIGlobalObject* aOwner);
already_AddRefed<dom::Promise> RequestAdapter(

View file

@ -59,6 +59,7 @@ parent:
async TextureViewDestroy(RawId selfId);
async SamplerDestroy(RawId selfId);
async DeviceDestroy(RawId selfId);
async DeviceDrop(RawId selfId);
async CommandEncoderFinish(RawId selfId, RawId deviceId, GPUCommandBufferDescriptor desc);
async CommandEncoderDestroy(RawId selfId);
@ -89,6 +90,7 @@ parent:
child:
async UncapturedError(RawId? aDeviceId, nsCString message);
async DropAction(ByteBuf buf);
async DeviceLost(RawId aDeviceId, uint8_t? reason, nsCString message);
async __delete__();
};

View file

@ -1128,6 +1128,29 @@ ipc::IPCResult WebGPUChild::RecvDropAction(const ipc::ByteBuf& aByteBuf) {
return IPC_OK();
}
ipc::IPCResult WebGPUChild::RecvDeviceLost(RawId aDeviceId,
Maybe<uint8_t> aReason,
const nsACString& aMessage) {
RefPtr<Device> device;
const auto itr = mDeviceMap.find(aDeviceId);
if (itr != mDeviceMap.end()) {
device = itr->second.get();
MOZ_ASSERT(device);
}
if (device) {
auto message = NS_ConvertUTF8toUTF16(aMessage);
if (aReason.isSome()) {
dom::GPUDeviceLostReason reason =
static_cast<dom::GPUDeviceLostReason>(*aReason);
device->ResolveLost(Some(reason), message);
} else {
device->ResolveLost(Nothing(), message);
}
}
return IPC_OK();
}
void WebGPUChild::DeviceCreateSwapChain(
RawId aSelfId, const RGBDescriptor& aRgbDesc, size_t maxBufferCount,
const layers::RemoteTextureOwnerId& aOwnerId,
@ -1168,7 +1191,7 @@ void WebGPUChild::RegisterDevice(Device* const aDevice) {
void WebGPUChild::UnregisterDevice(RawId aId) {
mDeviceMap.erase(aId);
if (IsOpen()) {
SendDeviceDestroy(aId);
SendDeviceDrop(aId);
}
}
@ -1192,17 +1215,7 @@ void WebGPUChild::ActorDestroy(ActorDestroyReason) {
continue;
}
RefPtr<dom::Promise> promise = device->MaybeGetLost();
if (!promise) {
continue;
}
auto info = MakeRefPtr<DeviceLostInfo>(device->GetParentObject(),
u"WebGPUChild destroyed"_ns);
// We have strong references to both the Device and the DeviceLostInfo and
// the Promise objects on the stack which keeps them alive for long enough.
promise->MaybeResolve(info);
device->ResolveLost(Nothing(), u"WebGPUChild destroyed"_ns);
}
}

View file

@ -144,6 +144,8 @@ class WebGPUChild final : public PWebGPUChild, public SupportsWeakPtr {
ipc::IPCResult RecvUncapturedError(Maybe<RawId> aDeviceId,
const nsACString& aMessage);
ipc::IPCResult RecvDropAction(const ipc::ByteBuf& aByteBuf);
ipc::IPCResult RecvDeviceLost(RawId aDeviceId, Maybe<uint8_t> aReason,
const nsACString& aMessage);
void ActorDestroy(ActorDestroyReason) override;
};

View file

@ -91,6 +91,7 @@ class ErrorBuffer {
ffi::WGPUErrorBufferType aType) {
switch (aType) {
case ffi::WGPUErrorBufferType_None:
case ffi::WGPUErrorBufferType_DeviceLost:
return {};
case ffi::WGPUErrorBufferType_Internal:
return Some(dom::GPUErrorFilter::Internal);
@ -107,6 +108,7 @@ class ErrorBuffer {
struct Error {
dom::GPUErrorFilter type;
bool isDeviceLost;
nsCString message;
};
@ -118,11 +120,19 @@ class ErrorBuffer {
// won't assert.
Maybe<Error> GetError() {
mAwaitingGetError = false;
if (mType == ffi::WGPUErrorBufferType_DeviceLost) {
// This error is for a lost device, so we return an Error struct
// with the isDeviceLost bool set to true. It doesn't matter what
// GPUErrorFilter type we use, so we just use Validation. The error
// will not be reported.
return Some(Error{dom::GPUErrorFilter::Validation, true,
nsCString{mMessageUtf8}});
}
auto filterType = ErrorTypeToFilterType(mType);
if (!filterType) {
return {};
}
return Some(Error{*filterType, nsCString{mMessageUtf8}});
return Some(Error{*filterType, false, nsCString{mMessageUtf8}});
}
};
@ -297,9 +307,35 @@ void WebGPUParent::MaintainDevices() {
ffi::wgpu_server_poll_all_devices(mContext.get(), false);
}
void WebGPUParent::LoseDevice(const RawId aDeviceId, Maybe<uint8_t> aReason,
const nsACString& aMessage) {
// Check to see if we've already sent a DeviceLost message to aDeviceId.
if (mLostDeviceIds.Contains(aDeviceId)) {
return;
}
if (!SendDeviceLost(aDeviceId, aReason, aMessage)) {
NS_ERROR("SendDeviceLost failed");
return;
}
mLostDeviceIds.Insert(aDeviceId);
}
bool WebGPUParent::ForwardError(const Maybe<RawId> aDeviceId,
ErrorBuffer& aError) {
if (auto error = aError.GetError()) {
// If this is a error has isDeviceLost true, then instead of reporting
// the error, we swallow it and call LoseDevice if we have an
// aDeviceID. This is to comply with the spec declaration in
// https://gpuweb.github.io/gpuweb/#lose-the-device
// "No errors are generated after device loss."
if (error->isDeviceLost) {
if (aDeviceId.isSome()) {
LoseDevice(*aDeviceId, Nothing(), error->message);
}
return false;
}
ReportError(aDeviceId, error->type, error->message);
return true;
}
@ -382,6 +418,10 @@ ipc::IPCResult WebGPUParent::RecvAdapterRequestDevice(
ffi::wgpu_server_adapter_request_device(
mContext.get(), aAdapterId, ToFFI(&aByteBuf), aDeviceId, error.ToFFI());
if (ForwardError(0, error)) {
uint8_t reasonDestroyed = 0; // GPUDeviceLostReason::Destroyed
auto maybeError = error.GetError();
MOZ_ASSERT(maybeError.isSome());
LoseDevice(aDeviceId, Some(reasonDestroyed), maybeError->message);
resolver(false);
} else {
mErrorScopeStackByDevice.insert({aDeviceId, {}});
@ -396,8 +436,14 @@ ipc::IPCResult WebGPUParent::RecvAdapterDestroy(RawId aAdapterId) {
}
ipc::IPCResult WebGPUParent::RecvDeviceDestroy(RawId aDeviceId) {
ffi::wgpu_server_device_destroy(mContext.get(), aDeviceId);
return IPC_OK();
}
ipc::IPCResult WebGPUParent::RecvDeviceDrop(RawId aDeviceId) {
ffi::wgpu_server_device_drop(mContext.get(), aDeviceId);
mErrorScopeStackByDevice.erase(aDeviceId);
mLostDeviceIds.Remove(aDeviceId);
return IPC_OK();
}
@ -435,7 +481,8 @@ ipc::IPCResult WebGPUParent::RecvCreateBuffer(
size = aDesc.mSize;
}
BufferMapData data = {std::move(shmem), hasMapFlags, offset, size};
BufferMapData data = {std::move(shmem), hasMapFlags, offset, size,
aDeviceId};
mSharedMemoryMap.insert({aBufferId, std::move(data)});
}
}
@ -488,9 +535,9 @@ static const char* MapStatusString(ffi::WGPUBufferMapAsyncStatus status) {
MOZ_CRASH("Bad ffi::WGPUBufferMapAsyncStatus");
}
static void MapCallback(ffi::WGPUBufferMapAsyncStatus status,
uint8_t* userdata) {
auto* req = reinterpret_cast<MapRequest*>(userdata);
void WebGPUParent::MapCallback(ffi::WGPUBufferMapAsyncStatus aStatus,
uint8_t* aUserData) {
auto* req = reinterpret_cast<MapRequest*>(aUserData);
if (!req->mParent->CanSend()) {
delete req;
@ -503,9 +550,18 @@ static void MapCallback(ffi::WGPUBufferMapAsyncStatus status,
auto* mapData = req->mParent->GetBufferMapData(bufferId);
MOZ_RELEASE_ASSERT(mapData);
if (status != ffi::WGPUBufferMapAsyncStatus_Success) {
if (aStatus != ffi::WGPUBufferMapAsyncStatus_Success) {
// A buffer map operation that fails with a DeviceError gets
// mapped to the ContextLost status. If we have this status, we
// need to lose the device.
if (aStatus == ffi::WGPUBufferMapAsyncStatus_ContextLost) {
req->mParent->LoseDevice(
mapData->mDeviceId, Nothing(),
nsPrintfCString("Buffer %" PRIu64 " invalid", bufferId));
}
result = BufferMapError(nsPrintfCString("Mapping WebGPU buffer failed: %s",
MapStatusString(status)));
MapStatusString(aStatus)));
} else {
auto size = req->mSize;
auto offset = req->mOffset;

View file

@ -42,6 +42,7 @@ class WebGPUParent final : public PWebGPUParent {
AdapterRequestDeviceResolver&& resolver);
ipc::IPCResult RecvAdapterDestroy(RawId aAdapterId);
ipc::IPCResult RecvDeviceDestroy(RawId aDeviceId);
ipc::IPCResult RecvDeviceDrop(RawId aDeviceId);
ipc::IPCResult RecvCreateBuffer(RawId aDeviceId, RawId aBufferId,
dom::GPUBufferDescriptor&& aDesc,
ipc::UnsafeSharedMemoryHandle&& aShmem);
@ -123,6 +124,7 @@ class WebGPUParent final : public PWebGPUParent {
bool mHasMapFlags;
uint64_t mMappedOffset;
uint64_t mMappedSize;
RawId mDeviceId;
};
BufferMapData* GetBufferMapData(RawId aBufferId);
@ -143,10 +145,14 @@ class WebGPUParent final : public PWebGPUParent {
std::shared_ptr<ExternalTexture> GetExternalTexture(ffi::WGPUTextureId aId);
private:
static void MapCallback(ffi::WGPUBufferMapAsyncStatus aStatus,
uint8_t* aUserData);
void DeallocBufferShmem(RawId aBufferId);
virtual ~WebGPUParent();
void MaintainDevices();
void LoseDevice(const RawId aDeviceId, Maybe<uint8_t> aReason,
const nsACString& aMessage);
bool ForwardError(const RawId aDeviceId, ErrorBuffer& aError) {
return ForwardError(Some(aDeviceId), aError);
@ -176,6 +182,10 @@ class WebGPUParent final : public PWebGPUParent {
std::unordered_map<ffi::WGPUTextureId, std::shared_ptr<ExternalTexture>>
mExternalTextures;
// Store a set of DeviceIds that have been SendDeviceLost. We use this to
// limit each Device to one DeviceLost message.
nsTHashSet<RawId> mLostDeviceIds;
};
} // namespace webgpu

View file

@ -3,6 +3,7 @@ subsuite = webgpu
run-if = !release_or_beta
prefs =
dom.webgpu.enabled=true
dom.webgpu.workers.enabled=true
gfx.offscreencanvas.enabled=true
support-files =
worker_wrapper.js
@ -15,7 +16,6 @@ support-files =
scheme = https
[test_basic_canvas.worker.html]
skip-if = true # Bug 1818379 - no webgpu in worker scopes, see bug 1808820
fail-if = (os == 'linux' && os_version == '18.04') || (os == 'win' && os_version == '6.1') || (os == 'mac')
[test_buffer_mapping.html]
fail-if = (os == 'linux' && os_version == '18.04') || (os == 'win' && os_version == '6.1') || (os == 'mac')
@ -39,5 +39,4 @@ fail-if = (os == 'linux' && os_version == '18.04') || (os == 'win' && os_version
[test_submit_render_empty.html]
fail-if = (os == 'linux' && os_version == '18.04') || (os == 'win' && os_version == '6.1') || (os == 'mac')
[test_submit_render_empty.worker.html]
skip-if = true # Bug 1818379 - no webgpu in worker scopes, see bug 1808820
fail-if = (os == 'linux' && os_version == '18.04') || (os == 'win' && os_version == '6.1') || (os == 'mac')

View file

@ -3,9 +3,9 @@ self.addEventListener("message", async function (event) {
const offscreen = event.data.offscreen;
const context = offscreen.getContext("webgpu");
const swapChainFormat = navigator.gpu.getPreferredCanvasFormat();
const adapter = await navigator.gpu.requestAdapter();
const device = await adapter.requestDevice();
const swapChainFormat = context.getPreferredFormat(adapter);
context.configure({
device,

View file

@ -20,7 +20,11 @@
const buffer = device.createBuffer({ size: 0, usage: 0 });
const error = await device.popErrorScope();
isnot(error, null);
isnot(
error,
null,
"Attempt to createBuffer with size 0 and usage 0 should generate an error."
);
try {
await device.popErrorScope();

View file

@ -21,7 +21,8 @@ self.addEventListener("message", async function (event) {
colorAttachments: [
{
view,
loadValue: { r: 0, g: 0, b: 0, a: 0 },
clearValue: { r: 0, g: 0, b: 0, a: 0 },
loadOp: "clear",
storeOp: "store",
},
],

View file

@ -27,15 +27,6 @@ interface DedicatedWorkerGlobalScope : WorkerGlobalScope {
attribute EventHandler onmessage;
attribute EventHandler onmessageerror;
// https://html.spec.whatwg.org/multipage/imagebitmap-and-animations.html#animation-frames
// Ideally we would just include AnimationFrameProvider to add the interface,
// but we cannot make an include conditional.
[Throws]
long requestAnimationFrame(FrameRequestCallback callback);
[Throws]
undefined cancelAnimationFrame(long handle);
};
// https://w3c.github.io/webrtc-encoded-transform/#RTCEncodedAudioFrame-methods
@ -43,3 +34,6 @@ partial interface DedicatedWorkerGlobalScope {
[Pref="media.peerconnection.enabled",
Pref="media.peerconnection.scripttransform.enabled"] attribute EventHandler onrtctransform;
};
// https://html.spec.whatwg.org/multipage/imagebitmap-and-animations.html#animation-frames
DedicatedWorkerGlobalScope includes AnimationFrameProvider;

View file

@ -11,8 +11,8 @@ dictionary GPUUncapturedErrorEventInit : EventInit {
required GPUError error;
};
[Pref="dom.webgpu.enabled",
Exposed=(Window /* ,DedicatedWorker*/), SecureContext]
[Func="mozilla::webgpu::Instance::PrefEnabled",
Exposed=(Window, DedicatedWorker), SecureContext]
interface GPUUncapturedErrorEvent: Event {
constructor(DOMString type, GPUUncapturedErrorEventInit gpuUncapturedErrorEventInitDict);
readonly attribute GPUError error;

View file

@ -15,8 +15,8 @@ dictionary GPUObjectDescriptorBase {
USVString label = "";
};
[Pref="dom.webgpu.enabled",
Exposed=(Window /* ,DedicatedWorker */), SecureContext]
[Func="mozilla::webgpu::Instance::PrefEnabled",
Exposed=(Window, DedicatedWorker), SecureContext]
interface GPUSupportedLimits {
readonly attribute unsigned long maxTextureDimension1D;
readonly attribute unsigned long maxTextureDimension2D;
@ -52,14 +52,14 @@ interface GPUSupportedLimits {
readonly attribute unsigned long maxComputeWorkgroupsPerDimension;
};
[Pref="dom.webgpu.enabled",
Exposed=(Window /* ,DedicatedWorker */), SecureContext]
[Func="mozilla::webgpu::Instance::PrefEnabled",
Exposed=(Window, DedicatedWorker), SecureContext]
interface GPUSupportedFeatures {
readonly setlike<DOMString>;
};
[Pref="dom.webgpu.enabled",
Exposed=(Window /* ,DedicatedWorker */), SecureContext]
[Func="mozilla::webgpu::Instance::PrefEnabled",
Exposed=(Window, DedicatedWorker), SecureContext]
interface GPUAdapterInfo {
readonly attribute DOMString vendor;
readonly attribute DOMString architecture;
@ -77,7 +77,7 @@ interface GPUAdapterInfo {
};
interface mixin NavigatorGPU {
[SameObject, Pref="dom.webgpu.enabled", Exposed=(Window /* ,DedicatedWorker */), SecureContext] readonly attribute GPU gpu;
[SameObject, Func="mozilla::webgpu::Instance::PrefEnabled", Exposed=(Window, DedicatedWorker), SecureContext] readonly attribute GPU gpu;
};
// NOTE: see `dom/webidl/Navigator.webidl`
// Navigator includes NavigatorGPU;
@ -85,8 +85,8 @@ interface mixin NavigatorGPU {
// WorkerNavigator includes NavigatorGPU;
[
Pref="dom.webgpu.enabled",
Exposed=(Window /* ,DedicatedWorker */), SecureContext
Func="mozilla::webgpu::Instance::PrefEnabled",
Exposed=(Window, DedicatedWorker), SecureContext
]
interface GPU {
[Throws]
@ -104,8 +104,8 @@ enum GPUPowerPreference {
"high-performance",
};
[Pref="dom.webgpu.enabled",
Exposed=(Window /* ,DedicatedWorker */), SecureContext]
[Func="mozilla::webgpu::Instance::PrefEnabled",
Exposed=(Window, DedicatedWorker), SecureContext]
interface GPUAdapter {
[SameObject] readonly attribute GPUSupportedFeatures features;
[SameObject] readonly attribute GPUSupportedLimits limits;
@ -138,8 +138,8 @@ enum GPUFeatureName {
"float32-filterable",
};
[Pref="dom.webgpu.enabled",
Exposed=(Window /* ,DedicatedWorker */), SecureContext]
[Func="mozilla::webgpu::Instance::PrefEnabled",
Exposed=(Window, DedicatedWorker), SecureContext]
interface GPUDevice : EventTarget {
[SameObject] readonly attribute GPUSupportedFeatures features;
[SameObject] readonly attribute GPUSupportedLimits limits;
@ -172,8 +172,8 @@ interface GPUDevice : EventTarget {
};
GPUDevice includes GPUObjectBase;
[Pref="dom.webgpu.enabled",
Exposed=(Window /* ,DedicatedWorker */), SecureContext]
[Func="mozilla::webgpu::Instance::PrefEnabled",
Exposed=(Window, DedicatedWorker), SecureContext]
interface GPUBuffer {
readonly attribute GPUSize64Out size;
readonly attribute GPUFlagsConstant usage;
@ -206,8 +206,8 @@ dictionary GPUBufferDescriptor
};
typedef [EnforceRange] unsigned long GPUBufferUsageFlags;
[Pref="dom.webgpu.enabled",
Exposed=(Window /* ,DedicatedWorker */), SecureContext]
[Func="mozilla::webgpu::Instance::PrefEnabled",
Exposed=(Window, DedicatedWorker), SecureContext]
interface GPUBufferUsage {
const GPUFlagsConstant MAP_READ = 0x0001;
const GPUFlagsConstant MAP_WRITE = 0x0002;
@ -222,15 +222,15 @@ interface GPUBufferUsage {
};
typedef [EnforceRange] unsigned long GPUMapModeFlags;
[Pref="dom.webgpu.enabled",
Exposed=(Window /* ,DedicatedWorker */), SecureContext]
[Func="mozilla::webgpu::Instance::PrefEnabled",
Exposed=(Window, DedicatedWorker), SecureContext]
interface GPUMapMode {
const GPUFlagsConstant READ = 0x0001;
const GPUFlagsConstant WRITE = 0x0002;
};
[Pref="dom.webgpu.enabled",
Exposed=(Window /* ,DedicatedWorker */), SecureContext]
[Func="mozilla::webgpu::Instance::PrefEnabled",
Exposed=(Window, DedicatedWorker), SecureContext]
interface GPUTexture {
GPUTextureView createView(optional GPUTextureViewDescriptor descriptor = {});
@ -265,8 +265,8 @@ enum GPUTextureDimension {
};
typedef [EnforceRange] unsigned long GPUTextureUsageFlags;
[Pref="dom.webgpu.enabled",
Exposed=(Window /* ,DedicatedWorker */), SecureContext]
[Func="mozilla::webgpu::Instance::PrefEnabled",
Exposed=(Window, DedicatedWorker), SecureContext]
interface GPUTextureUsage {
const GPUFlagsConstant COPY_SRC = 0x01;
const GPUFlagsConstant COPY_DST = 0x02;
@ -275,8 +275,8 @@ interface GPUTextureUsage {
const GPUFlagsConstant RENDER_ATTACHMENT = 0x10;
};
[Pref="dom.webgpu.enabled",
Exposed=(Window /* ,DedicatedWorker */), SecureContext]
[Func="mozilla::webgpu::Instance::PrefEnabled",
Exposed=(Window, DedicatedWorker), SecureContext]
interface GPUTextureView {
};
GPUTextureView includes GPUObjectBase;
@ -383,8 +383,8 @@ enum GPUTextureFormat {
"bc7-rgba-unorm-srgb",
};
[Pref="dom.webgpu.enabled",
Exposed=(Window /* ,DedicatedWorker */), SecureContext]
[Func="mozilla::webgpu::Instance::PrefEnabled",
Exposed=(Window, DedicatedWorker), SecureContext]
interface GPUSampler {
};
GPUSampler includes GPUObjectBase;
@ -430,8 +430,8 @@ enum GPUCompareFunction {
"always",
};
[Pref="dom.webgpu.enabled",
Exposed=(Window /* ,DedicatedWorker */), SecureContext]
[Func="mozilla::webgpu::Instance::PrefEnabled",
Exposed=(Window, DedicatedWorker), SecureContext]
interface GPUBindGroupLayout {
};
GPUBindGroupLayout includes GPUObjectBase;
@ -452,8 +452,8 @@ dictionary GPUBindGroupLayoutEntry {
};
typedef [EnforceRange] unsigned long GPUShaderStageFlags;
[Pref="dom.webgpu.enabled",
Exposed=(Window /* ,DedicatedWorker */), SecureContext]
[Func="mozilla::webgpu::Instance::PrefEnabled",
Exposed=(Window, DedicatedWorker), SecureContext]
interface GPUShaderStage {
const GPUFlagsConstant VERTEX = 0x1;
const GPUFlagsConstant FRAGMENT = 0x2;
@ -506,8 +506,8 @@ dictionary GPUStorageTextureBindingLayout {
GPUTextureViewDimension viewDimension = "2d";
};
[Pref="dom.webgpu.enabled",
Exposed=(Window /* ,DedicatedWorker */), SecureContext]
[Func="mozilla::webgpu::Instance::PrefEnabled",
Exposed=(Window, DedicatedWorker), SecureContext]
interface GPUBindGroup {
};
GPUBindGroup includes GPUObjectBase;
@ -531,8 +531,8 @@ dictionary GPUBufferBinding {
GPUSize64 size;
};
[Pref="dom.webgpu.enabled",
Exposed=(Window /* ,DedicatedWorker */), SecureContext]
[Func="mozilla::webgpu::Instance::PrefEnabled",
Exposed=(Window, DedicatedWorker), SecureContext]
interface GPUPipelineLayout {
};
GPUPipelineLayout includes GPUObjectBase;
@ -542,8 +542,8 @@ dictionary GPUPipelineLayoutDescriptor
required sequence<GPUBindGroupLayout> bindGroupLayouts;
};
[Pref="dom.webgpu.enabled",
Exposed=(Window /* ,DedicatedWorker */), SecureContext]
[Func="mozilla::webgpu::Instance::PrefEnabled",
Exposed=(Window, DedicatedWorker), SecureContext]
interface GPUShaderModule {
[Throws]
Promise<GPUCompilationInfo> compilationInfo(); // To be removed with <https://bugzilla.mozilla.org/show_bug.cgi?id=1846892>
@ -565,8 +565,8 @@ enum GPUCompilationMessageType {
"info",
};
[Pref="dom.webgpu.enabled",
Exposed=(Window /* ,DedicatedWorker */), SecureContext]
[Func="mozilla::webgpu::Instance::PrefEnabled",
Exposed=(Window, DedicatedWorker), SecureContext]
interface GPUCompilationMessage {
readonly attribute DOMString message;
readonly attribute GPUCompilationMessageType type;
@ -576,8 +576,8 @@ interface GPUCompilationMessage {
readonly attribute unsigned long long length;
};
[Pref="dom.webgpu.enabled",
Exposed=(Window /* ,DedicatedWorker */), SecureContext]
[Func="mozilla::webgpu::Instance::PrefEnabled",
Exposed=(Window, DedicatedWorker), SecureContext]
interface GPUCompilationInfo {
[Cached, Frozen, Pure]
readonly attribute sequence<GPUCompilationMessage> messages;
@ -603,8 +603,8 @@ dictionary GPUProgrammableStage {
//TODO: Serializable
// https://bugzilla.mozilla.org/show_bug.cgi?id=1696219
[Pref="dom.webgpu.enabled",
Exposed=(Window /* ,DedicatedWorker */), SecureContext]
[Func="mozilla::webgpu::Instance::PrefEnabled",
Exposed=(Window, DedicatedWorker), SecureContext]
interface GPUComputePipeline {
};
GPUComputePipeline includes GPUObjectBase;
@ -617,8 +617,8 @@ dictionary GPUComputePipelineDescriptor
//TODO: Serializable
// https://bugzilla.mozilla.org/show_bug.cgi?id=1696219
[Pref="dom.webgpu.enabled",
Exposed=(Window /* ,DedicatedWorker */), SecureContext]
[Func="mozilla::webgpu::Instance::PrefEnabled",
Exposed=(Window, DedicatedWorker), SecureContext]
interface GPURenderPipeline {
};
GPURenderPipeline includes GPUObjectBase;
@ -686,8 +686,8 @@ dictionary GPUBlendState {
};
typedef [EnforceRange] unsigned long GPUColorWriteFlags;
[Pref="dom.webgpu.enabled",
Exposed=(Window /* ,DedicatedWorker */), SecureContext]
[Func="mozilla::webgpu::Instance::PrefEnabled",
Exposed=(Window, DedicatedWorker), SecureContext]
interface GPUColorWrite {
const GPUFlagsConstant RED = 0x1;
const GPUFlagsConstant GREEN = 0x2;
@ -852,8 +852,8 @@ dictionary GPUImageCopyExternalImage {
boolean flipY = false;
};
[Pref="dom.webgpu.enabled",
Exposed=(Window /* ,DedicatedWorker */), SecureContext]
[Func="mozilla::webgpu::Instance::PrefEnabled",
Exposed=(Window, DedicatedWorker), SecureContext]
interface GPUCommandBuffer {
};
GPUCommandBuffer includes GPUObjectBase;
@ -865,8 +865,8 @@ dictionary GPUCommandBufferDescriptor
interface mixin GPUCommandsMixin {
};
[Pref="dom.webgpu.enabled",
Exposed=(Window /* ,DedicatedWorker */), SecureContext]
[Func="mozilla::webgpu::Instance::PrefEnabled",
Exposed=(Window, DedicatedWorker), SecureContext]
interface GPUCommandEncoder {
GPURenderPassEncoder beginRenderPass(GPURenderPassDescriptor descriptor);
GPUComputePassEncoder beginComputePass(optional GPUComputePassDescriptor descriptor = {});
@ -914,8 +914,8 @@ interface mixin GPUDebugCommandsMixin {
undefined insertDebugMarker(USVString markerLabel);
};
[Pref="dom.webgpu.enabled",
Exposed=(Window /* ,DedicatedWorker */), SecureContext]
[Func="mozilla::webgpu::Instance::PrefEnabled",
Exposed=(Window, DedicatedWorker), SecureContext]
interface GPUComputePassEncoder {
undefined setPipeline(GPUComputePipeline pipeline);
undefined dispatchWorkgroups(GPUSize32 workgroupCountX, optional GPUSize32 workgroupCountY = 1, optional GPUSize32 workgroupCountZ = 1);
@ -934,8 +934,8 @@ dictionary GPUComputePassDescriptor
: GPUObjectDescriptorBase {
};
[Pref="dom.webgpu.enabled",
Exposed=(Window /* ,DedicatedWorker */), SecureContext]
[Func="mozilla::webgpu::Instance::PrefEnabled",
Exposed=(Window, DedicatedWorker), SecureContext]
interface GPURenderPassEncoder {
undefined setViewport(float x, float y,
float width, float height,
@ -1026,8 +1026,8 @@ interface mixin GPURenderCommandsMixin {
undefined drawIndexedIndirect(GPUBuffer indirectBuffer, GPUSize64 indirectOffset);
};
[Pref="dom.webgpu.enabled",
Exposed=(Window /* ,DedicatedWorker */), SecureContext]
[Func="mozilla::webgpu::Instance::PrefEnabled",
Exposed=(Window, DedicatedWorker), SecureContext]
interface GPURenderBundle {
};
GPURenderBundle includes GPUObjectBase;
@ -1036,8 +1036,8 @@ dictionary GPURenderBundleDescriptor
: GPUObjectDescriptorBase {
};
[Pref="dom.webgpu.enabled",
Exposed=(Window /* ,DedicatedWorker */), SecureContext]
[Func="mozilla::webgpu::Instance::PrefEnabled",
Exposed=(Window, DedicatedWorker), SecureContext]
interface GPURenderBundleEncoder {
GPURenderBundle finish(optional GPURenderBundleDescriptor descriptor = {});
};
@ -1061,8 +1061,8 @@ dictionary GPUQueueDescriptor
// https://bugzilla.mozilla.org/show_bug.cgi?id=1696216
// https://github.com/heycam/webidl/issues/961
[Pref="dom.webgpu.enabled",
Exposed=(Window /* ,DedicatedWorker */), SecureContext]
[Func="mozilla::webgpu::Instance::PrefEnabled",
Exposed=(Window, DedicatedWorker), SecureContext]
interface GPUQueue {
undefined submit(sequence<GPUCommandBuffer> buffers);
@ -1092,8 +1092,8 @@ interface GPUQueue {
};
GPUQueue includes GPUObjectBase;
[Pref="dom.webgpu.enabled",
Exposed=(Window /* ,DedicatedWorker */), SecureContext]
[Func="mozilla::webgpu::Instance::PrefEnabled",
Exposed=(Window, DedicatedWorker), SecureContext]
interface GPUQuerySet {
undefined destroy();
};
@ -1120,8 +1120,8 @@ enum GPUQueryType {
"timestamp",
};
[Pref="dom.webgpu.enabled",
Exposed=(Window /* ,DedicatedWorker */), SecureContext]
[Func="mozilla::webgpu::Instance::PrefEnabled",
Exposed=(Window, DedicatedWorker), SecureContext]
interface GPUCanvasContext {
readonly attribute (HTMLCanvasElement or OffscreenCanvas) canvas;
@ -1150,8 +1150,8 @@ enum GPUDeviceLostReason {
"destroyed",
};
[Pref="dom.webgpu.enabled",
Exposed=(Window /* ,DedicatedWorker */), SecureContext]
[Func="mozilla::webgpu::Instance::PrefEnabled",
Exposed=(Window, DedicatedWorker), SecureContext]
interface GPUDeviceLostInfo {
readonly attribute any reason; // GPUDeviceLostReason or undefined
readonly attribute DOMString message;
@ -1162,30 +1162,30 @@ partial interface GPUDevice {
readonly attribute Promise<GPUDeviceLostInfo> lost;
};
[Pref="dom.webgpu.enabled",
Exposed=(Window /* ,DedicatedWorker */), SecureContext]
[Func="mozilla::webgpu::Instance::PrefEnabled",
Exposed=(Window, DedicatedWorker), SecureContext]
interface GPUError {
readonly attribute DOMString message;
};
[Pref="dom.webgpu.enabled",
Exposed=(Window /* ,DedicatedWorker */), SecureContext]
[Func="mozilla::webgpu::Instance::PrefEnabled",
Exposed=(Window, DedicatedWorker), SecureContext]
interface GPUValidationError
: GPUError {
[Throws]
constructor(DOMString message);
};
[Pref="dom.webgpu.enabled",
Exposed=(Window /* ,DedicatedWorker */), SecureContext]
[Func="mozilla::webgpu::Instance::PrefEnabled",
Exposed=(Window, DedicatedWorker), SecureContext]
interface GPUOutOfMemoryError
: GPUError {
[Throws]
constructor(DOMString message);
};
[Pref="dom.webgpu.enabled",
Exposed=(Window /* ,DedicatedWorker */), SecureContext]
[Func="mozilla::webgpu::Instance::PrefEnabled",
Exposed=(Window, DedicatedWorker), SecureContext]
interface GPUInternalError
: GPUError {
[Throws]
@ -1205,7 +1205,7 @@ partial interface GPUDevice {
};
partial interface GPUDevice {
[Exposed=(Window /* ,DedicatedWorker */)]
[Exposed=(Window, DedicatedWorker)]
attribute EventHandler onuncapturederror;
};

View file

@ -94,9 +94,10 @@ impl ErrorBuffer {
#[derive(Clone, Copy, Debug, Deserialize, Serialize)]
pub(crate) enum ErrorBufferType {
None = 0,
Internal = 1,
OutOfMemory = 2,
Validation = 3,
DeviceLost = 1,
Internal = 2,
OutOfMemory = 3,
Validation = 4,
}
/// A trait for querying the [`ErrorBufferType`] classification of an error. Used by
@ -174,7 +175,7 @@ mod foreign {
match self {
RequestDeviceError::OutOfMemory => ErrorBufferType::OutOfMemory,
RequestDeviceError::DeviceLost => ErrorBufferType::None,
RequestDeviceError::DeviceLost => ErrorBufferType::DeviceLost,
RequestDeviceError::Internal
| RequestDeviceError::InvalidAdapter
@ -437,7 +438,7 @@ mod foreign {
fn error_type(&self) -> ErrorBufferType {
match self {
DeviceError::Invalid | DeviceError::WrongDevice => ErrorBufferType::Validation,
DeviceError::Lost => ErrorBufferType::None,
DeviceError::Lost => ErrorBufferType::DeviceLost,
DeviceError::OutOfMemory => ErrorBufferType::OutOfMemory,
DeviceError::ResourceCreationFailed => ErrorBufferType::Internal,
}

View file

@ -226,6 +226,11 @@ pub extern "C" fn wgpu_server_adapter_drop(global: &Global, adapter_id: id::Adap
gfx_select!(adapter_id => global.adapter_drop(adapter_id))
}
#[no_mangle]
pub extern "C" fn wgpu_server_device_destroy(global: &Global, self_id: id::DeviceId) {
gfx_select!(self_id => global.device_destroy(self_id))
}
#[no_mangle]
pub extern "C" fn wgpu_server_device_drop(global: &Global, self_id: id::DeviceId) {
gfx_select!(self_id => global.device_drop(self_id))

View file

@ -0,0 +1,46 @@
// |jit-test| --wasm-tail-calls; --wasm-gc; skip-if: !wasmGcEnabled() || !wasmTailCallsEnabled()
// Tests if instance registers were restored properly when call_ref is used
// with tail calls.
var t = wasmEvalText(`(module
(type $t1 (func))
(func $f0 (param funcref i32 i32 i32 i32 i32 i32 i32 i32 i32)
local.get 0
ref.cast (ref $t1)
return_call_ref $t1
)
(func $f1 (param i32))
(elem declare func $f)
(func $f (param funcref)
(local i32 i32 i32 i32)
local.get 0
i32.const 1
i32.const 1
i32.const 1
i32.const 1
i32.const 1
i32.const 1
i32.const 1
i32.const 1
i32.const 1
return_call $f0
)
(func (export "f") (result funcref)
ref.func $f
)
)`);
var t2 = wasmEvalText(`(module
(import "" "f" (func $fi (result funcref)))
(type $t1 (func (param funcref)))
(elem declare func $f2)
(func $f2)
(func (export "test")
ref.func $f2
call $fi
ref.cast (ref $t1)
call_ref $t1
)
)`, {"": {f:t.exports.f},});
t2.exports.test();

View file

@ -5561,6 +5561,9 @@ void MacroAssembler::wasmCallRef(const wasm::CallSiteDesc& desc,
loadPtr(Address(calleeFnObj, uncheckedEntrySlotOffset), calleeScratch);
*slowCallOffset = call(desc, calleeScratch);
#ifdef ENABLE_WASM_TAIL_CALLS
wasmMarkSlowCall();
#endif
// Restore registers and realm and back to this caller's.
loadPtr(Address(getStackPointer(), WasmCallerInstanceOffsetBeforeCall),

View file

@ -93,6 +93,7 @@ void GenericPrinter::vprintf(const char* fmt, va_list ap) {
// Simple shortcut to avoid allocating strings.
if (strchr(fmt, '%') == nullptr) {
put(fmt);
return;
}
GenericPrinterPrintfTarget printer(*this);

View file

@ -284,14 +284,14 @@ class nsDisplayCanvas final : public nsPaintedDisplayItem {
if (!surface || !surface->IsValid()) {
return;
}
gfx::IntSize size = surface->GetSize();
transform = gfxUtils::SnapTransform(
transform, gfxRect(0, 0, size.width, size.height), nullptr);
transform, gfxRect(0, 0, canvasSizeInPx.width, canvasSizeInPx.height),
nullptr);
aCtx->Multiply(transform);
aCtx->GetDrawTarget()->FillRect(
Rect(0, 0, size.width, size.height),
Rect(0, 0, canvasSizeInPx.width, canvasSizeInPx.height),
SurfacePattern(surface, ExtendMode::CLAMP, Matrix(),
nsLayoutUtils::GetSamplingFilterForFrame(f)));
return;

View file

@ -0,0 +1,7 @@
<!doctype html>
<style>
a {
color: green;
}
</style>
<a href="">Which color?</a>

View file

@ -0,0 +1,12 @@
<!doctype html>
<style>
a {
--foo: green;
}
:visited {
--foo: red;
color: var(--foo);
}
</style>
<a href="visited-page.html">Which color?</a>

View file

@ -112,6 +112,8 @@ TEST_HARNESS_FILES.testing.mochitest.tests.layout.style.test["css-visited"] += [
"/layout/reftests/css-visited/svg-paint-currentcolor-visited.svg",
"/layout/reftests/css-visited/transition-on-visited-ref.html",
"/layout/reftests/css-visited/transition-on-visited.html",
"/layout/reftests/css-visited/variables-visited-ref.html",
"/layout/reftests/css-visited/variables-visited.html",
"/layout/reftests/css-visited/visited-inherit-1-ref.html",
"/layout/reftests/css-visited/visited-inherit-1.html",
"/layout/reftests/css-visited/visited-page.html",

View file

@ -95,6 +95,7 @@ var gTests = [
"== logical-box-border-color-visited-link-002.html logical-box-border-color-visited-link-ref.html",
"== logical-box-border-color-visited-link-003.html logical-box-border-color-visited-link-ref.html",
"== svg-paint-currentcolor-visited.svg svg-paint-currentcolor-visited-ref.svg",
"== variables-visited.html variables-visited-ref.html",
];
// We record the maximum number of times we had to look at a test before

View file

@ -2506,6 +2506,7 @@ package org.mozilla.geckoview {
public class WebExtension.MetaData {
ctor protected MetaData();
field public final boolean allowedInPrivateBrowsing;
field @Nullable public final String amoListingUrl;
field public final double averageRating;
field @NonNull public final String baseUrl;
field public final int blocklistState;

View file

@ -7,6 +7,7 @@ package org.mozilla.geckoview.test
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.MediumTest
import org.hamcrest.MatcherAssert.assertThat
import org.hamcrest.Matchers.greaterThan
import org.hamcrest.core.IsEqual.equalTo
import org.hamcrest.core.StringEndsWith.endsWith
import org.json.JSONObject
@ -2258,6 +2259,8 @@ class WebExtensionTest : BaseSessionTest() {
"xpinstall.signatures.required" to false,
"extensions.install.requireBuiltInCerts" to false,
"extensions.update.requireBuiltInCerts" to false,
"extensions.getAddons.cache.enabled" to true,
"extensions.getAddons.cache.lastUpdate" to 0,
),
)
mainSession.loadUri("https://example.com")
@ -2303,6 +2306,52 @@ class WebExtensionTest : BaseSessionTest() {
// Check that the WebExtension was not applied after being uninstalled
assertBodyBorderEqualTo("")
// This pref should have been updated because we expect the cached
// metadata to have been refreshed.
val geckoPrefs = sessionRule.getPrefs(
"extensions.getAddons.cache.lastUpdate",
)
assumeThat(geckoPrefs[0] as Int, greaterThan(0))
}
@Test
fun updateWithMetadataNotStale() {
val now = (System.currentTimeMillis() / 1000).toInt()
sessionRule.setPrefsUntilTestEnd(
mapOf(
"xpinstall.signatures.required" to false,
"extensions.install.requireBuiltInCerts" to false,
"extensions.update.requireBuiltInCerts" to false,
"extensions.getAddons.cache.enabled" to true,
"extensions.getAddons.cache.lastUpdate" to now,
),
)
sessionRule.delegateDuringNextWait(object : WebExtensionController.PromptDelegate {
@AssertCalled
override fun onInstallPrompt(extension: WebExtension): GeckoResult<AllowOrDeny> {
assertEquals(extension.metaData.version, "1.0")
return GeckoResult.allow()
}
})
// 1. Install
val update1 = sessionRule.waitForResult(
controller.install("https://example.org/tests/junit/update-1.xpi"),
)
// 2. Update
val update2 = sessionRule.waitForResult(controller.update(update1))
// 3. Uninstall
sessionRule.waitForResult(controller.uninstall(update2))
// This pref should not have been updated because the cache isn't stale
// (we set the pref to the current time at the top of this test case).
val geckoPrefs = sessionRule.getPrefs(
"extensions.getAddons.cache.lastUpdate",
)
assumeThat(geckoPrefs[0] as Int, equalTo(now))
}
// Test extension updating when the new extension has different permissions.

View file

@ -1979,6 +1979,9 @@ public class WebExtension {
*/
public final boolean temporary;
/** The link to the AMO detail page for this extension. See `AddonWrapper.amoListingURL`. */
public final @Nullable String amoListingUrl;
/** Override for testing. */
protected MetaData() {
icon = null;
@ -2006,6 +2009,7 @@ public class WebExtension {
reviewUrl = null;
updateDate = null;
downloadUrl = null;
amoListingUrl = null;
}
/* package */ MetaData(final GeckoBundle bundle) {
@ -2032,6 +2036,7 @@ public class WebExtension {
reviewUrl = bundle.getString("reviewURL");
updateDate = bundle.getString("updateDate");
downloadUrl = bundle.getString("downloadUrl");
amoListingUrl = bundle.getString("amoListingURL");
final int signedState = bundle.getInt("signedState", SignedStateFlags.UNKNOWN);
if (signedState <= SignedStateFlags.LAST) {

View file

@ -18,7 +18,7 @@ exclude: true
- Added `DisabledFlags.SIGNATURE` for extensions disabled because they aren't correctly signed. ([bug 1847266]({{bugzilla}}1847266))
- Added `Builder` pattern constructors for [`ReviewAnalysis`][120.2] and [`Recommendation`][120.3] (part of [bug 1846341]({{bugzilla}}1846341))
- Added `DisabledFlags.APP_VERSION` for extensions disabled because they aren't compatible with the application version. ([bug 1847266]({{bugzilla}}1847266))
- Added more metadata to the [WebExtension][120.4] class. ([bug 1850674]({{bugzilla}}1850674))
- Added more metadata to the [WebExtension][120.4] class. ([bug 1850674]({{bugzilla}}1850674), [bug 1858925]({{bugzilla}}1858925))
- Added session and translations controller. Includes [`TranslationsController`][120.5], [`TranslationsController.SessionTranslation`][120.6] (notably [translate][120.7]), and a [translations delegate][120.8].
[120.1]: {{javadoc_uri}}/WebExtensionController.html#disableExtensionProcessSpawning
@ -1449,4 +1449,4 @@ to allow adding gecko profiler markers.
[65.24]: {{javadoc_uri}}/CrashReporter.html#sendCrashReport(android.content.Context,android.os.Bundle,java.lang.String)
[65.25]: {{javadoc_uri}}/GeckoResult.html
[api-version]: 0909ac573b825169118c688f1975adc1f30cc252
[api-version]: 3615c62a176c6dd0815b7bf28e7b33c378849701

View file

@ -15,6 +15,7 @@ const lazy = {};
ChromeUtils.defineESModuleGetters(lazy, {
AddonManager: "resource://gre/modules/AddonManager.sys.mjs",
AddonRepository: "resource://gre/modules/addons/AddonRepository.sys.mjs",
AddonSettings: "resource://gre/modules/addons/AddonSettings.sys.mjs",
EventDispatcher: "resource://gre/modules/Messaging.sys.mjs",
Extension: "resource://gre/modules/Extension.sys.mjs",
@ -297,6 +298,7 @@ async function exportExtension(aAddon, aPermissions, aSourceURI) {
policy = await policy.readyPromise;
}
const {
amoListingURL,
averageRating,
blocklistState,
creator,
@ -371,6 +373,7 @@ async function exportExtension(aAddon, aPermissions, aSourceURI) {
isBuiltIn: isBuiltin,
webExtensionFlags: exportFlags(policy),
metaData: {
amoListingURL,
averageRating,
baseURL,
blocklistState,
@ -1063,6 +1066,20 @@ export var GeckoViewWebExtension = {
},
async updateWebExtension(aId) {
// Refresh the cached metadata when necessary. This allows us to always
// export relatively recent metadata to the embedder.
if (lazy.AddonRepository.isMetadataStale()) {
// We use a promise to avoid more than one call to `backgroundUpdateCheck()`
// when `updateWebExtension()` is called for multiple add-ons in parallel.
if (!this._promiseAddonRepositoryUpdate) {
this._promiseAddonRepositoryUpdate =
lazy.AddonRepository.backgroundUpdateCheck().finally(() => {
this._promiseAddonRepositoryUpdate = null;
});
}
await this._promiseAddonRepositoryUpdate;
}
const extension = await this.extensionById(aId);
const install = await this.checkForUpdate(extension);

View file

@ -1478,17 +1478,11 @@
# Communicates the toolbar color to platform (for e.g., prefers-color-scheme).
#
# Returns whether the toolbar is dark (0), light (1), or system (2).
#
# Default to "light" on macOS / Windows, and "system" elsewhere. The theming
# code sets it appropriately.
# Returns whether the toolbar is dark (0), light (1), or system (2). The
# theming code overrides it if appropriate.
- name: browser.theme.toolbar-theme
type: RelaxedAtomicUint32
#if defined(XP_WIN) || defined(XP_MACOSX)
value: 1
#else
value: 2
#endif
mirror: always
# Communicates the preferred content theme color to platform (for e.g.,
@ -1503,6 +1497,14 @@
mirror: always
rust: true
# Whether the firefox titlebar respects the
# -moz-windows-accent-color-in-titlebar setting on the tab strip.
- name: browser.theme.windows.accent-color-in-tabs.enabled
type: RelaxedAtomicBool
value: false
mirror: always
rust: true
# Blocked plugin content
- name: browser.safebrowsing.blockedURIs.enabled
type: bool
@ -4480,6 +4482,12 @@
value: @IS_NIGHTLY_BUILD@
mirror: always
# Is support for the Web GPU API enabled on DOM workers?
- name: dom.webgpu.workers.enabled
type: RelaxedAtomicBool
value: false
mirror: always
# Are WebGPU indirect draws/dispatches enabled?
- name: dom.webgpu.indirect-dispatch.enabled
type: RelaxedAtomicBool
@ -15495,13 +15503,6 @@
value: false
mirror: always
# Whether to use the accent color for the titlebar, if the relevant Windows
# setting says so. See bug 1851155.
- name: widget.windows.titlebar-accent.enabled
type: bool
value: false
mirror: always
- name: widget.windows.window_occlusion_tracking_display_state.enabled
type: bool
value: false

View file

@ -8,7 +8,7 @@
# documentation and how to modify this file.
repo: mozilla-central
created_at: '2021-10-14T12:50:40.073465'
updated_at: '2023-10-12T17:34:35.401624'
updated_at: '2023-10-16T17:08:49.465830'
export:
path: ./docs/mots/index.rst
format: rst
@ -408,6 +408,10 @@ people:
bmo_id: 448747
name: Gabriele Svelto
nick: gsvelto
- &gw
bmo_id: 504871
name: Glenn Watson
nick: gw
- &haik
bmo_id: 558190
name: Haik Aftandilian
@ -1739,6 +1743,7 @@ modules:
- *sotaro
- *jnicol
- *rhunt
- *gw
machine_name: core_graphics
- name: 'Core: HAL'
@ -4200,5 +4205,5 @@ modules:
- Ryan Tilder
group: dev-platform
hashes:
config: b6048ef220609203d8a630974dfd73321761dcce
export: dd5d7f2127edc8694c19f8f1a981e0687d8eb575
config: d8c4484d39645eb0b2e175b1a3634d6b3cc47426
export: a6e22c1edd65c7da848df1cb4c92c507e6803737

View file

@ -403,14 +403,13 @@ void nsStandardURL::InvalidateCache(bool invalidateCachedFile) {
// Return the number of "dots" in the string, or -1 if invalid. Note that the
// number of relevant entries in the bases/starts/ends arrays is number of
// dots + 1.
// Since the trailing dot is allowed, we pass and adjust "length".
//
// length is assumed to be <= host.Length(); the callers is responsible for that
// length is assumed to be <= host.Length(); the caller is responsible for that
//
// Note that the value returned is guaranteed to be in [-1, 3] range.
inline int32_t ValidateIPv4Number(const nsACString& host, int32_t bases[4],
int32_t dotIndex[3], bool& onlyBase10,
int32_t& length) {
int32_t length, bool trailingDot) {
MOZ_ASSERT(length <= (int32_t)host.Length());
if (length <= 0) {
return -1;
@ -426,16 +425,11 @@ inline int32_t ValidateIPv4Number(const nsACString& host, int32_t bases[4],
// A dot should not follow a dot, or be first - it can follow an x though.
if (!(lastWasNumber ||
(i >= 2 && (host[i - 1] == 'X' || host[i - 1] == 'x') &&
host[i - 2] == '0'))) {
host[i - 2] == '0')) ||
(i == (length - 1) && trailingDot)) {
return -1;
}
if (dotCount > 0 &&
i == (length - 1)) { // Trailing dot is OK; shorten and return
length--;
return dotCount;
}
if (dotCount > 2) {
return -1;
}
@ -562,11 +556,20 @@ nsresult nsStandardURL::NormalizeIPv4(const nsACString& host,
bool onlyBase10 = true; // Track this as a special case
int32_t dotIndex[3]; // The positions of the dots in the string
// The length may be adjusted by ValidateIPv4Number (ignoring the trailing
// period) so use "length", rather than host.Length() after that call.
int32_t length = static_cast<int32_t>(host.Length());
int32_t dotCount =
ValidateIPv4Number(host, bases, dotIndex, onlyBase10, length);
// Use "length" rather than host.Length() after call to
// ValidateIPv4Number because of potential trailing period.
nsDependentCSubstring filteredHost;
bool trailingDot = false;
if (host.Length() > 0 && host.Last() == '.') {
trailingDot = true;
filteredHost.Rebind(host.BeginReading(), host.Length() - 1);
} else {
filteredHost.Rebind(host.BeginReading(), host.Length());
}
int32_t length = static_cast<int32_t>(filteredHost.Length());
int32_t dotCount = ValidateIPv4Number(filteredHost, bases, dotIndex,
onlyBase10, length, trailingDot);
if (dotCount < 0 || length <= 0) {
return NS_ERROR_FAILURE;
}
@ -3995,7 +3998,7 @@ nsresult Test_ParseIPv4Number(const nsACString& input, int32_t base,
int32_t Test_ValidateIPv4Number(const nsACString& host, int32_t bases[4],
int32_t dotIndex[3], bool& onlyBase10,
int32_t& length) {
int32_t length) {
return mozilla::net::ValidateIPv4Number(host, bases, dotIndex, onlyBase10,
length);
length, false);
}

View file

@ -32,7 +32,7 @@ TrackerUriBlocked=The resource at “%1$S” was blocked because content blockin
UnsafeUriBlocked=The resource at “%1$S” was blocked by Safe Browsing.
# LOCALIZATION NOTE (StrictUrlProtocolSetter): %1$S is the URL that has attempted to be changed. %2$S is the invalid target protocol.
StrictUrlProtocolSetter=Url “%1%S“ change to protocol “%2$S“ was blocked.
StrictUrlProtocolSetter=Url “%1$S“ change to protocol “%2$S“ was blocked.
# LOCALIZATION NOTE (CORPBlocked): %1$S is the URL of the blocked resource. %2$S is the URL of the MDN page about CORP.
CORPBlocked=The resource at “%1$S” was blocked due to its Cross-Origin-Resource-Policy header (or lack thereof). See %2$S

View file

@ -226,6 +226,7 @@ inline bool nsHttpHeaderArray::IsSingletonHeader(const nsHttpAtom& header) {
header == nsHttp::If_Modified_Since ||
header == nsHttp::If_Unmodified_Since || header == nsHttp::From ||
header == nsHttp::Location || header == nsHttp::Max_Forwards ||
header == nsHttp::GlobalPrivacyControl ||
// Ignore-multiple-headers are singletons in the sense that they
// shouldn't be merged.
IsIgnoreMultipleHeader(header);

View file

@ -23,7 +23,7 @@ extern nsresult Test_ParseIPv4Number(const nsACString& input, int32_t base,
uint32_t& number, uint32_t maxNumber);
extern int32_t Test_ValidateIPv4Number(const nsACString& host, int32_t bases[4],
int32_t dotIndex[3], bool& onlyBase10,
int32_t& length);
int32_t length);
TEST(TestStandardURL, Simple)
{
nsCOMPtr<nsIURI> url;

View file

@ -684,7 +684,7 @@ macro_rules! bool_pref_feature {
/// to support new types in these entries and (2) ensuring that either
/// nsPresContext::MediaFeatureValuesChanged is called when the value that
/// would be returned by the evaluator function could change.
pub static MEDIA_FEATURES: [QueryFeatureDescription; 62] = [
pub static MEDIA_FEATURES: [QueryFeatureDescription; 63] = [
feature!(
atom!("width"),
AllowsRanges::Yes,
@ -1005,4 +1005,8 @@ pub static MEDIA_FEATURES: [QueryFeatureDescription; 62] = [
atom!("-moz-always-underline-links"),
"layout.css.always_underline_links"
),
bool_pref_feature!(
atom!("-moz-windows-accent-color-in-tabs"),
"browser.theme.windows.accent-color-in-tabs.enabled"
),
];

View file

@ -217,7 +217,7 @@ where
/// Whether we're cascading for visited or unvisited styles.
#[derive(Clone, Copy)]
pub enum CascadeMode<'a> {
pub enum CascadeMode<'a, 'b> {
/// We're cascading for unvisited styles.
Unvisited {
/// The visited rules that should match the visited style.
@ -225,12 +225,28 @@ pub enum CascadeMode<'a> {
},
/// We're cascading for visited styles.
Visited {
/// The writing mode of our unvisited style, needed to correctly resolve
/// logical properties..
writing_mode: WritingMode,
/// The cascade for our unvisited style.
unvisited_context: &'a computed::Context<'b>,
},
}
fn iter_declarations<'builder, 'decls: 'builder>(
iter: impl Iterator<Item = (&'decls PropertyDeclaration, CascadePriority)>,
declarations: &mut Declarations<'decls>,
mut custom_builder: Option<&mut CustomPropertiesBuilder<'builder>>,
) {
for (declaration, priority) in iter {
if let PropertyDeclaration::Custom(ref declaration) = *declaration {
if let Some(ref mut builder) = custom_builder {
builder.cascade(declaration, priority);
}
} else {
let id = declaration.id().as_longhand().unwrap();
declarations.note_declaration(declaration, priority, id);
}
}
}
/// NOTE: This function expects the declaration with more priority to appear
/// first.
pub fn apply_declarations<'a, E, I>(
@ -285,34 +301,32 @@ where
context.style().add_flags(cascade_input_flags);
let using_cached_reset_properties;
let mut cascade = Cascade::new(&mut context, cascade_mode, first_line_reparenting);
let mut data = CascadeData::default();
{
let mut builder =
CustomPropertiesBuilder::new(inherited_style.custom_properties(), stylist, is_root_element);
for (declaration, priority) in iter {
if let PropertyDeclaration::Custom(ref declaration) = *declaration {
builder.cascade(declaration, priority);
} else {
let id = declaration.id().as_longhand().unwrap();
data.note_declaration(declaration, priority, id);
}
}
cascade.context.builder.custom_properties = builder.build();
};
let properties_to_apply = match cascade.cascade_mode {
CascadeMode::Visited { writing_mode } => {
cascade.context.builder.writing_mode = writing_mode;
let mut cascade = Cascade::new(&mut context, first_line_reparenting);
let mut declarations = Default::default();
let mut shorthand_cache = ShorthandsWithPropertyReferencesCache::default();
let properties_to_apply = match cascade_mode {
CascadeMode::Visited { unvisited_context } => {
cascade.context.builder.custom_properties = unvisited_context.builder.custom_properties.clone();
cascade.context.builder.writing_mode = unvisited_context.builder.writing_mode;
// We never insert visited styles into the cache so we don't need to try looking it up.
// It also wouldn't be super-profitable, only a handful :visited properties are
// non-inherited.
using_cached_reset_properties = false;
// TODO(bug 1859385): If we match the same rules when visited and unvisited, we could
// try to avoid gathering the declarations. That'd be:
// unvisited_context.builder.rules.as_ref() == Some(rules)
iter_declarations(iter, &mut declarations, None);
LonghandIdSet::visited_dependent()
},
CascadeMode::Unvisited { visited_rules } => {
cascade.apply_prioritary_properties(&mut data);
cascade.context.builder.custom_properties = {
let mut builder =
CustomPropertiesBuilder::new(inherited_style.custom_properties(), stylist, is_root_element);
iter_declarations(iter, &mut declarations, Some(&mut builder));
builder.build()
};
cascade.apply_prioritary_properties(&declarations, &mut shorthand_cache);
if let Some(visited_rules) = visited_rules {
cascade.compute_visited_style_if_needed(
@ -336,7 +350,7 @@ where
},
};
cascade.apply_non_prioritary_properties(&mut data, &properties_to_apply);
cascade.apply_non_prioritary_properties(&declarations.longhand_declarations, &mut shorthand_cache, &properties_to_apply);
cascade.finished_applying_properties();
@ -536,22 +550,18 @@ struct Declaration<'a> {
next_index: DeclarationIndex,
}
/// A bit of a kitchen-sink struct for things that need to mutate in ways that otherwise rustc
/// can't reason about if we put these in Cascade.
/// The set of property declarations from our rules.
#[derive(Default)]
struct CascadeData<'a> {
struct Declarations<'a> {
/// Whether we have any prioritary property. This is just a minor optimization.
has_prioritary_properties: bool,
/// A cache for shorthands with property references, to avoid substituting the same value over
/// and over for each of the longhands.
shorthand_cache: ShorthandsWithPropertyReferencesCache,
/// A list of all the applicable longhand declarations.
longhand_declarations: SmallVec<[Declaration<'a>; 32]>,
/// The prioritary property position data.
prioritary_positions: [PrioritaryDeclarationPosition; PRIORITARY_PROPERTY_COUNT],
}
impl<'a> CascadeData<'a> {
impl<'a> Declarations<'a> {
fn note_prioritary_property(&mut self, id: PrioritaryPropertyId) {
let new_index = self.longhand_declarations.len();
if new_index >= DeclarationIndex::MAX as usize {
@ -593,7 +603,6 @@ impl<'a> CascadeData<'a> {
struct Cascade<'a, 'b: 'a> {
context: &'a mut computed::Context<'b>,
cascade_mode: CascadeMode<'a>,
first_line_reparenting: FirstLineReparenting<'b>,
ignore_colors: bool,
seen: LonghandIdSet,
@ -606,13 +615,11 @@ struct Cascade<'a, 'b: 'a> {
impl<'a, 'b: 'a> Cascade<'a, 'b> {
fn new(
context: &'a mut computed::Context<'b>,
cascade_mode: CascadeMode<'a>,
first_line_reparenting: FirstLineReparenting<'b>,
) -> Self {
let ignore_colors = !context.builder.device.use_document_colors();
Self {
context,
cascade_mode,
first_line_reparenting,
ignore_colors,
seen: LonghandIdSet::default(),
@ -678,10 +685,11 @@ impl<'a, 'b: 'a> Cascade<'a, 'b> {
fn apply_one_prioritary_property(
&mut self,
data: &mut CascadeData,
decls: &Declarations,
cache: &mut ShorthandsWithPropertyReferencesCache,
id: PrioritaryPropertyId,
) -> bool {
let mut index = data.prioritary_positions[id as usize].most_important;
let mut index = decls.prioritary_positions[id as usize].most_important;
if index == DeclarationIndex::MAX {
return false;
}
@ -692,13 +700,13 @@ impl<'a, 'b: 'a> Cascade<'a, 'b> {
"That could require more book-keeping"
);
loop {
let decl = data.longhand_declarations[index as usize];
let decl = decls.longhand_declarations[index as usize];
self.apply_one_longhand(
longhand_id,
longhand_id,
decl.decl,
decl.priority,
&mut data.shorthand_cache,
cache,
);
if self.seen.contains(longhand_id) {
return true; // Common case, we're done.
@ -721,27 +729,27 @@ impl<'a, 'b: 'a> Cascade<'a, 'b> {
false
}
fn apply_prioritary_properties(&mut self, data: &mut CascadeData) {
if !data.has_prioritary_properties {
fn apply_prioritary_properties(&mut self, decls: &Declarations, cache: &mut ShorthandsWithPropertyReferencesCache) {
if !decls.has_prioritary_properties {
return;
}
let has_writing_mode = self
.apply_one_prioritary_property(data, PrioritaryPropertyId::WritingMode) |
self.apply_one_prioritary_property(data, PrioritaryPropertyId::Direction) |
self.apply_one_prioritary_property(data, PrioritaryPropertyId::TextOrientation);
.apply_one_prioritary_property(decls, cache, PrioritaryPropertyId::WritingMode) |
self.apply_one_prioritary_property(decls, cache, PrioritaryPropertyId::Direction) |
self.apply_one_prioritary_property(decls, cache, PrioritaryPropertyId::TextOrientation);
if has_writing_mode {
self.compute_writing_mode();
}
if self.apply_one_prioritary_property(data, PrioritaryPropertyId::Zoom) {
if self.apply_one_prioritary_property(decls, cache, PrioritaryPropertyId::Zoom) {
self.compute_zoom();
}
// Compute font-family.
let has_font_family =
self.apply_one_prioritary_property(data, PrioritaryPropertyId::FontFamily);
let has_lang = self.apply_one_prioritary_property(data, PrioritaryPropertyId::XLang);
self.apply_one_prioritary_property(decls, cache, PrioritaryPropertyId::FontFamily);
let has_lang = self.apply_one_prioritary_property(decls, cache, PrioritaryPropertyId::XLang);
if has_lang {
self.recompute_initial_font_family_if_needed();
}
@ -750,15 +758,15 @@ impl<'a, 'b: 'a> Cascade<'a, 'b> {
}
// Compute font-size.
if self.apply_one_prioritary_property(data, PrioritaryPropertyId::XTextScale) {
if self.apply_one_prioritary_property(decls, cache, PrioritaryPropertyId::XTextScale) {
self.unzoom_fonts_if_needed();
}
let has_font_size =
self.apply_one_prioritary_property(data, PrioritaryPropertyId::FontSize);
self.apply_one_prioritary_property(decls, cache, PrioritaryPropertyId::FontSize);
let has_math_depth =
self.apply_one_prioritary_property(data, PrioritaryPropertyId::MathDepth);
self.apply_one_prioritary_property(decls, cache, PrioritaryPropertyId::MathDepth);
let has_min_font_size_ratio =
self.apply_one_prioritary_property(data, PrioritaryPropertyId::MozMinFontSizeRatio);
self.apply_one_prioritary_property(decls, cache, PrioritaryPropertyId::MozMinFontSizeRatio);
if has_math_depth && has_font_size {
self.recompute_math_font_size_if_needed();
@ -771,26 +779,27 @@ impl<'a, 'b: 'a> Cascade<'a, 'b> {
}
// Compute the rest of the first-available-font-affecting properties.
self.apply_one_prioritary_property(data, PrioritaryPropertyId::FontWeight);
self.apply_one_prioritary_property(data, PrioritaryPropertyId::FontStretch);
self.apply_one_prioritary_property(data, PrioritaryPropertyId::FontStyle);
self.apply_one_prioritary_property(data, PrioritaryPropertyId::FontSizeAdjust);
self.apply_one_prioritary_property(decls, cache, PrioritaryPropertyId::FontWeight);
self.apply_one_prioritary_property(decls, cache, PrioritaryPropertyId::FontStretch);
self.apply_one_prioritary_property(decls, cache, PrioritaryPropertyId::FontStyle);
self.apply_one_prioritary_property(decls, cache, PrioritaryPropertyId::FontSizeAdjust);
self.apply_one_prioritary_property(data, PrioritaryPropertyId::ColorScheme);
self.apply_one_prioritary_property(data, PrioritaryPropertyId::ForcedColorAdjust);
self.apply_one_prioritary_property(decls, cache, PrioritaryPropertyId::ColorScheme);
self.apply_one_prioritary_property(decls, cache, PrioritaryPropertyId::ForcedColorAdjust);
// Compute the line height.
self.apply_one_prioritary_property(data, PrioritaryPropertyId::LineHeight);
self.apply_one_prioritary_property(decls, cache, PrioritaryPropertyId::LineHeight);
}
fn apply_non_prioritary_properties(
&mut self,
data: &mut CascadeData,
longhand_declarations: &[Declaration],
shorthand_cache: &mut ShorthandsWithPropertyReferencesCache,
properties_to_apply: &LonghandIdSet,
) {
debug_assert!(!properties_to_apply.contains_any(LonghandIdSet::prioritary_properties()));
debug_assert!(self.declarations_to_apply_unless_overridden.is_empty());
for declaration in &data.longhand_declarations {
for declaration in &*longhand_declarations {
let longhand_id = declaration.decl.id().as_longhand().unwrap();
if !properties_to_apply.contains(longhand_id) {
continue;
@ -802,7 +811,7 @@ impl<'a, 'b: 'a> Cascade<'a, 'b> {
physical_longhand_id,
declaration.decl,
declaration.priority,
&mut data.shorthand_cache,
shorthand_cache,
);
}
if !self.declarations_to_apply_unless_overridden.is_empty() {
@ -823,7 +832,7 @@ impl<'a, 'b: 'a> Cascade<'a, 'b> {
physical_longhand_id: LonghandId,
declaration: &PropertyDeclaration,
priority: CascadePriority,
shorthand_cache: &mut ShorthandsWithPropertyReferencesCache,
cache: &mut ShorthandsWithPropertyReferencesCache,
) {
debug_assert!(!physical_longhand_id.is_logical());
let origin = priority.cascade_level().origin();
@ -841,7 +850,7 @@ impl<'a, 'b: 'a> Cascade<'a, 'b> {
}
}
let mut declaration = self.substitute_variables_if_needed(shorthand_cache, declaration);
let mut declaration = self.substitute_variables_if_needed(cache, declaration);
// When document colors are disabled, do special handling of
// properties that are marked as ignored in that mode.
@ -896,7 +905,6 @@ impl<'a, 'b: 'a> Cascade<'a, 'b> {
}
fn compute_zoom(&mut self) {
debug_assert!(matches!(self.cascade_mode, CascadeMode::Unvisited { .. }));
self.context.builder.effective_zoom = self
.context
.builder
@ -905,7 +913,6 @@ impl<'a, 'b: 'a> Cascade<'a, 'b> {
}
fn compute_writing_mode(&mut self) {
debug_assert!(matches!(self.cascade_mode, CascadeMode::Unvisited { .. }));
self.context.builder.writing_mode =
WritingMode::new(self.context.builder.get_inherited_box())
}
@ -921,7 +928,6 @@ impl<'a, 'b: 'a> Cascade<'a, 'b> {
) where
E: TElement,
{
debug_assert!(matches!(self.cascade_mode, CascadeMode::Unvisited { .. }));
let is_link = self.context.builder.pseudo.is_none() && element.unwrap().is_link();
macro_rules! visited_parent {
@ -934,8 +940,6 @@ impl<'a, 'b: 'a> Cascade<'a, 'b> {
};
}
let writing_mode = self.context.builder.writing_mode;
// We could call apply_declarations directly, but that'd cause
// another instantiation of this function which is not great.
let style = cascade_rules(
@ -947,7 +951,7 @@ impl<'a, 'b: 'a> Cascade<'a, 'b> {
visited_parent!(parent_style),
visited_parent!(layout_parent_style),
self.first_line_reparenting,
CascadeMode::Visited { writing_mode },
CascadeMode::Visited { unvisited_context: &*self.context },
// Cascade input flags don't matter for the visited style, they are
// in the main (unvisited) style.
Default::default(),

View file

@ -239,6 +239,13 @@ user-id = 4333
user-login = "joshtriplett"
user-name = "Josh Triplett"
[[publisher.flate2]]
version = "1.0.26"
when = "2023-04-28"
user-id = 4333
user-login = "joshtriplett"
user-name = "Josh Triplett"
[[publisher.freetype]]
version = "0.7.0"
when = "2020-07-14"
@ -1044,12 +1051,38 @@ criteria = "safe-to-deploy"
delta = "0.7.1 -> 0.8.0"
notes = "This was a small update to the crate which has to do with Rust language features and compiler versions, no substantial changes."
[[audits.bytecode-alliance.audits.miniz_oxide]]
who = "Alex Crichton <alex@alexcrichton.com>"
criteria = "safe-to-deploy"
version = "0.7.1"
notes = """
This crate is a Rust implementation of zlib compression/decompression and has
been used by default by the Rust standard library for quite some time. It's also
a default dependency of the popular `backtrace` crate for decompressing debug
information. This crate forbids unsafe code and does not otherwise access system
resources. It's originally a port of the `miniz.c` library as well, and given
its own longevity should be relatively hardened against some of the more common
compression-related issues.
"""
[[audits.bytecode-alliance.audits.mio]]
who = "Alex Crichton <alex@alexcrichton.com>"
criteria = "safe-to-deploy"
delta = "0.8.6 -> 0.8.8"
notes = "Mostly OS portability updates along with some minor bugfixes."
[[audits.bytecode-alliance.audits.object]]
who = "Alex Crichton <alex@alexcrichton.com>"
criteria = "safe-to-deploy"
delta = "0.30.3 -> 0.31.1"
notes = "A large-ish update to the crate but nothing out of the ordering. Support for new formats like xcoff, new constants, minor refactorings, etc. Nothing out of the ordinary."
[[audits.bytecode-alliance.audits.object]]
who = "Alex Crichton <alex@alexcrichton.com>"
criteria = "safe-to-deploy"
delta = "0.31.1 -> 0.32.0"
notes = "Various new features and refactorings as one would expect from an object parsing crate, all looks good."
[[audits.bytecode-alliance.audits.peeking_take_while]]
who = "Nick Fitzgerald <fitzgen@gmail.com>"
criteria = "safe-to-deploy"
@ -1095,6 +1128,12 @@ criteria = "safe-to-deploy"
version = "0.4.6"
notes = "provides a datastructure implemented using std's Vec. all uses of unsafe are just delegating to the underlying unsafe Vec methods."
[[audits.bytecode-alliance.audits.socket2]]
who = "Alex Crichton <alex@alexcrichton.com>"
criteria = "safe-to-deploy"
delta = "0.4.7 -> 0.4.9"
notes = "Minor OS compat updates but otherwise nothing major here."
[[audits.bytecode-alliance.audits.tempfile]]
who = "Pat Hickey <phickey@fastly.com>"
criteria = "safe-to-deploy"
@ -1271,6 +1310,12 @@ criteria = "safe-to-run"
version = "0.7.1"
aggregated-from = "https://chromium.googlesource.com/chromiumos/third_party/rust_crates/+/refs/heads/main/cargo-vet/audits.toml?format=TEXT"
[[audits.google.audits.tokio]]
who = "Vovo Yang <vovoy@google.com>"
criteria = "safe-to-run"
version = "1.29.1"
aggregated-from = "https://chromium.googlesource.com/chromiumos/third_party/rust_crates/+/refs/heads/main/cargo-vet/audits.toml?format=TEXT"
[[audits.google.audits.tokio-stream]]
who = "David Koloski <dkoloski@google.com>"
criteria = "safe-to-deploy"

View file

@ -2831,29 +2831,16 @@ export var BrowserTestUtils = {
/**
* A helper function for this test that returns a Promise that resolves
* once either the legacy or new migration wizard appears.
* once the migration wizard appears.
*
* @param {DOMWindow} window
* The top-level window that the about:preferences tab is likely to open
* in if the new migration wizard is enabled.
* @param {boolean} forceLegacy
* True if, despite the browser.migrate.content-modal.enabled pref value,
* the legacy XUL migration wizard is expected.
* @returns {Promise<Element>}
* Resolves to the dialog window in the legacy case, and the
* about:preferences tab otherwise.
* Resolves to the opened about:preferences tab with the migration wizard
* running and loaded in it.
*/
async waitForMigrationWizard(window, forceLegacy = false) {
if (!this._usingNewMigrationWizard || forceLegacy) {
return this.waitForCondition(() => {
let win = Services.wm.getMostRecentWindow("Browser:MigrationWizard");
if (win?.document?.readyState == "complete") {
return win;
}
return false;
}, "Wait for migration wizard to open");
}
async waitForMigrationWizard(window) {
let wizardReady = this.waitForEvent(window, "MigrationWizard:Ready");
let wizardTab = await this.waitForNewTab(window.gBrowser, url => {
return url.startsWith("about:preferences");
@ -2862,27 +2849,6 @@ export var BrowserTestUtils = {
return wizardTab;
},
/**
* Closes the migration wizard.
*
* @param {Element} wizardWindowOrTab
* The XUL dialog window for the migration wizard in the legacy case, and
* the about:preferences tab otherwise. In general, it's probably best to
* just pass whatever BrowserTestUtils.waitForMigrationWizard resolved to
* into this in order to handle both the old and new migration wizard.
* @param {boolean} forceLegacy
* True if, despite the browser.migrate.content-modal.enabled pref value,
* the legacy XUL migration wizard is expected.
* @returns {Promise<undefined>}
*/
closeMigrationWizard(wizardWindowOrTab, forceLegacy = false) {
if (!this._usingNewMigrationWizard || forceLegacy) {
return BrowserTestUtils.closeWindow(wizardWindowOrTab);
}
return BrowserTestUtils.removeTab(wizardWindowOrTab);
},
};
XPCOMUtils.defineLazyPreferenceGetter(
@ -2892,11 +2858,4 @@ XPCOMUtils.defineLazyPreferenceGetter(
false
);
XPCOMUtils.defineLazyPreferenceGetter(
BrowserTestUtils,
"_usingNewMigrationWizard",
"browser.migrate.content-modal.enabled",
false
);
Services.obs.addObserver(BrowserTestUtils, "test-complete");

View file

@ -47,7 +47,7 @@ Standard benchmarks are third-party tests (i.e. Speedometer) that we have integr
* **alert threshold**: 2.0
* **apps**: firefox, chrome, chromium, safari
* **expected**: pass
* **expose gecko profiler**: true
* **expose browser profiler**: true
* **gecko profile entries**: 14000000
* **gecko profile interval**: 1
* **lower is better**: true
@ -306,7 +306,7 @@ Standard benchmarks are third-party tests (i.e. Speedometer) that we have integr
* **alert threshold**: 2.0
* **apps**: firefox, chrome, chromium, safari
* **expected**: pass
* **expose gecko profiler**: true
* **expose browser profiler**: true
* **gecko profile entries**: 2000000
* **gecko profile interval**: 1
* **lower is better**: true
@ -567,7 +567,7 @@ Standard benchmarks are third-party tests (i.e. Speedometer) that we have integr
* **alert threshold**: 2.0
* **apps**: firefox, chrome, chromium, safari
* **expected**: pass
* **expose gecko profiler**: true
* **expose browser profiler**: true
* **gecko profile entries**: 14000000
* **gecko profile interval**: 1
* **lower is better**: false
@ -829,7 +829,7 @@ Standard benchmarks are third-party tests (i.e. Speedometer) that we have integr
* **alert threshold**: 2.0
* **apps**: firefox, chrome, chromium
* **expected**: pass
* **expose gecko profiler**: true
* **expose browser profiler**: true
* **gecko profile entries**: 14000000
* **gecko profile interval**: 1
* **lower is better**: true
@ -1086,7 +1086,7 @@ Standard benchmarks are third-party tests (i.e. Speedometer) that we have integr
* **alert threshold**: 2.0
* **apps**: firefox, chrome, chromium, safari
* **expected**: pass
* **expose gecko profiler**: true
* **expose browser profiler**: true
* **gecko profile entries**: 8000000
* **gecko profile interval**: 1
* **lower is better**: false
@ -1343,6 +1343,7 @@ Standard benchmarks are third-party tests (i.e. Speedometer) that we have integr
* **alert threshold**: 2.0
* **apps**: firefox, chrome, chromium, safari
* **expected**: pass
* **expose chrome trace**: true
* **expose gecko profiler**: true
* **gecko profile entries**: 8000000
* **gecko profile interval**: 1
@ -1600,7 +1601,7 @@ Standard benchmarks are third-party tests (i.e. Speedometer) that we have integr
* **alert threshold**: 2.0
* **apps**: firefox, chrome, chromium, safari, custom-car
* **expected**: pass
* **expose gecko profiler**: true
* **expose browser profiler**: true
* **gecko profile entries**: 14000000
* **gecko profile interval**: 1
* **lower is better**: false
@ -2281,7 +2282,7 @@ Standard benchmarks are third-party tests (i.e. Speedometer) that we have integr
* **browsertime args**: --browsertime.speedometer_iterations=5
* **custom data**: true
* **expected**: pass
* **expose gecko profiler**: true
* **expose browser profiler**: true
* **gecko profile entries**: 14000000
* **gecko profile interval**: 1
* **host from parent**: false
@ -2956,7 +2957,7 @@ Standard benchmarks are third-party tests (i.e. Speedometer) that we have integr
* **alert threshold**: 2.0
* **apps**: firefox, chrome, chromium, safari
* **expected**: pass
* **expose gecko profiler**: true
* **expose browser profiler**: true
* **gecko profile entries**: 8000000
* **gecko profile interval**: 1
* **lower is better**: false
@ -3215,7 +3216,7 @@ Standard benchmarks are third-party tests (i.e. Speedometer) that we have integr
* **alert threshold**: 2.0
* **apps**: firefox, chrome, chromium, safari
* **expected**: pass
* **expose gecko profiler**: true
* **expose browser profiler**: true
* **gecko profile entries**: 8000000
* **gecko profile interval**: 1
* **lower is better**: true
@ -3472,7 +3473,7 @@ Standard benchmarks are third-party tests (i.e. Speedometer) that we have integr
* **alert threshold**: 2.0
* **apps**: firefox
* **expected**: pass
* **expose gecko profiler**: true
* **expose browser profiler**: true
* **gecko profile entries**: 14000000
* **gecko profile interval**: 1
* **lower is better**: true
@ -3992,7 +3993,7 @@ Standard benchmarks are third-party tests (i.e. Speedometer) that we have integr
* **alert threshold**: 2.0
* **apps**: firefox, chrome, chromium, safari
* **expected**: pass
* **expose gecko profiler**: true
* **expose browser profiler**: true
* **gecko profile entries**: 8000000
* **gecko profile interval**: 1
* **lower is better**: false
@ -4299,7 +4300,7 @@ Standard benchmarks are third-party tests (i.e. Speedometer) that we have integr
* **alert threshold**: 2.0
* **apps**: firefox, chrome, chromium, safari
* **expected**: pass
* **expose gecko profiler**: true
* **expose browser profiler**: true
* **gecko profile entries**: 8000000
* **gecko profile interval**: 1
* **lower is better**: true
@ -4557,7 +4558,7 @@ Standard benchmarks are third-party tests (i.e. Speedometer) that we have integr
* **alert threshold**: 2.0
* **apps**: firefox
* **expected**: pass
* **expose gecko profiler**: true
* **expose browser profiler**: true
* **gecko profile entries**: 8000000
* **gecko profile interval**: 1
* **lower is better**: true
@ -4771,7 +4772,7 @@ Standard benchmarks are third-party tests (i.e. Speedometer) that we have integr
* **alert threshold**: 2.0
* **apps**: firefox
* **expected**: pass
* **expose gecko profiler**: true
* **expose browser profiler**: true
* **gecko profile entries**: 8000000
* **gecko profile interval**: 1
* **lower is better**: true
@ -4985,7 +4986,7 @@ Standard benchmarks are third-party tests (i.e. Speedometer) that we have integr
* **alert threshold**: 2.0
* **apps**: firefox, chrome, chromium
* **expected**: pass
* **expose gecko profiler**: true
* **expose browser profiler**: true
* **gecko profile entries**: 4000000
* **gecko profile interval**: 1
* **lower is better**: true
@ -5240,7 +5241,7 @@ Standard benchmarks are third-party tests (i.e. Speedometer) that we have integr
* **alert threshold**: 2.0
* **apps**: firefox
* **expected**: pass
* **expose gecko profiler**: true
* **expose browser profiler**: true
* **gecko profile entries**: 4000000
* **gecko profile interval**: 1
* **lower is better**: true
@ -5456,7 +5457,7 @@ Standard benchmarks are third-party tests (i.e. Speedometer) that we have integr
* **alert threshold**: 2.0
* **apps**: firefox
* **expected**: pass
* **expose gecko profiler**: true
* **expose browser profiler**: true
* **gecko profile entries**: 4000000
* **gecko profile interval**: 1
* **lower is better**: true
@ -5672,7 +5673,7 @@ Standard benchmarks are third-party tests (i.e. Speedometer) that we have integr
* **alert threshold**: 2.0
* **apps**: firefox, chrome, chromium, safari
* **expected**: pass
* **expose gecko profiler**: true
* **expose browser profiler**: true
* **gecko profile entries**: 4000000
* **gecko profile interval**: 1
* **lower is better**: true
@ -5931,7 +5932,7 @@ Standard benchmarks are third-party tests (i.e. Speedometer) that we have integr
* **alert threshold**: 2.0
* **apps**: firefox, geckoview, fenix,refbrow, chrome
* **expected**: pass
* **expose gecko profiler**: true
* **expose browser profiler**: true
* **gecko profile entries**: 50000000
* **gecko profile interval**: 1000
* **gecko profile threads**: MediaPlayback
@ -5954,7 +5955,7 @@ Standard benchmarks are third-party tests (i.e. Speedometer) that we have integr
* **alert threshold**: 2.0
* **apps**: firefox, geckoview, fenix, refbrow, chrome
* **expected**: pass
* **expose gecko profiler**: true
* **expose browser profiler**: true
* **gecko profile entries**: 50000000
* **gecko profile interval**: 1000
* **gecko profile threads**: MediaPlayback
@ -5995,7 +5996,7 @@ Standard benchmarks are third-party tests (i.e. Speedometer) that we have integr
* **alert threshold**: 2.0
* **apps**: firefox
* **expected**: pass
* **expose gecko profiler**: true
* **expose browser profiler**: true
* **gather cpuTime**: true
* **gecko profile entries**: 50000000
* **gecko profile interval**: 1000
@ -6197,7 +6198,7 @@ Standard benchmarks are third-party tests (i.e. Speedometer) that we have integr
* **alert threshold**: 2.0
* **apps**: firefox
* **expected**: pass
* **expose gecko profiler**: true
* **expose browser profiler**: true
* **gather cpuTime**: true
* **gecko profile entries**: 50000000
* **gecko profile interval**: 1000
@ -6399,7 +6400,7 @@ Standard benchmarks are third-party tests (i.e. Speedometer) that we have integr
* **alert threshold**: 2.0
* **apps**: firefox
* **expected**: pass
* **expose gecko profiler**: true
* **expose browser profiler**: true
* **gather cpuTime**: true
* **gecko profile entries**: 50000000
* **gecko profile interval**: 1000
@ -6601,7 +6602,7 @@ Standard benchmarks are third-party tests (i.e. Speedometer) that we have integr
* **alert threshold**: 2.0
* **apps**: firefox
* **expected**: pass
* **expose gecko profiler**: true
* **expose browser profiler**: true
* **gather cpuTime**: true
* **gecko profile entries**: 50000000
* **gecko profile interval**: 1000
@ -6803,7 +6804,7 @@ Standard benchmarks are third-party tests (i.e. Speedometer) that we have integr
* **alert threshold**: 2.0
* **apps**: firefox, geckoview, fenix, refbrow, chrome
* **expected**: pass
* **expose gecko profiler**: true
* **expose browser profiler**: true
* **gecko profile entries**: 50000000
* **gecko profile interval**: 1000
* **gecko profile threads**: MediaPlayback
@ -7063,7 +7064,7 @@ Standard benchmarks are third-party tests (i.e. Speedometer) that we have integr
* **alert threshold**: 2.0
* **apps**: firefox, geckoview, fenix, refbrow, chrome
* **expected**: pass
* **expose gecko profiler**: true
* **expose browser profiler**: true
* **gecko profile entries**: 50000000
* **gecko profile interval**: 1000
* **gecko profile threads**: MediaPlayback
@ -7322,7 +7323,7 @@ Standard benchmarks are third-party tests (i.e. Speedometer) that we have integr
* **alert threshold**: 2.0
* **apps**: firefox
* **expected**: pass
* **expose gecko profiler**: true
* **expose browser profiler**: true
* **gather cpuTime**: true
* **gecko profile entries**: 50000000
* **gecko profile interval**: 1000
@ -7524,7 +7525,7 @@ Standard benchmarks are third-party tests (i.e. Speedometer) that we have integr
* **alert threshold**: 2.0
* **apps**: firefox
* **expected**: pass
* **expose gecko profiler**: true
* **expose browser profiler**: true
* **gather cpuTime**: true
* **gecko profile entries**: 50000000
* **gecko profile interval**: 1000
@ -7726,7 +7727,7 @@ Standard benchmarks are third-party tests (i.e. Speedometer) that we have integr
* **alert threshold**: 2.0
* **apps**: firefox
* **expected**: pass
* **expose gecko profiler**: true
* **expose browser profiler**: true
* **gather cpuTime**: true
* **gecko profile entries**: 50000000
* **gecko profile interval**: 1000
@ -7928,7 +7929,7 @@ Standard benchmarks are third-party tests (i.e. Speedometer) that we have integr
* **alert threshold**: 2.0
* **apps**: firefox
* **expected**: pass
* **expose gecko profiler**: true
* **expose browser profiler**: true
* **gather cpuTime**: true
* **gecko profile entries**: 50000000
* **gecko profile interval**: 1000
@ -8130,7 +8131,7 @@ Standard benchmarks are third-party tests (i.e. Speedometer) that we have integr
* **alert threshold**: 2.0
* **apps**: firefox, geckoview, fenix, refbrow, chrome
* **expected**: pass
* **expose gecko profiler**: true
* **expose browser profiler**: true
* **gecko profile entries**: 50000000
* **gecko profile interval**: 1000
* **gecko profile threads**: MediaPlayback
@ -8347,7 +8348,7 @@ Standard benchmarks are third-party tests (i.e. Speedometer) that we have integr
* **alert threshold**: 2.0
* **apps**: firefox, geckoview, fenix, refbrow, chrome
* **expected**: pass
* **expose gecko profiler**: true
* **expose browser profiler**: true
* **gecko profile entries**: 50000000
* **gecko profile interval**: 1000
* **gecko profile threads**: MediaPlayback
@ -8564,7 +8565,7 @@ Standard benchmarks are third-party tests (i.e. Speedometer) that we have integr
* **alert threshold**: 2.0
* **apps**: firefox, geckoview, fenix, refbrow, chrome
* **expected**: pass
* **expose gecko profiler**: true
* **expose browser profiler**: true
* **gecko profile entries**: 50000000
* **gecko profile interval**: 1000
* **gecko profile threads**: MediaPlayback
@ -8781,7 +8782,7 @@ Standard benchmarks are third-party tests (i.e. Speedometer) that we have integr
* **alert threshold**: 2.0
* **apps**: firefox, geckoview, fenix, refbrow, chrome
* **expected**: pass
* **expose gecko profiler**: true
* **expose browser profiler**: true
* **gecko profile entries**: 50000000
* **gecko profile interval**: 1000
* **gecko profile threads**: MediaPlayback

View file

@ -30,7 +30,11 @@ module.exports = async function (context, commands) {
context.log.info("Cycle %d, starting the measure", count);
if (expose_profiler === "true") {
context.log.info("Custom profiler start!");
await commands.profiler.start();
if (context.options.browser === "firefox") {
await commands.profiler.start();
} else if (context.options.browser === "chrome") {
await commands.trace.start();
}
}
await commands.measure.start(url);
@ -52,7 +56,11 @@ module.exports = async function (context, commands) {
}
if (expose_profiler === "true") {
context.log.info("Custom profiler stop!");
await commands.profiler.stop();
if (context.options.browser === "firefox") {
await commands.profiler.stop();
} else if (context.options.browser === "chrome") {
await commands.trace.stop();
}
}
if (
data == null &&

View file

@ -29,7 +29,11 @@ module.exports = async function (context, commands) {
context.log.info("Cycle %d, starting the measure", count);
if (expose_profiler === "true") {
context.log.info("Custom profiler start!");
await commands.profiler.start();
if (context.options.browser === "firefox") {
await commands.profiler.start();
} else if (context.options.browser === "chrome") {
await commands.trace.start();
}
}
await commands.measure.start(url);
@ -53,7 +57,11 @@ module.exports = async function (context, commands) {
}
if (expose_profiler === "true") {
context.log.info("Custom profiler stop!");
await commands.profiler.stop();
if (context.options.browser === "firefox") {
await commands.profiler.stop();
} else if (context.options.browser === "chrome") {
await commands.trace.stop();
}
}
if (
!data_exists &&

View file

@ -236,16 +236,16 @@ class Browsertime(Perftest):
def clean_up(self):
super(Browsertime, self).clean_up()
def _expose_gecko_profiler(self, extra_profiler_run, test):
def _expose_browser_profiler(self, extra_profiler_run, test):
"""Use this method to check if we will use an exposed gecko profiler via browsertime.
The exposed gecko profiler let's us control the start/stop during tests.
At the moment we would only want this for the Firefox browser and for any test with the
`expose_gecko_profiler` field set true (e.g. benchmark tests).
The exposed browser profiler let's us control the start/stop during tests.
At the moment we would only want this for the Firefox or Chrome* applications and for
any test with the `expose_browser_profiler` field set true (e.g. benchmark tests).
"""
return (
extra_profiler_run
and test.get("expose_gecko_profiler")
and self.config["app"] in GECKO_PROFILER_APPS
and test.get("expose_browser_profiler")
and self.config["app"] in GECKO_PROFILER_APPS + TRACE_APPS
)
def _compose_cmd(self, test, timeout, extra_profiler_run=False):
@ -380,7 +380,7 @@ class Browsertime(Perftest):
os.environ.get("MOZ_FETCHES_DIR", "None"),
"--browsertime.expose_profiler",
"true"
if (self._expose_gecko_profiler(extra_profiler_run, test))
if self._expose_browser_profiler(extra_profiler_run, test)
else "false",
]
@ -582,7 +582,7 @@ class Browsertime(Perftest):
LOG.info("Composing Gecko Profiler commands")
self._init_gecko_profiling(test)
priority1_options.append("--firefox.geckoProfiler")
if self._expose_gecko_profiler(self.config.get("extra_profiler_run"), test):
if self._expose_browser_profiler(self.config.get("extra_profiler_run"), test):
priority1_options.extend(
[
"--firefox.geckoProfilerRecordingType",
@ -629,7 +629,10 @@ class Browsertime(Perftest):
LOG.info("Composing Chrome Trace commands")
self._init_chrome_trace(test)
priority1_options.extend(["--chrome.trace"])
if self._expose_browser_profiler(self.config.get("extra_profiler_run"), test):
priority1_options.extend(["--chrome.timelineRecordingType", "custom"])
# current categories to capture, we can modify this as needed in the future
# reference:

View file

@ -619,7 +619,7 @@ def get_raptor_test_list(args, oskey):
"accept_zero_vismet",
"interactive",
"host_from_parent",
"expose_gecko_profiler",
"expose_browser_profiler",
]
for setting in bool_settings:
if next_test.get(setting, None) is not None:

View file

@ -9,7 +9,7 @@ alert_threshold = 2.0
apps = firefox, chrome, chromium, safari
gecko_profile_entries = 14000000
gecko_profile_interval = 1
expose_gecko_profiler = true
expose_browser_profiler = true
lower_is_better = true
owner = :jandem and SpiderMonkey Team
page_cycles = 4

View file

@ -9,7 +9,7 @@ alert_threshold = 2.0
apps = firefox, chrome, chromium, safari
gecko_profile_entries = 2000000
gecko_profile_interval = 1
expose_gecko_profiler = true
expose_browser_profiler = true
lower_is_better = true
owner = PerfTest Team
page_cycles = 1

View file

@ -9,7 +9,7 @@ alert_threshold = 2.0
apps = firefox, chrome, chromium, safari
gecko_profile_entries = 14000000
gecko_profile_interval = 1
expose_gecko_profiler = true
expose_browser_profiler = true
lower_is_better = false
owner = :jandem and SpiderMonkey Team
page_cycles = 4

View file

@ -9,7 +9,7 @@ alert_threshold = 2.0
apps = firefox, chrome, chromium
gecko_profile_entries = 14000000
gecko_profile_interval = 1
expose_gecko_profiler = true
expose_browser_profiler = true
lower_is_better = true
owner = :jandem and SpiderMonkey Team
page_cycles = 30

View file

@ -9,7 +9,7 @@ alert_threshold = 2.0
apps = firefox, chrome, chromium, safari
gecko_profile_entries = 8000000
gecko_profile_interval = 1
expose_gecko_profiler = true
expose_browser_profiler = true
lower_is_better = false
page_cycles = 1
page_timeout = 600000

View file

@ -10,6 +10,7 @@ apps = firefox, chrome, chromium, safari
gecko_profile_entries = 8000000
gecko_profile_interval = 1
expose_gecko_profiler = true
expose_chrome_trace = true
lower_is_better = false
owner = :jgilbert and Graphics(gfx) Team
page_cycles = 5

View file

@ -9,7 +9,7 @@ alert_threshold = 2.0
apps = firefox, chrome, chromium, safari, custom-car
gecko_profile_entries = 14000000
gecko_profile_interval = 1
expose_gecko_profiler = true
expose_browser_profiler = true
lower_is_better = false
owner = SpiderMonkey Team
page_cycles = 5

View file

@ -9,7 +9,7 @@ alert_threshold = 2.0
apps = firefox, chrome, chromium, safari
gecko_profile_entries = 8000000
gecko_profile_interval = 1
expose_gecko_profiler = true
expose_browser_profiler = true
lower_is_better = false
owner = :emelio and Layout Team
page_cycles = 5

Some files were not shown because too many files have changed in this diff Show more