tor-commits
Threads by month
- ----- 2026 -----
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2025 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2024 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2023 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2022 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2021 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2020 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2019 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2018 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2017 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2016 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2015 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2014 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2013 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2012 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2011 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
September 2026
- 1 participants
- 32 discussions
[Git][tpo/applications/tor-browser][tor-browser-153.2.0esr-16.0-1] 5 commits: Bug 2045435 - Prune dying entries from wasm::Realm::instances_ during sweeping. r=jpages
by ma1 (@ma1) 01 Sep '26
by ma1 (@ma1) 01 Sep '26
01 Sep '26
ma1 pushed to branch tor-browser-153.2.0esr-16.0-1 at The Tor Project / Applications / Tor Browser
Commits:
3e75dcee by Yury Delendik at 2026-09-01T16:47:31+02:00
Bug 2045435 - Prune dying entries from wasm::Realm::instances_ during sweeping. r=jpages
wasm::Realm::instances_ is a weak list. Readers rely on the
instances()[i]->object() read barrier, but that barrier is a no-op once the
owning zone is being incrementally swept, and entries are otherwise only
removed at Instance finalization (~Instance -> unregisterInstance). So between
marking a zone's instance objects dead and finalizing them, the list could
still hand an about-to-be-finalized instance to a reader.
Prune such entries at the start of zone sweeping via a new
wasm::Realm::traceWeakInstances(), called from beginSweepingSweepGroup
alongside the other per-realm weak-collection sweeps. This makes instances_
behave like the engine's other weak collections, so it never exposes an
about-to-be-finalized instance to the mutator during sweep slices.
Differential Revision: https://phabricator.services.mozilla.com/D314032
- - - - -
3c493b1e by Ting-Yu Lin at 2026-09-01T16:47:32+02:00
Bug 2053578 - Use static_cast in nsSplittableFrame::UpdateFirstContinuationAndFirstInFlowCache(). r=layout-reviewers,jfkthame
`nsSplittableFrame` is a subclass of `nsIFrame`, it is sufficient to use
`static_cast`.
Differential Revision: https://phabricator.services.mozilla.com/D315157
- - - - -
a6141d0a by Ting-Yu Lin at 2026-09-01T16:47:32+02:00
Bug 2053578 - Update first-in-flow cache when a continuation is changing from fluid to non-fluid. r=layout-reviewers,jfkthame
The original case in bug 2053578 comment 5 can reproduce an ASAN
use-after-poison with the patch bug 2053578 comment 6 applied. However, with
unpatched code, the best we can do is using a DEBUG-only assertion to catch the
error condition that detect a stale first-in-flow cache in next-in-flow.
`bidi-inline-continuation-first-in-flow.html` is generated with the help of
Claude code, and it can trigger the assertion without other fix in this patch.
Differential Revision: https://phabricator.services.mozilla.com/D315158
- - - - -
23f5704c by Iain Ireland at 2026-09-01T16:47:33+02:00
Bug 2058626: Check for mutually exclusive flags when deserializing cloned RegExp r=spidermonkey-reviewers,jonco
Differential Revision: https://phabricator.services.mozilla.com/D316904
- - - - -
b781a92b by Gela at 2026-09-01T16:47:34+02:00
Bug 2053320 - Part 1: Don't tie Nimbus tooling to `HomeActivity` UI a=RyanVM
Original Revision: https://phabricator.services.mozilla.com/D319628
Differential Revision: https://phabricator.services.mozilla.com/D320784
- - - - -
12 changed files:
- js/src/gc/GCRuntime.h
- js/src/gc/Sweeping.cpp
- + js/src/jit-test/tests/structured-clone/bug2058626.js
- js/src/vm/StructuredClone.cpp
- js/src/wasm/WasmRealm.cpp
- js/src/wasm/WasmRealm.h
- layout/generic/nsSplittableFrame.cpp
- mobile/android/fenix/app/src/main/AndroidManifest.xml
- mobile/android/fenix/app/src/main/java/org/mozilla/fenix/HomeActivity.kt
- + mobile/android/fenix/app/src/main/java/org/mozilla/fenix/experiments/QANimbusToolingReceiver.kt
- + mobile/android/fenix/app/src/test/java/org/mozilla/fenix/experiments/QANimbusToolingReceiverTest.kt
- + testing/web-platform/tests/css/css-writing-modes/crashtests/bidi-inline-continuation-first-in-flow.html
Changes:
=====================================
js/src/gc/GCRuntime.h
=====================================
@@ -999,6 +999,7 @@ class GCRuntime {
void updateAtomsBitmap();
void sweepCCWrappers();
void sweepRealmGlobals();
+ void sweepWasmInstances();
void sweepEmbeddingWeakPointers(JS::GCContext* gcx);
void sweepMisc();
void sweepCompressionTasks();
=====================================
js/src/gc/Sweeping.cpp
=====================================
@@ -1387,6 +1387,13 @@ void GCRuntime::sweepRealmGlobals() {
}
}
+void GCRuntime::sweepWasmInstances() {
+ for (SweepGroupRealmsIter r(this); !r.done(); r.next()) {
+ AutoSetThreadIsSweeping threadIsSweeping(r->zone());
+ r->wasm.traceWeakInstances();
+ }
+}
+
void GCRuntime::sweepMisc() {
SweepingTracer trc(rt);
for (SweepGroupRealmsIter r(this); !r.done(); r.next()) {
@@ -1743,6 +1750,11 @@ IncrementalProgress GCRuntime::beginSweepingSweepGroup(JS::GCContext* gcx,
// This must happen before updating embedding weak pointers.
sweepRealmGlobals();
+ // Prune dying wasm instances from each realm's weak instance list now, at the
+ // start of sweeping, before the mutator can observe them via the (now no-op)
+ // instances() read barrier during later incremental slices.
+ sweepWasmInstances();
+
sweepEmbeddingWeakPointers(gcx);
maybeWriteCoverageAndSpew();
=====================================
js/src/jit-test/tests/structured-clone/bug2058626.js
=====================================
@@ -0,0 +1,16 @@
+function forge(pattern, srcFlags, flagsByte) {
+ var cb = serialize(new RegExp(pattern, srcFlags), undefined, { scope: "DifferentProcess" });
+ var u8 = new Uint8Array(cb.arraybuffer);
+ for (var i = 0; i + 8 <= u8.length; i += 4) {
+ var tag = u8[i+4] | (u8[i+5] << 8) | (u8[i+6] << 16) | (u8[i+7] << 24);
+ if ((tag >>> 0) === 0xFFFF0006) { u8[i] = flagsByte; break; }
+ }
+ cb.clonebuffer = u8.buffer;
+ return deserialize(cb, { scope: "DifferentProcess" });
+}
+try {
+ var forged = forge("[\\q{abc|de}]", "v", 0x90);
+ var bad = new RegExp(forged, "u");
+ try { bad.exec("abc"); } catch {}
+ var good = new RegExp("[\\q{abc|de}]", "u");
+} catch {}
=====================================
js/src/vm/StructuredClone.cpp
=====================================
@@ -3234,7 +3234,9 @@ bool JSStructuredCloneReader::startReadUnchecked(
}
case SCTAG_REGEXP_OBJECT: {
- if ((data & RegExpFlag::AllFlags) != data) {
+ // Reject invalid flags. /u and /v are mutually exclusive.
+ if ((data & RegExpFlag::AllFlags) != data ||
+ ((data & RegExpFlag::Unicode) && (data & RegExpFlag::UnicodeSets))) {
JS_ReportErrorNumberASCII(context(), GetErrorMessage, nullptr,
JSMSG_SC_BAD_SERIALIZED_DATA, "regexp");
return false;
=====================================
js/src/wasm/WasmRealm.cpp
=====================================
@@ -16,6 +16,7 @@
#include "wasm/WasmRealm.h"
+#include "gc/Marking.h"
#include "vm/GlobalObject.h"
#include "vm/Realm.h"
#include "wasm/WasmDebug.h"
@@ -111,6 +112,19 @@ void wasm::Realm::unregisterInstance(Instance& instance) {
}
}
+void wasm::Realm::traceWeakInstances() {
+ // Registration/unregistration of instances_ is tied to Instance lifetime, so
+ // an instance whose owning object is about to be finalized is still present
+ // here until ~Instance runs. Remove such entries now, at the start of zone
+ // sweeping, because the instances() read barrier that otherwise protects
+ // readers is a no-op once the zone is being swept. erase order is preserved,
+ // so the pointer-sorted invariant used by BinarySearchIf holds.
+ instances_.eraseIf([](Instance* instance) {
+ return js::gc::IsAboutToBeFinalizedUnbarriered(
+ instance->objectUnbarriered());
+ });
+}
+
void wasm::Realm::ensureProfilingLabels(bool profilingEnabled) {
for (Instance* instance : instances_) {
instance->ensureProfilingLabels(profilingEnabled);
=====================================
js/src/wasm/WasmRealm.h
=====================================
@@ -51,10 +51,17 @@ class Realm {
// Return a vector of all live instances in the realm. The lifetime of
// these Instances is determined by their owning WasmInstanceObject.
// Note that accessing instances()[i]->object() triggers a read barrier
- // since instances() is effectively a weak list.
+ // since instances() is effectively a weak list. This read barrier is only
+ // effective while the owning zone is being marked; traceWeakInstances()
+ // prunes dying entries at the start of sweeping so that the list never
+ // exposes an about-to-be-finalized instance to the mutator.
const InstanceVector& instances() const { return instances_; }
+ // Remove instances whose owning object is about to be finalized. Called at
+ // the start of zone sweeping, when the instances() read barrier is a no-op.
+ void traceWeakInstances();
+
// Ensure all Instances in this Realm have profiling labels created.
void ensureProfilingLabels(bool profilingEnabled);
=====================================
layout/generic/nsSplittableFrame.cpp
=====================================
@@ -9,6 +9,7 @@
#include "nsSplittableFrame.h"
+#include "mozilla/DebugOnly.h"
#include "mozilla/ReflowInput.h"
#include "nsContainerFrame.h"
#include "nsFieldSetFrame.h"
@@ -214,7 +215,7 @@ void nsSplittableFrame::UpdateFirstContinuationAndFirstInFlowCache() {
if (oldCachedFirstContinuation != newFirstContinuation) {
// Update the first-continuation cache for us and our next-continuations.
for (nsSplittableFrame* f = this; f;
- f = reinterpret_cast<nsSplittableFrame*>(f->GetNextContinuation())) {
+ f = static_cast<nsSplittableFrame*>(f->GetNextContinuation())) {
f->mFirstContinuation = newFirstContinuation;
}
}
@@ -227,7 +228,7 @@ void nsSplittableFrame::UpdateFirstContinuationAndFirstInFlowCache() {
// behavior when a frame list is destroyed from the front. To avoid that
// pathological behavior, we simply purge the cached values.
for (nsSplittableFrame* f = this; f;
- f = reinterpret_cast<nsSplittableFrame*>(f->GetNextContinuation())) {
+ f = static_cast<nsSplittableFrame*>(f->GetNextContinuation())) {
f->mFirstContinuation = nullptr;
}
}
@@ -239,22 +240,41 @@ void nsSplittableFrame::UpdateFirstContinuationAndFirstInFlowCache() {
if (oldCachedFirstInFlow != newFirstInFlow) {
// Update the first-in-flow cache for us and our next-in-flows.
for (nsSplittableFrame* f = this; f;
- f = reinterpret_cast<nsSplittableFrame*>(f->GetNextInFlow())) {
+ f = static_cast<nsSplittableFrame*>(f->GetNextInFlow())) {
f->mFirstInFlow = newFirstInFlow;
}
}
} else {
- // We become the new first-in-flow due to our prev-in-flow being removed.
- if (oldCachedFirstInFlow) {
- // It's tempting to update the first-in-flow cache for our
- // next-in-flows here, but that would result in overall O(n^2)
- // behavior when a frame list is destroyed from the front. To avoid that
- // pathological behavior, we simply purge the cached values.
+ if (GetPrevContinuation()) {
+ // We become the new first-in-flow after changing from fluid to non-fluid.
+ // Update the stale first-in-flow cache for us and all next-in-flows.
+ //
+ // Note that this has no counterpart in the above mFirstContinuation cache
+ // since GetPrevContinuation() does not depend on the
+ // NS_FRAME_IS_FLUID_CONTINUATION bit.
for (nsSplittableFrame* f = this; f;
- f = reinterpret_cast<nsSplittableFrame*>(f->GetNextInFlow())) {
- f->mFirstInFlow = nullptr;
+ f = static_cast<nsSplittableFrame*>(f->GetNextInFlow())) {
+ f->mFirstInFlow = this;
+ }
+ } else {
+ // We become the new first-in-flow due to our prev-in-flow being removed.
+ if (oldCachedFirstInFlow) {
+ // It's tempting to update the first-in-flow cache for our
+ // next-in-flows here, but that would result in overall O(n^2)
+ // behavior when a frame list is destroyed from the front. To avoid that
+ // pathological behavior, we simply purge the cached values.
+ for (nsSplittableFrame* f = this; f;
+ f = static_cast<nsSplittableFrame*>(f->GetNextInFlow())) {
+ f->mFirstInFlow = nullptr;
+ }
}
}
+
+ DebugOnly<nsSplittableFrame*> nextInFlow =
+ static_cast<nsSplittableFrame*>(GetNextInFlow());
+ MOZ_ASSERT(!nextInFlow || !nextInFlow->mFirstInFlow ||
+ nextInFlow->mFirstInFlow == this,
+ "Our next-in-flow caches a stale first-in-flow!");
}
}
=====================================
mobile/android/fenix/app/src/main/AndroidManifest.xml
=====================================
@@ -820,6 +820,16 @@
<action android:name="org.mozilla.fenix.TRIGGER_MESSAGE_WORKER" />
</intent-filter>
</receiver>
+
+ <receiver
+ android:name="org.mozilla.fenix.experiments.QANimbusToolingReceiver"
+ android:exported="true"
+ android:enabled="true"
+ android:permission="android.permission.DUMP">
+ <intent-filter>
+ <action android:name="org.mozilla.fenix.NIMBUS_TOOLING" />
+ </intent-filter>
+ </receiver>
</application>
</manifest>
=====================================
mobile/android/fenix/app/src/main/java/org/mozilla/fenix/HomeActivity.kt
=====================================
@@ -99,7 +99,6 @@ import mozilla.components.support.utils.toSafeIntent
import mozilla.components.support.webextensions.WebExtensionOptionsPageObserver
import mozilla.components.support.webextensions.WebExtensionPopupObserver
import mozilla.telemetry.glean.private.NoExtras
-import org.mozilla.experiments.nimbus.initializeTooling
import org.mozilla.fenix.GleanMetrics.AppIcon
import org.mozilla.fenix.GleanMetrics.Events
import org.mozilla.fenix.GleanMetrics.Metrics
@@ -463,9 +462,6 @@ open class HomeActivity : LocaleAwareAppCompatActivity(), NavHostActivity, Crash
}
}
}
-
- // Setup nimbus-cli tooling. This is a NOOP when launching normally.
- components.nimbus.sdk.initializeTooling(applicationContext, intent)
components.strictMode.attachListenerToDisablePenaltyDeath(supportFragmentManager)
MarkersFragmentLifecycleCallbacks.register(supportFragmentManager, components.core.engine)
=====================================
mobile/android/fenix/app/src/main/java/org/mozilla/fenix/experiments/QANimbusToolingReceiver.kt
=====================================
@@ -0,0 +1,57 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+package org.mozilla.fenix.experiments
+
+import android.content.BroadcastReceiver
+import android.content.Context
+import android.content.Intent
+import kotlinx.coroutines.CoroutineDispatcher
+import kotlinx.coroutines.CoroutineScope
+import kotlinx.coroutines.Dispatchers
+import kotlinx.coroutines.launch
+import mozilla.components.support.base.log.logger.Logger
+import org.mozilla.experiments.nimbus.initializeTooling
+import org.mozilla.fenix.ext.components
+
+private val logger = Logger("QANimbusToolingReceiver")
+
+/**
+ * Receiver triggered on demand via `nimbus-cli` to manually enroll into Nimbus experiments.
+ *
+ * ```
+ * adb shell am broadcast -a org.mozilla.fenix.NIMBUS_TOOLING \
+ * -p org.mozilla.fenix
+ * ```
+ *
+ * `-p org.mozilla.fenix` is the package name, so adjust that value for release/beta/nightly/debug.
+ *
+ * @param dispatcher the [CoroutineDispatcher] the tooling commands are applied on.
+ */
+class QANimbusToolingReceiver(private val dispatcher: CoroutineDispatcher = Dispatchers.IO) : BroadcastReceiver() {
+ override fun onReceive(context: Context, intent: Intent) {
+ if (intent.action != ACTION_NIMBUS_TOOLING) return
+
+ logger.info("Enqueueing QANimbusToolingReceiver via debug trigger")
+
+ val applicationContext = context.applicationContext
+
+ val pendingResult: PendingResult? = goAsync()
+ CoroutineScope(dispatcher).launch {
+ try {
+ applicationContext.components.nimbus.sdk.initializeTooling(
+ applicationContext,
+ intent,
+ )
+ } finally {
+ logger.info("Nimbus tooling command processed")
+ pendingResult?.finish()
+ }
+ }
+ }
+
+ companion object {
+ const val ACTION_NIMBUS_TOOLING = "org.mozilla.fenix.NIMBUS_TOOLING"
+ }
+}
=====================================
mobile/android/fenix/app/src/test/java/org/mozilla/fenix/experiments/QANimbusToolingReceiverTest.kt
=====================================
@@ -0,0 +1,117 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+package org.mozilla.fenix.experiments
+
+import android.content.Context
+import android.content.Intent
+import io.mockk.every
+import kotlinx.coroutines.ExperimentalCoroutinesApi
+import kotlinx.coroutines.Job
+import kotlinx.coroutines.test.UnconfinedTestDispatcher
+import mozilla.components.support.test.robolectric.testContext
+import org.junit.Assert.assertEquals
+import org.junit.Assert.assertFalse
+import org.junit.Assert.assertNull
+import org.junit.Assert.assertTrue
+import org.junit.Before
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.mozilla.fenix.experiments.QANimbusToolingReceiver.Companion.ACTION_NIMBUS_TOOLING
+import org.mozilla.fenix.ext.components
+import org.mozilla.fenix.nimbus.TestNimbusApi
+import org.robolectric.RobolectricTestRunner
+
+@OptIn(ExperimentalCoroutinesApi::class)
+@RunWith(RobolectricTestRunner::class)
+class QANimbusToolingReceiverTest {
+
+ private val nimbusApi = FakeNimbusApi(testContext)
+ private val receiver = QANimbusToolingReceiver(UnconfinedTestDispatcher())
+
+ @Before
+ fun setUp() {
+ every { testContext.components.nimbus.sdk } returns nimbusApi
+ }
+
+ @Test
+ fun `GIVEN a tooling command WHEN the tooling action is received THEN the command is applied`() {
+ receiver.onReceive(testContext, toolingIntent(ACTION_NIMBUS_TOOLING))
+
+ assertEquals(EXPERIMENTS, nimbusApi.appliedExperiments)
+ assertEquals(false, nimbusApi.fetchEnabled)
+ assertTrue(nimbusApi.databaseReset)
+ assertTrue(nimbusApi.stateDumped)
+ }
+
+ @Test
+ fun `GIVEN a tooling command WHEN another action is received THEN the command is ignored`() {
+ receiver.onReceive(testContext, toolingIntent("org.mozilla.fenix.ACTION_PRINT"))
+
+ assertNull(nimbusApi.appliedExperiments)
+ assertNull(nimbusApi.fetchEnabled)
+ assertFalse(nimbusApi.databaseReset)
+ assertFalse(nimbusApi.stateDumped)
+ }
+
+ @Test
+ fun `GIVEN no tooling command WHEN the tooling action is received THEN nothing is applied`() {
+ receiver.onReceive(testContext, Intent(ACTION_NIMBUS_TOOLING))
+
+ assertNull(nimbusApi.appliedExperiments)
+ assertNull(nimbusApi.fetchEnabled)
+ assertFalse(nimbusApi.databaseReset)
+ assertFalse(nimbusApi.stateDumped)
+ }
+
+ @Test
+ fun `GIVEN a tooling command without the version extra WHEN the tooling action is received THEN nothing is applied`() {
+ val intent = toolingIntent(ACTION_NIMBUS_TOOLING).apply { removeExtra("version") }
+
+ receiver.onReceive(testContext, intent)
+
+ assertNull(nimbusApi.appliedExperiments)
+ assertFalse(nimbusApi.stateDumped)
+ }
+
+ private fun toolingIntent(action: String) =
+ Intent(action).apply {
+ putExtra("nimbus-cli", null as String?)
+ putExtra("version", 1)
+ putExtra("experiments", EXPERIMENTS)
+ putExtra("reset-db", true)
+ putExtra("log-state", true)
+ }
+
+ private class FakeNimbusApi(context: Context) : TestNimbusApi(context) {
+ var appliedExperiments: String? = null
+ var fetchEnabled: Boolean? = null
+ var databaseReset = false
+ var stateDumped = false
+
+ override fun applyLocalExperiments(experimentsJson: String): Job {
+ appliedExperiments = experimentsJson
+ return completedJob()
+ }
+
+ override fun resetEnrollmentsDatabase(): Job {
+ databaseReset = true
+ return completedJob()
+ }
+
+ override fun setFetchEnabled(enabled: Boolean) {
+ fetchEnabled = enabled
+ }
+
+ override fun dumpStateToLog() {
+ stateDumped = true
+ }
+
+ private fun completedJob() = Job().apply { complete() }
+ }
+
+ companion object {
+ private const val EXPERIMENTS = """{"data":[]}"""
+ }
+}
=====================================
testing/web-platform/tests/css/css-writing-modes/crashtests/bidi-inline-continuation-first-in-flow.html
=====================================
@@ -0,0 +1,194 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<link rel="author" title="Ting-Yu Lin" href="mailto:tlin@mozilla.com">
+<link rel="help" href="https://bugzilla.mozilla.org/show_bug.cgi?id=2053578">
+
+<!-- The operations in <script> are generated from one of the runs from
+ the original testcase (Bug 2053578 Comment 5) that triggers
+ the assertion. -->
+
+<body></body>
+
+<script>
+var n1 = document.createElement("div");
+n1.style.width = "50px";
+var n2 = document.createElement("bdo");
+n2.setAttribute("dir", "rtl");
+var n3 = document.createTextNode("كلمة");
+n2.appendChild(n3);
+n1.appendChild(n2);
+var n4 = document.createElement("em");
+var n5 = document.createElement("span");
+var n6 = document.createElement("bdo");
+var n7 = document.createTextNode("word m mix");
+n6.appendChild(n7);
+n5.appendChild(n6);
+var n8 = document.createTextNode("מלל");
+n5.appendChild(n8);
+var n9 = document.createElement("b");
+n9.setAttribute("dir", "ltr");
+n9.style.unicodeBidi = "bidi-override";
+var n10 = document.createTextNode("alpha");
+n9.appendChild(n10);
+var n11 = document.createTextNode("نص مرحبا كلمة");
+n9.appendChild(n11);
+var n12 = document.createTextNode("مرحبا نص نص");
+n9.appendChild(n12);
+n5.appendChild(n9);
+n4.appendChild(n5);
+var n13 = document.createElement("span");
+n13.style.unicodeBidi = "bidi-override";
+var n14 = document.createElement("bdi");
+var n15 = document.createTextNode("اختبار");
+n14.appendChild(n15);
+n13.appendChild(n14);
+var n16 = document.createElement("em");
+var n17 = document.createTextNode("בדיקה מלל m نص");
+n16.appendChild(n17);
+var n18 = document.createTextNode("كلمة");
+n16.appendChild(n18);
+var n19 = document.createTextNode("نص مرحبا كلمة");
+n16.appendChild(n19);
+n13.appendChild(n16);
+var n20 = document.createElement("span");
+var n21 = document.createTextNode("שלום מלל בדיקה שלום");
+n20.appendChild(n21);
+n13.appendChild(n20);
+n4.appendChild(n13);
+n1.appendChild(n4);
+document.body.appendChild(n1);
+var n22 = document.createElement("bdo");
+n22.style.unicodeBidi = "plaintext";
+var n23 = document.createElement("span");
+var n24 = document.createTextNode("עברית שלום مرحبا alpha");
+n23.appendChild(n24);
+n22.appendChild(n23);
+n14.style.direction = "ltr";
+var n25 = document.createTextNode("كلمة שלום");
+n2.insertBefore(n25, n3);
+n21.remove();
+n9.setAttribute("dir", "ltr");
+var n26 = document.createTextNode("בדיקה mm");
+n4.appendChild(n26);
+n12.data = "שלום עברית שלום עברית";
+n7.remove();
+n2.style.direction = "ltr";
+n1.style.width = "102px";
+var n27 = document.createTextNode("עברית mm m");
+n22.appendChild(n27);
+n22.setAttribute("dir", "rtl");
+n4.style.direction = "";
+n14.style.direction = "rtl";
+n22.insertBefore(n9, n23);
+var n28 = document.createElement("br");
+n9.insertBefore(n28, n12);
+n14.style.unicodeBidi = "embed";
+n5.insertBefore(n22, n6);
+n9.style.direction = "";
+n9.style.unicodeBidi = "bidi-override";
+n22.remove();
+var n29 = document.createTextNode("עברית שלום");
+n5.insertBefore(n29, n6);
+n18.data = "mix";
+n19.data = "alpha m word";
+n20.style.direction = "ltr";
+var n30 = document.createTextNode("mm עברית עברית نص");
+n6.appendChild(n30);
+n6.style.unicodeBidi = "isolate";
+n20.style.unicodeBidi = "embed";
+n16.setAttribute("dir", "rtl");
+n2.insertBefore(n20, n25);
+var n31 = document.createTextNode("שלום مرحبا mix mm");
+n2.insertBefore(n31, n20);
+var n32 = document.createTextNode("m בדיקה word mix");
+n20.appendChild(n32);
+n16.removeAttribute("dir");
+n16.setAttribute("dir", "auto");
+n5.remove();
+n17.remove();
+n31.data = "בדיקה mm";
+n14.style.unicodeBidi = "isolate";
+n4.remove();
+n20.style.direction = "";
+n20.style.direction = "rtl";
+n3.data = "בדיקה שלום word";
+n25.data = "mm";
+n25.data = "m mix word";
+n1.style.direction = "rtl";
+n31.data = "كلمة اختبار";
+n20.remove();
+n3.data = "שלום نص";
+n31.remove();
+n2.setAttribute("dir", "ltr");
+n2.removeAttribute("dir");
+n1.insertBefore(n4, n2);
+n19.data = "מלל word";
+n15.remove();
+n13.setAttribute("dir", "ltr");
+n25.remove();
+n2.setAttribute("dir", "ltr");
+n14.appendChild(n2);
+n3.data = "مرحبا اختبار مرحبا";
+n18.data = "שלום";
+n16.insertBefore(n20, n18);
+var n33 = document.createTextNode("كلمة نص mm");
+n2.appendChild(n33);
+n19.data = "اختبار مرحبا كلمة كلمة";
+n18.data = "mm mix mix";
+var n34 = document.createElement("br");
+n4.insertBefore(n34, n13);
+n13.style.unicodeBidi = "isolate-override";
+n13.remove();
+n34.remove();
+n26.data = "كلمة كلمة مرحبا";
+n4.setAttribute("dir", "rtl");
+n26.data = "mm word m mm";
+n26.data = "اختبار نص";
+n4.setAttribute("dir", "auto");
+var n35 = document.createTextNode("مرحبا מלל كلمة שלום");
+n4.insertBefore(n35, n26);
+n4.style.unicodeBidi = "embed";
+n4.style.direction = "";
+var n36 = document.createTextNode("كلمة mix בדיקה نص");
+n4.insertBefore(n36, n35);
+n36.remove();
+n4.removeAttribute("dir");
+n4.style.unicodeBidi = "";
+n1.style.width = "87px";
+n26.data = "שלום מלל מלל עברית";
+var n37 = document.createElement("br");
+n4.insertBefore(n37, n35);
+n35.data = "مرحبا نص اختبار";
+n4.setAttribute("dir", "rtl");
+n33.data = "word alpha mm";
+var n38 = document.createTextNode("שלום word");
+n16.insertBefore(n38, n20);
+n2.setAttribute("dir", "auto");
+n2.remove();
+n37.remove();
+var n39 = document.createTextNode("עברית اختبار בדיקה");
+n13.appendChild(n39);
+var n40 = document.createTextNode("בדיקה m نص نص");
+n13.insertBefore(n40, n14);
+var n41 = document.createElement("br");
+n20.appendChild(n41);
+n35.data = "alpha";
+n16.remove();
+n39.data = "نص mm בדיקה اختبار";
+n40.data = "mix mix word m";
+n35.data = "שלום בדיקה";
+n35.data = "m mix mm m";
+n40.data = "مرحبا اختبار";
+n13.style.unicodeBidi = "embed";
+n4.insertBefore(n16, n26);
+var n42 = document.createTextNode("نص");
+n13.insertBefore(n42, n40);
+n41.remove();
+n20.insertBefore(n2, n32);
+document.body.offsetHeight;
+n39.data = "مرحبا alpha עברית word";
+n18.data = "שלום";
+n2.setAttribute("dir", "auto");
+document.body.offsetHeight;
+n20.style.direction = "ltr";
+</script>
View it on GitLab: https://gitlab.torproject.org/tpo/applications/tor-browser/-/compare/1f8e95…
--
View it on GitLab: https://gitlab.torproject.org/tpo/applications/tor-browser/-/compare/1f8e95…
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
1
0
[Git][tpo/applications/tor-browser][tor-browser-153.2.0esr-16.0-1] fixup! TB 27476: Implement about:torconnect captive portal within Tor Browser
by ma1 (@ma1) 01 Sep '26
by ma1 (@ma1) 01 Sep '26
01 Sep '26
ma1 pushed to branch tor-browser-153.2.0esr-16.0-1 at The Tor Project / Applications / Tor Browser
Commits:
1f8e95ba by hackademix at 2026-09-01T16:39:51+02:00
fixup! TB 27476: Implement about:torconnect captive portal within Tor Browser
TB 45264: Use parent process for TorConnect redirections
- - - - -
2 changed files:
- browser/components/torconnect/TorConnectChild.sys.mjs
- browser/components/torconnect/TorConnectParent.sys.mjs
Changes:
=====================================
browser/components/torconnect/TorConnectChild.sys.mjs
=====================================
@@ -60,12 +60,7 @@ export class TorConnectChild extends RemotePageChild {
console.error(`Invalid redirect URL "${redirect}"`);
}
- // Replace the destination to prevent "about:torconnect" entering the
- // history.
- // NOTE: This is done here, in the window actor, rather than in content
- // because we have the privilege to redirect to a "chrome:" uri here (for
- // when the HomePage is set to be blank).
- this.contentWindow.location.replace(replaceURI);
+ this.sendQuery("torconnect:redirect", replaceURI);
}
actorCreated() {
=====================================
browser/components/torconnect/TorConnectParent.sys.mjs
=====================================
@@ -112,6 +112,16 @@ export class TorConnectParent extends JSWindowActorParent {
return Promise.resolve(
TorConnectParent.fixupURIs(lazy.HomePage.get())[0]
);
+ case "torconnect:redirect":
+ this.browsingContext.top.embedderElement.loadURI(
+ Services.io.newURI(message.data),
+ {
+ triggeringPrincipal:
+ Services.scriptSecurityManager.getSystemPrincipal(),
+ loadFlags: Ci.nsIWebNavigation.LOAD_FLAGS_REPLACE_HISTORY,
+ }
+ );
+ break;
case "torconnect:set-quickstart":
TorConnect.quickstart = message.data;
break;
View it on GitLab: https://gitlab.torproject.org/tpo/applications/tor-browser/-/commit/1f8e95b…
--
View it on GitLab: https://gitlab.torproject.org/tpo/applications/tor-browser/-/commit/1f8e95b…
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
1
0
[Git][tpo/applications/tor-browser][tor-browser-153.2.0esr-16.0-1] 2 commits: fixup! TB 40562: Added Tor Browser preferences to 000-tor-browser.js
by brizental (@brizental) 01 Sep '26
by brizental (@brizental) 01 Sep '26
01 Sep '26
brizental pushed to branch tor-browser-153.2.0esr-16.0-1 at The Tor Project / Applications / Tor Browser
Commits:
825cf57f by Beatriz Rizental at 2026-09-01T11:26:22-03:00
fixup! TB 40562: Added Tor Browser preferences to 000-tor-browser.js
Bug 44155: Set local network access prefs to block it all.
This is defense-in-depth, since other mechanisms already block it.
- - - - -
7abdb578 by Beatriz Rizental at 2026-09-01T11:26:23-03:00
fixup! [android] Disable features and functionality
Bug 44155: Hide local network access toggles from settings.
These toggles are no-op already, since local network access is always
disabled throuh other mechanisms.
- - - - -
4 changed files:
- browser/app/profile/000-tor-browser.js
- mobile/android/fenix/app/src/main/java/org/mozilla/fenix/settings/sitepermissions/SitePermissionsDetailsExceptionsFragment.kt
- mobile/android/fenix/app/src/main/java/org/mozilla/fenix/settings/sitepermissions/SiteSettingsFragment.kt
- mobile/android/fenix/app/src/main/java/org/mozilla/fenix/utils/Settings.kt
Changes:
=====================================
browser/app/profile/000-tor-browser.js
=====================================
@@ -23,6 +23,10 @@ pref("network.http.max-persistent-connections-per-proxy", 256);
// Disable DNS over HTTPS. Set to explicitly off MODE_TRROFF = 5.
// See tor-browser#41906.
pref("network.trr.mode", 5, locked);
+// tor-browser#44155: Block Local Network Access (LNA)
+pref("network.lna.enabled", true);
+pref("network.lna.blocking", true);
+pref("network.lna.block_trackers", true);
// Treat .onions as secure
pref("dom.securecontext.allowlist_onions", true);
=====================================
mobile/android/fenix/app/src/main/java/org/mozilla/fenix/settings/sitepermissions/SitePermissionsDetailsExceptionsFragment.kt
=====================================
@@ -79,8 +79,6 @@ class SitePermissionsDetailsExceptionsFragment : PreferenceFragmentCompat(), Sys
@VisibleForTesting
internal fun bindCategoryPhoneFeatures() {
- val settings = provideSettings()
-
initPhoneFeature(CAMERA)
initPhoneFeature(LOCATION)
initPhoneFeature(MICROPHONE)
@@ -89,8 +87,10 @@ class SitePermissionsDetailsExceptionsFragment : PreferenceFragmentCompat(), Sys
initPhoneFeature(CROSS_ORIGIN_STORAGE_ACCESS)
initPhoneFeature(MEDIA_KEY_SYSTEM_ACCESS)
initAutoplayFeature()
- initPhoneFeature(LOCAL_DEVICE_ACCESS, visible = settings.isLnaFeatureEnabled)
- initPhoneFeature(LOCAL_NETWORK_ACCESS, visible = settings.isLnaFeatureEnabled)
+ // tor-browser#44155: Don't show local network / device toggles.
+ // We want it always in the same state: blocked.
+ initPhoneFeature(LOCAL_DEVICE_ACCESS, visible = false)
+ initPhoneFeature(LOCAL_NETWORK_ACCESS, visible = false)
bindClearPermissionsButton()
}
=====================================
mobile/android/fenix/app/src/main/java/org/mozilla/fenix/settings/sitepermissions/SiteSettingsFragment.kt
=====================================
@@ -92,7 +92,9 @@ class SiteSettingsFragment : PreferenceFragmentCompat(), SystemInsetsPaddedFragm
// not need to be bound
.filter { it != PhoneFeature.AUTOPLAY_INAUDIBLE }
.excludeFeatures(
- condition = { !requireComponents.settings.isLnaFeatureEnabled },
+ // tor-browser#44155: Don't show local network / device toggles.
+ // We want it always in the same state: blocked.
+ condition = { true },
features = setOf(
PhoneFeature.LOCAL_DEVICE_ACCESS,
PhoneFeature.LOCAL_NETWORK_ACCESS,
=====================================
mobile/android/fenix/app/src/main/java/org/mozilla/fenix/utils/Settings.kt
=====================================
@@ -1009,18 +1009,12 @@ class Settings(
/**
* Indicates if the request blocking feature for Local Network / Local Device Access blocking is enabled.
*/
- var isLnaBlockingEnabled by booleanPreference(
- key = appContext.getPreferenceKey(R.string.pref_key_enable_lna_blocking_enabled),
- default = { FxNimbus.features.lnaBlocking.value().blocking || Config.channel.isNightlyOrDebug },
- )
+ var isLnaBlockingEnabled: Boolean = true
/**
* Indicates if the Local Network / Local Device Access tracker blocking feature is enabled.
*/
- var isLnaTrackerBlockingEnabled by booleanPreference(
- key = appContext.getPreferenceKey(R.string.pref_key_enable_lna_tracker_blocking_enabled),
- default = { FxNimbus.features.lnaBlocking.value().blockTrackers },
- )
+ var isLnaTrackerBlockingEnabled: Boolean = true
/**
* Indicates if the overall Local Network / Local Device Access feature is enabled.
@@ -1029,10 +1023,7 @@ class Settings(
* allowing requests that originate from remote origins targeting either localhost addresses or
* local network addresses.
*/
- var isLnaFeatureEnabled by booleanPreference(
- key = appContext.getPreferenceKey(R.string.pref_key_enable_lna_feature_enabled),
- default = { FxNimbus.features.lnaBlocking.value().enabled || Config.channel.isNightlyOrDebug },
- )
+ val isLnaFeatureEnabled: Boolean = true
/**
* Indicates whether isolated content processes are enabled or not.
View it on GitLab: https://gitlab.torproject.org/tpo/applications/tor-browser/-/compare/12016c…
--
View it on GitLab: https://gitlab.torproject.org/tpo/applications/tor-browser/-/compare/12016c…
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
1
0
[Git][tpo/applications/tor-browser][tor-browser-153.2.0esr-16.0-1] 2 commits: fixup! TB 44806: Implement the tor integration in Rust.
by Pier Angelo Vendrame (@pierov) 01 Sep '26
by Pier Angelo Vendrame (@pierov) 01 Sep '26
01 Sep '26
Pier Angelo Vendrame pushed to branch tor-browser-153.2.0esr-16.0-1 at The Tor Project / Applications / Tor Browser
Commits:
0e74ee9b by Elena at 2026-08-31T17:50:01+02:00
fixup! TB 44806: Implement the tor integration in Rust.
TB 44930: Implement the commands on the Rust control port
Implemented string unescape.
- - - - -
12016c7b by Elena at 2026-08-31T17:50:01+02:00
fixup! TB 44806: Implement the tor integration in Rust.
TB 44930: Implement the commands on the Rust control port
Implemented the string escaping.
- - - - -
4 changed files:
- + toolkit/components/tor-integration/tor_provider/src/ctor/controller/escape.rs
- + toolkit/components/tor-integration/tor_provider/src/ctor/controller/mod.rs
- + toolkit/components/tor-integration/tor_provider/src/ctor/controller/unescape.rs
- toolkit/components/tor-integration/tor_provider/src/ctor/mod.rs
Changes:
=====================================
toolkit/components/tor-integration/tor_provider/src/ctor/controller/escape.rs
=====================================
@@ -0,0 +1,66 @@
+// Licensed under the Apache License, Version 2.0,
+// <http://apache.org/licenses/LICENSE-2.0> or the MIT license
+// <http://opensource.org/licenses/MIT>, at your option. This file may not be
+// copied, modified, or distributed except according to those terms.
+
+use std::fmt::Write;
+
+/// Adaptation of tor's esc_for_log (src/lib/log/escape.c).
+///
+/// However, we prefer the hex representation to the octal one.
+pub fn tor_escape_into<T: AsRef<[u8]>>(buf: T, dest: &mut String) {
+ dest.reserve(buf.as_ref().len() + 2);
+ dest.push('"');
+ for b in buf.as_ref() {
+ match *b {
+ b'\'' | b'"' | b'\\' => {
+ dest.push('\\');
+ dest.push(*b as char);
+ }
+ b'\n' => {
+ dest.push_str("\\n");
+ }
+ b'\t' => {
+ dest.push_str("\\t");
+ }
+ b'\r' => {
+ dest.push_str("\\r");
+ }
+ 0x20..=0x7E => {
+ dest.push(*b as char);
+ }
+ _ => {
+ write!(dest, "\\x{:02X}", b).expect("String::write_str always returns Ok(()).");
+ }
+ }
+ }
+ dest.push('"');
+}
+
+#[cfg(test)]
+mod tests {
+ use super::*;
+
+ fn tor_escape<T: AsRef<[u8]>>(buf: T) -> String {
+ let mut dest = String::new();
+ tor_escape_into(buf, &mut dest);
+ dest
+ }
+
+ #[test]
+ fn simple() {
+ assert_eq!(tor_escape("test"), "\"test\"");
+ }
+
+ #[test]
+ fn empty() {
+ assert_eq!(tor_escape(""), "\"\"");
+ }
+
+ #[test]
+ fn escaped() {
+ assert_eq!(tor_escape("'\"\\\r\n\t"), "\"\\'\\\"\\\\\\r\\n\\t\"");
+ assert_eq!(tor_escape("\0"), "\"\\x00\"");
+ assert_eq!(tor_escape("\u{1F9C5}"), "\"\\xF0\\x9F\\xA7\\x85\"");
+ }
+}
=====================================
toolkit/components/tor-integration/tor_provider/src/ctor/controller/mod.rs
=====================================
@@ -0,0 +1,10 @@
+// Licensed under the Apache License, Version 2.0,
+// <http://apache.org/licenses/LICENSE-2.0> or the MIT license
+// <http://opensource.org/licenses/MIT>, at your option. This file may not be
+// copied, modified, or distributed except according to those terms.
+
+mod escape;
+mod unescape;
+
+use escape::*;
+use unescape::*;
=====================================
toolkit/components/tor-integration/tor_provider/src/ctor/controller/unescape.rs
=====================================
@@ -0,0 +1,290 @@
+// Licensed under the Apache License, Version 2.0,
+// <http://apache.org/licenses/LICENSE-2.0> or the MIT license
+// <http://opensource.org/licenses/MIT>, at your option. This file may not be
+// copied, modified, or distributed except according to those terms.
+
+use std::borrow::Cow;
+use thiserror::Error;
+
+#[derive(Error, Debug, PartialEq, Eq)]
+pub enum UnescapeError {
+ #[error("space in an unquoted string")]
+ SpaceInUnquotedString,
+ #[error("unterminated escape sequence or string")]
+ Unterminated,
+ #[error("found an unescaped quote")]
+ UnescapedQuote,
+ #[error("found an invalid escape sequence")]
+ InvalidEscape,
+ #[error("an octal-escaped value exceeds 255")]
+ OctalOverflow,
+}
+
+/// Adaptation of tor's unescape_string (src/lib/encoding/cstring.c).
+/// The main difference is that we allow unquoted strings.
+pub fn tor_unescape<'a>(buf: &'a [u8]) -> Result<Cow<'a, [u8]>, UnescapeError> {
+ match buf.first() {
+ Some(b'"') => {
+ // Quoted string, consume the item and continue with the function.
+ }
+ _ => {
+ if buf.contains(&b' ') {
+ return Err(UnescapeError::SpaceInUnquotedString);
+ }
+ return Ok(Cow::Borrowed(buf));
+ }
+ }
+
+ if buf.len() == 1 || buf.last() != Some(&b'\"') {
+ return Err(UnescapeError::Unterminated);
+ }
+
+ // This is used only if we did an actual change.
+ let mut out = None;
+
+ // We already consumed the initial quote.
+ let mut i = 1;
+ while i < buf.len() {
+ let c = buf[i];
+ i += 1;
+ match c {
+ b'\"' => {
+ if i != buf.len() {
+ return Err(UnescapeError::UnescapedQuote);
+ }
+ return Ok(out
+ .map(|b| Cow::Owned(b))
+ .unwrap_or_else(|| Cow::Borrowed(&buf[1..buf.len() - 1])));
+ }
+ 0 | b'\n' => return Err(UnescapeError::Unterminated),
+ b'\\' => {
+ let esc = buf.get(i).ok_or(UnescapeError::Unterminated)?;
+ i += 1;
+ let out = out.get_or_insert_with(|| {
+ let mut b = Vec::with_capacity(buf.len() - 2);
+ b.extend(&buf[1..i - 2]);
+ b
+ });
+ out.push(match esc {
+ b'n' => Ok(b'\n'),
+ b'r' => Ok(b'\r'),
+ b't' => Ok(b'\t'),
+ b'x' | b'X' => unescape_hex(buf, &mut i),
+ b'0'..=b'7' => unescape_octal(buf, &mut i),
+ b'\'' | b'"' | b'\\' => Ok(*esc),
+ _ => Err(UnescapeError::InvalidEscape),
+ }?);
+ }
+ _ => {
+ if let Some(b) = out.as_mut() {
+ b.push(c);
+ }
+ }
+ }
+ }
+
+ // If we fall out of the loop we never saw a closing quote.
+ Err(UnescapeError::Unterminated)
+}
+
+fn unescape_hex(buf: &[u8], i: &mut usize) -> Result<u8, UnescapeError> {
+ // The C code expects exactly two hex digits.
+ *i += 2;
+ str::from_utf8(&buf[*i - 2..*i])
+ .ok()
+ .and_then(|s| u8::from_str_radix(s, 16).ok())
+ .ok_or(UnescapeError::InvalidEscape)
+}
+
+fn unescape_octal(buf: &[u8], i: &mut usize) -> Result<u8, UnescapeError> {
+ *i -= 1;
+ let mut len = 1;
+ while (*i + len) < buf.len() && len < 3 {
+ let b = buf[*i + len];
+ if b >= b'0' && b <= b'7' {
+ len += 1;
+ } else {
+ break;
+ }
+ }
+
+ *i += len;
+ // This should never fail with InvalidEscape, as we have just validated the
+ // octal values.
+ str::from_utf8(&buf[*i - len..*i])
+ .ok()
+ .and_then(|s| u16::from_str_radix(s, 8).ok())
+ .ok_or(UnescapeError::InvalidEscape)
+ .and_then(|v| v.try_into().map_err(|_| UnescapeError::OctalOverflow))
+}
+
+#[cfg(test)]
+mod tests {
+ use super::*;
+
+ #[test]
+ fn simple() {
+ assert_eq!(&*tor_unescape(b"test").unwrap(), b"test");
+ assert_eq!(&*tor_unescape(b"\"test\"").unwrap(), b"test");
+ }
+
+ #[test]
+ fn quote_in_unquoted() {
+ assert_eq!(&*tor_unescape(b"te\"st").unwrap(), b"te\"st");
+ assert_eq!(&*tor_unescape(b"test\"").unwrap(), b"test\"");
+ }
+
+ #[test]
+ fn empty() {
+ assert_eq!(&*tor_unescape(b"").unwrap(), &[]);
+ assert_eq!(&*tor_unescape(b"\"\"").unwrap(), &[]);
+ }
+
+ #[test]
+ fn unescape_simple() {
+ assert_eq!(&*tor_unescape(b"\"\\n\"").unwrap(), b"\n");
+ assert_eq!(&*tor_unescape(b"\"\\r\"").unwrap(), b"\r");
+ assert_eq!(&*tor_unescape(b"\"\\t\"").unwrap(), b"\t");
+ assert_eq!(&*tor_unescape(b"\"\\r\\n\"").unwrap(), b"\r\n");
+ assert_eq!(&*tor_unescape(b"\"'\"").unwrap(), b"'");
+ assert_eq!(&*tor_unescape(b"\"\\'\"").unwrap(), b"'");
+ assert_eq!(&*tor_unescape(b"\"\\\"\"").unwrap(), b"\"");
+ assert_eq!(&*tor_unescape(b"\"\\\\\"").unwrap(), b"\\");
+ }
+
+ #[test]
+ fn unescape_hex() {
+ assert_eq!(&*tor_unescape(b"\"\\x20\"").unwrap(), b" ");
+ assert_eq!(&*tor_unescape(b"\"\\x20test\"").unwrap(), b" test");
+ assert_eq!(&*tor_unescape(b"\"test\\x20\"").unwrap(), b"test ");
+ assert_eq!(&*tor_unescape(b"\"te\\x20st\"").unwrap(), b"te st");
+
+ assert_eq!(&*tor_unescape(b"\"\\X20\"").unwrap(), b" ");
+ assert_eq!(&*tor_unescape(b"\"\\X20test\"").unwrap(), b" test");
+ assert_eq!(&*tor_unescape(b"\"test\\X20\"").unwrap(), b"test ");
+ assert_eq!(&*tor_unescape(b"\"te\\X20st\"").unwrap(), b"te st");
+
+ assert_eq!(&*tor_unescape(b"\"\\x00\"").unwrap(), b"\0");
+ assert_eq!(&*tor_unescape(b"\"test\\x00\"").unwrap(), b"test\0");
+ assert_eq!(&*tor_unescape(b"\"\\x00test\"").unwrap(), b"\0test");
+ assert_eq!(&*tor_unescape(b"\"\\X00\"").unwrap(), b"\0");
+ assert_eq!(&*tor_unescape(b"\"TEST\\X00\"").unwrap(), b"TEST\0");
+ assert_eq!(&*tor_unescape(b"\"\\X00TEST\"").unwrap(), b"\0TEST");
+ }
+
+ #[test]
+ fn unescape_octal() {
+ assert_eq!(&*tor_unescape(b"\"\\0\"").unwrap(), b"\0");
+ assert_eq!(&*tor_unescape(b"\"\\00\"").unwrap(), b"\0");
+ assert_eq!(&*tor_unescape(b"\"\\000\"").unwrap(), b"\0");
+
+ assert_eq!(&*tor_unescape(b"\"\\2\"").unwrap(), b"\x02");
+ assert_eq!(&*tor_unescape(b"\"\\02\"").unwrap(), b"\x02");
+ assert_eq!(&*tor_unescape(b"\"\\002\"").unwrap(), b"\x02");
+
+ assert_eq!(&*tor_unescape(b"\"\\40\"").unwrap(), b" ");
+ assert_eq!(&*tor_unescape(b"\"\\040\"").unwrap(), b" ");
+
+ assert_eq!(&*tor_unescape(b"\"\\40test\"").unwrap(), b" test");
+ assert_eq!(&*tor_unescape(b"\"\\040test\"").unwrap(), b" test");
+ assert_eq!(&*tor_unescape(b"\"\\40test\\0\"").unwrap(), b" test\0");
+ }
+
+ #[test]
+ fn invalid_unicode() {
+ // Raw invalid sequence, without quotes
+ assert_eq!(*tor_unescape(b"\xF5").unwrap(), [0xF5u8]);
+ // Raw invalid sequence, with quotes
+ assert_eq!(*tor_unescape(b"\"\xF5\"").unwrap(), [0xF5u8]);
+ // Escaped invalid values, we will unescape but keep them as they are.
+ assert_eq!(*tor_unescape(b"\"\\xF5\"").unwrap(), [0xF5u8]);
+ assert_eq!(*tor_unescape(b"\"\\365\"").unwrap(), [0xF5u8]);
+ }
+
+ #[test]
+ fn unquoted_space() {
+ assert_eq!(
+ tor_unescape(b"test test").unwrap_err(),
+ UnescapeError::SpaceInUnquotedString
+ );
+ }
+
+ #[test]
+ fn unterminated() {
+ assert_eq!(
+ tor_unescape(b"\"test").unwrap_err(),
+ UnescapeError::Unterminated
+ );
+ assert_eq!(
+ tor_unescape(b"\"test\\\"").unwrap_err(),
+ UnescapeError::Unterminated
+ );
+ assert_eq!(
+ tor_unescape(b"\"test\n\"").unwrap_err(),
+ UnescapeError::Unterminated
+ );
+ assert_eq!(
+ tor_unescape(b"\"test\n").unwrap_err(),
+ UnescapeError::Unterminated
+ );
+ assert_eq!(
+ tor_unescape(b"\"test\0\"").unwrap_err(),
+ UnescapeError::Unterminated
+ );
+ assert_eq!(
+ tor_unescape(b"\"test\0").unwrap_err(),
+ UnescapeError::Unterminated
+ );
+
+ // Not having a final quote shortcircuits other errors.
+ assert_eq!(
+ tor_unescape(b"\"test \" test").unwrap_err(),
+ UnescapeError::Unterminated
+ );
+ }
+
+ #[test]
+ fn unescaped_quote() {
+ assert_eq!(
+ tor_unescape(b"\"test \" test\"").unwrap_err(),
+ UnescapeError::UnescapedQuote
+ );
+ }
+
+ #[test]
+ fn invalid_escape() {
+ assert_eq!(
+ tor_unescape(b"\"\\z\"").unwrap_err(),
+ UnescapeError::InvalidEscape,
+ );
+
+ assert_eq!(
+ tor_unescape(b"\"\\xy\"").unwrap_err(),
+ UnescapeError::InvalidEscape,
+ );
+ assert_eq!(
+ tor_unescape(b"\"\\XY\"").unwrap_err(),
+ UnescapeError::InvalidEscape,
+ );
+ assert_eq!(
+ tor_unescape(b"\"\\x1Y\"").unwrap_err(),
+ UnescapeError::InvalidEscape,
+ );
+ assert_eq!(
+ tor_unescape(b"\"\\x1\"").unwrap_err(),
+ UnescapeError::InvalidEscape,
+ );
+ assert_eq!(
+ tor_unescape(b"\"\\x\\xF5z\"").unwrap_err(),
+ UnescapeError::InvalidEscape,
+ );
+ }
+
+ #[test]
+ fn octal_overflow() {
+ assert_eq!(
+ tor_unescape(b"\"\\777\"").unwrap_err(),
+ UnescapeError::OctalOverflow,
+ );
+ }
+}
=====================================
toolkit/components/tor-integration/tor_provider/src/ctor/mod.rs
=====================================
@@ -4,6 +4,7 @@
// copied, modified, or distributed except according to those terms.
mod control_port;
+mod controller;
mod reply_parser;
pub use control_port::{ControlPort, ControlPortError, ControlSocket, ControlSocketError};
View it on GitLab: https://gitlab.torproject.org/tpo/applications/tor-browser/-/compare/4610d7…
--
View it on GitLab: https://gitlab.torproject.org/tpo/applications/tor-browser/-/compare/4610d7…
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
1
0
[Git][tpo/applications/tor-browser-build] Pushed new tag mb-15.0.21-build1
by Pier Angelo Vendrame (@pierov) 01 Sep '26
by Pier Angelo Vendrame (@pierov) 01 Sep '26
01 Sep '26
Pier Angelo Vendrame pushed new tag mb-15.0.21-build1 at The Tor Project / Applications / tor-browser-build
--
View it on GitLab: https://gitlab.torproject.org/tpo/applications/tor-browser-build/-/tree/mb-…
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
1
0
[Git][tpo/applications/tor-browser-build] Pushed new tag tbb-15.0.21-build1
by Pier Angelo Vendrame (@pierov) 01 Sep '26
by Pier Angelo Vendrame (@pierov) 01 Sep '26
01 Sep '26
Pier Angelo Vendrame pushed new tag tbb-15.0.21-build1 at The Tor Project / Applications / tor-browser-build
--
View it on GitLab: https://gitlab.torproject.org/tpo/applications/tor-browser-build/-/tree/tbb…
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
1
0
[Git][tpo/applications/tor-browser-build][maint-15.0] Bug 41852&41853: Prepare Tor, Mullvad Browser 15.0.21
by Pier Angelo Vendrame (@pierov) 01 Sep '26
by Pier Angelo Vendrame (@pierov) 01 Sep '26
01 Sep '26
Pier Angelo Vendrame pushed to branch maint-15.0 at The Tor Project / Applications / tor-browser-build
Commits:
0184a6b9 by Pier Angelo Vendrame at 2026-09-01T13:21:25+02:00
Bug 41852&41853: Prepare Tor, Mullvad Browser 15.0.21
- - - - -
10 changed files:
- projects/browser/Bundle-Data/Docs-MB/ChangeLog.txt
- projects/browser/Bundle-Data/Docs-TBB/ChangeLog.txt
- projects/browser/config
- projects/firefox/config
- projects/geckoview/config
- projects/go/config
- projects/openssl/config
- projects/translation/config
- rbm.conf
- tools/relprep.py
Changes:
=====================================
projects/browser/Bundle-Data/Docs-MB/ChangeLog.txt
=====================================
@@ -1,3 +1,11 @@
+Mullvad Browser 15.0.21 - September 01 2026
+ * All Platforms
+ * Updated Firefox to 140.15.0esr
+ * Updated NoScript to 13.6.32.1984
+ * Updated uBlock Origin to 1.74.0
+ * Bug 572: Websites can initiate a WebSocket connection to a lan/localhost port [mullvad-browser]
+ * Bug 45259: Backport Security Fixes from Firefox 155 [tor-browser]
+
Mullvad Browser 15.0.20 - August 18 2026
* All Platforms
* Updated Firefox to 140.14.0esr
=====================================
projects/browser/Bundle-Data/Docs-TBB/ChangeLog.txt
=====================================
@@ -1,3 +1,20 @@
+Tor Browser 15.0.21 - September 01 2026
+ * All Platforms
+ * Updated NoScript to 13.6.32.1984
+ * Updated OpenSSL to 3.5.8
+ * Bug 45223: Backport tor-browser#45220 - about:torconnect doesn't show when launching the browser in non-PBM [tor-browser]
+ * Bug 45253: Rebase Tor Browser stable onto 140.15.0esr [tor-browser]
+ * Bug 45259: Backport Security Fixes from Firefox 155 [tor-browser]
+ * Windows + macOS + Linux
+ * Updated Firefox to 140.15.0esr
+ * Bug 44996: Change the 32-bit linux message to the expired version for the final 15.0 release [tor-browser]
+ * Bug 45264: Use parent process for TorConnect redirections [tor-browser]
+ * Android
+ * Updated GeckoView to 140.15.0esr
+ * Build System
+ * Windows + Linux + Android
+ * Updated Go to 1.25.14
+
Tor Browser 15.0.20 - August 18 2026
* All Platforms
* Bug 45204: Rebase Tor Browser stable onto 140.14.0esr [tor-browser]
=====================================
projects/browser/config
=====================================
@@ -114,16 +114,15 @@ input_files:
- filename: dmg-root
enable: '[% ! c("var/android") %]'
- name: fenix-nightly-apk
- URL:
- https://ftp.mozilla.org/pub/fenix/nightly/2026/08/2026-08-17-09-28-31-fenix…
+ URL: https://ftp.mozilla.org/pub/fenix/nightly/2026/08/2026-08-31-19-30-04-fenix…
enable: '[% c("var/android") %]'
- sha256sum: 2def423d5714369709d63ce9fd544049afa35ee83db8e37409ea3fcfd0a7c8da
- - URL: https://dist.torproject.org/torbrowser/noscript/noscript-13.6.31.1984.xpi
+ sha256sum: 835deb4771c604805e2e4bcb76d84a4676998d939b4f350381bdab084425e705
+ - URL: https://dist.torproject.org/torbrowser/noscript/noscript-13.6.32.1984.xpi
name: noscript
- sha256sum: 28e1689956b610f328c582ae45b326cf666d909c468a87f01d48a10e256dad1f
- - URL: https://addons.mozilla.org/firefox/downloads/file/4940584/ublock_origin-1.7…
+ sha256sum: 05713eb71e43bee5a78c16c4a1e5c750b755a637c01ef1810e71f518d38fdb25
+ - URL: https://addons.mozilla.org/firefox/downloads/file/4981431/ublock_origin-1.7…
name: ublock-origin
- sha256sum: bccc51a773150af4af6e1fd62c7bfdeb7238b79ff2381b998fa9f2e38f64786a
+ sha256sum: 175756d74468c9ba45863f7fc333d3be670f82d5b066314e915814dd547d1652
enable: '[% c("var/mullvad-browser") %]'
- URL: https://cdn.mullvad.net/browser-extension/0.9.10/mullvad-browser-extension-…
name: mullvad-extension
=====================================
projects/firefox/config
=====================================
@@ -18,7 +18,7 @@ container:
use_container: 1
var:
- firefox_platform_version: '140.14.0'
+ firefox_platform_version: '140.15.0'
firefox_version: '[% c("var/firefox_platform_version") %]esr'
browser_series: '15.0'
browser_rebase: 1
=====================================
projects/geckoview/config
=====================================
@@ -20,7 +20,7 @@ container:
build_apk: 1
var:
- firefox_platform_version: '140.14.0'
+ firefox_platform_version: '140.15.0'
geckoview_version: '[% c("var/firefox_platform_version") %]esr'
browser_series: '15.0'
browser_rebase: 1
=====================================
projects/go/config
=====================================
@@ -1,11 +1,11 @@
# vim: filetype=yaml sw=2
-version: '1.25.13'
+version: '1.25.14'
filename: '[% project %]-[% c("version") %]-[% c("var/osname") %]-[% c("var/build_id") %].tar.[% c("compress_tar") %]'
container:
use_container: 1
var:
- source_sha256: 1d7e2f70b1ee9b93c7df8efcca71f5adcc6a59797a4336c2d10171bd4c174614
+ source_sha256: 9e83f44f5fc297378861b4e16cc6aa114be8add7993fb3ceb2c512380aa4d582
no_crosscompile: 1
setup: |
mkdir -p /var/tmp/dist
=====================================
projects/openssl/config
=====================================
@@ -1,5 +1,5 @@
# vim: filetype=yaml sw=2
-version: 3.5.7
+version: 3.5.8
filename: '[% project %]-[% c("version") %]-[% c("var/osname") %]-[% c("var/build_id") %].tar.[% c("compress_tar") %]'
container:
use_container: 1
@@ -33,5 +33,5 @@ input_files:
- name: '[% c("var/compiler") %]'
project: '[% c("var/compiler") %]'
- URL: 'https://github.com/openssl/openssl/releases/download/openssl-[% c("version") %]/openssl-[% c("version") %].tar.gz'
- sha256sum: a8c0d28a529ca480f9f36cf5792e2cd21984552a3c8e4aa11a24aa31aeac98e8
+ sha256sum: a8f84a39918ec6415ce765d9b429d313ba97b8143169c172e734b9514464f5b2
name: openssl
=====================================
projects/translation/config
=====================================
@@ -12,13 +12,13 @@ compress_tar: 'gz'
steps:
base-browser:
base-browser: '[% INCLUDE build %]'
- git_hash: 2740d708bcedd2fa43489c8d08cdd80b70bd8cf3
+ git_hash: e80343b33ffc78ea11668763ff299aa91ed80ec1
targets:
nightly:
git_hash: 'base-browser'
tor-browser:
tor-browser: '[% INCLUDE build %]'
- git_hash: 1768f6c09cb2c4a012daaf8b33d105a7b48f7bfd
+ git_hash: 2d99f98e524cb823f8125e19e7e51a552a8b6895
targets:
nightly:
git_hash: 'tor-browser'
@@ -32,7 +32,7 @@ steps:
fenix: '[% INCLUDE build %]'
# We need to bump the commit before releasing but just pointing to a branch
# might cause too much rebuidling of the Firefox part.
- git_hash: fadbeeb9f8724d06903be5aade3e0ce26529d178
+ git_hash: c1a923a6df0217c843361bd21412d3fc72d5b73a
compress_tar: 'zst'
targets:
nightly:
=====================================
rbm.conf
=====================================
@@ -74,11 +74,11 @@ buildconf:
git_signtag_opt: '-s'
var:
- torbrowser_version: '15.0.20'
+ torbrowser_version: '15.0.21'
torbrowser_build: 'build1'
# This should be the date of when the build is started. For the build
# to be reproducible, browser_release_date should always be in the past.
- browser_release_date: '2026/08/17 15:00:00'
+ browser_release_date: '2026/09/01 10:41:46'
browser_release_date_timestamp: '[% USE date; date.format(c("var/browser_release_date"), "%s") %]'
browser_default_channel: release
browser_platforms:
@@ -128,10 +128,9 @@ var:
updater_enabled: 1
build_mar: 1
torbrowser_incremental_from:
+ - 15.0.20
- 15.0.19
- 15.0.18
- - '[% IF c("var/tor-browser") %]15.0.17[% END %]'
- - '[% IF c("var/mullvad-browser") %]15.0.16[% END %]'
mar_channel_id: '[% c("var/projectname") %]-torproject-[% c("var/channel") %]'
# By default, we sort the list of installed packages. This allows sharing
=====================================
tools/relprep.py
=====================================
@@ -141,7 +141,8 @@ class ReleasePreparation:
if self.android:
self.update_zstd()
update_fenix_nightly_apk(self.base_path)
- self.update_go()
+ # Our version of go is EOL, stick with it.
+ # self.update_go()
self.update_manual()
self.update_moat_settings()
View it on GitLab: https://gitlab.torproject.org/tpo/applications/tor-browser-build/-/commit/0…
--
View it on GitLab: https://gitlab.torproject.org/tpo/applications/tor-browser-build/-/commit/0…
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
1
0
[Git][tpo/applications/mullvad-browser] Pushed new tag mullvad-browser-140.15.0esr-15.0-1-build2
by ma1 (@ma1) 01 Sep '26
by ma1 (@ma1) 01 Sep '26
01 Sep '26
ma1 pushed new tag mullvad-browser-140.15.0esr-15.0-1-build2 at The Tor Project / Applications / Mullvad Browser
--
View it on GitLab: https://gitlab.torproject.org/tpo/applications/mullvad-browser/-/tree/mullv…
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
1
0
[Git][tpo/applications/mullvad-browser][mullvad-browser-140.15.0esr-15.0-1] 12 commits: Bug 2039206 - Wire WindowDevicePixelRatio to image-set....
by ma1 (@ma1) 01 Sep '26
by ma1 (@ma1) 01 Sep '26
01 Sep '26
ma1 pushed to branch mullvad-browser-140.15.0esr-15.0-1 at The Tor Project / Applications / Mullvad Browser
Commits:
cba67382 by Pier Angelo Vendrame at 2026-09-01T12:53:58+02:00
Bug 2039206 - Wire WindowDevicePixelRatio to image-set. r=firefox-style-system-reviewers,emilio,layout-reviewers
Differential Revision: https://phabricator.services.mozilla.com/D300449
- - - - -
807aa22d by Yury Delendik at 2026-09-01T12:54:02+02:00
Bug 2045435 - Prune dying entries from wasm::Realm::instances_ during sweeping. r=jpages
wasm::Realm::instances_ is a weak list. Readers rely on the
instances()[i]->object() read barrier, but that barrier is a no-op once the
owning zone is being incrementally swept, and entries are otherwise only
removed at Instance finalization (~Instance -> unregisterInstance). So between
marking a zone's instance objects dead and finalizing them, the list could
still hand an about-to-be-finalized instance to a reader.
Prune such entries at the start of zone sweeping via a new
wasm::Realm::traceWeakInstances(), called from beginSweepingSweepGroup
alongside the other per-realm weak-collection sweeps. This makes instances_
behave like the engine's other weak collections, so it never exposes an
about-to-be-finalized instance to the mutator during sweep slices.
Differential Revision: https://phabricator.services.mozilla.com/D314032
- - - - -
9c69b32a by Gela at 2026-09-01T12:54:07+02:00
Bug 2053320 - Part 1: Don't tie Nimbus tooling to `HomeActivity` UI a=RyanVM
Original Revision: https://phabricator.services.mozilla.com/D319628
Differential Revision: https://phabricator.services.mozilla.com/D320784
- - - - -
82b64b84 by Ting-Yu Lin at 2026-09-01T12:54:11+02:00
Bug 2053578 - Use static_cast in nsSplittableFrame::UpdateFirstContinuationAndFirstInFlowCache(). r=layout-reviewers,jfkthame
`nsSplittableFrame` is a subclass of `nsIFrame`, it is sufficient to use
`static_cast`.
Differential Revision: https://phabricator.services.mozilla.com/D315157
- - - - -
d2276817 by Ting-Yu Lin at 2026-09-01T12:54:16+02:00
Bug 2053578 - Update first-in-flow cache when a continuation is changing from fluid to non-fluid. r=layout-reviewers,jfkthame
The original case in bug 2053578 comment 5 can reproduce an ASAN
use-after-poison with the patch bug 2053578 comment 6 applied. However, with
unpatched code, the best we can do is using a DEBUG-only assertion to catch the
error condition that detect a stale first-in-flow cache in next-in-flow.
`bidi-inline-continuation-first-in-flow.html` is generated with the help of
Claude code, and it can trigger the assertion without other fix in this patch.
Differential Revision: https://phabricator.services.mozilla.com/D315158
- - - - -
7f71204b by Jan Grulich at 2026-09-01T12:54:20+02:00
Bug 2054625 - WebRTC backport: Video capture: validate PipeWire camera frame size to prevent OOB read r=pehrsons
Check spa_chunk::size is not larger than mmap'ed buffer, which would
lead to an out-of-bounds read when the frame is copied. Validate that
chunk offset + size does not exceed maxsize before processing.
This is a simple backport of an WebRTC upstream change.
Upstream commit: 612430e8266881eaff478b0000cacc90fb71c918
Differential Revision: https://phabricator.services.mozilla.com/D316000
- - - - -
c829ecff by David Parks at 2026-09-01T12:54:25+02:00
Bug 2058006 - Call Destroy() unconditionally in ~StatusBarEntry. a=RyanVM DONTBUILD
Allow Destroy() to tear down mIconLoader, even if Init() does not successfully
complete initialization.
Original Revision: https://phabricator.services.mozilla.com/D318206
Differential Revision: https://phabricator.services.mozilla.com/D319879
- - - - -
3e16e633 by David Parks at 2026-09-01T12:54:29+02:00
Bug 2058013 - Only resolve non-filesystem picker results as shell libraries a=RyanVM DONTBUILD
A genuine shell library is a virtual item with no filesystem path. A
.library-ms file is an ordinary file whose XML can name any local directory.
filedialog::GetFolderResults was passing whatever IFileDialog::GetResult
returned to IShellLibrary::LoadLibraryFromItem and, on success, replacing it
with the library's embedded default save folder.
Gating the resolution on the item being affirmatively not a filesystem object
keeps real libraries selectable and resolving as before, while a .library-ms
file "resolves" to itself -- the .library-ms file, not its target.
Original Revision: https://phabricator.services.mozilla.com/D318246
Differential Revision: https://phabricator.services.mozilla.com/D319957
- - - - -
7f1c7443 by Iain Ireland at 2026-09-01T12:54:33+02:00
Bug 2058626: Check for mutually exclusive flags when deserializing cloned RegExp r=spidermonkey-reviewers,jonco
Differential Revision: https://phabricator.services.mozilla.com/D316904
- - - - -
3c51167f by Kui-Feng Lee at 2026-09-01T12:54:38+02:00
Bug 2058661 - Clear the drag source window's GdkDragContext pointer synchronously. r=stransky a=RyanVM DONTBUILD
gtk_drag_begin_with_coordinates() returns a borrowed context: GtkDragSourceInfo
holds the only reference, and gtk_drag_source_info_destroy() drops it as soon as
the drag-end handler returns. nsWindow::mSourceDragContext was cleared only from
an async runnable (EndDragSessionMainThread -> EndDragSessionImpl), so between
GTK's unref and that runnable the pointer dangled, and nsWindow::OnUnmap() calls
gtk_drag_cancel() on it. Content can land an unmap in that window with a
window.close() task queued before the drag ended.
EndDragSessionImpl keeps its own clear for drags torn down without a drag-end.
Original Revision: https://phabricator.services.mozilla.com/D315509
Differential Revision: https://phabricator.services.mozilla.com/D319278
- - - - -
bbe2eee1 by Kershaw Chang at 2026-09-01T12:54:42+02:00
Bug 2059199 - Reject NUL bytes in chunked trailer and chunk-size lines a=RyanVM DONTBUILD
A trailer field line whose first byte is NUL would look empty to the
end-of-trailer check, ending the trailer section early. We would then stop
reading the body before the server (or a shared proxy) does, and disagree
with them about where the response ends.
Original Revision: https://phabricator.services.mozilla.com/D317742
Differential Revision: https://phabricator.services.mozilla.com/D319931
- - - - -
7a932edc by Sotaro Ikeda at 2026-09-01T12:54:47+02:00
Bug 2061532 a=RyanVM DONTBUILD
ANGLE Metal backend usage is also going to disable RenderExternalTextureHost usage.
Original Revision: https://phabricator.services.mozilla.com/D318624
Differential Revision: https://phabricator.services.mozilla.com/D320769
- - - - -
24 changed files:
- dom/base/ResponsiveImageSelector.cpp
- gfx/webrender_bindings/RenderExternalTextureHost.cpp
- js/src/gc/GCRuntime.h
- js/src/gc/Sweeping.cpp
- + js/src/jit-test/tests/structured-clone/bug2058626.js
- js/src/vm/StructuredClone.cpp
- js/src/wasm/WasmRealm.cpp
- js/src/wasm/WasmRealm.h
- layout/base/nsPresContext.cpp
- layout/generic/nsSplittableFrame.cpp
- mobile/android/fenix/app/src/main/AndroidManifest.xml
- mobile/android/fenix/app/src/main/java/org/mozilla/fenix/HomeActivity.kt
- + mobile/android/fenix/app/src/main/java/org/mozilla/fenix/experiments/QANimbusToolingReceiver.kt
- + mobile/android/fenix/app/src/test/java/org/mozilla/fenix/experiments/QANimbusToolingReceiverTest.kt
- modules/libpref/init/StaticPrefList.yaml
- netwerk/protocol/http/nsHttpChunkedDecoder.cpp
- + testing/web-platform/tests/css/css-writing-modes/crashtests/bidi-inline-continuation-first-in-flow.html
- third_party/libwebrtc/modules/video_capture/linux/video_capture_pipewire.cc
- + third_party/libwebrtc/moz-patch-stack/612430e826.no-op-cherry-pick-msg
- toolkit/components/resistfingerprinting/tests/browser/browser_srcset.js
- toolkit/components/resistfingerprinting/tests/browser/srcset.html
- widget/gtk/nsDragService.cpp
- widget/windows/SystemStatusBar.cpp
- widget/windows/filedialog/WinFileDialogCommands.cpp
Changes:
=====================================
dom/base/ResponsiveImageSelector.cpp
=====================================
@@ -351,10 +351,6 @@ bool ResponsiveImageSelector::SelectImage(bool aReselect) {
if (overrideDPPX > 0) {
displayDensity = overrideDPPX;
}
- if (doc->ShouldResistFingerprinting(RFPTarget::WindowDevicePixelRatio)) {
- displayDensity =
- nsRFPService::GetDevicePixelRatioAtZoom(pctx->GetFullZoom());
- }
// Per spec, "In a UA-specific manner, choose one image source"
// - For now, select the lowest density greater than displayDensity, otherwise
=====================================
gfx/webrender_bindings/RenderExternalTextureHost.cpp
=====================================
@@ -126,7 +126,14 @@ bool RenderExternalTextureHost::IsReadyForDeletion() {
auto& textureSource = mTextureSources[0];
if (textureSource) {
- return textureSource->Sync(false);
+ if (textureSource->Sync(/* aBlocking */ true)) {
+ return true;
+ }
+ if (mGL && mGL->MakeCurrent() && !mGL->IsDestroyed()) {
+ mGL->fFinish();
+ return true;
+ }
+ return false;
}
return true;
@@ -153,7 +160,11 @@ wr::WrExternalImage RenderExternalTextureHost::Lock(uint8_t aChannelIndex,
void RenderExternalTextureHost::PrepareForUse() { mTextureUpdateNeeded = true; }
-void RenderExternalTextureHost::Unlock() {}
+void RenderExternalTextureHost::Unlock() {
+ if (mInitialized && mTextureSources[0]) {
+ mTextureSources[0]->MaybeFenceTexture();
+ }
+}
void RenderExternalTextureHost::UpdateTexture(size_t aIndex) {
MOZ_ASSERT(mSurfaces[aIndex]);
=====================================
js/src/gc/GCRuntime.h
=====================================
@@ -924,6 +924,7 @@ class GCRuntime {
void updateAtomsBitmap();
void sweepCCWrappers();
void sweepRealmGlobals();
+ void sweepWasmInstances();
void sweepEmbeddingWeakPointers(JS::GCContext* gcx);
void sweepMisc();
void sweepCompressionTasks();
=====================================
js/src/gc/Sweeping.cpp
=====================================
@@ -1396,6 +1396,13 @@ void GCRuntime::sweepRealmGlobals() {
}
}
+void GCRuntime::sweepWasmInstances() {
+ for (SweepGroupRealmsIter r(this); !r.done(); r.next()) {
+ AutoSetThreadIsSweeping threadIsSweeping(r->zone());
+ r->wasm.traceWeakInstances();
+ }
+}
+
void GCRuntime::sweepMisc() {
SweepingTracer trc(rt);
for (SweepGroupRealmsIter r(this); !r.done(); r.next()) {
@@ -1709,6 +1716,11 @@ IncrementalProgress GCRuntime::beginSweepingSweepGroup(JS::GCContext* gcx,
// This must happen before updating embedding weak pointers.
sweepRealmGlobals();
+ // Prune dying wasm instances from each realm's weak instance list now, at the
+ // start of sweeping, before the mutator can observe them via the (now no-op)
+ // instances() read barrier during later incremental slices.
+ sweepWasmInstances();
+
sweepEmbeddingWeakPointers(gcx);
{
=====================================
js/src/jit-test/tests/structured-clone/bug2058626.js
=====================================
@@ -0,0 +1,16 @@
+function forge(pattern, srcFlags, flagsByte) {
+ var cb = serialize(new RegExp(pattern, srcFlags), undefined, { scope: "DifferentProcess" });
+ var u8 = new Uint8Array(cb.arraybuffer);
+ for (var i = 0; i + 8 <= u8.length; i += 4) {
+ var tag = u8[i+4] | (u8[i+5] << 8) | (u8[i+6] << 16) | (u8[i+7] << 24);
+ if ((tag >>> 0) === 0xFFFF0006) { u8[i] = flagsByte; break; }
+ }
+ cb.clonebuffer = u8.buffer;
+ return deserialize(cb, { scope: "DifferentProcess" });
+}
+try {
+ var forged = forge("[\\q{abc|de}]", "v", 0x90);
+ var bad = new RegExp(forged, "u");
+ try { bad.exec("abc"); } catch {}
+ var good = new RegExp("[\\q{abc|de}]", "u");
+} catch {}
=====================================
js/src/vm/StructuredClone.cpp
=====================================
@@ -3189,7 +3189,9 @@ bool JSStructuredCloneReader::startRead(MutableHandleValue vp,
}
case SCTAG_REGEXP_OBJECT: {
- if ((data & RegExpFlag::AllFlags) != data) {
+ // Reject invalid flags. /u and /v are mutually exclusive.
+ if ((data & RegExpFlag::AllFlags) != data ||
+ ((data & RegExpFlag::Unicode) && (data & RegExpFlag::UnicodeSets))) {
JS_ReportErrorNumberASCII(context(), GetErrorMessage, nullptr,
JSMSG_SC_BAD_SERIALIZED_DATA, "regexp");
return false;
=====================================
js/src/wasm/WasmRealm.cpp
=====================================
@@ -18,6 +18,7 @@
#include "wasm/WasmRealm.h"
+#include "gc/Marking.h"
#include "vm/GlobalObject.h"
#include "vm/Realm.h"
#include "wasm/WasmDebug.h"
@@ -111,6 +112,19 @@ void wasm::Realm::unregisterInstance(Instance& instance) {
}
}
+void wasm::Realm::traceWeakInstances() {
+ // Registration/unregistration of instances_ is tied to Instance lifetime, so
+ // an instance whose owning object is about to be finalized is still present
+ // here until ~Instance runs. Remove such entries now, at the start of zone
+ // sweeping, because the instances() read barrier that otherwise protects
+ // readers is a no-op once the zone is being swept. erase order is preserved,
+ // so the pointer-sorted invariant used by BinarySearchIf holds.
+ instances_.eraseIf([](Instance* instance) {
+ return js::gc::IsAboutToBeFinalizedUnbarriered(
+ instance->objectUnbarriered());
+ });
+}
+
void wasm::Realm::ensureProfilingLabels(bool profilingEnabled) {
for (Instance* instance : instances_) {
instance->ensureProfilingLabels(profilingEnabled);
=====================================
js/src/wasm/WasmRealm.h
=====================================
@@ -53,10 +53,17 @@ class Realm {
// Return a vector of all live instances in the realm. The lifetime of
// these Instances is determined by their owning WasmInstanceObject.
// Note that accessing instances()[i]->object() triggers a read barrier
- // since instances() is effectively a weak list.
+ // since instances() is effectively a weak list. This read barrier is only
+ // effective while the owning zone is being marked; traceWeakInstances()
+ // prunes dying entries at the start of sweeping so that the list never
+ // exposes an about-to-be-finalized instance to the mutator.
const InstanceVector& instances() const { return instances_; }
+ // Remove instances whose owning object is about to be finalized. Called at
+ // the start of zone sweeping, when the instances() read barrier is a no-op.
+ void traceWeakInstances();
+
// Ensure all Instances in this Realm have profiling labels created.
void ensureProfilingLabels(bool profilingEnabled);
=====================================
layout/base/nsPresContext.cpp
=====================================
@@ -978,7 +978,11 @@ void nsPresContext::RecomputeBrowsingContextDependentData() {
auto systemZoom = LookAndFeel::SystemZoomSettings();
SetFullZoom(browsingContext->FullZoom() * systemZoom.mFullZoom);
SetTextZoom(browsingContext->TextZoom() * systemZoom.mTextZoom);
- SetOverrideDPPX(browsingContext->OverrideDPPX());
+ if (doc->ShouldResistFingerprinting(RFPTarget::WindowDevicePixelRatio)) {
+ SetOverrideDPPX(nsRFPService::GetDevicePixelRatioAtZoom(GetFullZoom()));
+ } else {
+ SetOverrideDPPX(browsingContext->OverrideDPPX());
+ }
}
auto* top = browsingContext->Top();
=====================================
layout/generic/nsSplittableFrame.cpp
=====================================
@@ -214,7 +214,7 @@ void nsSplittableFrame::UpdateFirstContinuationAndFirstInFlowCache() {
if (oldCachedFirstContinuation != newFirstContinuation) {
// Update the first-continuation cache for us and our next-continuations.
for (nsSplittableFrame* f = this; f;
- f = reinterpret_cast<nsSplittableFrame*>(f->GetNextContinuation())) {
+ f = static_cast<nsSplittableFrame*>(f->GetNextContinuation())) {
f->mFirstContinuation = newFirstContinuation;
}
}
@@ -227,7 +227,7 @@ void nsSplittableFrame::UpdateFirstContinuationAndFirstInFlowCache() {
// behavior when a frame list is destroyed from the front. To avoid that
// pathological behavior, we simply purge the cached values.
for (nsSplittableFrame* f = this; f;
- f = reinterpret_cast<nsSplittableFrame*>(f->GetNextContinuation())) {
+ f = static_cast<nsSplittableFrame*>(f->GetNextContinuation())) {
f->mFirstContinuation = nullptr;
}
}
@@ -239,22 +239,41 @@ void nsSplittableFrame::UpdateFirstContinuationAndFirstInFlowCache() {
if (oldCachedFirstInFlow != newFirstInFlow) {
// Update the first-in-flow cache for us and our next-in-flows.
for (nsSplittableFrame* f = this; f;
- f = reinterpret_cast<nsSplittableFrame*>(f->GetNextInFlow())) {
+ f = static_cast<nsSplittableFrame*>(f->GetNextInFlow())) {
f->mFirstInFlow = newFirstInFlow;
}
}
} else {
- // We become the new first-in-flow due to our prev-in-flow being removed.
- if (oldCachedFirstInFlow) {
- // It's tempting to update the first-in-flow cache for our
- // next-in-flows here, but that would result in overall O(n^2)
- // behavior when a frame list is destroyed from the front. To avoid that
- // pathological behavior, we simply purge the cached values.
+ if (GetPrevContinuation()) {
+ // We become the new first-in-flow after changing from fluid to non-fluid.
+ // Update the stale first-in-flow cache for us and all next-in-flows.
+ //
+ // Note that this has no counterpart in the above mFirstContinuation cache
+ // since GetPrevContinuation() does not depend on the
+ // NS_FRAME_IS_FLUID_CONTINUATION bit.
for (nsSplittableFrame* f = this; f;
- f = reinterpret_cast<nsSplittableFrame*>(f->GetNextInFlow())) {
- f->mFirstInFlow = nullptr;
+ f = static_cast<nsSplittableFrame*>(f->GetNextInFlow())) {
+ f->mFirstInFlow = this;
+ }
+ } else {
+ // We become the new first-in-flow due to our prev-in-flow being removed.
+ if (oldCachedFirstInFlow) {
+ // It's tempting to update the first-in-flow cache for our
+ // next-in-flows here, but that would result in overall O(n^2)
+ // behavior when a frame list is destroyed from the front. To avoid that
+ // pathological behavior, we simply purge the cached values.
+ for (nsSplittableFrame* f = this; f;
+ f = static_cast<nsSplittableFrame*>(f->GetNextInFlow())) {
+ f->mFirstInFlow = nullptr;
+ }
}
}
+
+ DebugOnly<nsSplittableFrame*> nextInFlow =
+ static_cast<nsSplittableFrame*>(GetNextInFlow());
+ MOZ_ASSERT(!nextInFlow || !nextInFlow->mFirstInFlow ||
+ nextInFlow->mFirstInFlow == this,
+ "Our next-in-flow caches a stale first-in-flow!");
}
}
=====================================
mobile/android/fenix/app/src/main/AndroidManifest.xml
=====================================
@@ -773,6 +773,16 @@
android:value="androidx.startup"
tools:node="remove" />
</provider>
+
+ <receiver
+ android:name="org.mozilla.fenix.experiments.QANimbusToolingReceiver"
+ android:exported="true"
+ android:enabled="true"
+ android:permission="android.permission.DUMP">
+ <intent-filter>
+ <action android:name="org.mozilla.fenix.NIMBUS_TOOLING" />
+ </intent-filter>
+ </receiver>
</application>
</manifest>
=====================================
mobile/android/fenix/app/src/main/java/org/mozilla/fenix/HomeActivity.kt
=====================================
@@ -82,7 +82,6 @@ import mozilla.components.support.utils.SafeIntent
import mozilla.components.support.utils.toSafeIntent
import mozilla.components.support.webextensions.WebExtensionPopupObserver
import mozilla.telemetry.glean.private.NoExtras
-import org.mozilla.experiments.nimbus.initializeTooling
import org.mozilla.fenix.GleanMetrics.AppIcon
import org.mozilla.fenix.GleanMetrics.Events
import org.mozilla.fenix.GleanMetrics.Metrics
@@ -310,8 +309,6 @@ open class HomeActivity : LocaleAwareAppCompatActivity(), NavHostActivity {
// DO NOT MOVE ANYTHING ABOVE THIS getProfilerTime CALL.
val startTimeProfiler = components.core.engine.profiler?.getProfilerTime()
- // Setup nimbus-cli tooling. This is a NOOP when launching normally.
- components.nimbus.sdk.initializeTooling(applicationContext, intent)
components.strictMode.attachListenerToDisablePenaltyDeath(supportFragmentManager)
MarkersFragmentLifecycleCallbacks.register(supportFragmentManager, components.core.engine)
=====================================
mobile/android/fenix/app/src/main/java/org/mozilla/fenix/experiments/QANimbusToolingReceiver.kt
=====================================
@@ -0,0 +1,57 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+package org.mozilla.fenix.experiments
+
+import android.content.BroadcastReceiver
+import android.content.Context
+import android.content.Intent
+import kotlinx.coroutines.CoroutineDispatcher
+import kotlinx.coroutines.CoroutineScope
+import kotlinx.coroutines.Dispatchers
+import kotlinx.coroutines.launch
+import mozilla.components.support.base.log.logger.Logger
+import org.mozilla.experiments.nimbus.initializeTooling
+import org.mozilla.fenix.ext.components
+
+private val logger = Logger("QANimbusToolingReceiver")
+
+/**
+ * Receiver triggered on demand via `nimbus-cli` to manually enroll into Nimbus experiments.
+ *
+ * ```
+ * adb shell am broadcast -a org.mozilla.fenix.NIMBUS_TOOLING \
+ * -p org.mozilla.fenix
+ * ```
+ *
+ * `-p org.mozilla.fenix` is the package name, so adjust that value for release/beta/nightly/debug.
+ *
+ * @param dispatcher the [CoroutineDispatcher] the tooling commands are applied on.
+ */
+class QANimbusToolingReceiver(private val dispatcher: CoroutineDispatcher = Dispatchers.IO) : BroadcastReceiver() {
+ override fun onReceive(context: Context, intent: Intent) {
+ if (intent.action != ACTION_NIMBUS_TOOLING) return
+
+ logger.info("Enqueueing QANimbusToolingReceiver via debug trigger")
+
+ val applicationContext = context.applicationContext
+
+ val pendingResult: PendingResult? = goAsync()
+ CoroutineScope(dispatcher).launch {
+ try {
+ applicationContext.components.nimbus.sdk.initializeTooling(
+ applicationContext,
+ intent,
+ )
+ } finally {
+ logger.info("Nimbus tooling command processed")
+ pendingResult?.finish()
+ }
+ }
+ }
+
+ companion object {
+ const val ACTION_NIMBUS_TOOLING = "org.mozilla.fenix.NIMBUS_TOOLING"
+ }
+}
=====================================
mobile/android/fenix/app/src/test/java/org/mozilla/fenix/experiments/QANimbusToolingReceiverTest.kt
=====================================
@@ -0,0 +1,117 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+package org.mozilla.fenix.experiments
+
+import android.content.Context
+import android.content.Intent
+import io.mockk.every
+import kotlinx.coroutines.ExperimentalCoroutinesApi
+import kotlinx.coroutines.Job
+import kotlinx.coroutines.test.UnconfinedTestDispatcher
+import mozilla.components.support.test.robolectric.testContext
+import org.junit.Assert.assertEquals
+import org.junit.Assert.assertFalse
+import org.junit.Assert.assertNull
+import org.junit.Assert.assertTrue
+import org.junit.Before
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.mozilla.fenix.experiments.QANimbusToolingReceiver.Companion.ACTION_NIMBUS_TOOLING
+import org.mozilla.fenix.ext.components
+import org.mozilla.fenix.nimbus.TestNimbusApi
+import org.robolectric.RobolectricTestRunner
+
+@OptIn(ExperimentalCoroutinesApi::class)
+@RunWith(RobolectricTestRunner::class)
+class QANimbusToolingReceiverTest {
+
+ private val nimbusApi = FakeNimbusApi(testContext)
+ private val receiver = QANimbusToolingReceiver(UnconfinedTestDispatcher())
+
+ @Before
+ fun setUp() {
+ every { testContext.components.nimbus.sdk } returns nimbusApi
+ }
+
+ @Test
+ fun `GIVEN a tooling command WHEN the tooling action is received THEN the command is applied`() {
+ receiver.onReceive(testContext, toolingIntent(ACTION_NIMBUS_TOOLING))
+
+ assertEquals(EXPERIMENTS, nimbusApi.appliedExperiments)
+ assertEquals(false, nimbusApi.fetchEnabled)
+ assertTrue(nimbusApi.databaseReset)
+ assertTrue(nimbusApi.stateDumped)
+ }
+
+ @Test
+ fun `GIVEN a tooling command WHEN another action is received THEN the command is ignored`() {
+ receiver.onReceive(testContext, toolingIntent("org.mozilla.fenix.ACTION_PRINT"))
+
+ assertNull(nimbusApi.appliedExperiments)
+ assertNull(nimbusApi.fetchEnabled)
+ assertFalse(nimbusApi.databaseReset)
+ assertFalse(nimbusApi.stateDumped)
+ }
+
+ @Test
+ fun `GIVEN no tooling command WHEN the tooling action is received THEN nothing is applied`() {
+ receiver.onReceive(testContext, Intent(ACTION_NIMBUS_TOOLING))
+
+ assertNull(nimbusApi.appliedExperiments)
+ assertNull(nimbusApi.fetchEnabled)
+ assertFalse(nimbusApi.databaseReset)
+ assertFalse(nimbusApi.stateDumped)
+ }
+
+ @Test
+ fun `GIVEN a tooling command without the version extra WHEN the tooling action is received THEN nothing is applied`() {
+ val intent = toolingIntent(ACTION_NIMBUS_TOOLING).apply { removeExtra("version") }
+
+ receiver.onReceive(testContext, intent)
+
+ assertNull(nimbusApi.appliedExperiments)
+ assertFalse(nimbusApi.stateDumped)
+ }
+
+ private fun toolingIntent(action: String) =
+ Intent(action).apply {
+ putExtra("nimbus-cli", null as String?)
+ putExtra("version", 1)
+ putExtra("experiments", EXPERIMENTS)
+ putExtra("reset-db", true)
+ putExtra("log-state", true)
+ }
+
+ private class FakeNimbusApi(context: Context) : TestNimbusApi(context) {
+ var appliedExperiments: String? = null
+ var fetchEnabled: Boolean? = null
+ var databaseReset = false
+ var stateDumped = false
+
+ override fun applyLocalExperiments(experimentsJson: String): Job {
+ appliedExperiments = experimentsJson
+ return completedJob()
+ }
+
+ override fun resetEnrollmentsDatabase(): Job {
+ databaseReset = true
+ return completedJob()
+ }
+
+ override fun setFetchEnabled(enabled: Boolean) {
+ fetchEnabled = enabled
+ }
+
+ override fun dumpStateToLog() {
+ stateDumped = true
+ }
+
+ private fun completedJob() = Job().apply { complete() }
+ }
+
+ companion object {
+ private const val EXPERIMENTS = """{"data":[]}"""
+ }
+}
=====================================
modules/libpref/init/StaticPrefList.yaml
=====================================
@@ -7826,7 +7826,7 @@
#ifdef XP_MACOSX
- name: gfx.webrender.enable-client-storage
type: bool
- value: true
+ value: false
mirror: once
#endif
=====================================
netwerk/protocol/http/nsHttpChunkedDecoder.cpp
=====================================
@@ -106,8 +106,15 @@ nsresult nsHttpChunkedDecoder::ParseChunkRemaining(char* buf, uint32_t count,
count = mLineBuf.Length();
}
+ // NUL is illegal in a chunk-size or trailer field line (RFC 9110 section
+ // 5.5).
+ if (memchr(buf, '\0', count)) {
+ LOG(("chunked line contains embedded NUL; rejecting\n"));
+ return NS_ERROR_UNEXPECTED;
+ }
+
if (mWaitEOF) {
- if (*buf) {
+ if (count) {
LOG(("got trailer: %s\n", buf));
// allocate a header array for the trailers on demand
if (!mTrailers) {
@@ -130,7 +137,7 @@ nsresult nsHttpChunkedDecoder::ParseChunkRemaining(char* buf, uint32_t count,
mReachedEOF = true;
LOG(("reached end of chunked-body\n"));
}
- } else if (*buf) {
+ } else if (count) {
char* endptr;
unsigned long parsedval; // could be 64 bit, could be 32
=====================================
testing/web-platform/tests/css/css-writing-modes/crashtests/bidi-inline-continuation-first-in-flow.html
=====================================
@@ -0,0 +1,194 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<link rel="author" title="Ting-Yu Lin" href="mailto:tlin@mozilla.com">
+<link rel="help" href="https://bugzilla.mozilla.org/show_bug.cgi?id=2053578">
+
+<!-- The operations in <script> are generated from one of the runs from
+ the original testcase (Bug 2053578 Comment 5) that triggers
+ the assertion. -->
+
+<body></body>
+
+<script>
+var n1 = document.createElement("div");
+n1.style.width = "50px";
+var n2 = document.createElement("bdo");
+n2.setAttribute("dir", "rtl");
+var n3 = document.createTextNode("كلمة");
+n2.appendChild(n3);
+n1.appendChild(n2);
+var n4 = document.createElement("em");
+var n5 = document.createElement("span");
+var n6 = document.createElement("bdo");
+var n7 = document.createTextNode("word m mix");
+n6.appendChild(n7);
+n5.appendChild(n6);
+var n8 = document.createTextNode("מלל");
+n5.appendChild(n8);
+var n9 = document.createElement("b");
+n9.setAttribute("dir", "ltr");
+n9.style.unicodeBidi = "bidi-override";
+var n10 = document.createTextNode("alpha");
+n9.appendChild(n10);
+var n11 = document.createTextNode("نص مرحبا كلمة");
+n9.appendChild(n11);
+var n12 = document.createTextNode("مرحبا نص نص");
+n9.appendChild(n12);
+n5.appendChild(n9);
+n4.appendChild(n5);
+var n13 = document.createElement("span");
+n13.style.unicodeBidi = "bidi-override";
+var n14 = document.createElement("bdi");
+var n15 = document.createTextNode("اختبار");
+n14.appendChild(n15);
+n13.appendChild(n14);
+var n16 = document.createElement("em");
+var n17 = document.createTextNode("בדיקה מלל m نص");
+n16.appendChild(n17);
+var n18 = document.createTextNode("كلمة");
+n16.appendChild(n18);
+var n19 = document.createTextNode("نص مرحبا كلمة");
+n16.appendChild(n19);
+n13.appendChild(n16);
+var n20 = document.createElement("span");
+var n21 = document.createTextNode("שלום מלל בדיקה שלום");
+n20.appendChild(n21);
+n13.appendChild(n20);
+n4.appendChild(n13);
+n1.appendChild(n4);
+document.body.appendChild(n1);
+var n22 = document.createElement("bdo");
+n22.style.unicodeBidi = "plaintext";
+var n23 = document.createElement("span");
+var n24 = document.createTextNode("עברית שלום مرحبا alpha");
+n23.appendChild(n24);
+n22.appendChild(n23);
+n14.style.direction = "ltr";
+var n25 = document.createTextNode("كلمة שלום");
+n2.insertBefore(n25, n3);
+n21.remove();
+n9.setAttribute("dir", "ltr");
+var n26 = document.createTextNode("בדיקה mm");
+n4.appendChild(n26);
+n12.data = "שלום עברית שלום עברית";
+n7.remove();
+n2.style.direction = "ltr";
+n1.style.width = "102px";
+var n27 = document.createTextNode("עברית mm m");
+n22.appendChild(n27);
+n22.setAttribute("dir", "rtl");
+n4.style.direction = "";
+n14.style.direction = "rtl";
+n22.insertBefore(n9, n23);
+var n28 = document.createElement("br");
+n9.insertBefore(n28, n12);
+n14.style.unicodeBidi = "embed";
+n5.insertBefore(n22, n6);
+n9.style.direction = "";
+n9.style.unicodeBidi = "bidi-override";
+n22.remove();
+var n29 = document.createTextNode("עברית שלום");
+n5.insertBefore(n29, n6);
+n18.data = "mix";
+n19.data = "alpha m word";
+n20.style.direction = "ltr";
+var n30 = document.createTextNode("mm עברית עברית نص");
+n6.appendChild(n30);
+n6.style.unicodeBidi = "isolate";
+n20.style.unicodeBidi = "embed";
+n16.setAttribute("dir", "rtl");
+n2.insertBefore(n20, n25);
+var n31 = document.createTextNode("שלום مرحبا mix mm");
+n2.insertBefore(n31, n20);
+var n32 = document.createTextNode("m בדיקה word mix");
+n20.appendChild(n32);
+n16.removeAttribute("dir");
+n16.setAttribute("dir", "auto");
+n5.remove();
+n17.remove();
+n31.data = "בדיקה mm";
+n14.style.unicodeBidi = "isolate";
+n4.remove();
+n20.style.direction = "";
+n20.style.direction = "rtl";
+n3.data = "בדיקה שלום word";
+n25.data = "mm";
+n25.data = "m mix word";
+n1.style.direction = "rtl";
+n31.data = "كلمة اختبار";
+n20.remove();
+n3.data = "שלום نص";
+n31.remove();
+n2.setAttribute("dir", "ltr");
+n2.removeAttribute("dir");
+n1.insertBefore(n4, n2);
+n19.data = "מלל word";
+n15.remove();
+n13.setAttribute("dir", "ltr");
+n25.remove();
+n2.setAttribute("dir", "ltr");
+n14.appendChild(n2);
+n3.data = "مرحبا اختبار مرحبا";
+n18.data = "שלום";
+n16.insertBefore(n20, n18);
+var n33 = document.createTextNode("كلمة نص mm");
+n2.appendChild(n33);
+n19.data = "اختبار مرحبا كلمة كلمة";
+n18.data = "mm mix mix";
+var n34 = document.createElement("br");
+n4.insertBefore(n34, n13);
+n13.style.unicodeBidi = "isolate-override";
+n13.remove();
+n34.remove();
+n26.data = "كلمة كلمة مرحبا";
+n4.setAttribute("dir", "rtl");
+n26.data = "mm word m mm";
+n26.data = "اختبار نص";
+n4.setAttribute("dir", "auto");
+var n35 = document.createTextNode("مرحبا מלל كلمة שלום");
+n4.insertBefore(n35, n26);
+n4.style.unicodeBidi = "embed";
+n4.style.direction = "";
+var n36 = document.createTextNode("كلمة mix בדיקה نص");
+n4.insertBefore(n36, n35);
+n36.remove();
+n4.removeAttribute("dir");
+n4.style.unicodeBidi = "";
+n1.style.width = "87px";
+n26.data = "שלום מלל מלל עברית";
+var n37 = document.createElement("br");
+n4.insertBefore(n37, n35);
+n35.data = "مرحبا نص اختبار";
+n4.setAttribute("dir", "rtl");
+n33.data = "word alpha mm";
+var n38 = document.createTextNode("שלום word");
+n16.insertBefore(n38, n20);
+n2.setAttribute("dir", "auto");
+n2.remove();
+n37.remove();
+var n39 = document.createTextNode("עברית اختبار בדיקה");
+n13.appendChild(n39);
+var n40 = document.createTextNode("בדיקה m نص نص");
+n13.insertBefore(n40, n14);
+var n41 = document.createElement("br");
+n20.appendChild(n41);
+n35.data = "alpha";
+n16.remove();
+n39.data = "نص mm בדיקה اختبار";
+n40.data = "mix mix word m";
+n35.data = "שלום בדיקה";
+n35.data = "m mix mm m";
+n40.data = "مرحبا اختبار";
+n13.style.unicodeBidi = "embed";
+n4.insertBefore(n16, n26);
+var n42 = document.createTextNode("نص");
+n13.insertBefore(n42, n40);
+n41.remove();
+n20.insertBefore(n2, n32);
+document.body.offsetHeight;
+n39.data = "مرحبا alpha עברית word";
+n18.data = "שלום";
+n2.setAttribute("dir", "auto");
+document.body.offsetHeight;
+n20.style.direction = "ltr";
+</script>
=====================================
third_party/libwebrtc/modules/video_capture/linux/video_capture_pipewire.cc
=====================================
@@ -443,6 +443,14 @@ void VideoCaptureModulePipeWire::ProcessBuffers() {
continue;
}
+ if (static_cast<uint64_t>(spaBuffer->datas[0].chunk->offset) +
+ spaBuffer->datas[0].chunk->size >
+ spaBuffer->datas[0].maxsize) {
+ RTC_LOG(LS_ERROR) << "Dropping frame with invalid size";
+ pw_stream_queue_buffer(stream_, buffer);
+ continue;
+ }
+
if (spaBuffer->datas[0].type == SPA_DATA_DmaBuf ||
spaBuffer->datas[0].type == SPA_DATA_MemFd) {
ScopedBuf frame;
=====================================
third_party/libwebrtc/moz-patch-stack/612430e826.no-op-cherry-pick-msg
=====================================
@@ -0,0 +1 @@
+We cherry-picked this in bug 2054625.
\ No newline at end of file
=====================================
toolkit/components/resistfingerprinting/tests/browser/browser_srcset.js
=====================================
@@ -4,8 +4,20 @@
"use strict";
+async function precacheAssets(tab, zoom) {
+ await SpecialPowers.spawn(tab.linkedBrowser, [zoom], async zoom => {
+ const { Layout } = ChromeUtils.importESModule(
+ "chrome://mochitests/content/browser/accessible/tests/browser/Layout.sys.mjs"
+ );
+ Layout.zoomDocument(content.document, zoom);
+ const { promise, resolve } = Promise.withResolvers();
+ content.requestAnimationFrame(resolve);
+ await promise;
+ });
+}
+
async function runForZoomLevel(tab, zoom) {
- const [dpr, scale] = await SpecialPowers.spawn(
+ const [dpr, scale, cssScale] = await SpecialPowers.spawn(
tab.linkedBrowser,
[zoom],
async zoom => {
@@ -28,9 +40,16 @@ async function runForZoomLevel(tab, zoom) {
// Workaround: content.devicePixelRatio has the unspoofed value.
const dpr = content.wrappedJSObject.devicePixelRatio;
const current = img.currentSrc;
- const scale =
- current.substring(current.length - 6, current.length - 4) / 10;
- return [dpr, scale];
+ const scale = parseFloat(current.match(/.*\/([0-9\.]+)\.png$/)[1]);
+
+ const height = content.document
+ .getElementById("imgset")
+ .getBoundingClientRect().height;
+ // The images have been crafted so that it is possible to reverse their
+ // measured size (altered by the zoom) and find the scale.
+ const cssScale = Math.round((height * dpr - 50) / 10) / 10 + 0.3;
+
+ return [dpr, scale, cssScale];
}
);
@@ -39,6 +58,11 @@ async function runForZoomLevel(tab, zoom) {
true,
`Image scale (${scale}) is within DPR (${dpr})`
);
+ is(
+ Math.abs(dpr - cssScale) < 0.1,
+ true,
+ `CSS scale (${cssScale}) is within DPR (${dpr})`
+ );
}
add_task(async () => {
@@ -53,6 +77,9 @@ add_task(async () => {
) + "srcset.html";
const tab = await BrowserTestUtils.openNewForegroundTab(gBrowser, testPage);
+ for (let zoom = 0.3; zoom < 2.09; zoom += 0.1) {
+ await precacheAssets(tab, zoom);
+ }
for (let zoom = 0.3; zoom < 2.09; zoom += 0.1) {
await runForZoomLevel(tab, zoom);
}
=====================================
toolkit/components/resistfingerprinting/tests/browser/srcset.html
=====================================
@@ -1,12 +1,57 @@
<!DOCTYPE html>
<html>
+ <head>
+ <style>
+#imgset {
+ content: image-set(
+ url('data:image/svg+xml;utf8,<svg xmlns="http://www.w3.org/2000/svg" width="50" height="50"></svg>') 0.3x,
+ url('data:image/svg+xml;utf8,<svg xmlns="http://www.w3.org/2000/svg" width="60" height="60"></svg>') 0.4x,
+ url('data:image/svg+xml;utf8,<svg xmlns="http://www.w3.org/2000/svg" width="70" height="70"></svg>') 0.5x,
+ url('data:image/svg+xml;utf8,<svg xmlns="http://www.w3.org/2000/svg" width="80" height="80"></svg>') 0.6x,
+ url('data:image/svg+xml;utf8,<svg xmlns="http://www.w3.org/2000/svg" width="90" height="90"></svg>') 0.7x,
+ url('data:image/svg+xml;utf8,<svg xmlns="http://www.w3.org/2000/svg" width="100" height="100"></svg>') 0.8x,
+ url('data:image/svg+xml;utf8,<svg xmlns="http://www.w3.org/2000/svg" width="110" height="110"></svg>') 0.9x,
+ url('data:image/svg+xml;utf8,<svg xmlns="http://www.w3.org/2000/svg" width="120" height="120"></svg>') 1.0x,
+ url('data:image/svg+xml;utf8,<svg xmlns="http://www.w3.org/2000/svg" width="130" height="130"></svg>') 1.1x,
+ url('data:image/svg+xml;utf8,<svg xmlns="http://www.w3.org/2000/svg" width="140" height="140"></svg>') 1.2x,
+ url('data:image/svg+xml;utf8,<svg xmlns="http://www.w3.org/2000/svg" width="150" height="150"></svg>') 1.3x,
+ url('data:image/svg+xml;utf8,<svg xmlns="http://www.w3.org/2000/svg" width="160" height="160"></svg>') 1.4x,
+ url('data:image/svg+xml;utf8,<svg xmlns="http://www.w3.org/2000/svg" width="170" height="170"></svg>') 1.5x,
+ url('data:image/svg+xml;utf8,<svg xmlns="http://www.w3.org/2000/svg" width="180" height="180"></svg>') 1.6x,
+ url('data:image/svg+xml;utf8,<svg xmlns="http://www.w3.org/2000/svg" width="190" height="190"></svg>') 1.7x,
+ url('data:image/svg+xml;utf8,<svg xmlns="http://www.w3.org/2000/svg" width="200" height="200"></svg>') 1.8x,
+ url('data:image/svg+xml;utf8,<svg xmlns="http://www.w3.org/2000/svg" width="210" height="210"></svg>') 1.9x,
+ url('data:image/svg+xml;utf8,<svg xmlns="http://www.w3.org/2000/svg" width="220" height="220"></svg>') 2.0x,
+ url('data:image/svg+xml;utf8,<svg xmlns="http://www.w3.org/2000/svg" width="230" height="230"></svg>') 2.1x,
+ url('data:image/svg+xml;utf8,<svg xmlns="http://www.w3.org/2000/svg" width="240" height="240"></svg>') 2.2x,
+ url('data:image/svg+xml;utf8,<svg xmlns="http://www.w3.org/2000/svg" width="250" height="250"></svg>') 2.3x,
+ url('data:image/svg+xml;utf8,<svg xmlns="http://www.w3.org/2000/svg" width="260" height="260"></svg>') 2.4x,
+ url('data:image/svg+xml;utf8,<svg xmlns="http://www.w3.org/2000/svg" width="270" height="270"></svg>') 2.5x,
+ url('data:image/svg+xml;utf8,<svg xmlns="http://www.w3.org/2000/svg" width="280" height="280"></svg>') 2.6x,
+ url('data:image/svg+xml;utf8,<svg xmlns="http://www.w3.org/2000/svg" width="290" height="290"></svg>') 2.7x,
+ url('data:image/svg+xml;utf8,<svg xmlns="http://www.w3.org/2000/svg" width="300" height="300"></svg>') 2.8x,
+ url('data:image/svg+xml;utf8,<svg xmlns="http://www.w3.org/2000/svg" width="310" height="310"></svg>') 2.9x,
+ url('data:image/svg+xml;utf8,<svg xmlns="http://www.w3.org/2000/svg" width="320" height="320"></svg>') 3.0x,
+ url('data:image/svg+xml;utf8,<svg xmlns="http://www.w3.org/2000/svg" width="330" height="330"></svg>') 3.1x,
+ url('data:image/svg+xml;utf8,<svg xmlns="http://www.w3.org/2000/svg" width="340" height="340"></svg>') 3.2x,
+ url('data:image/svg+xml;utf8,<svg xmlns="http://www.w3.org/2000/svg" width="350" height="350"></svg>') 3.3x,
+ url('data:image/svg+xml;utf8,<svg xmlns="http://www.w3.org/2000/svg" width="360" height="360"></svg>') 3.4x,
+ url('data:image/svg+xml;utf8,<svg xmlns="http://www.w3.org/2000/svg" width="370" height="370"></svg>') 3.5x,
+ url('data:image/svg+xml;utf8,<svg xmlns="http://www.w3.org/2000/svg" width="380" height="380"></svg>') 3.6x,
+ url('data:image/svg+xml;utf8,<svg xmlns="http://www.w3.org/2000/svg" width="390" height="390"></svg>') 3.7x,
+ url('data:image/svg+xml;utf8,<svg xmlns="http://www.w3.org/2000/svg" width="400" height="400"></svg>') 3.8x,
+ url('data:image/svg+xml;utf8,<svg xmlns="http://www.w3.org/2000/svg" width="410" height="410"></svg>') 3.9x,
+ url('data:image/svg+xml;utf8,<svg xmlns="http://www.w3.org/2000/svg" width="420" height="420"></svg>') 4.0x
+ );
+}
+ </style>
+ </head>
<body>
<img id="srcset" srcset="
- 03.png 0.3x, 04.png 0.4x, 05.png 0.5x, 06.png 0.6x, 07.png 0.7x, 08.png 0.8x, 09.png 0.9x,
- 10.png 1x, 11.png 1.1x, 12.png 1.2x, 13.png 1.3x, 14.png 1.4x, 15.png 1.5x, 16.png 1.6x, 17.png 1.7x, 18.png 1.8x, 19.png 1.9x,
- 20.png 2x, 21.png 2.1x, 22.png 2.2x, 23.png 2.3x, 24.png 2.4x, 25.png 2.5x, 26.png 2.6x, 27.png 2.7x, 28.png 2.8x, 29.png 2.9x,
- 30.png 3x, 31.png 3.1x, 32.png 3.2x, 33.png 3.3x, 34.png 3.4x, 35.png 3.5x, 36.png 3.6x, 37.png 3.7x, 38.png 3.8x, 39.png 3.9x,
- 40.png 4x
+ 0.65.png 0.65x, 0.85.png 0.85x, 1.05.png 1.05x, 1.25.png 1.25x, 1.45.png 1.45x, 1.65.png 1.65x, 1.85.png 1.85x,
+ 2.05.png 2.05x, 2.25.png 2.25x, 2.45.png 2.45x, 2.65.png 2.65x, 2.9.png 2.9x, 3.0.png 3.0x, 3.2.png 3.2x,
+ 3.4.png 3.4x, 3.6.png 3.6x, 3.8.png 3.8x, 4.0.png 4.0x
">
+ <div id="imgset"></div>
</body>
</html>
=====================================
widget/gtk/nsDragService.cpp
=====================================
@@ -1741,6 +1741,14 @@ void nsDragSession::SourceEndDragSession(GdkDragContext* aContext,
LOGDRAGSERVICE("SourceEndDragSession(%p) result %s\n", aContext,
kGtkDragResults[aResult]);
+ // GTK drops its last reference right after drag-end, so reset ours now.
+ //
+ // Clearing only from EndDragSessionImpl is not enough (bug 2058661): that
+ // runs from an async runnable, by which point the pointer already dangles.
+ if (mSourceWindow) {
+ mSourceWindow->SetDragSource(nullptr);
+ }
+
// this just releases the list of data items that we provide
mSourceDataItems = nullptr;
=====================================
widget/windows/SystemStatusBar.cpp
=====================================
@@ -91,10 +91,10 @@ StatusBarEntry::StatusBarEntry(Element* aMenu) : mMenu(aMenu), mInitted(false) {
}
StatusBarEntry::~StatusBarEntry() {
+ Destroy();
if (!mInitted) {
return;
}
- Destroy();
::Shell_NotifyIconW(NIM_DELETE, &mIconData);
VERIFY(::DestroyWindow(mIconData.hWnd));
}
=====================================
widget/windows/filedialog/WinFileDialogCommands.cpp
=====================================
@@ -233,18 +233,24 @@ mozilla::Result<nsString, Error> GetFolderResults(::IFileDialog* dialog) {
}
// If the user chose a Win7 Library, resolve to the library's
- // default save folder.
- RefPtr<IShellLibrary> shellLib;
- RefPtr<IShellItem> folderPath;
- MOZ_ENSURE_HRESULT_OK(
- "CoCreateInstance(CLSID_ShellLibrary)",
- CoCreateInstance(CLSID_ShellLibrary, nullptr, CLSCTX_INPROC_SERVER,
- IID_IShellLibrary, getter_AddRefs(shellLib)));
-
- if (shellLib && SUCCEEDED(shellLib->LoadLibraryFromItem(item, STGM_READ)) &&
- SUCCEEDED(shellLib->GetDefaultSaveFolder(DSFT_DETECT, IID_IShellItem,
- getter_AddRefs(folderPath)))) {
- item.swap(folderPath);
+ // default save folder. Only do this for items which are known not to be
+ // filesystem objects, like the user's Documents library (but not .library-ms
+ // files).
+ SFGAOF attrs = 0;
+ if (SUCCEEDED(item->GetAttributes(SFGAO_FILESYSTEM, &attrs)) &&
+ !(attrs & SFGAO_FILESYSTEM)) {
+ RefPtr<IShellLibrary> shellLib;
+ RefPtr<IShellItem> folderPath;
+ MOZ_ENSURE_HRESULT_OK(
+ "CoCreateInstance(CLSID_ShellLibrary)",
+ CoCreateInstance(CLSID_ShellLibrary, nullptr, CLSCTX_INPROC_SERVER,
+ IID_IShellLibrary, getter_AddRefs(shellLib)));
+
+ if (shellLib && SUCCEEDED(shellLib->LoadLibraryFromItem(item, STGM_READ)) &&
+ SUCCEEDED(shellLib->GetDefaultSaveFolder(DSFT_DETECT, IID_IShellItem,
+ getter_AddRefs(folderPath)))) {
+ item.swap(folderPath);
+ }
}
// get the folder's file system path
View it on GitLab: https://gitlab.torproject.org/tpo/applications/mullvad-browser/-/compare/4c…
--
View it on GitLab: https://gitlab.torproject.org/tpo/applications/mullvad-browser/-/compare/4c…
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
1
0
[Git][tpo/applications/tor-browser] Pushed new tag tor-browser-140.15.0esr-15.0-1-build2
by ma1 (@ma1) 01 Sep '26
by ma1 (@ma1) 01 Sep '26
01 Sep '26
ma1 pushed new tag tor-browser-140.15.0esr-15.0-1-build2 at The Tor Project / Applications / Tor Browser
--
View it on GitLab: https://gitlab.torproject.org/tpo/applications/tor-browser/-/tree/tor-brows…
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
1
0