morgan pushed to branch mullvad-browser-151.0a1-16.0-1 at The Tor Project / Applications / Mullvad Browser Commits: 401853b1 by Beatriz Rizental at 2026-06-02T11:59:09+00:00 fixup! BB 43243: Modify mozharness scripts for Base Browser I was trying to run the mozharness scripts with a nightly build and I learned that they weren't working due to the nightly not being a debug app. This addresses that issue and should work for any version of the app. Without it the geckoview-config.yaml file isn't read, unless the app is android:debuggable. - - - - - f2571a7e by Beatriz Rizental at 2026-06-02T11:59:20+00:00 fixup! BB 43243: Modify mozharness scripts for Base Browser Bug 44212: Refactor a bit of the marionette on android mozharness support - - - - - d4385988 by Beatriz Rizental at 2026-06-02T12:00:07+00:00 fixup! Add CI for Base Browser Bug 44990: Fix dubious ownership CI issue - - - - - 3f1a4133 by Leo Tenenbaum at 2026-06-02T12:01:11+00:00 Bug 2040704 - Respect privacy.spoof_english in datetime input validation messages. r=timhuang,dom-core,hsivonen Differential Revision: https://phabricator.services.mozilla.com/D301531 - - - - - 357e36ea by Pier Angelo Vendrame at 2026-06-02T12:01:23+00:00 BB 44999: Fix the privacy panel. This commit might be dropped in 153. For 152, it might need adjustments, or we might be able to cherry-pick upstream's commit (Bug 2042309). - - - - - 9 changed files: - .gitlab/ci/mixins.yml - browser/components/preferences/config/privacy.mjs - dom/html/input/DateTimeInputTypes.cpp - dom/html/input/DateTimeInputTypes.h - dom/html/test/browser.toml - + dom/html/test/browser_validationmessage_spoof_english.js - intl/locale/AppDateTimeFormat.cpp - intl/locale/AppDateTimeFormat.h - testing/mozharness/scripts/android_emulator_unittest.py Changes: ===================================== .gitlab/ci/mixins.yml ===================================== @@ -2,7 +2,12 @@ variables: GIT_STRATEGY: "none" FETCH_TIMEOUT: 180 # 3 minutes + + GIT_CONFIG_GLOBAL: "/tmp/gitconfig" before_script: + - | + echo "[safe]" > "$GIT_CONFIG_GLOBAL" + echo " directory = *" >> "$GIT_CONFIG_GLOBAL" - git init - git remote add local "$LOCAL_REPO_PATH" - | ===================================== browser/components/preferences/config/privacy.mjs ===================================== @@ -742,6 +742,13 @@ Preferences.addAll([ }, { id: "app.normandy.enabled", type: "bool" }, { id: "browser.privacySegmentation.preferences.show", type: "bool" }, + + // Preference instances for prefs that we need to monitor while the page is open. + { id: "app.shield.optoutstudies.enabled", type: "bool" }, + { id: "browser.discovery.enabled", type: "bool" }, + { id: "datareporting.healthreport.uploadEnabled", type: "bool" }, + { id: "datareporting.usage.uploadEnabled", type: "bool" }, + { id: "dom.private-attribution.submission.enabled", type: "bool" }, ]); if (SECURITY_PRIVACY_STATUS_CARD_ENABLED) { @@ -2961,17 +2968,6 @@ Preferences.addSetting({ !ipProtectionSubscribedToVpn.value, }); -// Study opt out -if (lazy.AppConstants.MOZ_DATA_REPORTING) { - Preferences.addAll([ - // Preference instances for prefs that we need to monitor while the page is open. - { id: "app.shield.optoutstudies.enabled", type: "bool" }, - { id: "browser.discovery.enabled", type: "bool" }, - { id: "datareporting.healthreport.uploadEnabled", type: "bool" }, - { id: "datareporting.usage.uploadEnabled", type: "bool" }, - { id: "dom.private-attribution.submission.enabled", type: "bool" }, - ]); -} // Privacy segmentation section Preferences.add({ id: "browser.dataFeatureRecommendations.enabled", ===================================== dom/html/input/DateTimeInputTypes.cpp ===================================== @@ -109,21 +109,19 @@ bool DateTimeInputTypeBase::HasBadInput() const { return !allEmpty && IsValueEmpty(); } -// Format PRExplodedTime according to current locale -static bool FormatDateTime( +bool DateTimeInputTypeBase::FormatDateTime( const PRExplodedTime& aTime, const intl::DateTimeFormat::ComponentsBag& aComponents, - nsAString& aFormatted) { + nsAString& aFormatted) const { // AppDateTimeFormat is not thread-safe. MOZ_ASSERT(NS_IsMainThread(), "Should only be called from main thread"); - return NS_SUCCEEDED( - intl::AppDateTimeFormat::Format(aComponents, &aTime, aFormatted)); + return NS_SUCCEEDED(intl::AppDateTimeFormat::FormatForDocument( + aComponents, &aTime, mInputElement->OwnerDoc(), aFormatted)); } -// Format timestamp according to current locale -static bool FormatDateTime( +bool DateTimeInputTypeBase::FormatDateTime( double aValue, const intl::DateTimeFormat::ComponentsBag& aComponents, - nsAString& aFormatted) { + nsAString& aFormatted) const { PRExplodedTime exploded; PRTime time = static_cast<PRTime>(aValue * PR_USEC_PER_MSEC); PR_ExplodeTime( ===================================== dom/html/input/DateTimeInputTypes.h ===================================== @@ -6,6 +6,9 @@ #define mozilla_dom_DateTimeInputTypes_h_ #include "mozilla/dom/InputType.h" +#include "mozilla/intl/DateTimeFormat.h" + +struct PRExplodedTime; namespace mozilla::dom { @@ -39,6 +42,19 @@ class DateTimeInputTypeBase : public InputType { bool GetTimeFromMs(double aValue, uint16_t* aHours, uint16_t* aMinutes, uint16_t* aSeconds, uint16_t* aMilliseconds) const; + /** + * Format PRExplodedTime according to current locale + */ + bool FormatDateTime(const PRExplodedTime& aTime, + const intl::DateTimeFormat::ComponentsBag& aComponents, + nsAString& aFormatted) const; + /** + * Format timestamp according to current locale + */ + bool FormatDateTime(double aValue, + const intl::DateTimeFormat::ComponentsBag& aComponents, + nsAString& aFormatted) const; + // Minimum year limited by HTML standard, year >= 1. static const double kMinimumYear; // Maximum year limited by ECMAScript date object range, year <= 275760. ===================================== dom/html/test/browser.toml ===================================== @@ -51,3 +51,5 @@ support-files = [ "empty.html", "image_yellow.png", ] + +["browser_validationmessage_spoof_english.js"] ===================================== dom/html/test/browser_validationmessage_spoof_english.js ===================================== @@ -0,0 +1,86 @@ +/* Any copyright is dedicated to the Public Domain. + https://creativecommons.org/publicdomain/zero/1.0/ */ + +"use strict"; + +/* +Test for bug 2040704 - datetime input validation messages should use + en_US localization when English spoofing is enabled. +*/ + +const originalAvailableLocales = Services.locale.availableLocales; +const originalRequestedLocales = Services.locale.requestedLocales; + +async function runTest(test) { + for (let spoof of [false, true]) { + await SpecialPowers.pushPrefEnv({ + set: [ + ["privacy.spoof_english", spoof ? 2 : 0], + ["privacy.resistFingerprinting", spoof], + ], + }); + let source = `<!DOCTYPE html> +<input type="${test.type}" min="${test.min}" value="${test.value}">`; + let result = await BrowserTestUtils.withNewTab( + "data:text/html," + source, + browser => { + return SpecialPowers.spawn(browser, [], () => { + return content.eval( + 'document.querySelector("input").validationMessage' + ); + }); + } + ); + let expectIncludes = test[spoof ? "en" : "de"]; + let expectDoesNotInclude = test[spoof ? "de" : "en"]; + ok( + result.includes(expectIncludes), + `With spoofing ${spoof ? "enabled" : "disabled"}: expect validationMessage ` + + `to include "${expectIncludes}": "${result}"` + ); + ok( + !result.includes(expectDoesNotInclude), + `With spoofing ${spoof ? "enabled" : "disabled"}: expect validationMessage ` + + `to not include "${expectDoesNotInclude}": "${result}"` + ); + } +} + +const tests = [ + { + type: "date", + min: "2000-01-01", + value: "1999-01-01", + en: "01/01/2000", + de: "01.01.2000", + }, + { + type: "time", + min: "16:00", + value: "15:00", + en: "4:00 PM", + de: "16:00", + }, + { + type: "datetime-local", + min: "2000-01-01T00:00", + value: "1999-01-01T00:00", + en: "01/01/2000", + de: "01.01.2000", + }, +]; + +add_task(() => { + Services.locale.availableLocales = ["de-DE"]; + Services.locale.requestedLocales = ["de-DE"]; +}); + +for (let test of tests) { + add_task(() => runTest(test)); +} + +add_task(() => { + // restore previous locales + Services.locale.availableLocales = originalAvailableLocales; + Services.locale.requestedLocales = originalRequestedLocales; +}); ===================================== intl/locale/AppDateTimeFormat.cpp ===================================== @@ -10,6 +10,8 @@ #include "mozilla/intl/LocaleService.h" #include "OSPreferences.h" #include "mozIOSPreferences.h" +#include "nsContentUtils.h" +#include "nsRFPService.h" #ifdef DEBUG # include "nsThreadManager.h" #endif @@ -64,6 +66,14 @@ nsresult AppDateTimeFormat::Format(const DateTimeFormat::StyleBag& aStyle, nsresult AppDateTimeFormat::Format(const DateTimeFormat::ComponentsBag& aBag, const PRExplodedTime* aExplodedTime, nsAString& aStringOut) { + return FormatForDocument(aBag, aExplodedTime, nullptr, aStringOut); +} + +/*static*/ +nsresult AppDateTimeFormat::FormatForDocument( + const DateTimeFormat::ComponentsBag& aBag, + const PRExplodedTime* aExplodedTime, const dom::Document* aForDocument, + nsAString& aStringOut) { // set up locale data nsresult rv = Initialize(); if (NS_FAILED(rv)) { @@ -75,12 +85,17 @@ nsresult AppDateTimeFormat::Format(const DateTimeFormat::ComponentsBag& aBag, nsAutoString timeZoneID; BuildTimeZoneString(aExplodedTime->tm_params, timeZoneID); - auto genResult = DateTimePatternGenerator::TryCreate(sLocale->get()); + const bool spoofEnglish = + aForDocument && nsContentUtils::ShouldResistFingerprinting( + aForDocument, mozilla::RFPTarget::JSLocale); + const nsCString& locale = + spoofEnglish ? nsRFPService::GetSpoofedJSLocale() : *sLocale; + auto genResult = DateTimePatternGenerator::TryCreate(locale.get()); NS_ENSURE_TRUE(genResult.isOk(), NS_ERROR_FAILURE); auto dateTimePatternGenerator = genResult.unwrap(); auto result = DateTimeFormat::TryCreateFromComponents( - *sLocale, aBag, dateTimePatternGenerator.get(), + locale, aBag, dateTimePatternGenerator.get(), Some(Span<const char16_t>(timeZoneID.Data(), timeZoneID.Length()))); NS_ENSURE_TRUE(result.isOk(), NS_ERROR_FAILURE); auto dateTimeFormat = result.unwrap(); ===================================== intl/locale/AppDateTimeFormat.h ===================================== @@ -13,6 +13,10 @@ #include "prtime.h" #include "mozilla/intl/DateTimeFormat.h" +namespace mozilla::dom { +class Document; +} + namespace mozilla::intl { /** @@ -48,6 +52,15 @@ class AppDateTimeFormat { const PRExplodedTime* aExplodedTime, nsAString& aStringOut); + /** + * Format a DateTime for a document, respecting the privacy.spoof_english + * preference. + */ + static nsresult FormatForDocument(const DateTimeFormat::ComponentsBag& aStyle, + const PRExplodedTime* aExplodedTime, + const dom::Document* aForDocument, + nsAString& aStringOut); + /** * If the app locale changes, the cached locale needs to be reset. */ ===================================== testing/mozharness/scripts/android_emulator_unittest.py ===================================== @@ -13,6 +13,8 @@ import sys import tempfile import time +import yaml + # load modules from parent dir here = os.path.abspath(os.path.dirname(__file__)) sys.path.insert(1, os.path.dirname(here)) @@ -515,17 +517,22 @@ class AndroidEmulatorTest( self.run_command([adb, "forward", "tcp:2828", "tcp:2828"]) with tempfile.NamedTemporaryFile(suffix=".yaml") as tmp_file: - tmp_file.write( - b"""args: -- --marionette -- --remote-allow-system-access -""" - ) + config = {"args": ["--marionette", "--remote-allow-system-access"]} + + tmp_file.write(yaml.dump(config, encoding="utf-8")) tmp_file.flush() remote_path = f"/data/local/tmp/{self.package_name}-geckoview-config.yaml" self.run_command([adb, "push", tmp_file.name, remote_path]) + self.run_command([ + adb, + "shell", + "am", + "set-debug-app", + "--persistent", + self.package_name, + ]) self.run_command([ adb, "shell", @@ -569,7 +576,7 @@ class AndroidEmulatorTest( if requirements: self.register_virtualenv_module(requirements=[requirements]) - if ("marionette", "marionette") in suites: + if any("marionette" in suite_name for _, suite_name in self._query_suites()): self._configure_marionette_virtualenv(action) def download_and_extract(self): @@ -609,7 +616,7 @@ class AndroidEmulatorTest( for per_test_suite, suite in suites: self.test_suite = suite - if self.test_suite == "marionette": + if "marionette" in self.test_suite: self._marionette_setup() try: @@ -693,9 +700,10 @@ class AndroidEmulatorTest( @PostScriptAction("run-tests") def marionette_teardown(self, *args, **kwargs): - if ("marionette", "marionette") in self._query_suites(): + if any("marionette" in suite_name for _, suite_name in self._query_suites()): adb = self.query_exe("adb") self.run_command([adb, "shell", "am", "force-stop", self.package_name]) + self.run_command([adb, "shell", "am", "clear-debug-app"]) self.run_command([adb, "uninstall", self.package_name]) self.run_command([ adb, View it on GitLab: https://gitlab.torproject.org/tpo/applications/mullvad-browser/-/compare/3c6... -- View it on GitLab: https://gitlab.torproject.org/tpo/applications/mullvad-browser/-/compare/3c6... You're receiving this email because of your account on gitlab.torproject.org. Manage all notifications: https://gitlab.torproject.org/-/profile/notifications | Help: https://gitlab.torproject.org/help