ma1 pushed to branch mullvad-browser-128.14.0esr-14.5-1 at The Tor Project / Applications / Mullvad Browser
Commits:
-
febf4c83
by Emma Zuehlcke at 2025-09-15T22:21:22+02:00
-
cc301f19
by Daniel Holbert at 2025-09-15T22:21:23+02:00
-
111779ce
by Makoto Kato at 2025-09-15T22:21:25+02:00
-
4c25597a
by Jon Coppeard at 2025-09-15T22:21:26+02:00
-
ea3d5632
by longsonr at 2025-09-15T22:21:28+02:00
-
ecda78b0
by Lee Salzman at 2025-09-15T22:21:29+02:00
-
aee1e3a4
by Jed Davis at 2025-09-15T22:21:30+02:00
-
58c8b96e
by Lee Salzman at 2025-09-15T22:21:32+02:00
-
67e9d857
by Lee Salzman at 2025-09-15T22:21:33+02:00
-
658910d2
by Lee Salzman at 2025-09-15T22:21:35+02:00
12 changed files:
- browser/locales/en-US/browser/webrtcIndicator.ftl
- browser/modules/webrtcUI.sys.mjs
- dom/canvas/WebGLContext.cpp
- dom/security/nsContentSecurityManager.cpp
- gfx/2d/FilterProcessingScalar.cpp
- gfx/2d/InlineTranslator.h
- gfx/layers/ipc/CanvasTranslator.cpp
- js/src/gc/Statistics.cpp
- layout/printing/PrintTranslator.h
- mobile/android/geckoview/src/main/java/org/mozilla/gecko/util/IntentUtils.java
- modules/libpref/init/StaticPrefList.yaml
- security/sandbox/linux/launch/SandboxLaunch.cpp
Changes:
... | ... | @@ -60,7 +60,7 @@ webrtc-screen-system-menu = |
60 | 60 | ## These strings are only used on Mac for menus attached to icons
|
61 | 61 | ## near the clock on the mac menubar.
|
62 | 62 | ## Variables:
|
63 | -## $streamTitle (String): the title of the tab using the share.
|
|
63 | +## $streamTitle (String): the host of the tab using the share.
|
|
64 | 64 | ## $tabCount (Number): the title of the tab using the share.
|
65 | 65 | |
66 | 66 | webrtc-indicator-menuitem-control-sharing =
|
... | ... | @@ -1048,8 +1048,10 @@ export function showStreamSharingMenu(win, event, inclWindow = false) { |
1048 | 1048 | let stream = activeStreams[0];
|
1049 | 1049 | |
1050 | 1050 | const sharingItem = doc.createXULElement("menuitem");
|
1051 | - const streamTitle = stream.browser.contentTitle || stream.uri;
|
|
1052 | - doc.l10n.setAttributes(sharingItem, l10nIds[0], { streamTitle });
|
|
1051 | + const displayHost = getDisplayHostForStream(stream);
|
|
1052 | + doc.l10n.setAttributes(sharingItem, l10nIds[0], {
|
|
1053 | + streamTitle: displayHost,
|
|
1054 | + });
|
|
1053 | 1055 | sharingItem.setAttribute("disabled", "true");
|
1054 | 1056 | menu.appendChild(sharingItem);
|
1055 | 1057 | |
... | ... | @@ -1073,11 +1075,11 @@ export function showStreamSharingMenu(win, event, inclWindow = false) { |
1073 | 1075 | |
1074 | 1076 | for (let stream of activeStreams) {
|
1075 | 1077 | const controlItem = doc.createXULElement("menuitem");
|
1076 | - const streamTitle = stream.browser.contentTitle || stream.uri;
|
|
1078 | + const displayHost = getDisplayHostForStream(stream);
|
|
1077 | 1079 | doc.l10n.setAttributes(
|
1078 | 1080 | controlItem,
|
1079 | 1081 | "webrtc-indicator-menuitem-control-sharing-on",
|
1080 | - { streamTitle }
|
|
1082 | + { streamTitle: displayHost }
|
|
1081 | 1083 | );
|
1082 | 1084 | controlItem.stream = stream;
|
1083 | 1085 | controlItem.addEventListener("command", this);
|
... | ... | @@ -1086,6 +1088,25 @@ export function showStreamSharingMenu(win, event, inclWindow = false) { |
1086 | 1088 | }
|
1087 | 1089 | }
|
1088 | 1090 | |
1091 | +function getDisplayHostForStream(stream) {
|
|
1092 | + let uri = Services.io.newURI(stream.uri);
|
|
1093 | + |
|
1094 | + let displayHost;
|
|
1095 | + |
|
1096 | + try {
|
|
1097 | + displayHost = uri.displayHost;
|
|
1098 | + } catch (ex) {
|
|
1099 | + displayHost = null;
|
|
1100 | + }
|
|
1101 | + |
|
1102 | + // Host getter threw or returned "". Fall back to spec.
|
|
1103 | + if (displayHost == null || displayHost == "") {
|
|
1104 | + displayHost = uri.displaySpec;
|
|
1105 | + }
|
|
1106 | + |
|
1107 | + return displayHost;
|
|
1108 | +}
|
|
1109 | + |
|
1089 | 1110 | function onTabSharingMenuPopupShowing(e) {
|
1090 | 1111 | const streams = webrtcUI.getActiveStreams(true, true, true, true);
|
1091 | 1112 | for (let streamInfo of streams) {
|
... | ... | @@ -7,8 +7,9 @@ |
7 | 7 | |
8 | 8 | #include <algorithm>
|
9 | 9 | #include <bitset>
|
10 | +#include <cctype>
|
|
11 | +#include <iterator>
|
|
10 | 12 | #include <queue>
|
11 | -#include <regex>
|
|
12 | 13 | |
13 | 14 | #include "AccessCheck.h"
|
14 | 15 | #include "CompositableHost.h"
|
... | ... | @@ -2204,30 +2205,59 @@ Maybe<std::string> WebGLContext::GetString(const GLenum pname) const { |
2204 | 2205 | // ---------------------------------
|
2205 | 2206 | |
2206 | 2207 | Maybe<webgl::IndexedName> webgl::ParseIndexed(const std::string& str) {
|
2207 | - static const std::regex kRegex("(.*)\\[([0-9]+)\\]");
|
|
2208 | - |
|
2209 | - std::smatch match;
|
|
2210 | - if (!std::regex_match(str, match, kRegex)) return {};
|
|
2208 | + // Check if the string ends with a close bracket
|
|
2209 | + if (str.size() < 2 || str.back() != ']') {
|
|
2210 | + return {};
|
|
2211 | + }
|
|
2212 | + // Search for the open bracket, only allow digits between brackets
|
|
2213 | + const size_t closeBracket = str.size() - 1;
|
|
2214 | + size_t openBracket = closeBracket;
|
|
2215 | + for (;;) {
|
|
2216 | + char c = str[--openBracket];
|
|
2217 | + if (isdigit(c)) {
|
|
2218 | + if (openBracket <= 0) {
|
|
2219 | + // At the beginning of string without an open bracket
|
|
2220 | + return {};
|
|
2221 | + }
|
|
2222 | + } else if (c == '[') {
|
|
2223 | + // Found the open bracket
|
|
2224 | + break;
|
|
2225 | + } else {
|
|
2226 | + // Found a non-digit
|
|
2227 | + return {};
|
|
2228 | + }
|
|
2229 | + }
|
|
2211 | 2230 | |
2212 | - const auto index = std::stoull(match[2]);
|
|
2213 | - return Some(webgl::IndexedName{match[1], index});
|
|
2231 | + // Ensure non-empty digit sequence
|
|
2232 | + size_t firstDigit = openBracket + 1;
|
|
2233 | + if (firstDigit >= closeBracket) {
|
|
2234 | + return {};
|
|
2235 | + }
|
|
2236 | + const auto index =
|
|
2237 | + std::stoull(str.substr(firstDigit, closeBracket - firstDigit));
|
|
2238 | + std::string name = str.substr(0, openBracket);
|
|
2239 | + return Some(webgl::IndexedName{name, index});
|
|
2214 | 2240 | }
|
2215 | 2241 | |
2216 | 2242 | // ExplodeName("foo.bar[3].x") -> ["foo", ".", "bar", "[", "3", "]", ".", "x"]
|
2217 | 2243 | static std::vector<std::string> ExplodeName(const std::string& str) {
|
2218 | 2244 | std::vector<std::string> ret;
|
2219 | - |
|
2220 | - static const std::regex kSep("[.[\\]]");
|
|
2221 | - |
|
2222 | - auto itr = std::regex_token_iterator<decltype(str.begin())>(
|
|
2223 | - str.begin(), str.end(), kSep, {-1, 0});
|
|
2224 | - const auto end = decltype(itr)();
|
|
2225 | - |
|
2226 | - for (; itr != end; ++itr) {
|
|
2227 | - const auto& part = itr->str();
|
|
2228 | - if (part.size()) {
|
|
2229 | - ret.push_back(part);
|
|
2245 | + size_t curPos = 0;
|
|
2246 | + while (curPos < str.size()) {
|
|
2247 | + // Find the next separator
|
|
2248 | + size_t nextPos = str.find_first_of(".[]", curPos);
|
|
2249 | + if (nextPos == std::string::npos) {
|
|
2250 | + // If no separator found, add remaining substring
|
|
2251 | + ret.push_back(str.substr(curPos));
|
|
2252 | + break;
|
|
2253 | + }
|
|
2254 | + // Add string between separators, if not empty
|
|
2255 | + if (curPos < nextPos) {
|
|
2256 | + ret.push_back(str.substr(curPos, nextPos - curPos));
|
|
2230 | 2257 | }
|
2258 | + // Add the separator
|
|
2259 | + ret.push_back(str.substr(nextPos, 1));
|
|
2260 | + curPos = nextPos + 1;
|
|
2231 | 2261 | }
|
2232 | 2262 | return ret;
|
2233 | 2263 | }
|
... | ... | @@ -45,6 +45,7 @@ |
45 | 45 | #include "mozilla/Logging.h"
|
46 | 46 | #include "mozilla/Maybe.h"
|
47 | 47 | #include "mozilla/Preferences.h"
|
48 | +#include "mozilla/StaticPrefs_content.h"
|
|
48 | 49 | #include "mozilla/StaticPrefs_dom.h"
|
49 | 50 | #include "mozilla/StaticPrefs_security.h"
|
50 | 51 | #include "mozilla/Telemetry.h"
|
... | ... | @@ -364,10 +365,17 @@ static nsresult DoCORSChecks(nsIChannel* aChannel, nsILoadInfo* aLoadInfo, |
364 | 365 | return NS_OK;
|
365 | 366 | }
|
366 | 367 | |
367 | - // We use the triggering principal here, rather than the loading principal
|
|
368 | - // to ensure that anonymous CORS content in the browser resources and in
|
|
369 | - // WebExtensions is allowed to load.
|
|
370 | - nsIPrincipal* principal = aLoadInfo->TriggeringPrincipal();
|
|
368 | + nsIPrincipal* principal = aLoadInfo->GetLoadingPrincipal();
|
|
369 | + if (StaticPrefs::content_cors_use_triggering_principal()) {
|
|
370 | + // We use the triggering principal here, rather than the loading principal,
|
|
371 | + // to ensure that WebExtensions can reuse their own resources from content
|
|
372 | + // that they inject into a page.
|
|
373 | + //
|
|
374 | + // TODO(dholbert): Is there actually a legitimate reason that WebExtensions
|
|
375 | + // might need this (as opposed to exposing their resources for use in
|
|
376 | + // web-content via the 'web_accessible_resources' manifest field)?
|
|
377 | + principal = aLoadInfo->TriggeringPrincipal();
|
|
378 | + }
|
|
371 | 379 | RefPtr<nsCORSListenerProxy> corsListener = new nsCORSListenerProxy(
|
372 | 380 | aInAndOutListener, principal,
|
373 | 381 | aLoadInfo->GetCookiePolicy() == nsILoadInfo::SEC_COOKIES_INCLUDE);
|
... | ... | @@ -47,12 +47,12 @@ static void ApplyMorphologyHorizontal_Scalar( |
47 | 47 | x++, startX++, endX++) {
|
48 | 48 | int32_t sourceIndex = y * aSourceStride + 4 * startX;
|
49 | 49 | uint8_t u[4];
|
50 | - for (size_t i = 0; i < 4; i++) {
|
|
50 | + for (int32_t i = 0; i < int32_t(std::size(u)); i++) {
|
|
51 | 51 | u[i] = aSourceData[sourceIndex + i];
|
52 | 52 | }
|
53 | 53 | sourceIndex += 4;
|
54 | 54 | for (int32_t ix = startX + 1; ix <= endX; ix++, sourceIndex += 4) {
|
55 | - for (size_t i = 0; i < 4; i++) {
|
|
55 | + for (int32_t i = 0; i < int32_t(std::size(u)); i++) {
|
|
56 | 56 | if (Operator == MORPHOLOGY_OPERATOR_ERODE) {
|
57 | 57 | u[i] = umin(u[i], aSourceData[sourceIndex + i]);
|
58 | 58 | } else {
|
... | ... | @@ -62,7 +62,7 @@ static void ApplyMorphologyHorizontal_Scalar( |
62 | 62 | }
|
63 | 63 | |
64 | 64 | int32_t destIndex = y * aDestStride + 4 * x;
|
65 | - for (size_t i = 0; i < 4; i++) {
|
|
65 | + for (int32_t i = 0; i < int32_t(std::size(u)); i++) {
|
|
66 | 66 | aDestData[destIndex + i] = u[i];
|
67 | 67 | }
|
68 | 68 | }
|
... | ... | @@ -97,13 +97,13 @@ static void ApplyMorphologyVertical_Scalar( |
97 | 97 | for (int32_t x = aDestRect.X(); x < aDestRect.XMost(); x++) {
|
98 | 98 | int32_t sourceIndex = startY * aSourceStride + 4 * x;
|
99 | 99 | uint8_t u[4];
|
100 | - for (size_t i = 0; i < 4; i++) {
|
|
100 | + for (int32_t i = 0; i < int32_t(std::size(u)); i++) {
|
|
101 | 101 | u[i] = aSourceData[sourceIndex + i];
|
102 | 102 | }
|
103 | 103 | sourceIndex += aSourceStride;
|
104 | 104 | for (int32_t iy = startY + 1; iy <= endY;
|
105 | 105 | iy++, sourceIndex += aSourceStride) {
|
106 | - for (size_t i = 0; i < 4; i++) {
|
|
106 | + for (int32_t i = 0; i < int32_t(std::size(u)); i++) {
|
|
107 | 107 | if (Operator == MORPHOLOGY_OPERATOR_ERODE) {
|
108 | 108 | u[i] = umin(u[i], aSourceData[sourceIndex + i]);
|
109 | 109 | } else {
|
... | ... | @@ -113,7 +113,7 @@ static void ApplyMorphologyVertical_Scalar( |
113 | 113 | }
|
114 | 114 | |
115 | 115 | int32_t destIndex = y * aDestStride + 4 * x;
|
116 | - for (size_t i = 0; i < 4; i++) {
|
|
116 | + for (int32_t i = 0; i < int32_t(std::size(u)); i++) {
|
|
117 | 117 | aDestData[destIndex + i] = u[i];
|
118 | 118 | }
|
119 | 119 | }
|
... | ... | @@ -92,7 +92,11 @@ class InlineTranslator : public Translator { |
92 | 92 | already_AddRefed<SourceSurface> LookupExternalSurface(uint64_t aKey) override;
|
93 | 93 | |
94 | 94 | void AddDrawTarget(ReferencePtr aRefPtr, DrawTarget* aDT) final {
|
95 | - mDrawTargets.InsertOrUpdate(aRefPtr, RefPtr{aDT});
|
|
95 | + RefPtr<DrawTarget>& value = mDrawTargets.LookupOrInsert(aRefPtr);
|
|
96 | + if (mCurrentDT && mCurrentDT == value) {
|
|
97 | + mCurrentDT = nullptr;
|
|
98 | + }
|
|
99 | + value = aDT;
|
|
96 | 100 | }
|
97 | 101 | |
98 | 102 | void AddPath(ReferencePtr aRefPtr, Path* aPath) final {
|
... | ... | @@ -189,6 +189,10 @@ mozilla::ipc::IPCResult CanvasTranslator::RecvInitTranslator( |
189 | 189 | }
|
190 | 190 | |
191 | 191 | // Use the first buffer as our current buffer.
|
192 | + if (aBufferHandles.IsEmpty()) {
|
|
193 | + Deactivate();
|
|
194 | + return IPC_FAIL(this, "No canvas buffer shared memory supplied.");
|
|
195 | + }
|
|
192 | 196 | mDefaultBufferSize = aBufferSize;
|
193 | 197 | auto handleIter = aBufferHandles.begin();
|
194 | 198 | if (!CreateAndMapShmem(mCurrentShmem.shmem, std::move(*handleIter),
|
... | ... | @@ -365,11 +369,19 @@ void CanvasTranslator::GetDataSurface(uint64_t aSurfaceRef) { |
365 | 369 | }
|
366 | 370 | |
367 | 371 | void CanvasTranslator::RecycleBuffer() {
|
372 | + if (!mCurrentShmem.IsValid()) {
|
|
373 | + return;
|
|
374 | + }
|
|
375 | + |
|
368 | 376 | mCanvasShmems.emplace(std::move(mCurrentShmem));
|
369 | 377 | NextBuffer();
|
370 | 378 | }
|
371 | 379 | |
372 | 380 | void CanvasTranslator::NextBuffer() {
|
381 | + if (mCanvasShmems.empty()) {
|
|
382 | + return;
|
|
383 | + }
|
|
384 | + |
|
373 | 385 | // Check and signal the writer when we finish with a buffer, because it
|
374 | 386 | // might have hit the buffer count limit and be waiting to use our old one.
|
375 | 387 | CheckAndSignalWriter();
|
... | ... | @@ -1518,7 +1518,7 @@ void Statistics::recordParallelPhase(PhaseKind phaseKind, |
1518 | 1518 | TimeDuration duration) {
|
1519 | 1519 | MOZ_ASSERT(CurrentThreadCanAccessRuntime(gc->rt));
|
1520 | 1520 | |
1521 | - if (aborted) {
|
|
1521 | + if (slices_.empty()) {
|
|
1522 | 1522 | return;
|
1523 | 1523 | }
|
1524 | 1524 |
... | ... | @@ -85,7 +85,11 @@ class PrintTranslator final : public Translator { |
85 | 85 | }
|
86 | 86 | |
87 | 87 | void AddDrawTarget(ReferencePtr aRefPtr, DrawTarget* aDT) final {
|
88 | - mDrawTargets.InsertOrUpdate(aRefPtr, RefPtr{aDT});
|
|
88 | + RefPtr<DrawTarget>& value = mDrawTargets.LookupOrInsert(aRefPtr);
|
|
89 | + if (mCurrentDT && mCurrentDT == value) {
|
|
90 | + mCurrentDT = nullptr;
|
|
91 | + }
|
|
92 | + value = aDT;
|
|
89 | 93 | }
|
90 | 94 | |
91 | 95 | void AddPath(ReferencePtr aRefPtr, Path* aPath) final {
|
... | ... | @@ -119,11 +123,11 @@ class PrintTranslator final : public Translator { |
119 | 123 | }
|
120 | 124 | |
121 | 125 | void RemoveDrawTarget(ReferencePtr aRefPtr) final {
|
122 | - ReferencePtr currentDT = mCurrentDT;
|
|
123 | - if (currentDT == aRefPtr) {
|
|
126 | + RefPtr<DrawTarget> removedDT;
|
|
127 | + if (mDrawTargets.Remove(aRefPtr, getter_AddRefs(removedDT)) &&
|
|
128 | + mCurrentDT == removedDT) {
|
|
124 | 129 | mCurrentDT = nullptr;
|
125 | 130 | }
|
126 | - mDrawTargets.Remove(aRefPtr);
|
|
127 | 131 | }
|
128 | 132 | |
129 | 133 | bool SetCurrentDrawTarget(ReferencePtr aRefPtr) final {
|
... | ... | @@ -72,7 +72,7 @@ public class IntentUtils { |
72 | 72 | }
|
73 | 73 | |
74 | 74 | if (("intent".equals(scheme) || "android-app".equals(scheme))) {
|
75 | - // Bug 1356893 - Rject intents with file data schemes.
|
|
75 | + // Bug 1356893 - Reject intents with file data schemes.
|
|
76 | 76 | return getSafeIntent(aUri) != null;
|
77 | 77 | }
|
78 | 78 | |
... | ... | @@ -98,8 +98,11 @@ public class IntentUtils { |
98 | 98 | }
|
99 | 99 | |
100 | 100 | final Uri data = intent.getData();
|
101 | - if (data != null && "file".equals(normalizeUriScheme(data).getScheme())) {
|
|
102 | - return null;
|
|
101 | + if (data != null) {
|
|
102 | + final String scheme = normalizeUriScheme(data).getScheme();
|
|
103 | + if ("file".equals(scheme) || "fido".equals(scheme)) {
|
|
104 | + return null;
|
|
105 | + }
|
|
103 | 106 | }
|
104 | 107 | |
105 | 108 | // Only open applications which can accept arbitrary data from a browser.
|
... | ... | @@ -1953,6 +1953,14 @@ |
1953 | 1953 | value: false
|
1954 | 1954 | mirror: always
|
1955 | 1955 | |
1956 | +# If true, we'll use the triggering principal rather than the loading principal
|
|
1957 | +# when doing CORS checks. This might be needed for WebExtensions to load their
|
|
1958 | +# own resources from content that they inject into sites.
|
|
1959 | +- name: content.cors.use_triggering_principal
|
|
1960 | + type: bool
|
|
1961 | + value: false
|
|
1962 | + mirror: always
|
|
1963 | + |
|
1956 | 1964 | # Back off timer notification after count.
|
1957 | 1965 | # -1 means never.
|
1958 | 1966 | - name: content.notify.backoffcount
|
... | ... | @@ -304,6 +304,8 @@ void SandboxLaunch::Configure(GeckoProcessType aType, SandboxingKind aKind, |
304 | 304 | return;
|
305 | 305 | }
|
306 | 306 | |
307 | + // Warning: don't combine multiple case labels, even if the code is
|
|
308 | + // currently the same, to avoid mistakes when changes are made.
|
|
307 | 309 | switch (aType) {
|
308 | 310 | case GeckoProcessType_Socket:
|
309 | 311 | if (level >= 1) {
|
... | ... | @@ -312,6 +314,12 @@ void SandboxLaunch::Configure(GeckoProcessType aType, SandboxingKind aKind, |
312 | 314 | }
|
313 | 315 | break;
|
314 | 316 | case GeckoProcessType_GMPlugin:
|
317 | + if (level >= 1) {
|
|
318 | + canChroot = true;
|
|
319 | + flags |= CLONE_NEWIPC;
|
|
320 | + flags |= CLONE_NEWNET;
|
|
321 | + }
|
|
322 | + break;
|
|
315 | 323 | case GeckoProcessType_RDD:
|
316 | 324 | if (level >= 1) {
|
317 | 325 | canChroot = true;
|