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
-
cc401020
by Harveer Singh at 2026-08-16T00:43:29+02:00
-
1f1e6dc7
by Rob Wu at 2026-08-16T00:43:29+02:00
-
2d64ea04
by Lee Salzman at 2026-08-16T00:43:30+02:00
-
80ebdf97
by Jim Blandy at 2026-08-16T00:43:30+02:00
-
8848a3c2
by Karl Tomlinson at 2026-08-17T08:29:53+02:00
-
34762463
by Andrea Marchesini at 2026-08-17T08:42:39+02:00
-
047e6deb
by Henri Sivonen at 2026-08-17T08:50:18+02:00
-
993e59cd
by Olli Pettay at 2026-08-17T10:01:44+02:00
-
c0635dbe
by Dimi at 2026-08-17T10:19:53+02:00
-
723477b9
by Lee Salzman at 2026-08-17T10:49:08+02:00
-
7017edb5
by Emilio Cobos Álvarez at 2026-08-17T10:51:23+02:00
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:
| ... | ... | @@ -12196,6 +12196,23 @@ nsContentUtils::GetSubresourceCacheValidationInfo(nsIRequest* aRequest, |
| 12196 | 12196 | if (!info.mMustRevalidate) {
|
| 12197 | 12197 | Unused << httpChannel->IsNoCacheResponse(&info.mMustRevalidate);
|
| 12198 | 12198 | }
|
| 12199 | + |
|
| 12200 | + if (!info.mMustRevalidate) {
|
|
| 12201 | + nsAutoCString vary;
|
|
| 12202 | + (void)httpChannel->GetResponseHeader("vary"_ns, vary);
|
|
| 12203 | + info.mMustRevalidate = [&] {
|
|
| 12204 | + for (const nsACString& token :
|
|
| 12205 | + nsCCharSeparatedTokenizer(vary, ',').ToRange()) {
|
|
| 12206 | + if (token.EqualsLiteral("*")) {
|
|
| 12207 | + return true;
|
|
| 12208 | + }
|
|
| 12209 | + if (token.EqualsIgnoreCase("cookie")) {
|
|
| 12210 | + return true;
|
|
| 12211 | + }
|
|
| 12212 | + }
|
|
| 12213 | + return false;
|
|
| 12214 | + }();
|
|
| 12215 | + }
|
|
| 12199 | 12216 | }
|
| 12200 | 12217 | |
| 12201 | 12218 | // data: URIs are safe to cache across documents under any circumstance, so we
|
| ... | ... | @@ -47,7 +47,7 @@ namespace { |
| 47 | 47 | |
| 48 | 48 | // Const variable for generate padding size.
|
| 49 | 49 | // XXX This will be tweaked to something more meaningful in Bug 1383656.
|
| 50 | -const int64_t kRoundUpNumber = 20480;
|
|
| 50 | +const int64_t kRoundUpNumber = 131072;
|
|
| 51 | 51 | |
| 52 | 52 | // At the moment, the encrypted stream block size is assumed to be unchangeable
|
| 53 | 53 | // between encrypting and decrypting blobs. This assumptions holds as long as we
|
| ... | ... | @@ -1361,8 +1361,8 @@ void WebGLContext::UniformData( |
| 1361 | 1361 | // -
|
| 1362 | 1362 | |
| 1363 | 1363 | const auto lengthInType = data.size();
|
| 1364 | - const auto elemCount = lengthInType / channels;
|
|
| 1365 | - if (elemCount > 1 && !validationInfo.isArray) {
|
|
| 1364 | + const size_t availElemCount = lengthInType / channels;
|
|
| 1365 | + if (availElemCount > 1 && !validationInfo.isArray) {
|
|
| 1366 | 1366 | GenerateError(
|
| 1367 | 1367 | LOCAL_GL_INVALID_OPERATION,
|
| 1368 | 1368 | "(uniform %s) `values` length (%u) must exactly match size of %s.",
|
| ... | ... | @@ -1370,6 +1370,10 @@ void WebGLContext::UniformData( |
| 1370 | 1370 | EnumString(activeInfo.elemType).c_str());
|
| 1371 | 1371 | return;
|
| 1372 | 1372 | }
|
| 1373 | + const size_t elemCount =
|
|
| 1374 | + validationInfo.isArray
|
|
| 1375 | + ? std::min(availElemCount, size_t(activeInfo.elemCount))
|
|
| 1376 | + : availElemCount;
|
|
| 1373 | 1377 | |
| 1374 | 1378 | // -
|
| 1375 | 1379 |
| ... | ... | @@ -99,13 +99,13 @@ void CookieStoreNotificationWatcherWrapper::ResolvePromiseWhenNotified( |
| 99 | 99 | mEventTarget(GetCurrentSerialEventTarget()) {}
|
| 100 | 100 | |
| 101 | 101 | NS_IMETHOD Run() override {
|
| 102 | - mPromise->MaybeResolveWithUndefined();
|
|
| 103 | - mPromise = nullptr;
|
|
| 102 | + if (mPromise) {
|
|
| 103 | + mPromise->MaybeResolveWithUndefined();
|
|
| 104 | + mPromise = nullptr;
|
|
| 105 | + }
|
|
| 104 | 106 | return NS_OK;
|
| 105 | 107 | }
|
| 106 | 108 | |
| 107 | - bool HasPromise() const { return !!mPromise; }
|
|
| 108 | - |
|
| 109 | 109 | private:
|
| 110 | 110 | ~PromiseResolver() {
|
| 111 | 111 | NS_ProxyRelease(
|
| ... | ... | @@ -140,10 +140,8 @@ void CookieStoreNotificationWatcherWrapper::ResolvePromiseWhenNotified( |
| 140 | 140 | auto callback = [resolver = RefPtr(resolver),
|
| 141 | 141 | eventTarget = RefPtr(GetCurrentSerialEventTarget()),
|
| 142 | 142 | workerRef = RefPtr(workerRef)] {
|
| 143 | - if (resolver->HasPromise()) {
|
|
| 144 | - RefPtr<Runnable> runnable(resolver);
|
|
| 145 | - eventTarget->Dispatch(runnable.forget());
|
|
| 146 | - }
|
|
| 143 | + RefPtr<Runnable> runnable(resolver);
|
|
| 144 | + eventTarget->Dispatch(runnable.forget());
|
|
| 147 | 145 | };
|
| 148 | 146 | |
| 149 | 147 | if (!NS_IsMainThread()) {
|
| ... | ... | @@ -25,7 +25,7 @@ namespace { |
| 25 | 25 | |
| 26 | 26 | // Const variable for generate padding size
|
| 27 | 27 | // XXX This will be tweaked to something more meaningful in Bug 1383656.
|
| 28 | -const uint32_t kMaxRandomNumber = 102400;
|
|
| 28 | +const uint32_t kMaxRandomNumber = 1048576;
|
|
| 29 | 29 | |
| 30 | 30 | } // namespace
|
| 31 | 31 |
| ... | ... | @@ -256,8 +256,8 @@ void EndSubmitClick(EventChainVisitor& aVisitor) { |
| 256 | 256 | }
|
| 257 | 257 | |
| 258 | 258 | void HTMLButtonElement::ActivationBehavior(EventChainPostVisitor& aVisitor) {
|
| 259 | + auto endSubmit = MakeScopeExit([&] { EndSubmitClick(aVisitor); });
|
|
| 259 | 260 | if (!aVisitor.mPresContext) {
|
| 260 | - // Should check whether EndSubmitClick is needed here.
|
|
| 261 | 261 | return;
|
| 262 | 262 | }
|
| 263 | 263 |
| ... | ... | @@ -817,6 +817,10 @@ nsresult HTMLFormElement::SubmitSubmission( |
| 817 | 817 | return NS_OK;
|
| 818 | 818 | }
|
| 819 | 819 | |
| 820 | + if (doc->GetSandboxFlags() & SANDBOXED_FORMS) {
|
|
| 821 | + return NS_OK;
|
|
| 822 | + }
|
|
| 823 | + |
|
| 820 | 824 | // javascript URIs are not really submissions; they just call a function.
|
| 821 | 825 | // Also, they may synchronously call submit(), and we want them to be able to
|
| 822 | 826 | // do so while still disallowing other double submissions. (Bug 139798)
|
| ... | ... | @@ -767,7 +767,10 @@ already_AddRefed<AudioWorkletNode> AudioWorkletNode::Constructor( |
| 767 | 767 | // can share memory.
|
| 768 | 768 | JS::CloneDataPolicy cloneDataPolicy;
|
| 769 | 769 | cloneDataPolicy.allowIntraClusterClonableSharedObjects();
|
| 770 | - cloneDataPolicy.allowSharedMemoryObjects();
|
|
| 770 | + nsIGlobalObject* currentGlobal = xpc::CurrentNativeGlobal(cx);
|
|
| 771 | + if (currentGlobal->IsSharedMemoryAllowed()) {
|
|
| 772 | + cloneDataPolicy.allowSharedMemoryObjects();
|
|
| 773 | + }
|
|
| 771 | 774 | |
| 772 | 775 | // StructuredCloneHolder does not have a move constructor. Instead allocate
|
| 773 | 776 | // memory so that the pointer can be passed to the rendering thread.
|
| ... | ... | @@ -63,3 +63,5 @@ pixman-export.patch: make sure pixman symbols are not exported in libxul |
| 63 | 63 | pixman-interp.patch: use lower quality interpolation by default on mobile
|
| 64 | 64 | |
| 65 | 65 | pixman-rename.patch: include pixman-rename.h for renaming of external symbols
|
| 66 | + |
|
| 67 | +pixman-edge-saturate.patch: Saturate when rounding up trapezoid edges |
| ... | ... | @@ -1451,13 +1451,22 @@ find_name (tt_name_t *name, unsigned long size, int name_id, int platform, int e |
| 1451 | 1451 | {
|
| 1452 | 1452 | tt_name_record_t *record;
|
| 1453 | 1453 | unsigned int i, len;
|
| 1454 | + unsigned long max_records;
|
|
| 1454 | 1455 | char *str;
|
| 1455 | 1456 | char *p;
|
| 1456 | 1457 | cairo_bool_t has_tag;
|
| 1457 | 1458 | cairo_status_t status;
|
| 1458 | 1459 | |
| 1459 | 1460 | str = NULL;
|
| 1460 | - for (i = 0; i < MIN(be16_to_cpu (name->num_records), size / sizeof(name->records[0])); i++) {
|
|
| 1461 | + /* records[] starts after the 6-byte tt_name_t header (format,
|
|
| 1462 | + * num_records, strings_offset); only records lying entirely within the
|
|
| 1463 | + * size-byte table may be read. */
|
|
| 1464 | + if (size < offsetof (tt_name_t, records)) {
|
|
| 1465 | + *str_out = NULL;
|
|
| 1466 | + return CAIRO_STATUS_SUCCESS;
|
|
| 1467 | + }
|
|
| 1468 | + max_records = (size - offsetof (tt_name_t, records)) / sizeof(name->records[0]);
|
|
| 1469 | + for (i = 0; i < MIN(be16_to_cpu (name->num_records), max_records); i++) {
|
|
| 1461 | 1470 | record = &(name->records[i]);
|
| 1462 | 1471 | if (be16_to_cpu (record->name) == name_id &&
|
| 1463 | 1472 | be16_to_cpu (record->platform) == platform &&
|
| ... | ... | @@ -53,10 +53,13 @@ RASTERIZE_EDGES (pixman_image_t *image, |
| 53 | 53 | * when the sample point lies exactly on the line, we round towards
|
| 54 | 54 | * north-west.
|
| 55 | 55 | *
|
| 56 | + * Use 64 bits to get a saturating add, in case lx or rx are near
|
|
| 57 | + * the limits of pixman_fixed_t.
|
|
| 58 | + *
|
|
| 56 | 59 | * (The AA case does a similar adjustment in RENDER_SAMPLES_X)
|
| 57 | 60 | */
|
| 58 | - lx += X_FRAC_FIRST(1) - pixman_fixed_e;
|
|
| 59 | - rx += X_FRAC_FIRST(1) - pixman_fixed_e;
|
|
| 61 | + lx = (pixman_fixed_t) MIN ((int64_t) lx + (X_FRAC_FIRST(1) - pixman_fixed_e), INT32_MAX);
|
|
| 62 | + rx = (pixman_fixed_t) MIN ((int64_t) rx + (X_FRAC_FIRST(1) - pixman_fixed_e), INT32_MAX);
|
|
| 60 | 63 | #endif
|
| 61 | 64 | /* clip X */
|
| 62 | 65 | if (lx < 0)
|
| 1 | +diff --git a/gfx/cairo/cairo/src/cairo-truetype-subset.c b/gfx/cairo/cairo/src/cairo-truetype-subset.c
|
|
| 2 | +--- a/gfx/cairo/cairo/src/cairo-truetype-subset.c
|
|
| 3 | ++++ b/gfx/cairo/cairo/src/cairo-truetype-subset.c
|
|
| 4 | +@@ -1446,23 +1446,32 @@ cleanup:
|
|
| 5 | + */
|
|
| 6 | + #define MAX_FONT_NAME_LENGTH 127
|
|
| 7 | +
|
|
| 8 | + static cairo_status_t
|
|
| 9 | + find_name (tt_name_t *name, unsigned long size, int name_id, int platform, int encoding, int language, char **str_out)
|
|
| 10 | + {
|
|
| 11 | + tt_name_record_t *record;
|
|
| 12 | + unsigned int i, len;
|
|
| 13 | ++ unsigned long max_records;
|
|
| 14 | + char *str;
|
|
| 15 | + char *p;
|
|
| 16 | + cairo_bool_t has_tag;
|
|
| 17 | + cairo_status_t status;
|
|
| 18 | +
|
|
| 19 | + str = NULL;
|
|
| 20 | +- for (i = 0; i < MIN(be16_to_cpu (name->num_records), size / sizeof(name->records[0])); i++) {
|
|
| 21 | ++ /* records[] starts after the 6-byte tt_name_t header (format,
|
|
| 22 | ++ * num_records, strings_offset); only records lying entirely within the
|
|
| 23 | ++ * size-byte table may be read. */
|
|
| 24 | ++ if (size < offsetof (tt_name_t, records)) {
|
|
| 25 | ++ *str_out = NULL;
|
|
| 26 | ++ return CAIRO_STATUS_SUCCESS;
|
|
| 27 | ++ }
|
|
| 28 | ++ max_records = (size - offsetof (tt_name_t, records)) / sizeof(name->records[0]);
|
|
| 29 | ++ for (i = 0; i < MIN(be16_to_cpu (name->num_records), max_records); i++) {
|
|
| 30 | + record = &(name->records[i]);
|
|
| 31 | + if (be16_to_cpu (record->name) == name_id &&
|
|
| 32 | + be16_to_cpu (record->platform) == platform &&
|
|
| 33 | + be16_to_cpu (record->encoding) == encoding &&
|
|
| 34 | + (language == -1 || be16_to_cpu (record->language) == language)) {
|
|
| 35 | +
|
|
| 36 | + len = be16_to_cpu (record->length);
|
|
| 37 | + if (platform == 3 && len > MAX_FONT_NAME_LENGTH*2) /* UTF-16 name */ |
| 1 | +From: Jim Blandy <jimb@mozilla.com>
|
|
| 2 | +Subject: Saturate when rounding up trapezoid edges
|
|
| 3 | + |
|
| 4 | +diff --git a/gfx/cairo/libpixman/src/pixman-edge-imp.h b/gfx/cairo/libpixman/src/pixman-edge-imp.h
|
|
| 5 | +index a4698eddb281..39e8d71d2568 100644
|
|
| 6 | +--- a/gfx/cairo/libpixman/src/pixman-edge-imp.h
|
|
| 7 | ++++ b/gfx/cairo/libpixman/src/pixman-edge-imp.h
|
|
| 8 | +@@ -53,10 +53,13 @@ RASTERIZE_EDGES (pixman_image_t *image,
|
|
| 9 | + * when the sample point lies exactly on the line, we round towards
|
|
| 10 | + * north-west.
|
|
| 11 | + *
|
|
| 12 | ++ * Use 64 bits to get a saturating add, in case lx or rx are near
|
|
| 13 | ++ * the limits of pixman_fixed_t.
|
|
| 14 | ++ *
|
|
| 15 | + * (The AA case does a similar adjustment in RENDER_SAMPLES_X)
|
|
| 16 | + */
|
|
| 17 | +- lx += X_FRAC_FIRST(1) - pixman_fixed_e;
|
|
| 18 | +- rx += X_FRAC_FIRST(1) - pixman_fixed_e;
|
|
| 19 | ++ lx = (pixman_fixed_t) MIN ((int64_t) lx + (X_FRAC_FIRST(1) - pixman_fixed_e), INT32_MAX);
|
|
| 20 | ++ rx = (pixman_fixed_t) MIN ((int64_t) rx + (X_FRAC_FIRST(1) - pixman_fixed_e), INT32_MAX);
|
|
| 21 | + #endif
|
|
| 22 | + /* clip X */
|
|
| 23 | + if (lx < 0) |
| ... | ... | @@ -25,6 +25,7 @@ import androidx.core.graphics.drawable.toDrawable |
| 25 | 25 | import mozilla.components.support.base.android.NoObscuredTouchesDialogFragment
|
| 26 | 26 | import mozilla.components.support.base.log.logger.Logger
|
| 27 | 27 | import mozilla.components.support.ktx.util.PromptAbuserDetector
|
| 28 | +import mozilla.components.support.utils.OnEnterAnimationCompleteListener
|
|
| 28 | 29 | |
| 29 | 30 | internal const val KEY_SESSION_ID = "KEY_SESSION_ID"
|
| 30 | 31 | internal const val KEY_TITLE = "KEY_TITLE"
|
| ... | ... | @@ -42,7 +43,9 @@ private const val KEY_IS_NOTIFICATION_REQUEST = "KEY_IS_NOTIFICATION_REQUEST" |
| 42 | 43 | private const val DEFAULT_VALUE = Int.MAX_VALUE
|
| 43 | 44 | private const val KEY_PERMISSION_ID = "KEY_PERMISSION_ID"
|
| 44 | 45 | |
| 45 | -internal open class SitePermissionsDialogFragment : NoObscuredTouchesDialogFragment() {
|
|
| 46 | +internal open class SitePermissionsDialogFragment :
|
|
| 47 | + NoObscuredTouchesDialogFragment(),
|
|
| 48 | + OnEnterAnimationCompleteListener {
|
|
| 46 | 49 | |
| 47 | 50 | private val logger = Logger("SitePermissionsDialogFragment")
|
| 48 | 51 | |
| ... | ... | @@ -124,6 +127,11 @@ internal open class SitePermissionsDialogFragment : NoObscuredTouchesDialogFragm |
| 124 | 127 | feature?.onDismiss(permissionRequestId, sessionId)
|
| 125 | 128 | }
|
| 126 | 129 | |
| 130 | + override fun onEnterAnimationComplete() {
|
|
| 131 | + // Extend the positive button click delay.
|
|
| 132 | + promptAbuserDetector.updateJSDialogAbusedState()
|
|
| 133 | + }
|
|
| 134 | + |
|
| 127 | 135 | private fun Dialog.setContainerView(rootView: View) {
|
| 128 | 136 | if (dialogShouldWidthMatchParent) {
|
| 129 | 137 | setContentView(rootView)
|
| 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 mozilla.components.support.utils
|
|
| 6 | + |
|
| 7 | +/**
|
|
| 8 | + * Allows forwarding [android.app.Activity.onEnterAnimationComplete] to other classes
|
|
| 9 | + * (e.g. fragments) that want to participate in handling it.
|
|
| 10 | + */
|
|
| 11 | +interface OnEnterAnimationCompleteListener {
|
|
| 12 | + /**
|
|
| 13 | + * Called when the Activity's entering animation has completed.
|
|
| 14 | + */
|
|
| 15 | + fun onEnterAnimationComplete()
|
|
| 16 | +} |
| ... | ... | @@ -13,6 +13,7 @@ import androidx.annotation.VisibleForTesting |
| 13 | 13 | import androidx.core.net.toUri
|
| 14 | 14 | import mozilla.components.browser.state.selector.findCustomTab
|
| 15 | 15 | import mozilla.components.browser.state.state.SessionState
|
| 16 | +import mozilla.components.support.utils.OnEnterAnimationCompleteListener
|
|
| 16 | 17 | import mozilla.components.support.utils.SafeIntent
|
| 17 | 18 | import org.mozilla.fenix.HomeActivity
|
| 18 | 19 | import org.mozilla.fenix.ext.components
|
| ... | ... | @@ -95,5 +96,14 @@ open class ExternalAppBrowserActivity : HomeActivity() { |
| 95 | 96 | override fun onEnterAnimationComplete() {
|
| 96 | 97 | super.onEnterAnimationComplete()
|
| 97 | 98 | isFinishedAnimating = true
|
| 99 | + |
|
| 100 | + val fragments = supportFragmentManager.fragments.toMutableList()
|
|
| 101 | + while (fragments.isNotEmpty()) {
|
|
| 102 | + val fragment = fragments.removeAt(0)
|
|
| 103 | + if (fragment is OnEnterAnimationCompleteListener) {
|
|
| 104 | + fragment.onEnterAnimationComplete()
|
|
| 105 | + }
|
|
| 106 | + fragments.addAll(fragment.childFragmentManager.fragments)
|
|
| 107 | + }
|
|
| 98 | 108 | }
|
| 99 | 109 | } |
| 1 | +diff --git a/expat/expat/lib/xmltok.c b/expat/expat/lib/xmltok.c
|
|
| 2 | +--- a/expat/expat/lib/xmltok.c
|
|
| 3 | ++++ b/expat/expat/lib/xmltok.c
|
|
| 4 | +@@ -707,7 +707,9 @@ unicode_byte_type(char hi, char lo) {
|
|
| 5 | + fromLim = *fromP + (((fromLim - *fromP) >> 1) << 1); /* shrink to even */ \
|
|
| 6 | + /* Avoid copying first half only of surrogate */ \
|
|
| 7 | + if (fromLim - *fromP > ((toLim - *toP) << 1) \
|
|
| 8 | +- && (GET_HI(fromLim - 2) & 0xF8) == 0xD8) { \
|
|
| 9 | ++/* BEGIN MOZILLA CHANGE (Only high surrogate mask) */ \
|
|
| 10 | ++ && (GET_HI(fromLim - 2) & 0xFC) == 0xD8) { \
|
|
| 11 | ++/* END MOZILLA CHANGE */ \
|
|
| 12 | + fromLim -= 2; \
|
|
| 13 | + res = XML_CONVERT_INPUT_INCOMPLETE; \
|
|
| 14 | + } \ |
| ... | ... | @@ -705,7 +705,9 @@ unicode_byte_type(char hi, char lo) { |
| 705 | 705 | fromLim = *fromP + (((fromLim - *fromP) >> 1) << 1); /* shrink to even */ \
|
| 706 | 706 | /* Avoid copying first half only of surrogate */ \
|
| 707 | 707 | if (fromLim - *fromP > ((toLim - *toP) << 1) \
|
| 708 | - && (GET_HI(fromLim - 2) & 0xF8) == 0xD8) { \
|
|
| 708 | +/* BEGIN MOZILLA CHANGE (Only high surrogate mask) */ \
|
|
| 709 | + && (GET_HI(fromLim - 2) & 0xFC) == 0xD8) { \
|
|
| 710 | +/* END MOZILLA CHANGE */ \
|
|
| 709 | 711 | fromLim -= 2; \
|
| 710 | 712 | res = XML_CONVERT_INPUT_INCOMPLETE; \
|
| 711 | 713 | } \
|
| ... | ... | @@ -62,3 +62,4 @@ vendoring: |
| 62 | 62 | - 10_version_limit.patch
|
| 63 | 63 | - 11_no_debug_report.patch
|
| 64 | 64 | - 12_unused.patch
|
| 65 | + - 13_high_surrogate_mask.patch |
| ... | ... | @@ -474,6 +474,10 @@ export class FormAutofillParent extends JSWindowActorParent { |
| 474 | 474 | }
|
| 475 | 475 | |
| 476 | 476 | const iframeBC = BrowsingContext.get(field.browsingContextId);
|
| 477 | + if (!iframeBC || iframeBC.parent != browsingContext) {
|
|
| 478 | + continue;
|
|
| 479 | + }
|
|
| 480 | + |
|
| 477 | 481 | const [fields] = await this.identifyAllSubTreeFields(
|
| 478 | 482 | iframeBC,
|
| 479 | 483 | focusedBCId,
|
| ... | ... | @@ -931,16 +931,24 @@ function shouldVerifySignedState(aAddonType, aLocation) { |
| 931 | 931 | * or undefined if the file wasn't signed.
|
| 932 | 932 | */
|
| 933 | 933 | export var verifyBundleSignedState = async function (aBundle, aAddon) {
|
| 934 | - let pkg = Package.get(aBundle);
|
|
| 935 | 934 | try {
|
| 936 | - let { signedState, signedTypes } = await pkg.verifySignedState(
|
|
| 937 | - aAddon.id,
|
|
| 938 | - aAddon.type,
|
|
| 939 | - aAddon.location
|
|
| 940 | - );
|
|
| 941 | - return { signedState, signedTypes };
|
|
| 942 | - } finally {
|
|
| 943 | - pkg.close();
|
|
| 935 | + let pkg = Package.get(aBundle);
|
|
| 936 | + try {
|
|
| 937 | + let { signedState, signedTypes } = await pkg.verifySignedState(
|
|
| 938 | + aAddon.id,
|
|
| 939 | + aAddon.type,
|
|
| 940 | + aAddon.location
|
|
| 941 | + );
|
|
| 942 | + return { signedState, signedTypes };
|
|
| 943 | + } finally {
|
|
| 944 | + pkg.close();
|
|
| 945 | + }
|
|
| 946 | + } catch (e) {
|
|
| 947 | + logger.warn(`verifyBundleSignedState failed for ${aAddon.id}`, e);
|
|
| 948 | + if (!shouldVerifySignedState(aAddon.type, aAddon.location)) {
|
|
| 949 | + return { signedState: AddonManager.SIGNEDSTATE_NOT_REQUIRED };
|
|
| 950 | + }
|
|
| 951 | + return { signedState: AddonManager.SIGNEDSTATE_BROKEN };
|
|
| 944 | 952 | }
|
| 945 | 953 | };
|
| 946 | 954 |
| ... | ... | @@ -23,6 +23,13 @@ function verifySignatures() { |
| 23 | 23 | });
|
| 24 | 24 | }
|
| 25 | 25 | |
| 26 | +async function writeCorruptedXPIFile(extensionId) {
|
|
| 27 | + let file = AddonTestUtils.getFileForAddon(profileDir, extensionId);
|
|
| 28 | + // Clear any handles to the file before replacing it; Windows is very picky.
|
|
| 29 | + Services.obs.notifyObservers(file, "flush-cache-entry");
|
|
| 30 | + await IOUtils.writeUTF8(file.path, "not a XPI file anymore");
|
|
| 31 | +}
|
|
| 32 | + |
|
| 26 | 33 | createAppInfo("xpcshell@tests.mozilla.org", "XPCShell", "4", "48");
|
| 27 | 34 | |
| 28 | 35 | add_setup(async () => {
|
| ... | ... | @@ -581,3 +588,159 @@ add_task(async function test_xpi_signed_in_or_before_feb_2018() { |
| 581 | 588 | |
| 582 | 589 | ExtensionTestUtils.failOnSchemaWarnings(true);
|
| 583 | 590 | });
|
| 591 | + |
|
| 592 | +add_task(
|
|
| 593 | + {
|
|
| 594 | + ...useAMOStageCert(),
|
|
| 595 | + // This test verifies a behavior that is only hit on builds where the
|
|
| 596 | + // enterprise policies are enabled (and skipped in build where enterprise
|
|
| 597 | + // policies are disabled, like in mobile builds).
|
|
| 598 | + skip_if: () => !Services.policies,
|
|
| 599 | + },
|
|
| 600 | + async function test_adminInstallOnly_on_verify_with_invalid_manifest() {
|
|
| 601 | + const { sinon } = ChromeUtils.importESModule(
|
|
| 602 | + "resource://testing-common/Sinon.sys.mjs"
|
|
| 603 | + );
|
|
| 604 | + const sandbox = sinon.createSandbox();
|
|
| 605 | + |
|
| 606 | + const { addon: addon1 } = await promiseInstallFile(
|
|
| 607 | + do_get_file(`${DATA}/signed1.xpi`)
|
|
| 608 | + );
|
|
| 609 | + const { addon: addon2 } = await promiseInstallFile(
|
|
| 610 | + do_get_file(`${DATA}/long.xpi`)
|
|
| 611 | + );
|
|
| 612 | + |
|
| 613 | + const { XPIExports } = ChromeUtils.importESModule(
|
|
| 614 | + "resource://gre/modules/addons/XPIExports.sys.mjs"
|
|
| 615 | + );
|
|
| 616 | + sinon
|
|
| 617 | + .stub(XPIExports.XPIInstall, "loadManifestFromFile")
|
|
| 618 | + .callsFake((_sourceBundle, _location) => {
|
|
| 619 | + throw new Error("FAKE invalid manifest error");
|
|
| 620 | + });
|
|
| 621 | + |
|
| 622 | + const { messages } = await AddonTestUtils.promiseConsoleOutput(async () => {
|
|
| 623 | + await verifySignatures();
|
|
| 624 | + });
|
|
| 625 | + sandbox.restore();
|
|
| 626 | + |
|
| 627 | + // Expect a logged warning for each of the two extensions.
|
|
| 628 | + AddonTestUtils.checkMessages(messages, {
|
|
| 629 | + expected: [
|
|
| 630 | + {
|
|
| 631 | + message:
|
|
| 632 | + /XPI_verifySignature Warning on 'test@somewhere.com': Error: FAKE invalid manifest error/,
|
|
| 633 | + },
|
|
| 634 | + {
|
|
| 635 | + message:
|
|
| 636 | + /XPI_verifySignature Warning on '123456789.*@somewhere.com': Error: FAKE invalid manifest error/,
|
|
| 637 | + },
|
|
| 638 | + ],
|
|
| 639 | + });
|
|
| 640 | + |
|
| 641 | + await addon1.uninstall();
|
|
| 642 | + await addon2.uninstall();
|
|
| 643 | + }
|
|
| 644 | +);
|
|
| 645 | + |
|
| 646 | +add_task(useAMOStageCert(), async function test_broken_file() {
|
|
| 647 | + await promiseInstallFile(do_get_file(`${DATA}/signed1.xpi`));
|
|
| 648 | + |
|
| 649 | + let addon = await promiseAddonByID(ID);
|
|
| 650 | + Assert.notEqual(addon, null);
|
|
| 651 | + Assert.equal(addon.appDisabled, false);
|
|
| 652 | + Assert.equal(addon.isActive, true);
|
|
| 653 | + Assert.equal(addon.signedState, AddonManager.SIGNEDSTATE_SIGNED);
|
|
| 654 | + |
|
| 655 | + await writeCorruptedXPIFile(ID);
|
|
| 656 | + |
|
| 657 | + let changedProperties = [];
|
|
| 658 | + let listener = {
|
|
| 659 | + onPropertyChanged(addon, properties) {
|
|
| 660 | + changedProperties.push(...properties);
|
|
| 661 | + },
|
|
| 662 | + };
|
|
| 663 | + |
|
| 664 | + AddonManager.addAddonListener(listener);
|
|
| 665 | + |
|
| 666 | + const disablePromise = promiseAddonEvent("onDisabling");
|
|
| 667 | + let changes;
|
|
| 668 | + const { messages } = await AddonTestUtils.promiseConsoleOutput(async () => {
|
|
| 669 | + changes = await verifySignatures();
|
|
| 670 | + });
|
|
| 671 | + await disablePromise;
|
|
| 672 | + |
|
| 673 | + Assert.equal(changes.enabled.length, 0);
|
|
| 674 | + Assert.equal(changes.disabled.length, 1);
|
|
| 675 | + Assert.equal(changes.disabled[0], ID);
|
|
| 676 | + |
|
| 677 | + Assert.deepEqual(
|
|
| 678 | + changedProperties,
|
|
| 679 | + ["signedState", "signedTypes", "appDisabled"],
|
|
| 680 | + "Got onPropertyChanged events for signedState and appDisabled"
|
|
| 681 | + );
|
|
| 682 | + |
|
| 683 | + Assert.ok(addon.appDisabled);
|
|
| 684 | + Assert.ok(!addon.isActive);
|
|
| 685 | + Assert.equal(addon.signedState, AddonManager.SIGNEDSTATE_BROKEN);
|
|
| 686 | + |
|
| 687 | + await addon.uninstall();
|
|
| 688 | + AddonManager.removeAddonListener(listener);
|
|
| 689 | + |
|
| 690 | + AddonTestUtils.checkMessages(messages, {
|
|
| 691 | + expected: [
|
|
| 692 | + { message: /verifyBundleSignedState failed for test@somewhere.com/ },
|
|
| 693 | + ],
|
|
| 694 | + });
|
|
| 695 | +});
|
|
| 696 | + |
|
| 697 | +// Verify that verifySignatures() does not change signedState for addons that
|
|
| 698 | +// do not require signatures, even if the underlying file got corrupted.
|
|
| 699 | +add_task(
|
|
| 700 | + {
|
|
| 701 | + ...useAMOStageCert(),
|
|
| 702 | + // # Non-extension add-ons are not supported on Android.
|
|
| 703 | + skip_if: () => AppConstants.platform == "android",
|
|
| 704 | + },
|
|
| 705 | + async function test_broken_file_not_requiring_signatures() {
|
|
| 706 | + // Note: If dictionaries ever require signatures (bug 1753276), change this
|
|
| 707 | + // test to another test case where shouldVerifySignedState returns false.
|
|
| 708 | + let addon = await promiseInstallWebExtension({
|
|
| 709 | + useAddonManager: true,
|
|
| 710 | + manifest: {
|
|
| 711 | + browser_specific_settings: { gecko: { id: "broken@dict" } },
|
|
| 712 | + dictionaries: { "en-US": "en-US.dic" },
|
|
| 713 | + },
|
|
| 714 | + files: { "en-US.dic": "", "en-US.aff": "" },
|
|
| 715 | + });
|
|
| 716 | + Assert.equal(addon.signedState, AddonManager.SIGNEDSTATE_NOT_REQUIRED);
|
|
| 717 | + |
|
| 718 | + await writeCorruptedXPIFile(addon.id);
|
|
| 719 | + |
|
| 720 | + let listener = {
|
|
| 721 | + onPropertyChanged(_addon) {
|
|
| 722 | + Assert.ok(false, `Got unexpected onPropertyChanged for ${_addon.id}`);
|
|
| 723 | + },
|
|
| 724 | + };
|
|
| 725 | + |
|
| 726 | + AddonManager.addAddonListener(listener);
|
|
| 727 | + |
|
| 728 | + let changes;
|
|
| 729 | + const { messages } = await AddonTestUtils.promiseConsoleOutput(async () => {
|
|
| 730 | + changes = await verifySignatures();
|
|
| 731 | + });
|
|
| 732 | + Assert.equal(changes.enabled.length, 0);
|
|
| 733 | + Assert.equal(changes.disabled.length, 0);
|
|
| 734 | + |
|
| 735 | + Assert.equal(addon.appDisabled, false);
|
|
| 736 | + Assert.equal(addon.isActive, true);
|
|
| 737 | + Assert.equal(addon.signedState, AddonManager.SIGNEDSTATE_NOT_REQUIRED);
|
|
| 738 | + |
|
| 739 | + await addon.uninstall();
|
|
| 740 | + AddonManager.removeAddonListener(listener);
|
|
| 741 | + |
|
| 742 | + AddonTestUtils.checkMessages(messages, {
|
|
| 743 | + expected: [{ message: /verifyBundleSignedState failed for broken@dict/ }],
|
|
| 744 | + });
|
|
| 745 | + }
|
|
| 746 | +); |