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:
Changes:
.gitlab/ci/mixins.yml
| ... |
... |
@@ -2,7 +2,12 @@ |
|
2
|
2
|
variables:
|
|
3
|
3
|
GIT_STRATEGY: "none"
|
|
4
|
4
|
FETCH_TIMEOUT: 180 # 3 minutes
|
|
|
5
|
+
|
|
|
6
|
+ GIT_CONFIG_GLOBAL: "/tmp/gitconfig"
|
|
5
|
7
|
before_script:
|
|
|
8
|
+ - |
|
|
|
9
|
+ echo "[safe]" > "$GIT_CONFIG_GLOBAL"
|
|
|
10
|
+ echo " directory = *" >> "$GIT_CONFIG_GLOBAL"
|
|
6
|
11
|
- git init
|
|
7
|
12
|
- git remote add local "$LOCAL_REPO_PATH"
|
|
8
|
13
|
- |
|
browser/components/preferences/config/privacy.mjs
| ... |
... |
@@ -742,6 +742,13 @@ Preferences.addAll([ |
|
742
|
742
|
},
|
|
743
|
743
|
{ id: "app.normandy.enabled", type: "bool" },
|
|
744
|
744
|
{ id: "browser.privacySegmentation.preferences.show", type: "bool" },
|
|
|
745
|
+
|
|
|
746
|
+ // Preference instances for prefs that we need to monitor while the page is open.
|
|
|
747
|
+ { id: "app.shield.optoutstudies.enabled", type: "bool" },
|
|
|
748
|
+ { id: "browser.discovery.enabled", type: "bool" },
|
|
|
749
|
+ { id: "datareporting.healthreport.uploadEnabled", type: "bool" },
|
|
|
750
|
+ { id: "datareporting.usage.uploadEnabled", type: "bool" },
|
|
|
751
|
+ { id: "dom.private-attribution.submission.enabled", type: "bool" },
|
|
745
|
752
|
]);
|
|
746
|
753
|
|
|
747
|
754
|
if (SECURITY_PRIVACY_STATUS_CARD_ENABLED) {
|
| ... |
... |
@@ -2961,17 +2968,6 @@ Preferences.addSetting({ |
|
2961
|
2968
|
!ipProtectionSubscribedToVpn.value,
|
|
2962
|
2969
|
});
|
|
2963
|
2970
|
|
|
2964
|
|
-// Study opt out
|
|
2965
|
|
-if (lazy.AppConstants.MOZ_DATA_REPORTING) {
|
|
2966
|
|
- Preferences.addAll([
|
|
2967
|
|
- // Preference instances for prefs that we need to monitor while the page is open.
|
|
2968
|
|
- { id: "app.shield.optoutstudies.enabled", type: "bool" },
|
|
2969
|
|
- { id: "browser.discovery.enabled", type: "bool" },
|
|
2970
|
|
- { id: "datareporting.healthreport.uploadEnabled", type: "bool" },
|
|
2971
|
|
- { id: "datareporting.usage.uploadEnabled", type: "bool" },
|
|
2972
|
|
- { id: "dom.private-attribution.submission.enabled", type: "bool" },
|
|
2973
|
|
- ]);
|
|
2974
|
|
-}
|
|
2975
|
2971
|
// Privacy segmentation section
|
|
2976
|
2972
|
Preferences.add({
|
|
2977
|
2973
|
id: "browser.dataFeatureRecommendations.enabled",
|
dom/html/input/DateTimeInputTypes.cpp
| ... |
... |
@@ -109,21 +109,19 @@ bool DateTimeInputTypeBase::HasBadInput() const { |
|
109
|
109
|
return !allEmpty && IsValueEmpty();
|
|
110
|
110
|
}
|
|
111
|
111
|
|
|
112
|
|
-// Format PRExplodedTime according to current locale
|
|
113
|
|
-static bool FormatDateTime(
|
|
|
112
|
+bool DateTimeInputTypeBase::FormatDateTime(
|
|
114
|
113
|
const PRExplodedTime& aTime,
|
|
115
|
114
|
const intl::DateTimeFormat::ComponentsBag& aComponents,
|
|
116
|
|
- nsAString& aFormatted) {
|
|
|
115
|
+ nsAString& aFormatted) const {
|
|
117
|
116
|
// AppDateTimeFormat is not thread-safe.
|
|
118
|
117
|
MOZ_ASSERT(NS_IsMainThread(), "Should only be called from main thread");
|
|
119
|
|
- return NS_SUCCEEDED(
|
|
120
|
|
- intl::AppDateTimeFormat::Format(aComponents, &aTime, aFormatted));
|
|
|
118
|
+ return NS_SUCCEEDED(intl::AppDateTimeFormat::FormatForDocument(
|
|
|
119
|
+ aComponents, &aTime, mInputElement->OwnerDoc(), aFormatted));
|
|
121
|
120
|
}
|
|
122
|
121
|
|
|
123
|
|
-// Format timestamp according to current locale
|
|
124
|
|
-static bool FormatDateTime(
|
|
|
122
|
+bool DateTimeInputTypeBase::FormatDateTime(
|
|
125
|
123
|
double aValue, const intl::DateTimeFormat::ComponentsBag& aComponents,
|
|
126
|
|
- nsAString& aFormatted) {
|
|
|
124
|
+ nsAString& aFormatted) const {
|
|
127
|
125
|
PRExplodedTime exploded;
|
|
128
|
126
|
PRTime time = static_cast<PRTime>(aValue * PR_USEC_PER_MSEC);
|
|
129
|
127
|
PR_ExplodeTime(
|
dom/html/input/DateTimeInputTypes.h
| ... |
... |
@@ -6,6 +6,9 @@ |
|
6
|
6
|
#define mozilla_dom_DateTimeInputTypes_h_
|
|
7
|
7
|
|
|
8
|
8
|
#include "mozilla/dom/InputType.h"
|
|
|
9
|
+#include "mozilla/intl/DateTimeFormat.h"
|
|
|
10
|
+
|
|
|
11
|
+struct PRExplodedTime;
|
|
9
|
12
|
|
|
10
|
13
|
namespace mozilla::dom {
|
|
11
|
14
|
|
| ... |
... |
@@ -39,6 +42,19 @@ class DateTimeInputTypeBase : public InputType { |
|
39
|
42
|
bool GetTimeFromMs(double aValue, uint16_t* aHours, uint16_t* aMinutes,
|
|
40
|
43
|
uint16_t* aSeconds, uint16_t* aMilliseconds) const;
|
|
41
|
44
|
|
|
|
45
|
+ /**
|
|
|
46
|
+ * Format PRExplodedTime according to current locale
|
|
|
47
|
+ */
|
|
|
48
|
+ bool FormatDateTime(const PRExplodedTime& aTime,
|
|
|
49
|
+ const intl::DateTimeFormat::ComponentsBag& aComponents,
|
|
|
50
|
+ nsAString& aFormatted) const;
|
|
|
51
|
+ /**
|
|
|
52
|
+ * Format timestamp according to current locale
|
|
|
53
|
+ */
|
|
|
54
|
+ bool FormatDateTime(double aValue,
|
|
|
55
|
+ const intl::DateTimeFormat::ComponentsBag& aComponents,
|
|
|
56
|
+ nsAString& aFormatted) const;
|
|
|
57
|
+
|
|
42
|
58
|
// Minimum year limited by HTML standard, year >= 1.
|
|
43
|
59
|
static const double kMinimumYear;
|
|
44
|
60
|
// Maximum year limited by ECMAScript date object range, year <= 275760.
|
dom/html/test/browser.toml
| ... |
... |
@@ -51,3 +51,5 @@ support-files = [ |
|
51
|
51
|
"empty.html",
|
|
52
|
52
|
"image_yellow.png",
|
|
53
|
53
|
]
|
|
|
54
|
+
|
|
|
55
|
+["browser_validationmessage_spoof_english.js"] |
dom/html/test/browser_validationmessage_spoof_english.js
|
|
1
|
+/* Any copyright is dedicated to the Public Domain.
|
|
|
2
|
+ https://creativecommons.org/publicdomain/zero/1.0/ */
|
|
|
3
|
+
|
|
|
4
|
+"use strict";
|
|
|
5
|
+
|
|
|
6
|
+/*
|
|
|
7
|
+Test for bug 2040704 - datetime input validation messages should use
|
|
|
8
|
+ en_US localization when English spoofing is enabled.
|
|
|
9
|
+*/
|
|
|
10
|
+
|
|
|
11
|
+const originalAvailableLocales = Services.locale.availableLocales;
|
|
|
12
|
+const originalRequestedLocales = Services.locale.requestedLocales;
|
|
|
13
|
+
|
|
|
14
|
+async function runTest(test) {
|
|
|
15
|
+ for (let spoof of [false, true]) {
|
|
|
16
|
+ await SpecialPowers.pushPrefEnv({
|
|
|
17
|
+ set: [
|
|
|
18
|
+ ["privacy.spoof_english", spoof ? 2 : 0],
|
|
|
19
|
+ ["privacy.resistFingerprinting", spoof],
|
|
|
20
|
+ ],
|
|
|
21
|
+ });
|
|
|
22
|
+ let source = `<!DOCTYPE html>
|
|
|
23
|
+<input type="${test.type}" min="${test.min}" value="${test.value}">`;
|
|
|
24
|
+ let result = await BrowserTestUtils.withNewTab(
|
|
|
25
|
+ "data:text/html," + source,
|
|
|
26
|
+ browser => {
|
|
|
27
|
+ return SpecialPowers.spawn(browser, [], () => {
|
|
|
28
|
+ return content.eval(
|
|
|
29
|
+ 'document.querySelector("input").validationMessage'
|
|
|
30
|
+ );
|
|
|
31
|
+ });
|
|
|
32
|
+ }
|
|
|
33
|
+ );
|
|
|
34
|
+ let expectIncludes = test[spoof ? "en" : "de"];
|
|
|
35
|
+ let expectDoesNotInclude = test[spoof ? "de" : "en"];
|
|
|
36
|
+ ok(
|
|
|
37
|
+ result.includes(expectIncludes),
|
|
|
38
|
+ `With spoofing ${spoof ? "enabled" : "disabled"}: expect validationMessage ` +
|
|
|
39
|
+ `to include "${expectIncludes}": "${result}"`
|
|
|
40
|
+ );
|
|
|
41
|
+ ok(
|
|
|
42
|
+ !result.includes(expectDoesNotInclude),
|
|
|
43
|
+ `With spoofing ${spoof ? "enabled" : "disabled"}: expect validationMessage ` +
|
|
|
44
|
+ `to not include "${expectDoesNotInclude}": "${result}"`
|
|
|
45
|
+ );
|
|
|
46
|
+ }
|
|
|
47
|
+}
|
|
|
48
|
+
|
|
|
49
|
+const tests = [
|
|
|
50
|
+ {
|
|
|
51
|
+ type: "date",
|
|
|
52
|
+ min: "2000-01-01",
|
|
|
53
|
+ value: "1999-01-01",
|
|
|
54
|
+ en: "01/01/2000",
|
|
|
55
|
+ de: "01.01.2000",
|
|
|
56
|
+ },
|
|
|
57
|
+ {
|
|
|
58
|
+ type: "time",
|
|
|
59
|
+ min: "16:00",
|
|
|
60
|
+ value: "15:00",
|
|
|
61
|
+ en: "4:00 PM",
|
|
|
62
|
+ de: "16:00",
|
|
|
63
|
+ },
|
|
|
64
|
+ {
|
|
|
65
|
+ type: "datetime-local",
|
|
|
66
|
+ min: "2000-01-01T00:00",
|
|
|
67
|
+ value: "1999-01-01T00:00",
|
|
|
68
|
+ en: "01/01/2000",
|
|
|
69
|
+ de: "01.01.2000",
|
|
|
70
|
+ },
|
|
|
71
|
+];
|
|
|
72
|
+
|
|
|
73
|
+add_task(() => {
|
|
|
74
|
+ Services.locale.availableLocales = ["de-DE"];
|
|
|
75
|
+ Services.locale.requestedLocales = ["de-DE"];
|
|
|
76
|
+});
|
|
|
77
|
+
|
|
|
78
|
+for (let test of tests) {
|
|
|
79
|
+ add_task(() => runTest(test));
|
|
|
80
|
+}
|
|
|
81
|
+
|
|
|
82
|
+add_task(() => {
|
|
|
83
|
+ // restore previous locales
|
|
|
84
|
+ Services.locale.availableLocales = originalAvailableLocales;
|
|
|
85
|
+ Services.locale.requestedLocales = originalRequestedLocales;
|
|
|
86
|
+}); |
intl/locale/AppDateTimeFormat.cpp
| ... |
... |
@@ -10,6 +10,8 @@ |
|
10
|
10
|
#include "mozilla/intl/LocaleService.h"
|
|
11
|
11
|
#include "OSPreferences.h"
|
|
12
|
12
|
#include "mozIOSPreferences.h"
|
|
|
13
|
+#include "nsContentUtils.h"
|
|
|
14
|
+#include "nsRFPService.h"
|
|
13
|
15
|
#ifdef DEBUG
|
|
14
|
16
|
# include "nsThreadManager.h"
|
|
15
|
17
|
#endif
|
| ... |
... |
@@ -64,6 +66,14 @@ nsresult AppDateTimeFormat::Format(const DateTimeFormat::StyleBag& aStyle, |
|
64
|
66
|
nsresult AppDateTimeFormat::Format(const DateTimeFormat::ComponentsBag& aBag,
|
|
65
|
67
|
const PRExplodedTime* aExplodedTime,
|
|
66
|
68
|
nsAString& aStringOut) {
|
|
|
69
|
+ return FormatForDocument(aBag, aExplodedTime, nullptr, aStringOut);
|
|
|
70
|
+}
|
|
|
71
|
+
|
|
|
72
|
+/*static*/
|
|
|
73
|
+nsresult AppDateTimeFormat::FormatForDocument(
|
|
|
74
|
+ const DateTimeFormat::ComponentsBag& aBag,
|
|
|
75
|
+ const PRExplodedTime* aExplodedTime, const dom::Document* aForDocument,
|
|
|
76
|
+ nsAString& aStringOut) {
|
|
67
|
77
|
// set up locale data
|
|
68
|
78
|
nsresult rv = Initialize();
|
|
69
|
79
|
if (NS_FAILED(rv)) {
|
| ... |
... |
@@ -75,12 +85,17 @@ nsresult AppDateTimeFormat::Format(const DateTimeFormat::ComponentsBag& aBag, |
|
75
|
85
|
nsAutoString timeZoneID;
|
|
76
|
86
|
BuildTimeZoneString(aExplodedTime->tm_params, timeZoneID);
|
|
77
|
87
|
|
|
78
|
|
- auto genResult = DateTimePatternGenerator::TryCreate(sLocale->get());
|
|
|
88
|
+ const bool spoofEnglish =
|
|
|
89
|
+ aForDocument && nsContentUtils::ShouldResistFingerprinting(
|
|
|
90
|
+ aForDocument, mozilla::RFPTarget::JSLocale);
|
|
|
91
|
+ const nsCString& locale =
|
|
|
92
|
+ spoofEnglish ? nsRFPService::GetSpoofedJSLocale() : *sLocale;
|
|
|
93
|
+ auto genResult = DateTimePatternGenerator::TryCreate(locale.get());
|
|
79
|
94
|
NS_ENSURE_TRUE(genResult.isOk(), NS_ERROR_FAILURE);
|
|
80
|
95
|
auto dateTimePatternGenerator = genResult.unwrap();
|
|
81
|
96
|
|
|
82
|
97
|
auto result = DateTimeFormat::TryCreateFromComponents(
|
|
83
|
|
- *sLocale, aBag, dateTimePatternGenerator.get(),
|
|
|
98
|
+ locale, aBag, dateTimePatternGenerator.get(),
|
|
84
|
99
|
Some(Span<const char16_t>(timeZoneID.Data(), timeZoneID.Length())));
|
|
85
|
100
|
NS_ENSURE_TRUE(result.isOk(), NS_ERROR_FAILURE);
|
|
86
|
101
|
auto dateTimeFormat = result.unwrap();
|
intl/locale/AppDateTimeFormat.h
| ... |
... |
@@ -13,6 +13,10 @@ |
|
13
|
13
|
#include "prtime.h"
|
|
14
|
14
|
#include "mozilla/intl/DateTimeFormat.h"
|
|
15
|
15
|
|
|
|
16
|
+namespace mozilla::dom {
|
|
|
17
|
+class Document;
|
|
|
18
|
+}
|
|
|
19
|
+
|
|
16
|
20
|
namespace mozilla::intl {
|
|
17
|
21
|
|
|
18
|
22
|
/**
|
| ... |
... |
@@ -48,6 +52,15 @@ class AppDateTimeFormat { |
|
48
|
52
|
const PRExplodedTime* aExplodedTime,
|
|
49
|
53
|
nsAString& aStringOut);
|
|
50
|
54
|
|
|
|
55
|
+ /**
|
|
|
56
|
+ * Format a DateTime for a document, respecting the privacy.spoof_english
|
|
|
57
|
+ * preference.
|
|
|
58
|
+ */
|
|
|
59
|
+ static nsresult FormatForDocument(const DateTimeFormat::ComponentsBag& aStyle,
|
|
|
60
|
+ const PRExplodedTime* aExplodedTime,
|
|
|
61
|
+ const dom::Document* aForDocument,
|
|
|
62
|
+ nsAString& aStringOut);
|
|
|
63
|
+
|
|
51
|
64
|
/**
|
|
52
|
65
|
* If the app locale changes, the cached locale needs to be reset.
|
|
53
|
66
|
*/
|
testing/mozharness/scripts/android_emulator_unittest.py
| ... |
... |
@@ -13,6 +13,8 @@ import sys |
|
13
|
13
|
import tempfile
|
|
14
|
14
|
import time
|
|
15
|
15
|
|
|
|
16
|
+import yaml
|
|
|
17
|
+
|
|
16
|
18
|
# load modules from parent dir
|
|
17
|
19
|
here = os.path.abspath(os.path.dirname(__file__))
|
|
18
|
20
|
sys.path.insert(1, os.path.dirname(here))
|
| ... |
... |
@@ -515,17 +517,22 @@ class AndroidEmulatorTest( |
|
515
|
517
|
self.run_command([adb, "forward", "tcp:2828", "tcp:2828"])
|
|
516
|
518
|
|
|
517
|
519
|
with tempfile.NamedTemporaryFile(suffix=".yaml") as tmp_file:
|
|
518
|
|
- tmp_file.write(
|
|
519
|
|
- b"""args:
|
|
520
|
|
-- --marionette
|
|
521
|
|
-- --remote-allow-system-access
|
|
522
|
|
-"""
|
|
523
|
|
- )
|
|
|
520
|
+ config = {"args": ["--marionette", "--remote-allow-system-access"]}
|
|
|
521
|
+
|
|
|
522
|
+ tmp_file.write(yaml.dump(config, encoding="utf-8"))
|
|
524
|
523
|
tmp_file.flush()
|
|
525
|
524
|
|
|
526
|
525
|
remote_path = f"/data/local/tmp/{self.package_name}-geckoview-config.yaml"
|
|
527
|
526
|
self.run_command([adb, "push", tmp_file.name, remote_path])
|
|
528
|
527
|
|
|
|
528
|
+ self.run_command([
|
|
|
529
|
+ adb,
|
|
|
530
|
+ "shell",
|
|
|
531
|
+ "am",
|
|
|
532
|
+ "set-debug-app",
|
|
|
533
|
+ "--persistent",
|
|
|
534
|
+ self.package_name,
|
|
|
535
|
+ ])
|
|
529
|
536
|
self.run_command([
|
|
530
|
537
|
adb,
|
|
531
|
538
|
"shell",
|
| ... |
... |
@@ -569,7 +576,7 @@ class AndroidEmulatorTest( |
|
569
|
576
|
if requirements:
|
|
570
|
577
|
self.register_virtualenv_module(requirements=[requirements])
|
|
571
|
578
|
|
|
572
|
|
- if ("marionette", "marionette") in suites:
|
|
|
579
|
+ if any("marionette" in suite_name for _, suite_name in self._query_suites()):
|
|
573
|
580
|
self._configure_marionette_virtualenv(action)
|
|
574
|
581
|
|
|
575
|
582
|
def download_and_extract(self):
|
| ... |
... |
@@ -609,7 +616,7 @@ class AndroidEmulatorTest( |
|
609
|
616
|
for per_test_suite, suite in suites:
|
|
610
|
617
|
self.test_suite = suite
|
|
611
|
618
|
|
|
612
|
|
- if self.test_suite == "marionette":
|
|
|
619
|
+ if "marionette" in self.test_suite:
|
|
613
|
620
|
self._marionette_setup()
|
|
614
|
621
|
|
|
615
|
622
|
try:
|
| ... |
... |
@@ -693,9 +700,10 @@ class AndroidEmulatorTest( |
|
693
|
700
|
|
|
694
|
701
|
@PostScriptAction("run-tests")
|
|
695
|
702
|
def marionette_teardown(self, *args, **kwargs):
|
|
696
|
|
- if ("marionette", "marionette") in self._query_suites():
|
|
|
703
|
+ if any("marionette" in suite_name for _, suite_name in self._query_suites()):
|
|
697
|
704
|
adb = self.query_exe("adb")
|
|
698
|
705
|
self.run_command([adb, "shell", "am", "force-stop", self.package_name])
|
|
|
706
|
+ self.run_command([adb, "shell", "am", "clear-debug-app"])
|
|
699
|
707
|
self.run_command([adb, "uninstall", self.package_name])
|
|
700
|
708
|
self.run_command([
|
|
701
|
709
|
adb,
|
|