ma1 pushed to branch tor-browser-140.14.0esr-15.0-1 at The Tor Project / Applications / Tor Browser Commits: 28d2f8a6 by Marcin Koziński at 2026-08-16T00:43:28+02:00 Bug 1978587 - Forward onEnterAnimationComplete to interested fragments r=android-reviewers,twhite Differential Revision: https://phabricator.services.mozilla.com/D306954 - - - - - cc401020 by Harveer Singh at 2026-08-16T00:43:29+02:00 Bug 2025732: Increase Cache API opaque response padding. a=RyanVM DONTBUILD Original Revision: https://phabricator.services.mozilla.com/D313509 Differential Revision: https://phabricator.services.mozilla.com/D315608 - - - - - 1f1e6dc7 by Rob Wu at 2026-08-16T00:43:29+02:00 Bug 2045676 - Gracefully handle broken files in verifyBundleSignedState a=RyanVM DONTBUILD Original Revision: https://phabricator.services.mozilla.com/D305180 Differential Revision: https://phabricator.services.mozilla.com/D315593 - - - - - 2d64ea04 by Lee Salzman at 2026-08-16T00:43:30+02:00 Bug 2045711. a=diannaS Original Revision: https://phabricator.services.mozilla.com/D311733 Differential Revision: https://phabricator.services.mozilla.com/D312888 - - - - - 80ebdf97 by Jim Blandy at 2026-08-16T00:43:30+02:00 Bug 2045796: Saturate when rounding up pixman trapezoid edges. a=diannaS DONTBUILD When rounding the edge coordinates of a non-antialised edge upwards, use saturating addition, just in case the coordinates are close to the limit of `pixman_fixed_t`'s range. Original Revision: https://phabricator.services.mozilla.com/D314811 Differential Revision: https://phabricator.services.mozilla.com/D317287 - - - - - 8848a3c2 by Karl Tomlinson at 2026-08-17T08:29:53+02:00 Bug 2050380 Make shared memory transfer conditional on IsSharedMemoryAllowed() a=diannaS DONTBUILD Original Revision: https://phabricator.services.mozilla.com/D312653 Differential Revision: https://phabricator.services.mozilla.com/D313366 - - - - - 34762463 by Andrea Marchesini at 2026-08-17T08:42:39+02:00 Bug 2048353 - Improve worker shutdown support in CookieStoreNotificationWatcher a=diannaS DONTBUILD Original Revision: https://phabricator.services.mozilla.com/D311765 Differential Revision: https://phabricator.services.mozilla.com/D314995 - - - - - 047e6deb by Henri Sivonen at 2026-08-17T08:50:18+02:00 Bug 2053153 - Only check for high surrogates when intending to avoid split pair. a=diannaS DONTBUILD Original Revision: https://phabricator.services.mozilla.com/D314632 Differential Revision: https://phabricator.services.mozilla.com/D316466 - - - - - 993e59cd by Olli Pettay at 2026-08-17T10:01:44+02:00 Bug 2054643, make button's EndSubmitClick handling consistent with input type=button, a=diannaS DONTBUILD Original Revision: https://phabricator.services.mozilla.com/D315429 Differential Revision: https://phabricator.services.mozilla.com/D315956 - - - - - c0635dbe by Dimi at 2026-08-17T10:19:53+02:00 Bug 2054687 - Check iframe's parent matches the browsing context, a=diannaS DONTBUILD Original Revision: https://phabricator.services.mozilla.com/D312950 Differential Revision: https://phabricator.services.mozilla.com/D313656 - - - - - 723477b9 by Lee Salzman at 2026-08-17T10:49:08+02:00 Bug 2060000. r=gfx-reviewers,nical, a=dsmith Differential Revision: https://phabricator.services.mozilla.com/D315942 - - - - - 7017edb5 by Emilio Cobos Álvarez at 2026-08-17T10:51:23+02:00 Bug 2060048 - Force revalidation of Vary: cookie subresources. a=diannaS I think this would fix the image, css, and script caches. Original Revision: https://phabricator.services.mozilla.com/D315988 Differential Revision: https://phabricator.services.mozilla.com/D317319 - - - - - 22 changed files: - dom/base/nsContentUtils.cpp - dom/cache/FileUtils.cpp - dom/canvas/WebGLContextGL.cpp - dom/cookiestore/CookieStoreNotificationWatcherWrapper.cpp - dom/fetch/InternalResponse.cpp - dom/html/HTMLButtonElement.cpp - dom/html/HTMLFormElement.cpp - dom/media/webaudio/AudioWorkletNode.cpp - gfx/cairo/README - gfx/cairo/cairo/src/cairo-truetype-subset.c - gfx/cairo/libpixman/src/pixman-edge-imp.h - + gfx/cairo/patches/0043-Bug-2045711-records-size-check.patch - + gfx/cairo/pixman-edge-saturate.patch - mobile/android/android-components/components/feature/sitepermissions/src/main/java/mozilla/components/feature/sitepermissions/SitePermissionsDialogFragment.kt - + mobile/android/android-components/components/support/utils/src/main/java/mozilla/components/support/utils/OnEnterAnimationCompleteListener.kt - mobile/android/fenix/app/src/main/java/org/mozilla/fenix/customtabs/ExternalAppBrowserActivity.kt - + parser/expat/13_high_surrogate_mask.patch - parser/expat/expat/lib/xmltok.c - parser/expat/moz.yaml - toolkit/components/formautofill/FormAutofillParent.sys.mjs - toolkit/mozapps/extensions/internal/XPIInstall.sys.mjs - toolkit/mozapps/extensions/test/xpcshell/test_signed_verify.js Changes: ===================================== dom/base/nsContentUtils.cpp ===================================== @@ -12196,6 +12196,23 @@ nsContentUtils::GetSubresourceCacheValidationInfo(nsIRequest* aRequest, if (!info.mMustRevalidate) { Unused << httpChannel->IsNoCacheResponse(&info.mMustRevalidate); } + + if (!info.mMustRevalidate) { + nsAutoCString vary; + (void)httpChannel->GetResponseHeader("vary"_ns, vary); + info.mMustRevalidate = [&] { + for (const nsACString& token : + nsCCharSeparatedTokenizer(vary, ',').ToRange()) { + if (token.EqualsLiteral("*")) { + return true; + } + if (token.EqualsIgnoreCase("cookie")) { + return true; + } + } + return false; + }(); + } } // data: URIs are safe to cache across documents under any circumstance, so we ===================================== dom/cache/FileUtils.cpp ===================================== @@ -47,7 +47,7 @@ namespace { // Const variable for generate padding size. // XXX This will be tweaked to something more meaningful in Bug 1383656. -const int64_t kRoundUpNumber = 20480; +const int64_t kRoundUpNumber = 131072; // At the moment, the encrypted stream block size is assumed to be unchangeable // between encrypting and decrypting blobs. This assumptions holds as long as we ===================================== dom/canvas/WebGLContextGL.cpp ===================================== @@ -1361,8 +1361,8 @@ void WebGLContext::UniformData( // - const auto lengthInType = data.size(); - const auto elemCount = lengthInType / channels; - if (elemCount > 1 && !validationInfo.isArray) { + const size_t availElemCount = lengthInType / channels; + if (availElemCount > 1 && !validationInfo.isArray) { GenerateError( LOCAL_GL_INVALID_OPERATION, "(uniform %s) `values` length (%u) must exactly match size of %s.", @@ -1370,6 +1370,10 @@ void WebGLContext::UniformData( EnumString(activeInfo.elemType).c_str()); return; } + const size_t elemCount = + validationInfo.isArray + ? std::min(availElemCount, size_t(activeInfo.elemCount)) + : availElemCount; // - ===================================== dom/cookiestore/CookieStoreNotificationWatcherWrapper.cpp ===================================== @@ -99,13 +99,13 @@ void CookieStoreNotificationWatcherWrapper::ResolvePromiseWhenNotified( mEventTarget(GetCurrentSerialEventTarget()) {} NS_IMETHOD Run() override { - mPromise->MaybeResolveWithUndefined(); - mPromise = nullptr; + if (mPromise) { + mPromise->MaybeResolveWithUndefined(); + mPromise = nullptr; + } return NS_OK; } - bool HasPromise() const { return !!mPromise; } - private: ~PromiseResolver() { NS_ProxyRelease( @@ -140,10 +140,8 @@ void CookieStoreNotificationWatcherWrapper::ResolvePromiseWhenNotified( auto callback = [resolver = RefPtr(resolver), eventTarget = RefPtr(GetCurrentSerialEventTarget()), workerRef = RefPtr(workerRef)] { - if (resolver->HasPromise()) { - RefPtr<Runnable> runnable(resolver); - eventTarget->Dispatch(runnable.forget()); - } + RefPtr<Runnable> runnable(resolver); + eventTarget->Dispatch(runnable.forget()); }; if (!NS_IsMainThread()) { ===================================== dom/fetch/InternalResponse.cpp ===================================== @@ -25,7 +25,7 @@ namespace { // Const variable for generate padding size // XXX This will be tweaked to something more meaningful in Bug 1383656. -const uint32_t kMaxRandomNumber = 102400; +const uint32_t kMaxRandomNumber = 1048576; } // namespace ===================================== dom/html/HTMLButtonElement.cpp ===================================== @@ -256,8 +256,8 @@ void EndSubmitClick(EventChainVisitor& aVisitor) { } void HTMLButtonElement::ActivationBehavior(EventChainPostVisitor& aVisitor) { + auto endSubmit = MakeScopeExit([&] { EndSubmitClick(aVisitor); }); if (!aVisitor.mPresContext) { - // Should check whether EndSubmitClick is needed here. return; } ===================================== dom/html/HTMLFormElement.cpp ===================================== @@ -817,6 +817,10 @@ nsresult HTMLFormElement::SubmitSubmission( return NS_OK; } + if (doc->GetSandboxFlags() & SANDBOXED_FORMS) { + return NS_OK; + } + // javascript URIs are not really submissions; they just call a function. // Also, they may synchronously call submit(), and we want them to be able to // do so while still disallowing other double submissions. (Bug 139798) ===================================== dom/media/webaudio/AudioWorkletNode.cpp ===================================== @@ -767,7 +767,10 @@ already_AddRefed<AudioWorkletNode> AudioWorkletNode::Constructor( // can share memory. JS::CloneDataPolicy cloneDataPolicy; cloneDataPolicy.allowIntraClusterClonableSharedObjects(); - cloneDataPolicy.allowSharedMemoryObjects(); + nsIGlobalObject* currentGlobal = xpc::CurrentNativeGlobal(cx); + if (currentGlobal->IsSharedMemoryAllowed()) { + cloneDataPolicy.allowSharedMemoryObjects(); + } // StructuredCloneHolder does not have a move constructor. Instead allocate // memory so that the pointer can be passed to the rendering thread. ===================================== gfx/cairo/README ===================================== @@ -63,3 +63,5 @@ pixman-export.patch: make sure pixman symbols are not exported in libxul pixman-interp.patch: use lower quality interpolation by default on mobile pixman-rename.patch: include pixman-rename.h for renaming of external symbols + +pixman-edge-saturate.patch: Saturate when rounding up trapezoid edges ===================================== gfx/cairo/cairo/src/cairo-truetype-subset.c ===================================== @@ -1451,13 +1451,22 @@ find_name (tt_name_t *name, unsigned long size, int name_id, int platform, int e { tt_name_record_t *record; unsigned int i, len; + unsigned long max_records; char *str; char *p; cairo_bool_t has_tag; cairo_status_t status; str = NULL; - for (i = 0; i < MIN(be16_to_cpu (name->num_records), size / sizeof(name->records[0])); i++) { + /* records[] starts after the 6-byte tt_name_t header (format, + * num_records, strings_offset); only records lying entirely within the + * size-byte table may be read. */ + if (size < offsetof (tt_name_t, records)) { + *str_out = NULL; + return CAIRO_STATUS_SUCCESS; + } + max_records = (size - offsetof (tt_name_t, records)) / sizeof(name->records[0]); + for (i = 0; i < MIN(be16_to_cpu (name->num_records), max_records); i++) { record = &(name->records[i]); if (be16_to_cpu (record->name) == name_id && be16_to_cpu (record->platform) == platform && ===================================== gfx/cairo/libpixman/src/pixman-edge-imp.h ===================================== @@ -53,10 +53,13 @@ RASTERIZE_EDGES (pixman_image_t *image, * when the sample point lies exactly on the line, we round towards * north-west. * + * Use 64 bits to get a saturating add, in case lx or rx are near + * the limits of pixman_fixed_t. + * * (The AA case does a similar adjustment in RENDER_SAMPLES_X) */ - lx += X_FRAC_FIRST(1) - pixman_fixed_e; - rx += X_FRAC_FIRST(1) - pixman_fixed_e; + lx = (pixman_fixed_t) MIN ((int64_t) lx + (X_FRAC_FIRST(1) - pixman_fixed_e), INT32_MAX); + rx = (pixman_fixed_t) MIN ((int64_t) rx + (X_FRAC_FIRST(1) - pixman_fixed_e), INT32_MAX); #endif /* clip X */ if (lx < 0) ===================================== gfx/cairo/patches/0043-Bug-2045711-records-size-check.patch ===================================== @@ -0,0 +1,37 @@ +diff --git a/gfx/cairo/cairo/src/cairo-truetype-subset.c b/gfx/cairo/cairo/src/cairo-truetype-subset.c +--- a/gfx/cairo/cairo/src/cairo-truetype-subset.c ++++ b/gfx/cairo/cairo/src/cairo-truetype-subset.c +@@ -1446,23 +1446,32 @@ cleanup: + */ + #define MAX_FONT_NAME_LENGTH 127 + + static cairo_status_t + find_name (tt_name_t *name, unsigned long size, int name_id, int platform, int encoding, int language, char **str_out) + { + tt_name_record_t *record; + unsigned int i, len; ++ unsigned long max_records; + char *str; + char *p; + cairo_bool_t has_tag; + cairo_status_t status; + + str = NULL; +- for (i = 0; i < MIN(be16_to_cpu (name->num_records), size / sizeof(name->records[0])); i++) { ++ /* records[] starts after the 6-byte tt_name_t header (format, ++ * num_records, strings_offset); only records lying entirely within the ++ * size-byte table may be read. */ ++ if (size < offsetof (tt_name_t, records)) { ++ *str_out = NULL; ++ return CAIRO_STATUS_SUCCESS; ++ } ++ max_records = (size - offsetof (tt_name_t, records)) / sizeof(name->records[0]); ++ for (i = 0; i < MIN(be16_to_cpu (name->num_records), max_records); i++) { + record = &(name->records[i]); + if (be16_to_cpu (record->name) == name_id && + be16_to_cpu (record->platform) == platform && + be16_to_cpu (record->encoding) == encoding && + (language == -1 || be16_to_cpu (record->language) == language)) { + + len = be16_to_cpu (record->length); + if (platform == 3 && len > MAX_FONT_NAME_LENGTH*2) /* UTF-16 name */ ===================================== gfx/cairo/pixman-edge-saturate.patch ===================================== @@ -0,0 +1,23 @@ +From: Jim Blandy <jimb@mozilla.com> +Subject: Saturate when rounding up trapezoid edges + +diff --git a/gfx/cairo/libpixman/src/pixman-edge-imp.h b/gfx/cairo/libpixman/src/pixman-edge-imp.h +index a4698eddb281..39e8d71d2568 100644 +--- a/gfx/cairo/libpixman/src/pixman-edge-imp.h ++++ b/gfx/cairo/libpixman/src/pixman-edge-imp.h +@@ -53,10 +53,13 @@ RASTERIZE_EDGES (pixman_image_t *image, + * when the sample point lies exactly on the line, we round towards + * north-west. + * ++ * Use 64 bits to get a saturating add, in case lx or rx are near ++ * the limits of pixman_fixed_t. ++ * + * (The AA case does a similar adjustment in RENDER_SAMPLES_X) + */ +- lx += X_FRAC_FIRST(1) - pixman_fixed_e; +- rx += X_FRAC_FIRST(1) - pixman_fixed_e; ++ lx = (pixman_fixed_t) MIN ((int64_t) lx + (X_FRAC_FIRST(1) - pixman_fixed_e), INT32_MAX); ++ rx = (pixman_fixed_t) MIN ((int64_t) rx + (X_FRAC_FIRST(1) - pixman_fixed_e), INT32_MAX); + #endif + /* clip X */ + if (lx < 0) ===================================== mobile/android/android-components/components/feature/sitepermissions/src/main/java/mozilla/components/feature/sitepermissions/SitePermissionsDialogFragment.kt ===================================== @@ -25,6 +25,7 @@ import androidx.core.graphics.drawable.toDrawable import mozilla.components.support.base.android.NoObscuredTouchesDialogFragment import mozilla.components.support.base.log.logger.Logger import mozilla.components.support.ktx.util.PromptAbuserDetector +import mozilla.components.support.utils.OnEnterAnimationCompleteListener internal const val KEY_SESSION_ID = "KEY_SESSION_ID" internal const val KEY_TITLE = "KEY_TITLE" @@ -42,7 +43,9 @@ private const val KEY_IS_NOTIFICATION_REQUEST = "KEY_IS_NOTIFICATION_REQUEST" private const val DEFAULT_VALUE = Int.MAX_VALUE private const val KEY_PERMISSION_ID = "KEY_PERMISSION_ID" -internal open class SitePermissionsDialogFragment : NoObscuredTouchesDialogFragment() { +internal open class SitePermissionsDialogFragment : + NoObscuredTouchesDialogFragment(), + OnEnterAnimationCompleteListener { private val logger = Logger("SitePermissionsDialogFragment") @@ -124,6 +127,11 @@ internal open class SitePermissionsDialogFragment : NoObscuredTouchesDialogFragm feature?.onDismiss(permissionRequestId, sessionId) } + override fun onEnterAnimationComplete() { + // Extend the positive button click delay. + promptAbuserDetector.updateJSDialogAbusedState() + } + private fun Dialog.setContainerView(rootView: View) { if (dialogShouldWidthMatchParent) { setContentView(rootView) ===================================== mobile/android/android-components/components/support/utils/src/main/java/mozilla/components/support/utils/OnEnterAnimationCompleteListener.kt ===================================== @@ -0,0 +1,16 @@ +/* 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 mozilla.components.support.utils + +/** + * Allows forwarding [android.app.Activity.onEnterAnimationComplete] to other classes + * (e.g. fragments) that want to participate in handling it. + */ +interface OnEnterAnimationCompleteListener { + /** + * Called when the Activity's entering animation has completed. + */ + fun onEnterAnimationComplete() +} ===================================== mobile/android/fenix/app/src/main/java/org/mozilla/fenix/customtabs/ExternalAppBrowserActivity.kt ===================================== @@ -13,6 +13,7 @@ import androidx.annotation.VisibleForTesting import androidx.core.net.toUri import mozilla.components.browser.state.selector.findCustomTab import mozilla.components.browser.state.state.SessionState +import mozilla.components.support.utils.OnEnterAnimationCompleteListener import mozilla.components.support.utils.SafeIntent import org.mozilla.fenix.HomeActivity import org.mozilla.fenix.ext.components @@ -95,5 +96,14 @@ open class ExternalAppBrowserActivity : HomeActivity() { override fun onEnterAnimationComplete() { super.onEnterAnimationComplete() isFinishedAnimating = true + + val fragments = supportFragmentManager.fragments.toMutableList() + while (fragments.isNotEmpty()) { + val fragment = fragments.removeAt(0) + if (fragment is OnEnterAnimationCompleteListener) { + fragment.onEnterAnimationComplete() + } + fragments.addAll(fragment.childFragmentManager.fragments) + } } } ===================================== parser/expat/13_high_surrogate_mask.patch ===================================== @@ -0,0 +1,14 @@ +diff --git a/expat/expat/lib/xmltok.c b/expat/expat/lib/xmltok.c +--- a/expat/expat/lib/xmltok.c ++++ b/expat/expat/lib/xmltok.c +@@ -707,7 +707,9 @@ unicode_byte_type(char hi, char lo) { + fromLim = *fromP + (((fromLim - *fromP) >> 1) << 1); /* shrink to even */ \ + /* Avoid copying first half only of surrogate */ \ + if (fromLim - *fromP > ((toLim - *toP) << 1) \ +- && (GET_HI(fromLim - 2) & 0xF8) == 0xD8) { \ ++/* BEGIN MOZILLA CHANGE (Only high surrogate mask) */ \ ++ && (GET_HI(fromLim - 2) & 0xFC) == 0xD8) { \ ++/* END MOZILLA CHANGE */ \ + fromLim -= 2; \ + res = XML_CONVERT_INPUT_INCOMPLETE; \ + } \ ===================================== parser/expat/expat/lib/xmltok.c ===================================== @@ -705,7 +705,9 @@ unicode_byte_type(char hi, char lo) { fromLim = *fromP + (((fromLim - *fromP) >> 1) << 1); /* shrink to even */ \ /* Avoid copying first half only of surrogate */ \ if (fromLim - *fromP > ((toLim - *toP) << 1) \ - && (GET_HI(fromLim - 2) & 0xF8) == 0xD8) { \ +/* BEGIN MOZILLA CHANGE (Only high surrogate mask) */ \ + && (GET_HI(fromLim - 2) & 0xFC) == 0xD8) { \ +/* END MOZILLA CHANGE */ \ fromLim -= 2; \ res = XML_CONVERT_INPUT_INCOMPLETE; \ } \ ===================================== parser/expat/moz.yaml ===================================== @@ -62,3 +62,4 @@ vendoring: - 10_version_limit.patch - 11_no_debug_report.patch - 12_unused.patch + - 13_high_surrogate_mask.patch ===================================== toolkit/components/formautofill/FormAutofillParent.sys.mjs ===================================== @@ -474,6 +474,10 @@ export class FormAutofillParent extends JSWindowActorParent { } const iframeBC = BrowsingContext.get(field.browsingContextId); + if (!iframeBC || iframeBC.parent != browsingContext) { + continue; + } + const [fields] = await this.identifyAllSubTreeFields( iframeBC, focusedBCId, ===================================== toolkit/mozapps/extensions/internal/XPIInstall.sys.mjs ===================================== @@ -931,16 +931,24 @@ function shouldVerifySignedState(aAddonType, aLocation) { * or undefined if the file wasn't signed. */ export var verifyBundleSignedState = async function (aBundle, aAddon) { - let pkg = Package.get(aBundle); try { - let { signedState, signedTypes } = await pkg.verifySignedState( - aAddon.id, - aAddon.type, - aAddon.location - ); - return { signedState, signedTypes }; - } finally { - pkg.close(); + let pkg = Package.get(aBundle); + try { + let { signedState, signedTypes } = await pkg.verifySignedState( + aAddon.id, + aAddon.type, + aAddon.location + ); + return { signedState, signedTypes }; + } finally { + pkg.close(); + } + } catch (e) { + logger.warn(`verifyBundleSignedState failed for ${aAddon.id}`, e); + if (!shouldVerifySignedState(aAddon.type, aAddon.location)) { + return { signedState: AddonManager.SIGNEDSTATE_NOT_REQUIRED }; + } + return { signedState: AddonManager.SIGNEDSTATE_BROKEN }; } }; ===================================== toolkit/mozapps/extensions/test/xpcshell/test_signed_verify.js ===================================== @@ -23,6 +23,13 @@ function verifySignatures() { }); } +async function writeCorruptedXPIFile(extensionId) { + let file = AddonTestUtils.getFileForAddon(profileDir, extensionId); + // Clear any handles to the file before replacing it; Windows is very picky. + Services.obs.notifyObservers(file, "flush-cache-entry"); + await IOUtils.writeUTF8(file.path, "not a XPI file anymore"); +} + createAppInfo("xpcshell@tests.mozilla.org", "XPCShell", "4", "48"); add_setup(async () => { @@ -581,3 +588,159 @@ add_task(async function test_xpi_signed_in_or_before_feb_2018() { ExtensionTestUtils.failOnSchemaWarnings(true); }); + +add_task( + { + ...useAMOStageCert(), + // This test verifies a behavior that is only hit on builds where the + // enterprise policies are enabled (and skipped in build where enterprise + // policies are disabled, like in mobile builds). + skip_if: () => !Services.policies, + }, + async function test_adminInstallOnly_on_verify_with_invalid_manifest() { + const { sinon } = ChromeUtils.importESModule( + "resource://testing-common/Sinon.sys.mjs" + ); + const sandbox = sinon.createSandbox(); + + const { addon: addon1 } = await promiseInstallFile( + do_get_file(`${DATA}/signed1.xpi`) + ); + const { addon: addon2 } = await promiseInstallFile( + do_get_file(`${DATA}/long.xpi`) + ); + + const { XPIExports } = ChromeUtils.importESModule( + "resource://gre/modules/addons/XPIExports.sys.mjs" + ); + sinon + .stub(XPIExports.XPIInstall, "loadManifestFromFile") + .callsFake((_sourceBundle, _location) => { + throw new Error("FAKE invalid manifest error"); + }); + + const { messages } = await AddonTestUtils.promiseConsoleOutput(async () => { + await verifySignatures(); + }); + sandbox.restore(); + + // Expect a logged warning for each of the two extensions. + AddonTestUtils.checkMessages(messages, { + expected: [ + { + message: + /XPI_verifySignature Warning on 'test@somewhere.com': Error: FAKE invalid manifest error/, + }, + { + message: + /XPI_verifySignature Warning on '123456789.*@somewhere.com': Error: FAKE invalid manifest error/, + }, + ], + }); + + await addon1.uninstall(); + await addon2.uninstall(); + } +); + +add_task(useAMOStageCert(), async function test_broken_file() { + await promiseInstallFile(do_get_file(`${DATA}/signed1.xpi`)); + + let addon = await promiseAddonByID(ID); + Assert.notEqual(addon, null); + Assert.equal(addon.appDisabled, false); + Assert.equal(addon.isActive, true); + Assert.equal(addon.signedState, AddonManager.SIGNEDSTATE_SIGNED); + + await writeCorruptedXPIFile(ID); + + let changedProperties = []; + let listener = { + onPropertyChanged(addon, properties) { + changedProperties.push(...properties); + }, + }; + + AddonManager.addAddonListener(listener); + + const disablePromise = promiseAddonEvent("onDisabling"); + let changes; + const { messages } = await AddonTestUtils.promiseConsoleOutput(async () => { + changes = await verifySignatures(); + }); + await disablePromise; + + Assert.equal(changes.enabled.length, 0); + Assert.equal(changes.disabled.length, 1); + Assert.equal(changes.disabled[0], ID); + + Assert.deepEqual( + changedProperties, + ["signedState", "signedTypes", "appDisabled"], + "Got onPropertyChanged events for signedState and appDisabled" + ); + + Assert.ok(addon.appDisabled); + Assert.ok(!addon.isActive); + Assert.equal(addon.signedState, AddonManager.SIGNEDSTATE_BROKEN); + + await addon.uninstall(); + AddonManager.removeAddonListener(listener); + + AddonTestUtils.checkMessages(messages, { + expected: [ + { message: /verifyBundleSignedState failed for test@somewhere.com/ }, + ], + }); +}); + +// Verify that verifySignatures() does not change signedState for addons that +// do not require signatures, even if the underlying file got corrupted. +add_task( + { + ...useAMOStageCert(), + // # Non-extension add-ons are not supported on Android. + skip_if: () => AppConstants.platform == "android", + }, + async function test_broken_file_not_requiring_signatures() { + // Note: If dictionaries ever require signatures (bug 1753276), change this + // test to another test case where shouldVerifySignedState returns false. + let addon = await promiseInstallWebExtension({ + useAddonManager: true, + manifest: { + browser_specific_settings: { gecko: { id: "broken@dict" } }, + dictionaries: { "en-US": "en-US.dic" }, + }, + files: { "en-US.dic": "", "en-US.aff": "" }, + }); + Assert.equal(addon.signedState, AddonManager.SIGNEDSTATE_NOT_REQUIRED); + + await writeCorruptedXPIFile(addon.id); + + let listener = { + onPropertyChanged(_addon) { + Assert.ok(false, `Got unexpected onPropertyChanged for ${_addon.id}`); + }, + }; + + AddonManager.addAddonListener(listener); + + let changes; + const { messages } = await AddonTestUtils.promiseConsoleOutput(async () => { + changes = await verifySignatures(); + }); + Assert.equal(changes.enabled.length, 0); + Assert.equal(changes.disabled.length, 0); + + Assert.equal(addon.appDisabled, false); + Assert.equal(addon.isActive, true); + Assert.equal(addon.signedState, AddonManager.SIGNEDSTATE_NOT_REQUIRED); + + await addon.uninstall(); + AddonManager.removeAddonListener(listener); + + AddonTestUtils.checkMessages(messages, { + expected: [{ message: /verifyBundleSignedState failed for broken@dict/ }], + }); + } +); View it on GitLab: https://gitlab.torproject.org/tpo/applications/tor-browser/-/compare/e71cf32... -- View it on GitLab: https://gitlab.torproject.org/tpo/applications/tor-browser/-/compare/e71cf32... 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
participants (1)
-
ma1 (@ma1)