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
-
807aa22d
by Yury Delendik at 2026-09-01T12:54:02+02:00
-
9c69b32a
by Gela at 2026-09-01T12:54:07+02:00
-
82b64b84
by Ting-Yu Lin at 2026-09-01T12:54:11+02:00
-
d2276817
by Ting-Yu Lin at 2026-09-01T12:54:16+02:00
-
7f71204b
by Jan Grulich at 2026-09-01T12:54:20+02:00
-
c829ecff
by David Parks at 2026-09-01T12:54:25+02:00
-
3e16e633
by David Parks at 2026-09-01T12:54:29+02:00
-
7f1c7443
by Iain Ireland at 2026-09-01T12:54:33+02:00
-
3c51167f
by Kui-Feng Lee at 2026-09-01T12:54:38+02:00
-
bbe2eee1
by Kershaw Chang at 2026-09-01T12:54:42+02:00
-
7a932edc
by Sotaro Ikeda at 2026-09-01T12:54:47+02:00
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:
| ... | ... | @@ -351,10 +351,6 @@ bool ResponsiveImageSelector::SelectImage(bool aReselect) { |
| 351 | 351 | if (overrideDPPX > 0) {
|
| 352 | 352 | displayDensity = overrideDPPX;
|
| 353 | 353 | }
|
| 354 | - if (doc->ShouldResistFingerprinting(RFPTarget::WindowDevicePixelRatio)) {
|
|
| 355 | - displayDensity =
|
|
| 356 | - nsRFPService::GetDevicePixelRatioAtZoom(pctx->GetFullZoom());
|
|
| 357 | - }
|
|
| 358 | 354 | |
| 359 | 355 | // Per spec, "In a UA-specific manner, choose one image source"
|
| 360 | 356 | // - For now, select the lowest density greater than displayDensity, otherwise
|
| ... | ... | @@ -126,7 +126,14 @@ bool RenderExternalTextureHost::IsReadyForDeletion() { |
| 126 | 126 | |
| 127 | 127 | auto& textureSource = mTextureSources[0];
|
| 128 | 128 | if (textureSource) {
|
| 129 | - return textureSource->Sync(false);
|
|
| 129 | + if (textureSource->Sync(/* aBlocking */ true)) {
|
|
| 130 | + return true;
|
|
| 131 | + }
|
|
| 132 | + if (mGL && mGL->MakeCurrent() && !mGL->IsDestroyed()) {
|
|
| 133 | + mGL->fFinish();
|
|
| 134 | + return true;
|
|
| 135 | + }
|
|
| 136 | + return false;
|
|
| 130 | 137 | }
|
| 131 | 138 | |
| 132 | 139 | return true;
|
| ... | ... | @@ -153,7 +160,11 @@ wr::WrExternalImage RenderExternalTextureHost::Lock(uint8_t aChannelIndex, |
| 153 | 160 | |
| 154 | 161 | void RenderExternalTextureHost::PrepareForUse() { mTextureUpdateNeeded = true; }
|
| 155 | 162 | |
| 156 | -void RenderExternalTextureHost::Unlock() {}
|
|
| 163 | +void RenderExternalTextureHost::Unlock() {
|
|
| 164 | + if (mInitialized && mTextureSources[0]) {
|
|
| 165 | + mTextureSources[0]->MaybeFenceTexture();
|
|
| 166 | + }
|
|
| 167 | +}
|
|
| 157 | 168 | |
| 158 | 169 | void RenderExternalTextureHost::UpdateTexture(size_t aIndex) {
|
| 159 | 170 | MOZ_ASSERT(mSurfaces[aIndex]);
|
| ... | ... | @@ -924,6 +924,7 @@ class GCRuntime { |
| 924 | 924 | void updateAtomsBitmap();
|
| 925 | 925 | void sweepCCWrappers();
|
| 926 | 926 | void sweepRealmGlobals();
|
| 927 | + void sweepWasmInstances();
|
|
| 927 | 928 | void sweepEmbeddingWeakPointers(JS::GCContext* gcx);
|
| 928 | 929 | void sweepMisc();
|
| 929 | 930 | void sweepCompressionTasks();
|
| ... | ... | @@ -1396,6 +1396,13 @@ void GCRuntime::sweepRealmGlobals() { |
| 1396 | 1396 | }
|
| 1397 | 1397 | }
|
| 1398 | 1398 | |
| 1399 | +void GCRuntime::sweepWasmInstances() {
|
|
| 1400 | + for (SweepGroupRealmsIter r(this); !r.done(); r.next()) {
|
|
| 1401 | + AutoSetThreadIsSweeping threadIsSweeping(r->zone());
|
|
| 1402 | + r->wasm.traceWeakInstances();
|
|
| 1403 | + }
|
|
| 1404 | +}
|
|
| 1405 | + |
|
| 1399 | 1406 | void GCRuntime::sweepMisc() {
|
| 1400 | 1407 | SweepingTracer trc(rt);
|
| 1401 | 1408 | for (SweepGroupRealmsIter r(this); !r.done(); r.next()) {
|
| ... | ... | @@ -1709,6 +1716,11 @@ IncrementalProgress GCRuntime::beginSweepingSweepGroup(JS::GCContext* gcx, |
| 1709 | 1716 | // This must happen before updating embedding weak pointers.
|
| 1710 | 1717 | sweepRealmGlobals();
|
| 1711 | 1718 | |
| 1719 | + // Prune dying wasm instances from each realm's weak instance list now, at the
|
|
| 1720 | + // start of sweeping, before the mutator can observe them via the (now no-op)
|
|
| 1721 | + // instances() read barrier during later incremental slices.
|
|
| 1722 | + sweepWasmInstances();
|
|
| 1723 | + |
|
| 1712 | 1724 | sweepEmbeddingWeakPointers(gcx);
|
| 1713 | 1725 | |
| 1714 | 1726 | {
|
| 1 | +function forge(pattern, srcFlags, flagsByte) {
|
|
| 2 | + var cb = serialize(new RegExp(pattern, srcFlags), undefined, { scope: "DifferentProcess" });
|
|
| 3 | + var u8 = new Uint8Array(cb.arraybuffer);
|
|
| 4 | + for (var i = 0; i + 8 <= u8.length; i += 4) {
|
|
| 5 | + var tag = u8[i+4] | (u8[i+5] << 8) | (u8[i+6] << 16) | (u8[i+7] << 24);
|
|
| 6 | + if ((tag >>> 0) === 0xFFFF0006) { u8[i] = flagsByte; break; }
|
|
| 7 | + }
|
|
| 8 | + cb.clonebuffer = u8.buffer;
|
|
| 9 | + return deserialize(cb, { scope: "DifferentProcess" });
|
|
| 10 | +}
|
|
| 11 | +try {
|
|
| 12 | + var forged = forge("[\\q{abc|de}]", "v", 0x90);
|
|
| 13 | + var bad = new RegExp(forged, "u");
|
|
| 14 | + try { bad.exec("abc"); } catch {}
|
|
| 15 | + var good = new RegExp("[\\q{abc|de}]", "u");
|
|
| 16 | +} catch {} |
| ... | ... | @@ -3189,7 +3189,9 @@ bool JSStructuredCloneReader::startRead(MutableHandleValue vp, |
| 3189 | 3189 | }
|
| 3190 | 3190 | |
| 3191 | 3191 | case SCTAG_REGEXP_OBJECT: {
|
| 3192 | - if ((data & RegExpFlag::AllFlags) != data) {
|
|
| 3192 | + // Reject invalid flags. /u and /v are mutually exclusive.
|
|
| 3193 | + if ((data & RegExpFlag::AllFlags) != data ||
|
|
| 3194 | + ((data & RegExpFlag::Unicode) && (data & RegExpFlag::UnicodeSets))) {
|
|
| 3193 | 3195 | JS_ReportErrorNumberASCII(context(), GetErrorMessage, nullptr,
|
| 3194 | 3196 | JSMSG_SC_BAD_SERIALIZED_DATA, "regexp");
|
| 3195 | 3197 | return false;
|
| ... | ... | @@ -18,6 +18,7 @@ |
| 18 | 18 | |
| 19 | 19 | #include "wasm/WasmRealm.h"
|
| 20 | 20 | |
| 21 | +#include "gc/Marking.h"
|
|
| 21 | 22 | #include "vm/GlobalObject.h"
|
| 22 | 23 | #include "vm/Realm.h"
|
| 23 | 24 | #include "wasm/WasmDebug.h"
|
| ... | ... | @@ -111,6 +112,19 @@ void wasm::Realm::unregisterInstance(Instance& instance) { |
| 111 | 112 | }
|
| 112 | 113 | }
|
| 113 | 114 | |
| 115 | +void wasm::Realm::traceWeakInstances() {
|
|
| 116 | + // Registration/unregistration of instances_ is tied to Instance lifetime, so
|
|
| 117 | + // an instance whose owning object is about to be finalized is still present
|
|
| 118 | + // here until ~Instance runs. Remove such entries now, at the start of zone
|
|
| 119 | + // sweeping, because the instances() read barrier that otherwise protects
|
|
| 120 | + // readers is a no-op once the zone is being swept. erase order is preserved,
|
|
| 121 | + // so the pointer-sorted invariant used by BinarySearchIf holds.
|
|
| 122 | + instances_.eraseIf([](Instance* instance) {
|
|
| 123 | + return js::gc::IsAboutToBeFinalizedUnbarriered(
|
|
| 124 | + instance->objectUnbarriered());
|
|
| 125 | + });
|
|
| 126 | +}
|
|
| 127 | + |
|
| 114 | 128 | void wasm::Realm::ensureProfilingLabels(bool profilingEnabled) {
|
| 115 | 129 | for (Instance* instance : instances_) {
|
| 116 | 130 | instance->ensureProfilingLabels(profilingEnabled);
|
| ... | ... | @@ -53,10 +53,17 @@ class Realm { |
| 53 | 53 | // Return a vector of all live instances in the realm. The lifetime of
|
| 54 | 54 | // these Instances is determined by their owning WasmInstanceObject.
|
| 55 | 55 | // Note that accessing instances()[i]->object() triggers a read barrier
|
| 56 | - // since instances() is effectively a weak list.
|
|
| 56 | + // since instances() is effectively a weak list. This read barrier is only
|
|
| 57 | + // effective while the owning zone is being marked; traceWeakInstances()
|
|
| 58 | + // prunes dying entries at the start of sweeping so that the list never
|
|
| 59 | + // exposes an about-to-be-finalized instance to the mutator.
|
|
| 57 | 60 | |
| 58 | 61 | const InstanceVector& instances() const { return instances_; }
|
| 59 | 62 | |
| 63 | + // Remove instances whose owning object is about to be finalized. Called at
|
|
| 64 | + // the start of zone sweeping, when the instances() read barrier is a no-op.
|
|
| 65 | + void traceWeakInstances();
|
|
| 66 | + |
|
| 60 | 67 | // Ensure all Instances in this Realm have profiling labels created.
|
| 61 | 68 | |
| 62 | 69 | void ensureProfilingLabels(bool profilingEnabled);
|
| ... | ... | @@ -978,7 +978,11 @@ void nsPresContext::RecomputeBrowsingContextDependentData() { |
| 978 | 978 | auto systemZoom = LookAndFeel::SystemZoomSettings();
|
| 979 | 979 | SetFullZoom(browsingContext->FullZoom() * systemZoom.mFullZoom);
|
| 980 | 980 | SetTextZoom(browsingContext->TextZoom() * systemZoom.mTextZoom);
|
| 981 | - SetOverrideDPPX(browsingContext->OverrideDPPX());
|
|
| 981 | + if (doc->ShouldResistFingerprinting(RFPTarget::WindowDevicePixelRatio)) {
|
|
| 982 | + SetOverrideDPPX(nsRFPService::GetDevicePixelRatioAtZoom(GetFullZoom()));
|
|
| 983 | + } else {
|
|
| 984 | + SetOverrideDPPX(browsingContext->OverrideDPPX());
|
|
| 985 | + }
|
|
| 982 | 986 | }
|
| 983 | 987 | |
| 984 | 988 | auto* top = browsingContext->Top();
|
| ... | ... | @@ -214,7 +214,7 @@ void nsSplittableFrame::UpdateFirstContinuationAndFirstInFlowCache() { |
| 214 | 214 | if (oldCachedFirstContinuation != newFirstContinuation) {
|
| 215 | 215 | // Update the first-continuation cache for us and our next-continuations.
|
| 216 | 216 | for (nsSplittableFrame* f = this; f;
|
| 217 | - f = reinterpret_cast<nsSplittableFrame*>(f->GetNextContinuation())) {
|
|
| 217 | + f = static_cast<nsSplittableFrame*>(f->GetNextContinuation())) {
|
|
| 218 | 218 | f->mFirstContinuation = newFirstContinuation;
|
| 219 | 219 | }
|
| 220 | 220 | }
|
| ... | ... | @@ -227,7 +227,7 @@ void nsSplittableFrame::UpdateFirstContinuationAndFirstInFlowCache() { |
| 227 | 227 | // behavior when a frame list is destroyed from the front. To avoid that
|
| 228 | 228 | // pathological behavior, we simply purge the cached values.
|
| 229 | 229 | for (nsSplittableFrame* f = this; f;
|
| 230 | - f = reinterpret_cast<nsSplittableFrame*>(f->GetNextContinuation())) {
|
|
| 230 | + f = static_cast<nsSplittableFrame*>(f->GetNextContinuation())) {
|
|
| 231 | 231 | f->mFirstContinuation = nullptr;
|
| 232 | 232 | }
|
| 233 | 233 | }
|
| ... | ... | @@ -239,22 +239,41 @@ void nsSplittableFrame::UpdateFirstContinuationAndFirstInFlowCache() { |
| 239 | 239 | if (oldCachedFirstInFlow != newFirstInFlow) {
|
| 240 | 240 | // Update the first-in-flow cache for us and our next-in-flows.
|
| 241 | 241 | for (nsSplittableFrame* f = this; f;
|
| 242 | - f = reinterpret_cast<nsSplittableFrame*>(f->GetNextInFlow())) {
|
|
| 242 | + f = static_cast<nsSplittableFrame*>(f->GetNextInFlow())) {
|
|
| 243 | 243 | f->mFirstInFlow = newFirstInFlow;
|
| 244 | 244 | }
|
| 245 | 245 | }
|
| 246 | 246 | } else {
|
| 247 | - // We become the new first-in-flow due to our prev-in-flow being removed.
|
|
| 248 | - if (oldCachedFirstInFlow) {
|
|
| 249 | - // It's tempting to update the first-in-flow cache for our
|
|
| 250 | - // next-in-flows here, but that would result in overall O(n^2)
|
|
| 251 | - // behavior when a frame list is destroyed from the front. To avoid that
|
|
| 252 | - // pathological behavior, we simply purge the cached values.
|
|
| 247 | + if (GetPrevContinuation()) {
|
|
| 248 | + // We become the new first-in-flow after changing from fluid to non-fluid.
|
|
| 249 | + // Update the stale first-in-flow cache for us and all next-in-flows.
|
|
| 250 | + //
|
|
| 251 | + // Note that this has no counterpart in the above mFirstContinuation cache
|
|
| 252 | + // since GetPrevContinuation() does not depend on the
|
|
| 253 | + // NS_FRAME_IS_FLUID_CONTINUATION bit.
|
|
| 253 | 254 | for (nsSplittableFrame* f = this; f;
|
| 254 | - f = reinterpret_cast<nsSplittableFrame*>(f->GetNextInFlow())) {
|
|
| 255 | - f->mFirstInFlow = nullptr;
|
|
| 255 | + f = static_cast<nsSplittableFrame*>(f->GetNextInFlow())) {
|
|
| 256 | + f->mFirstInFlow = this;
|
|
| 257 | + }
|
|
| 258 | + } else {
|
|
| 259 | + // We become the new first-in-flow due to our prev-in-flow being removed.
|
|
| 260 | + if (oldCachedFirstInFlow) {
|
|
| 261 | + // It's tempting to update the first-in-flow cache for our
|
|
| 262 | + // next-in-flows here, but that would result in overall O(n^2)
|
|
| 263 | + // behavior when a frame list is destroyed from the front. To avoid that
|
|
| 264 | + // pathological behavior, we simply purge the cached values.
|
|
| 265 | + for (nsSplittableFrame* f = this; f;
|
|
| 266 | + f = static_cast<nsSplittableFrame*>(f->GetNextInFlow())) {
|
|
| 267 | + f->mFirstInFlow = nullptr;
|
|
| 268 | + }
|
|
| 256 | 269 | }
|
| 257 | 270 | }
|
| 271 | + |
|
| 272 | + DebugOnly<nsSplittableFrame*> nextInFlow =
|
|
| 273 | + static_cast<nsSplittableFrame*>(GetNextInFlow());
|
|
| 274 | + MOZ_ASSERT(!nextInFlow || !nextInFlow->mFirstInFlow ||
|
|
| 275 | + nextInFlow->mFirstInFlow == this,
|
|
| 276 | + "Our next-in-flow caches a stale first-in-flow!");
|
|
| 258 | 277 | }
|
| 259 | 278 | }
|
| 260 | 279 |
| ... | ... | @@ -773,6 +773,16 @@ |
| 773 | 773 | android:value="androidx.startup"
|
| 774 | 774 | tools:node="remove" />
|
| 775 | 775 | </provider>
|
| 776 | + |
|
| 777 | + <receiver
|
|
| 778 | + android:name="org.mozilla.fenix.experiments.QANimbusToolingReceiver"
|
|
| 779 | + android:exported="true"
|
|
| 780 | + android:enabled="true"
|
|
| 781 | + android:permission="android.permission.DUMP">
|
|
| 782 | + <intent-filter>
|
|
| 783 | + <action android:name="org.mozilla.fenix.NIMBUS_TOOLING" />
|
|
| 784 | + </intent-filter>
|
|
| 785 | + </receiver>
|
|
| 776 | 786 | </application>
|
| 777 | 787 | |
| 778 | 788 | </manifest> |
| ... | ... | @@ -82,7 +82,6 @@ import mozilla.components.support.utils.SafeIntent |
| 82 | 82 | import mozilla.components.support.utils.toSafeIntent
|
| 83 | 83 | import mozilla.components.support.webextensions.WebExtensionPopupObserver
|
| 84 | 84 | import mozilla.telemetry.glean.private.NoExtras
|
| 85 | -import org.mozilla.experiments.nimbus.initializeTooling
|
|
| 86 | 85 | import org.mozilla.fenix.GleanMetrics.AppIcon
|
| 87 | 86 | import org.mozilla.fenix.GleanMetrics.Events
|
| 88 | 87 | import org.mozilla.fenix.GleanMetrics.Metrics
|
| ... | ... | @@ -310,8 +309,6 @@ open class HomeActivity : LocaleAwareAppCompatActivity(), NavHostActivity { |
| 310 | 309 | // DO NOT MOVE ANYTHING ABOVE THIS getProfilerTime CALL.
|
| 311 | 310 | val startTimeProfiler = components.core.engine.profiler?.getProfilerTime()
|
| 312 | 311 | |
| 313 | - // Setup nimbus-cli tooling. This is a NOOP when launching normally.
|
|
| 314 | - components.nimbus.sdk.initializeTooling(applicationContext, intent)
|
|
| 315 | 312 | components.strictMode.attachListenerToDisablePenaltyDeath(supportFragmentManager)
|
| 316 | 313 | MarkersFragmentLifecycleCallbacks.register(supportFragmentManager, components.core.engine)
|
| 317 | 314 |
| 1 | +/* This Source Code Form is subject to the terms of the Mozilla Public
|
|
| 2 | + * License, v. 2.0. If a copy of the MPL was not distributed with this
|
|
| 3 | + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
|
| 4 | + |
|
| 5 | +package org.mozilla.fenix.experiments
|
|
| 6 | + |
|
| 7 | +import android.content.BroadcastReceiver
|
|
| 8 | +import android.content.Context
|
|
| 9 | +import android.content.Intent
|
|
| 10 | +import kotlinx.coroutines.CoroutineDispatcher
|
|
| 11 | +import kotlinx.coroutines.CoroutineScope
|
|
| 12 | +import kotlinx.coroutines.Dispatchers
|
|
| 13 | +import kotlinx.coroutines.launch
|
|
| 14 | +import mozilla.components.support.base.log.logger.Logger
|
|
| 15 | +import org.mozilla.experiments.nimbus.initializeTooling
|
|
| 16 | +import org.mozilla.fenix.ext.components
|
|
| 17 | + |
|
| 18 | +private val logger = Logger("QANimbusToolingReceiver")
|
|
| 19 | + |
|
| 20 | +/**
|
|
| 21 | + * Receiver triggered on demand via `nimbus-cli` to manually enroll into Nimbus experiments.
|
|
| 22 | + *
|
|
| 23 | + * ```
|
|
| 24 | + * adb shell am broadcast -a org.mozilla.fenix.NIMBUS_TOOLING \
|
|
| 25 | + * -p org.mozilla.fenix
|
|
| 26 | + * ```
|
|
| 27 | + *
|
|
| 28 | + * `-p org.mozilla.fenix` is the package name, so adjust that value for release/beta/nightly/debug.
|
|
| 29 | + *
|
|
| 30 | + * @param dispatcher the [CoroutineDispatcher] the tooling commands are applied on.
|
|
| 31 | + */
|
|
| 32 | +class QANimbusToolingReceiver(private val dispatcher: CoroutineDispatcher = Dispatchers.IO) : BroadcastReceiver() {
|
|
| 33 | + override fun onReceive(context: Context, intent: Intent) {
|
|
| 34 | + if (intent.action != ACTION_NIMBUS_TOOLING) return
|
|
| 35 | + |
|
| 36 | + logger.info("Enqueueing QANimbusToolingReceiver via debug trigger")
|
|
| 37 | + |
|
| 38 | + val applicationContext = context.applicationContext
|
|
| 39 | + |
|
| 40 | + val pendingResult: PendingResult? = goAsync()
|
|
| 41 | + CoroutineScope(dispatcher).launch {
|
|
| 42 | + try {
|
|
| 43 | + applicationContext.components.nimbus.sdk.initializeTooling(
|
|
| 44 | + applicationContext,
|
|
| 45 | + intent,
|
|
| 46 | + )
|
|
| 47 | + } finally {
|
|
| 48 | + logger.info("Nimbus tooling command processed")
|
|
| 49 | + pendingResult?.finish()
|
|
| 50 | + }
|
|
| 51 | + }
|
|
| 52 | + }
|
|
| 53 | + |
|
| 54 | + companion object {
|
|
| 55 | + const val ACTION_NIMBUS_TOOLING = "org.mozilla.fenix.NIMBUS_TOOLING"
|
|
| 56 | + }
|
|
| 57 | +} |
| 1 | +/* This Source Code Form is subject to the terms of the Mozilla Public
|
|
| 2 | + * License, v. 2.0. If a copy of the MPL was not distributed with this
|
|
| 3 | + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
|
| 4 | + |
|
| 5 | +package org.mozilla.fenix.experiments
|
|
| 6 | + |
|
| 7 | +import android.content.Context
|
|
| 8 | +import android.content.Intent
|
|
| 9 | +import io.mockk.every
|
|
| 10 | +import kotlinx.coroutines.ExperimentalCoroutinesApi
|
|
| 11 | +import kotlinx.coroutines.Job
|
|
| 12 | +import kotlinx.coroutines.test.UnconfinedTestDispatcher
|
|
| 13 | +import mozilla.components.support.test.robolectric.testContext
|
|
| 14 | +import org.junit.Assert.assertEquals
|
|
| 15 | +import org.junit.Assert.assertFalse
|
|
| 16 | +import org.junit.Assert.assertNull
|
|
| 17 | +import org.junit.Assert.assertTrue
|
|
| 18 | +import org.junit.Before
|
|
| 19 | +import org.junit.Test
|
|
| 20 | +import org.junit.runner.RunWith
|
|
| 21 | +import org.mozilla.fenix.experiments.QANimbusToolingReceiver.Companion.ACTION_NIMBUS_TOOLING
|
|
| 22 | +import org.mozilla.fenix.ext.components
|
|
| 23 | +import org.mozilla.fenix.nimbus.TestNimbusApi
|
|
| 24 | +import org.robolectric.RobolectricTestRunner
|
|
| 25 | + |
|
| 26 | +@OptIn(ExperimentalCoroutinesApi::class)
|
|
| 27 | +@RunWith(RobolectricTestRunner::class)
|
|
| 28 | +class QANimbusToolingReceiverTest {
|
|
| 29 | + |
|
| 30 | + private val nimbusApi = FakeNimbusApi(testContext)
|
|
| 31 | + private val receiver = QANimbusToolingReceiver(UnconfinedTestDispatcher())
|
|
| 32 | + |
|
| 33 | + @Before
|
|
| 34 | + fun setUp() {
|
|
| 35 | + every { testContext.components.nimbus.sdk } returns nimbusApi
|
|
| 36 | + }
|
|
| 37 | + |
|
| 38 | + @Test
|
|
| 39 | + fun `GIVEN a tooling command WHEN the tooling action is received THEN the command is applied`() {
|
|
| 40 | + receiver.onReceive(testContext, toolingIntent(ACTION_NIMBUS_TOOLING))
|
|
| 41 | + |
|
| 42 | + assertEquals(EXPERIMENTS, nimbusApi.appliedExperiments)
|
|
| 43 | + assertEquals(false, nimbusApi.fetchEnabled)
|
|
| 44 | + assertTrue(nimbusApi.databaseReset)
|
|
| 45 | + assertTrue(nimbusApi.stateDumped)
|
|
| 46 | + }
|
|
| 47 | + |
|
| 48 | + @Test
|
|
| 49 | + fun `GIVEN a tooling command WHEN another action is received THEN the command is ignored`() {
|
|
| 50 | + receiver.onReceive(testContext, toolingIntent("org.mozilla.fenix.ACTION_PRINT"))
|
|
| 51 | + |
|
| 52 | + assertNull(nimbusApi.appliedExperiments)
|
|
| 53 | + assertNull(nimbusApi.fetchEnabled)
|
|
| 54 | + assertFalse(nimbusApi.databaseReset)
|
|
| 55 | + assertFalse(nimbusApi.stateDumped)
|
|
| 56 | + }
|
|
| 57 | + |
|
| 58 | + @Test
|
|
| 59 | + fun `GIVEN no tooling command WHEN the tooling action is received THEN nothing is applied`() {
|
|
| 60 | + receiver.onReceive(testContext, Intent(ACTION_NIMBUS_TOOLING))
|
|
| 61 | + |
|
| 62 | + assertNull(nimbusApi.appliedExperiments)
|
|
| 63 | + assertNull(nimbusApi.fetchEnabled)
|
|
| 64 | + assertFalse(nimbusApi.databaseReset)
|
|
| 65 | + assertFalse(nimbusApi.stateDumped)
|
|
| 66 | + }
|
|
| 67 | + |
|
| 68 | + @Test
|
|
| 69 | + fun `GIVEN a tooling command without the version extra WHEN the tooling action is received THEN nothing is applied`() {
|
|
| 70 | + val intent = toolingIntent(ACTION_NIMBUS_TOOLING).apply { removeExtra("version") }
|
|
| 71 | + |
|
| 72 | + receiver.onReceive(testContext, intent)
|
|
| 73 | + |
|
| 74 | + assertNull(nimbusApi.appliedExperiments)
|
|
| 75 | + assertFalse(nimbusApi.stateDumped)
|
|
| 76 | + }
|
|
| 77 | + |
|
| 78 | + private fun toolingIntent(action: String) =
|
|
| 79 | + Intent(action).apply {
|
|
| 80 | + putExtra("nimbus-cli", null as String?)
|
|
| 81 | + putExtra("version", 1)
|
|
| 82 | + putExtra("experiments", EXPERIMENTS)
|
|
| 83 | + putExtra("reset-db", true)
|
|
| 84 | + putExtra("log-state", true)
|
|
| 85 | + }
|
|
| 86 | + |
|
| 87 | + private class FakeNimbusApi(context: Context) : TestNimbusApi(context) {
|
|
| 88 | + var appliedExperiments: String? = null
|
|
| 89 | + var fetchEnabled: Boolean? = null
|
|
| 90 | + var databaseReset = false
|
|
| 91 | + var stateDumped = false
|
|
| 92 | + |
|
| 93 | + override fun applyLocalExperiments(experimentsJson: String): Job {
|
|
| 94 | + appliedExperiments = experimentsJson
|
|
| 95 | + return completedJob()
|
|
| 96 | + }
|
|
| 97 | + |
|
| 98 | + override fun resetEnrollmentsDatabase(): Job {
|
|
| 99 | + databaseReset = true
|
|
| 100 | + return completedJob()
|
|
| 101 | + }
|
|
| 102 | + |
|
| 103 | + override fun setFetchEnabled(enabled: Boolean) {
|
|
| 104 | + fetchEnabled = enabled
|
|
| 105 | + }
|
|
| 106 | + |
|
| 107 | + override fun dumpStateToLog() {
|
|
| 108 | + stateDumped = true
|
|
| 109 | + }
|
|
| 110 | + |
|
| 111 | + private fun completedJob() = Job().apply { complete() }
|
|
| 112 | + }
|
|
| 113 | + |
|
| 114 | + companion object {
|
|
| 115 | + private const val EXPERIMENTS = """{"data":[]}"""
|
|
| 116 | + }
|
|
| 117 | +} |
| ... | ... | @@ -7826,7 +7826,7 @@ |
| 7826 | 7826 | #ifdef XP_MACOSX
|
| 7827 | 7827 | - name: gfx.webrender.enable-client-storage
|
| 7828 | 7828 | type: bool
|
| 7829 | - value: true
|
|
| 7829 | + value: false
|
|
| 7830 | 7830 | mirror: once
|
| 7831 | 7831 | #endif
|
| 7832 | 7832 |
| ... | ... | @@ -106,8 +106,15 @@ nsresult nsHttpChunkedDecoder::ParseChunkRemaining(char* buf, uint32_t count, |
| 106 | 106 | count = mLineBuf.Length();
|
| 107 | 107 | }
|
| 108 | 108 | |
| 109 | + // NUL is illegal in a chunk-size or trailer field line (RFC 9110 section
|
|
| 110 | + // 5.5).
|
|
| 111 | + if (memchr(buf, '\0', count)) {
|
|
| 112 | + LOG(("chunked line contains embedded NUL; rejecting\n"));
|
|
| 113 | + return NS_ERROR_UNEXPECTED;
|
|
| 114 | + }
|
|
| 115 | + |
|
| 109 | 116 | if (mWaitEOF) {
|
| 110 | - if (*buf) {
|
|
| 117 | + if (count) {
|
|
| 111 | 118 | LOG(("got trailer: %s\n", buf));
|
| 112 | 119 | // allocate a header array for the trailers on demand
|
| 113 | 120 | if (!mTrailers) {
|
| ... | ... | @@ -130,7 +137,7 @@ nsresult nsHttpChunkedDecoder::ParseChunkRemaining(char* buf, uint32_t count, |
| 130 | 137 | mReachedEOF = true;
|
| 131 | 138 | LOG(("reached end of chunked-body\n"));
|
| 132 | 139 | }
|
| 133 | - } else if (*buf) {
|
|
| 140 | + } else if (count) {
|
|
| 134 | 141 | char* endptr;
|
| 135 | 142 | unsigned long parsedval; // could be 64 bit, could be 32
|
| 136 | 143 |
| 1 | +<!DOCTYPE html>
|
|
| 2 | +<meta charset="utf-8">
|
|
| 3 | +<link rel="author" title="Ting-Yu Lin" href="mailto:tlin@mozilla.com">
|
|
| 4 | +<link rel="help" href="https://bugzilla.mozilla.org/show_bug.cgi?id=2053578">
|
|
| 5 | + |
|
| 6 | +<!-- The operations in <script> are generated from one of the runs from
|
|
| 7 | + the original testcase (Bug 2053578 Comment 5) that triggers
|
|
| 8 | + the assertion. -->
|
|
| 9 | + |
|
| 10 | +<body></body>
|
|
| 11 | + |
|
| 12 | +<script>
|
|
| 13 | +var n1 = document.createElement("div");
|
|
| 14 | +n1.style.width = "50px";
|
|
| 15 | +var n2 = document.createElement("bdo");
|
|
| 16 | +n2.setAttribute("dir", "rtl");
|
|
| 17 | +var n3 = document.createTextNode("كلمة");
|
|
| 18 | +n2.appendChild(n3);
|
|
| 19 | +n1.appendChild(n2);
|
|
| 20 | +var n4 = document.createElement("em");
|
|
| 21 | +var n5 = document.createElement("span");
|
|
| 22 | +var n6 = document.createElement("bdo");
|
|
| 23 | +var n7 = document.createTextNode("word m mix");
|
|
| 24 | +n6.appendChild(n7);
|
|
| 25 | +n5.appendChild(n6);
|
|
| 26 | +var n8 = document.createTextNode("מלל");
|
|
| 27 | +n5.appendChild(n8);
|
|
| 28 | +var n9 = document.createElement("b");
|
|
| 29 | +n9.setAttribute("dir", "ltr");
|
|
| 30 | +n9.style.unicodeBidi = "bidi-override";
|
|
| 31 | +var n10 = document.createTextNode("alpha");
|
|
| 32 | +n9.appendChild(n10);
|
|
| 33 | +var n11 = document.createTextNode("نص مرحبا كلمة");
|
|
| 34 | +n9.appendChild(n11);
|
|
| 35 | +var n12 = document.createTextNode("مرحبا نص نص");
|
|
| 36 | +n9.appendChild(n12);
|
|
| 37 | +n5.appendChild(n9);
|
|
| 38 | +n4.appendChild(n5);
|
|
| 39 | +var n13 = document.createElement("span");
|
|
| 40 | +n13.style.unicodeBidi = "bidi-override";
|
|
| 41 | +var n14 = document.createElement("bdi");
|
|
| 42 | +var n15 = document.createTextNode("اختبار");
|
|
| 43 | +n14.appendChild(n15);
|
|
| 44 | +n13.appendChild(n14);
|
|
| 45 | +var n16 = document.createElement("em");
|
|
| 46 | +var n17 = document.createTextNode("בדיקה מלל m نص");
|
|
| 47 | +n16.appendChild(n17);
|
|
| 48 | +var n18 = document.createTextNode("كلمة");
|
|
| 49 | +n16.appendChild(n18);
|
|
| 50 | +var n19 = document.createTextNode("نص مرحبا كلمة");
|
|
| 51 | +n16.appendChild(n19);
|
|
| 52 | +n13.appendChild(n16);
|
|
| 53 | +var n20 = document.createElement("span");
|
|
| 54 | +var n21 = document.createTextNode("שלום מלל בדיקה שלום");
|
|
| 55 | +n20.appendChild(n21);
|
|
| 56 | +n13.appendChild(n20);
|
|
| 57 | +n4.appendChild(n13);
|
|
| 58 | +n1.appendChild(n4);
|
|
| 59 | +document.body.appendChild(n1);
|
|
| 60 | +var n22 = document.createElement("bdo");
|
|
| 61 | +n22.style.unicodeBidi = "plaintext";
|
|
| 62 | +var n23 = document.createElement("span");
|
|
| 63 | +var n24 = document.createTextNode("עברית שלום مرحبا alpha");
|
|
| 64 | +n23.appendChild(n24);
|
|
| 65 | +n22.appendChild(n23);
|
|
| 66 | +n14.style.direction = "ltr";
|
|
| 67 | +var n25 = document.createTextNode("كلمة שלום");
|
|
| 68 | +n2.insertBefore(n25, n3);
|
|
| 69 | +n21.remove();
|
|
| 70 | +n9.setAttribute("dir", "ltr");
|
|
| 71 | +var n26 = document.createTextNode("בדיקה mm");
|
|
| 72 | +n4.appendChild(n26);
|
|
| 73 | +n12.data = "שלום עברית שלום עברית";
|
|
| 74 | +n7.remove();
|
|
| 75 | +n2.style.direction = "ltr";
|
|
| 76 | +n1.style.width = "102px";
|
|
| 77 | +var n27 = document.createTextNode("עברית mm m");
|
|
| 78 | +n22.appendChild(n27);
|
|
| 79 | +n22.setAttribute("dir", "rtl");
|
|
| 80 | +n4.style.direction = "";
|
|
| 81 | +n14.style.direction = "rtl";
|
|
| 82 | +n22.insertBefore(n9, n23);
|
|
| 83 | +var n28 = document.createElement("br");
|
|
| 84 | +n9.insertBefore(n28, n12);
|
|
| 85 | +n14.style.unicodeBidi = "embed";
|
|
| 86 | +n5.insertBefore(n22, n6);
|
|
| 87 | +n9.style.direction = "";
|
|
| 88 | +n9.style.unicodeBidi = "bidi-override";
|
|
| 89 | +n22.remove();
|
|
| 90 | +var n29 = document.createTextNode("עברית שלום");
|
|
| 91 | +n5.insertBefore(n29, n6);
|
|
| 92 | +n18.data = "mix";
|
|
| 93 | +n19.data = "alpha m word";
|
|
| 94 | +n20.style.direction = "ltr";
|
|
| 95 | +var n30 = document.createTextNode("mm עברית עברית نص");
|
|
| 96 | +n6.appendChild(n30);
|
|
| 97 | +n6.style.unicodeBidi = "isolate";
|
|
| 98 | +n20.style.unicodeBidi = "embed";
|
|
| 99 | +n16.setAttribute("dir", "rtl");
|
|
| 100 | +n2.insertBefore(n20, n25);
|
|
| 101 | +var n31 = document.createTextNode("שלום مرحبا mix mm");
|
|
| 102 | +n2.insertBefore(n31, n20);
|
|
| 103 | +var n32 = document.createTextNode("m בדיקה word mix");
|
|
| 104 | +n20.appendChild(n32);
|
|
| 105 | +n16.removeAttribute("dir");
|
|
| 106 | +n16.setAttribute("dir", "auto");
|
|
| 107 | +n5.remove();
|
|
| 108 | +n17.remove();
|
|
| 109 | +n31.data = "בדיקה mm";
|
|
| 110 | +n14.style.unicodeBidi = "isolate";
|
|
| 111 | +n4.remove();
|
|
| 112 | +n20.style.direction = "";
|
|
| 113 | +n20.style.direction = "rtl";
|
|
| 114 | +n3.data = "בדיקה שלום word";
|
|
| 115 | +n25.data = "mm";
|
|
| 116 | +n25.data = "m mix word";
|
|
| 117 | +n1.style.direction = "rtl";
|
|
| 118 | +n31.data = "كلمة اختبار";
|
|
| 119 | +n20.remove();
|
|
| 120 | +n3.data = "שלום نص";
|
|
| 121 | +n31.remove();
|
|
| 122 | +n2.setAttribute("dir", "ltr");
|
|
| 123 | +n2.removeAttribute("dir");
|
|
| 124 | +n1.insertBefore(n4, n2);
|
|
| 125 | +n19.data = "מלל word";
|
|
| 126 | +n15.remove();
|
|
| 127 | +n13.setAttribute("dir", "ltr");
|
|
| 128 | +n25.remove();
|
|
| 129 | +n2.setAttribute("dir", "ltr");
|
|
| 130 | +n14.appendChild(n2);
|
|
| 131 | +n3.data = "مرحبا اختبار مرحبا";
|
|
| 132 | +n18.data = "שלום";
|
|
| 133 | +n16.insertBefore(n20, n18);
|
|
| 134 | +var n33 = document.createTextNode("كلمة نص mm");
|
|
| 135 | +n2.appendChild(n33);
|
|
| 136 | +n19.data = "اختبار مرحبا كلمة كلمة";
|
|
| 137 | +n18.data = "mm mix mix";
|
|
| 138 | +var n34 = document.createElement("br");
|
|
| 139 | +n4.insertBefore(n34, n13);
|
|
| 140 | +n13.style.unicodeBidi = "isolate-override";
|
|
| 141 | +n13.remove();
|
|
| 142 | +n34.remove();
|
|
| 143 | +n26.data = "كلمة كلمة مرحبا";
|
|
| 144 | +n4.setAttribute("dir", "rtl");
|
|
| 145 | +n26.data = "mm word m mm";
|
|
| 146 | +n26.data = "اختبار نص";
|
|
| 147 | +n4.setAttribute("dir", "auto");
|
|
| 148 | +var n35 = document.createTextNode("مرحبا מלל كلمة שלום");
|
|
| 149 | +n4.insertBefore(n35, n26);
|
|
| 150 | +n4.style.unicodeBidi = "embed";
|
|
| 151 | +n4.style.direction = "";
|
|
| 152 | +var n36 = document.createTextNode("كلمة mix בדיקה نص");
|
|
| 153 | +n4.insertBefore(n36, n35);
|
|
| 154 | +n36.remove();
|
|
| 155 | +n4.removeAttribute("dir");
|
|
| 156 | +n4.style.unicodeBidi = "";
|
|
| 157 | +n1.style.width = "87px";
|
|
| 158 | +n26.data = "שלום מלל מלל עברית";
|
|
| 159 | +var n37 = document.createElement("br");
|
|
| 160 | +n4.insertBefore(n37, n35);
|
|
| 161 | +n35.data = "مرحبا نص اختبار";
|
|
| 162 | +n4.setAttribute("dir", "rtl");
|
|
| 163 | +n33.data = "word alpha mm";
|
|
| 164 | +var n38 = document.createTextNode("שלום word");
|
|
| 165 | +n16.insertBefore(n38, n20);
|
|
| 166 | +n2.setAttribute("dir", "auto");
|
|
| 167 | +n2.remove();
|
|
| 168 | +n37.remove();
|
|
| 169 | +var n39 = document.createTextNode("עברית اختبار בדיקה");
|
|
| 170 | +n13.appendChild(n39);
|
|
| 171 | +var n40 = document.createTextNode("בדיקה m نص نص");
|
|
| 172 | +n13.insertBefore(n40, n14);
|
|
| 173 | +var n41 = document.createElement("br");
|
|
| 174 | +n20.appendChild(n41);
|
|
| 175 | +n35.data = "alpha";
|
|
| 176 | +n16.remove();
|
|
| 177 | +n39.data = "نص mm בדיקה اختبار";
|
|
| 178 | +n40.data = "mix mix word m";
|
|
| 179 | +n35.data = "שלום בדיקה";
|
|
| 180 | +n35.data = "m mix mm m";
|
|
| 181 | +n40.data = "مرحبا اختبار";
|
|
| 182 | +n13.style.unicodeBidi = "embed";
|
|
| 183 | +n4.insertBefore(n16, n26);
|
|
| 184 | +var n42 = document.createTextNode("نص");
|
|
| 185 | +n13.insertBefore(n42, n40);
|
|
| 186 | +n41.remove();
|
|
| 187 | +n20.insertBefore(n2, n32);
|
|
| 188 | +document.body.offsetHeight;
|
|
| 189 | +n39.data = "مرحبا alpha עברית word";
|
|
| 190 | +n18.data = "שלום";
|
|
| 191 | +n2.setAttribute("dir", "auto");
|
|
| 192 | +document.body.offsetHeight;
|
|
| 193 | +n20.style.direction = "ltr";
|
|
| 194 | +</script> |
| ... | ... | @@ -443,6 +443,14 @@ void VideoCaptureModulePipeWire::ProcessBuffers() { |
| 443 | 443 | continue;
|
| 444 | 444 | }
|
| 445 | 445 | |
| 446 | + if (static_cast<uint64_t>(spaBuffer->datas[0].chunk->offset) +
|
|
| 447 | + spaBuffer->datas[0].chunk->size >
|
|
| 448 | + spaBuffer->datas[0].maxsize) {
|
|
| 449 | + RTC_LOG(LS_ERROR) << "Dropping frame with invalid size";
|
|
| 450 | + pw_stream_queue_buffer(stream_, buffer);
|
|
| 451 | + continue;
|
|
| 452 | + }
|
|
| 453 | + |
|
| 446 | 454 | if (spaBuffer->datas[0].type == SPA_DATA_DmaBuf ||
|
| 447 | 455 | spaBuffer->datas[0].type == SPA_DATA_MemFd) {
|
| 448 | 456 | ScopedBuf frame;
|
| 1 | +We cherry-picked this in bug 2054625. |
|
| \ No newline at end of file |
| ... | ... | @@ -4,8 +4,20 @@ |
| 4 | 4 | |
| 5 | 5 | "use strict";
|
| 6 | 6 | |
| 7 | +async function precacheAssets(tab, zoom) {
|
|
| 8 | + await SpecialPowers.spawn(tab.linkedBrowser, [zoom], async zoom => {
|
|
| 9 | + const { Layout } = ChromeUtils.importESModule(
|
|
| 10 | + "chrome://mochitests/content/browser/accessible/tests/browser/Layout.sys.mjs"
|
|
| 11 | + );
|
|
| 12 | + Layout.zoomDocument(content.document, zoom);
|
|
| 13 | + const { promise, resolve } = Promise.withResolvers();
|
|
| 14 | + content.requestAnimationFrame(resolve);
|
|
| 15 | + await promise;
|
|
| 16 | + });
|
|
| 17 | +}
|
|
| 18 | + |
|
| 7 | 19 | async function runForZoomLevel(tab, zoom) {
|
| 8 | - const [dpr, scale] = await SpecialPowers.spawn(
|
|
| 20 | + const [dpr, scale, cssScale] = await SpecialPowers.spawn(
|
|
| 9 | 21 | tab.linkedBrowser,
|
| 10 | 22 | [zoom],
|
| 11 | 23 | async zoom => {
|
| ... | ... | @@ -28,9 +40,16 @@ async function runForZoomLevel(tab, zoom) { |
| 28 | 40 | // Workaround: content.devicePixelRatio has the unspoofed value.
|
| 29 | 41 | const dpr = content.wrappedJSObject.devicePixelRatio;
|
| 30 | 42 | const current = img.currentSrc;
|
| 31 | - const scale =
|
|
| 32 | - current.substring(current.length - 6, current.length - 4) / 10;
|
|
| 33 | - return [dpr, scale];
|
|
| 43 | + const scale = parseFloat(current.match(/.*\/([0-9\.]+)\.png$/)[1]);
|
|
| 44 | + |
|
| 45 | + const height = content.document
|
|
| 46 | + .getElementById("imgset")
|
|
| 47 | + .getBoundingClientRect().height;
|
|
| 48 | + // The images have been crafted so that it is possible to reverse their
|
|
| 49 | + // measured size (altered by the zoom) and find the scale.
|
|
| 50 | + const cssScale = Math.round((height * dpr - 50) / 10) / 10 + 0.3;
|
|
| 51 | + |
|
| 52 | + return [dpr, scale, cssScale];
|
|
| 34 | 53 | }
|
| 35 | 54 | );
|
| 36 | 55 | |
| ... | ... | @@ -39,6 +58,11 @@ async function runForZoomLevel(tab, zoom) { |
| 39 | 58 | true,
|
| 40 | 59 | `Image scale (${scale}) is within DPR (${dpr})`
|
| 41 | 60 | );
|
| 61 | + is(
|
|
| 62 | + Math.abs(dpr - cssScale) < 0.1,
|
|
| 63 | + true,
|
|
| 64 | + `CSS scale (${cssScale}) is within DPR (${dpr})`
|
|
| 65 | + );
|
|
| 42 | 66 | }
|
| 43 | 67 | |
| 44 | 68 | add_task(async () => {
|
| ... | ... | @@ -53,6 +77,9 @@ add_task(async () => { |
| 53 | 77 | ) + "srcset.html";
|
| 54 | 78 | const tab = await BrowserTestUtils.openNewForegroundTab(gBrowser, testPage);
|
| 55 | 79 | |
| 80 | + for (let zoom = 0.3; zoom < 2.09; zoom += 0.1) {
|
|
| 81 | + await precacheAssets(tab, zoom);
|
|
| 82 | + }
|
|
| 56 | 83 | for (let zoom = 0.3; zoom < 2.09; zoom += 0.1) {
|
| 57 | 84 | await runForZoomLevel(tab, zoom);
|
| 58 | 85 | }
|
| 1 | 1 | <!DOCTYPE html>
|
| 2 | 2 | <html>
|
| 3 | + <head>
|
|
| 4 | + <style>
|
|
| 5 | +#imgset {
|
|
| 6 | + content: image-set(
|
|
| 7 | + url('data:image/svg+xml;utf8,<svg xmlns="http://www.w3.org/2000/svg" width="50" height="50"></svg>') 0.3x,
|
|
| 8 | + url('data:image/svg+xml;utf8,<svg xmlns="http://www.w3.org/2000/svg" width="60" height="60"></svg>') 0.4x,
|
|
| 9 | + url('data:image/svg+xml;utf8,<svg xmlns="http://www.w3.org/2000/svg" width="70" height="70"></svg>') 0.5x,
|
|
| 10 | + url('data:image/svg+xml;utf8,<svg xmlns="http://www.w3.org/2000/svg" width="80" height="80"></svg>') 0.6x,
|
|
| 11 | + url('data:image/svg+xml;utf8,<svg xmlns="http://www.w3.org/2000/svg" width="90" height="90"></svg>') 0.7x,
|
|
| 12 | + url('data:image/svg+xml;utf8,<svg xmlns="http://www.w3.org/2000/svg" width="100" height="100"></svg>') 0.8x,
|
|
| 13 | + url('data:image/svg+xml;utf8,<svg xmlns="http://www.w3.org/2000/svg" width="110" height="110"></svg>') 0.9x,
|
|
| 14 | + url('data:image/svg+xml;utf8,<svg xmlns="http://www.w3.org/2000/svg" width="120" height="120"></svg>') 1.0x,
|
|
| 15 | + url('data:image/svg+xml;utf8,<svg xmlns="http://www.w3.org/2000/svg" width="130" height="130"></svg>') 1.1x,
|
|
| 16 | + url('data:image/svg+xml;utf8,<svg xmlns="http://www.w3.org/2000/svg" width="140" height="140"></svg>') 1.2x,
|
|
| 17 | + url('data:image/svg+xml;utf8,<svg xmlns="http://www.w3.org/2000/svg" width="150" height="150"></svg>') 1.3x,
|
|
| 18 | + url('data:image/svg+xml;utf8,<svg xmlns="http://www.w3.org/2000/svg" width="160" height="160"></svg>') 1.4x,
|
|
| 19 | + url('data:image/svg+xml;utf8,<svg xmlns="http://www.w3.org/2000/svg" width="170" height="170"></svg>') 1.5x,
|
|
| 20 | + url('data:image/svg+xml;utf8,<svg xmlns="http://www.w3.org/2000/svg" width="180" height="180"></svg>') 1.6x,
|
|
| 21 | + url('data:image/svg+xml;utf8,<svg xmlns="http://www.w3.org/2000/svg" width="190" height="190"></svg>') 1.7x,
|
|
| 22 | + url('data:image/svg+xml;utf8,<svg xmlns="http://www.w3.org/2000/svg" width="200" height="200"></svg>') 1.8x,
|
|
| 23 | + url('data:image/svg+xml;utf8,<svg xmlns="http://www.w3.org/2000/svg" width="210" height="210"></svg>') 1.9x,
|
|
| 24 | + url('data:image/svg+xml;utf8,<svg xmlns="http://www.w3.org/2000/svg" width="220" height="220"></svg>') 2.0x,
|
|
| 25 | + url('data:image/svg+xml;utf8,<svg xmlns="http://www.w3.org/2000/svg" width="230" height="230"></svg>') 2.1x,
|
|
| 26 | + url('data:image/svg+xml;utf8,<svg xmlns="http://www.w3.org/2000/svg" width="240" height="240"></svg>') 2.2x,
|
|
| 27 | + url('data:image/svg+xml;utf8,<svg xmlns="http://www.w3.org/2000/svg" width="250" height="250"></svg>') 2.3x,
|
|
| 28 | + url('data:image/svg+xml;utf8,<svg xmlns="http://www.w3.org/2000/svg" width="260" height="260"></svg>') 2.4x,
|
|
| 29 | + url('data:image/svg+xml;utf8,<svg xmlns="http://www.w3.org/2000/svg" width="270" height="270"></svg>') 2.5x,
|
|
| 30 | + url('data:image/svg+xml;utf8,<svg xmlns="http://www.w3.org/2000/svg" width="280" height="280"></svg>') 2.6x,
|
|
| 31 | + url('data:image/svg+xml;utf8,<svg xmlns="http://www.w3.org/2000/svg" width="290" height="290"></svg>') 2.7x,
|
|
| 32 | + url('data:image/svg+xml;utf8,<svg xmlns="http://www.w3.org/2000/svg" width="300" height="300"></svg>') 2.8x,
|
|
| 33 | + url('data:image/svg+xml;utf8,<svg xmlns="http://www.w3.org/2000/svg" width="310" height="310"></svg>') 2.9x,
|
|
| 34 | + url('data:image/svg+xml;utf8,<svg xmlns="http://www.w3.org/2000/svg" width="320" height="320"></svg>') 3.0x,
|
|
| 35 | + url('data:image/svg+xml;utf8,<svg xmlns="http://www.w3.org/2000/svg" width="330" height="330"></svg>') 3.1x,
|
|
| 36 | + url('data:image/svg+xml;utf8,<svg xmlns="http://www.w3.org/2000/svg" width="340" height="340"></svg>') 3.2x,
|
|
| 37 | + url('data:image/svg+xml;utf8,<svg xmlns="http://www.w3.org/2000/svg" width="350" height="350"></svg>') 3.3x,
|
|
| 38 | + url('data:image/svg+xml;utf8,<svg xmlns="http://www.w3.org/2000/svg" width="360" height="360"></svg>') 3.4x,
|
|
| 39 | + url('data:image/svg+xml;utf8,<svg xmlns="http://www.w3.org/2000/svg" width="370" height="370"></svg>') 3.5x,
|
|
| 40 | + url('data:image/svg+xml;utf8,<svg xmlns="http://www.w3.org/2000/svg" width="380" height="380"></svg>') 3.6x,
|
|
| 41 | + url('data:image/svg+xml;utf8,<svg xmlns="http://www.w3.org/2000/svg" width="390" height="390"></svg>') 3.7x,
|
|
| 42 | + url('data:image/svg+xml;utf8,<svg xmlns="http://www.w3.org/2000/svg" width="400" height="400"></svg>') 3.8x,
|
|
| 43 | + url('data:image/svg+xml;utf8,<svg xmlns="http://www.w3.org/2000/svg" width="410" height="410"></svg>') 3.9x,
|
|
| 44 | + url('data:image/svg+xml;utf8,<svg xmlns="http://www.w3.org/2000/svg" width="420" height="420"></svg>') 4.0x
|
|
| 45 | + );
|
|
| 46 | +}
|
|
| 47 | + </style>
|
|
| 48 | + </head>
|
|
| 3 | 49 | <body>
|
| 4 | 50 | <img id="srcset" srcset="
|
| 5 | - 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,
|
|
| 6 | - 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,
|
|
| 7 | - 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,
|
|
| 8 | - 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,
|
|
| 9 | - 40.png 4x
|
|
| 51 | + 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,
|
|
| 52 | + 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,
|
|
| 53 | + 3.4.png 3.4x, 3.6.png 3.6x, 3.8.png 3.8x, 4.0.png 4.0x
|
|
| 10 | 54 | ">
|
| 55 | + <div id="imgset"></div>
|
|
| 11 | 56 | </body>
|
| 12 | 57 | </html> |
| ... | ... | @@ -1741,6 +1741,14 @@ void nsDragSession::SourceEndDragSession(GdkDragContext* aContext, |
| 1741 | 1741 | LOGDRAGSERVICE("SourceEndDragSession(%p) result %s\n", aContext,
|
| 1742 | 1742 | kGtkDragResults[aResult]);
|
| 1743 | 1743 | |
| 1744 | + // GTK drops its last reference right after drag-end, so reset ours now.
|
|
| 1745 | + //
|
|
| 1746 | + // Clearing only from EndDragSessionImpl is not enough (bug 2058661): that
|
|
| 1747 | + // runs from an async runnable, by which point the pointer already dangles.
|
|
| 1748 | + if (mSourceWindow) {
|
|
| 1749 | + mSourceWindow->SetDragSource(nullptr);
|
|
| 1750 | + }
|
|
| 1751 | + |
|
| 1744 | 1752 | // this just releases the list of data items that we provide
|
| 1745 | 1753 | mSourceDataItems = nullptr;
|
| 1746 | 1754 |
| ... | ... | @@ -91,10 +91,10 @@ StatusBarEntry::StatusBarEntry(Element* aMenu) : mMenu(aMenu), mInitted(false) { |
| 91 | 91 | }
|
| 92 | 92 | |
| 93 | 93 | StatusBarEntry::~StatusBarEntry() {
|
| 94 | + Destroy();
|
|
| 94 | 95 | if (!mInitted) {
|
| 95 | 96 | return;
|
| 96 | 97 | }
|
| 97 | - Destroy();
|
|
| 98 | 98 | ::Shell_NotifyIconW(NIM_DELETE, &mIconData);
|
| 99 | 99 | VERIFY(::DestroyWindow(mIconData.hWnd));
|
| 100 | 100 | }
|
| ... | ... | @@ -233,18 +233,24 @@ mozilla::Result<nsString, Error> GetFolderResults(::IFileDialog* dialog) { |
| 233 | 233 | }
|
| 234 | 234 | |
| 235 | 235 | // If the user chose a Win7 Library, resolve to the library's
|
| 236 | - // default save folder.
|
|
| 237 | - RefPtr<IShellLibrary> shellLib;
|
|
| 238 | - RefPtr<IShellItem> folderPath;
|
|
| 239 | - MOZ_ENSURE_HRESULT_OK(
|
|
| 240 | - "CoCreateInstance(CLSID_ShellLibrary)",
|
|
| 241 | - CoCreateInstance(CLSID_ShellLibrary, nullptr, CLSCTX_INPROC_SERVER,
|
|
| 242 | - IID_IShellLibrary, getter_AddRefs(shellLib)));
|
|
| 243 | - |
|
| 244 | - if (shellLib && SUCCEEDED(shellLib->LoadLibraryFromItem(item, STGM_READ)) &&
|
|
| 245 | - SUCCEEDED(shellLib->GetDefaultSaveFolder(DSFT_DETECT, IID_IShellItem,
|
|
| 246 | - getter_AddRefs(folderPath)))) {
|
|
| 247 | - item.swap(folderPath);
|
|
| 236 | + // default save folder. Only do this for items which are known not to be
|
|
| 237 | + // filesystem objects, like the user's Documents library (but not .library-ms
|
|
| 238 | + // files).
|
|
| 239 | + SFGAOF attrs = 0;
|
|
| 240 | + if (SUCCEEDED(item->GetAttributes(SFGAO_FILESYSTEM, &attrs)) &&
|
|
| 241 | + !(attrs & SFGAO_FILESYSTEM)) {
|
|
| 242 | + RefPtr<IShellLibrary> shellLib;
|
|
| 243 | + RefPtr<IShellItem> folderPath;
|
|
| 244 | + MOZ_ENSURE_HRESULT_OK(
|
|
| 245 | + "CoCreateInstance(CLSID_ShellLibrary)",
|
|
| 246 | + CoCreateInstance(CLSID_ShellLibrary, nullptr, CLSCTX_INPROC_SERVER,
|
|
| 247 | + IID_IShellLibrary, getter_AddRefs(shellLib)));
|
|
| 248 | + |
|
| 249 | + if (shellLib && SUCCEEDED(shellLib->LoadLibraryFromItem(item, STGM_READ)) &&
|
|
| 250 | + SUCCEEDED(shellLib->GetDefaultSaveFolder(DSFT_DETECT, IID_IShellItem,
|
|
| 251 | + getter_AddRefs(folderPath)))) {
|
|
| 252 | + item.swap(folderPath);
|
|
| 253 | + }
|
|
| 248 | 254 | }
|
| 249 | 255 | |
| 250 | 256 | // get the folder's file system path
|