tbb-commits
Threads by month
- ----- 2026 -----
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2025 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2024 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2023 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2022 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2021 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2020 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2019 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2018 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2017 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2016 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2015 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2014 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- 1 participants
- 20867 discussions
[Git][tpo/applications/tor-browser-build] Pushed new tag tbb-15.0.20-build1
by Pier Angelo Vendrame (@pierov) 17 Aug '26
by Pier Angelo Vendrame (@pierov) 17 Aug '26
17 Aug '26
Pier Angelo Vendrame pushed new tag tbb-15.0.20-build1 at The Tor Project / Applications / tor-browser-build
--
View it on GitLab: https://gitlab.torproject.org/tpo/applications/tor-browser-build/-/tree/tbb…
You're receiving this email because of your account on gitlab.torproject.org. Manage all notifications: https://gitlab.torproject.org/-/profile/notifications | Help: https://gitlab.torproject.org/help
1
0
[Git][tpo/applications/tor-browser-build][maint-15.0] Bug 41848&41849: Prepare Tor, Mullvad Browser 15.0.20
by Pier Angelo Vendrame (@pierov) 17 Aug '26
by Pier Angelo Vendrame (@pierov) 17 Aug '26
17 Aug '26
Pier Angelo Vendrame pushed to branch maint-15.0 at The Tor Project / Applications / tor-browser-build
Commits:
97ba05df by Pier Angelo Vendrame at 2026-08-17T17:51:21+02:00
Bug 41848&41849: Prepare Tor, Mullvad Browser 15.0.20
- - - - -
8 changed files:
- projects/browser/Bundle-Data/Docs-MB/ChangeLog.txt
- projects/browser/Bundle-Data/Docs-TBB/ChangeLog.txt
- projects/browser/config
- projects/firefox/config
- projects/geckoview/config
- projects/go/config
- projects/translation/config
- rbm.conf
Changes:
=====================================
projects/browser/Bundle-Data/Docs-MB/ChangeLog.txt
=====================================
@@ -1,3 +1,13 @@
+Mullvad Browser 15.0.20 - August 18 2026
+ * All Platforms
+ * Updated Firefox to 140.14.0esr
+ * Updated uBlock Origin to 1.73.0
+ * Bug 570: Rebase Mullvad Browser stable onto 140.14.0esr [mullvad-browser]
+ * Bug 45219: Backport Security Fixes from Firefox 154 [tor-browser]
+ * Build System
+ * All Platforms
+ * Bug 41850: Update keyring/torbrowser.gpg for new subkey and updated expiration date of the main key [tor-browser-build]
+
Mullvad Browser 15.0.19 - July 21 2026
* All Platforms
* Updated Firefox to 140.13.0esr
=====================================
projects/browser/Bundle-Data/Docs-TBB/ChangeLog.txt
=====================================
@@ -1,3 +1,18 @@
+Tor Browser 15.0.20 - August 18 2026
+ * All Platforms
+ * Bug 45204: Rebase Tor Browser stable onto 140.14.0esr [tor-browser]
+ * Bug 45219: Backport Security Fixes from Firefox 154 [tor-browser]
+ * Windows + macOS + Linux
+ * Updated Firefox to 140.14.0esr
+ * Android
+ * Updated GeckoView to 140.14.0esr
+ * Build System
+ * All Platforms
+ * Bug 41839: Backport tor-browser-build-browser#41831: Update libevent to 2.1.13 [tor-browser-build]
+ * Bug 41850: Update keyring/torbrowser.gpg for new subkey and updated expiration date of the main key [tor-browser-build]
+ * Windows + Linux + Android
+ * Updated Go to 1.25.13
+
Tor Browser 15.0.19 - July 21 2026
* All Platforms
* Updated NoScript to 13.6.31.1984
=====================================
projects/browser/config
=====================================
@@ -114,15 +114,16 @@ input_files:
- filename: dmg-root
enable: '[% ! c("var/android") %]'
- name: fenix-nightly-apk
- URL: https://ftp.mozilla.org/pub/fenix/nightly/2026/07/2026-07-19-21-13-19-fenix…
+ URL:
+ https://ftp.mozilla.org/pub/fenix/nightly/2026/08/2026-08-17-09-28-31-fenix…
enable: '[% c("var/android") %]'
- sha256sum: 022c3902342f38a3c83c0ab20067decaae6c9488dd824fda2259aba2b4997195
+ sha256sum: 2def423d5714369709d63ce9fd544049afa35ee83db8e37409ea3fcfd0a7c8da
- URL: https://dist.torproject.org/torbrowser/noscript/noscript-13.6.31.1984.xpi
name: noscript
sha256sum: 28e1689956b610f328c582ae45b326cf666d909c468a87f01d48a10e256dad1f
- - URL: https://addons.mozilla.org/firefox/downloads/file/4888680/ublock_origin-1.7…
+ - URL: https://addons.mozilla.org/firefox/downloads/file/4940584/ublock_origin-1.7…
name: ublock-origin
- sha256sum: 40c315b0da7871868155ecfae7a50a58dfa0920aebd865e008214986f1b7c578
+ sha256sum: bccc51a773150af4af6e1fd62c7bfdeb7238b79ff2381b998fa9f2e38f64786a
enable: '[% c("var/mullvad-browser") %]'
- URL: https://cdn.mullvad.net/browser-extension/0.9.10/mullvad-browser-extension-…
name: mullvad-extension
=====================================
projects/firefox/config
=====================================
@@ -18,7 +18,7 @@ container:
use_container: 1
var:
- firefox_platform_version: '140.13.0'
+ firefox_platform_version: '140.14.0'
firefox_version: '[% c("var/firefox_platform_version") %]esr'
browser_series: '15.0'
browser_rebase: 1
=====================================
projects/geckoview/config
=====================================
@@ -20,7 +20,7 @@ container:
build_apk: 1
var:
- firefox_platform_version: '140.13.0'
+ firefox_platform_version: '140.14.0'
geckoview_version: '[% c("var/firefox_platform_version") %]esr'
browser_series: '15.0'
browser_rebase: 1
=====================================
projects/go/config
=====================================
@@ -1,11 +1,11 @@
# vim: filetype=yaml sw=2
-version: '1.25.12'
+version: '1.25.13'
filename: '[% project %]-[% c("version") %]-[% c("var/osname") %]-[% c("var/build_id") %].tar.[% c("compress_tar") %]'
container:
use_container: 1
var:
- source_sha256: f90dcee4bd023fa376374ea0a5a6ebe553537b39c426ffd8c689469b45519932
+ source_sha256: 1d7e2f70b1ee9b93c7df8efcca71f5adcc6a59797a4336c2d10171bd4c174614
no_crosscompile: 1
setup: |
mkdir -p /var/tmp/dist
=====================================
projects/translation/config
=====================================
@@ -12,13 +12,13 @@ compress_tar: 'gz'
steps:
base-browser:
base-browser: '[% INCLUDE build %]'
- git_hash: 38c3b4e6b3b71f9dbf84ed31c0552b2461e506c5
+ git_hash: 2740d708bcedd2fa43489c8d08cdd80b70bd8cf3
targets:
nightly:
git_hash: 'base-browser'
tor-browser:
tor-browser: '[% INCLUDE build %]'
- git_hash: 510b52a6963331b5c67471921ed5c54a77370840
+ git_hash: 1768f6c09cb2c4a012daaf8b33d105a7b48f7bfd
targets:
nightly:
git_hash: 'tor-browser'
@@ -32,7 +32,7 @@ steps:
fenix: '[% INCLUDE build %]'
# We need to bump the commit before releasing but just pointing to a branch
# might cause too much rebuidling of the Firefox part.
- git_hash: ef9b8c76e315678f5c1841ad63f35870106492e4
+ git_hash: fadbeeb9f8724d06903be5aade3e0ce26529d178
compress_tar: 'zst'
targets:
nightly:
=====================================
rbm.conf
=====================================
@@ -74,11 +74,11 @@ buildconf:
git_signtag_opt: '-s'
var:
- torbrowser_version: '15.0.19'
+ torbrowser_version: '15.0.20'
torbrowser_build: 'build1'
# This should be the date of when the build is started. For the build
# to be reproducible, browser_release_date should always be in the past.
- browser_release_date: '2026/07/20 08:00:00'
+ browser_release_date: '2026/08/17 15:00:00'
browser_release_date_timestamp: '[% USE date; date.format(c("var/browser_release_date"), "%s") %]'
browser_default_channel: release
browser_platforms:
@@ -128,10 +128,10 @@ var:
updater_enabled: 1
build_mar: 1
torbrowser_incremental_from:
+ - 15.0.19
- 15.0.18
- '[% IF c("var/tor-browser") %]15.0.17[% END %]'
- - 15.0.16
- - '[% IF c("var/mullvad-browser") %]15.0.14[% END %]'
+ - '[% IF c("var/mullvad-browser") %]15.0.16[% END %]'
mar_channel_id: '[% c("var/projectname") %]-torproject-[% c("var/channel") %]'
# By default, we sort the list of installed packages. This allows sharing
View it on GitLab: https://gitlab.torproject.org/tpo/applications/tor-browser-build/-/commit/9…
--
View it on GitLab: https://gitlab.torproject.org/tpo/applications/tor-browser-build/-/commit/9…
You're receiving this email because of your account on gitlab.torproject.org. Manage all notifications: https://gitlab.torproject.org/-/profile/notifications | Help: https://gitlab.torproject.org/help
1
0
[Git][tpo/applications/mullvad-browser] Pushed new tag mullvad-browser-140.14.0esr-15.0-1-build2
by ma1 (@ma1) 17 Aug '26
by ma1 (@ma1) 17 Aug '26
17 Aug '26
ma1 pushed new tag mullvad-browser-140.14.0esr-15.0-1-build2 at The Tor Project / Applications / Mullvad Browser
--
View it on GitLab: https://gitlab.torproject.org/tpo/applications/mullvad-browser/-/tree/mullv…
You're receiving this email because of your account on gitlab.torproject.org. Manage all notifications: https://gitlab.torproject.org/-/profile/notifications | Help: https://gitlab.torproject.org/help
1
0
[Git][tpo/applications/mullvad-browser][mullvad-browser-140.14.0esr-15.0-1] 12 commits: Bug 1978587 - Forward onEnterAnimationComplete to interested fragments r=android-reviewers,twhite
by ma1 (@ma1) 17 Aug '26
by ma1 (@ma1) 17 Aug '26
17 Aug '26
ma1 pushed to branch mullvad-browser-140.14.0esr-15.0-1 at The Tor Project / Applications / Mullvad Browser
Commits:
9d3a9bf3 by Marcin Koziński at 2026-08-17T17:52:12+02:00
Bug 1978587 - Forward onEnterAnimationComplete to interested fragments r=android-reviewers,twhite
Differential Revision: https://phabricator.services.mozilla.com/D306954
- - - - -
14ab8ec4 by Harveer Singh at 2026-08-17T17:52:12+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
- - - - -
18daf32e by Rob Wu at 2026-08-17T17:52:13+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
- - - - -
51071b8f by Lee Salzman at 2026-08-17T17:52:13+02:00
Bug 2045711. a=diannaS
Original Revision: https://phabricator.services.mozilla.com/D311733
Differential Revision: https://phabricator.services.mozilla.com/D312888
- - - - -
bea32e17 by Jim Blandy at 2026-08-17T17:52:14+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
- - - - -
4c1edd7e by Karl Tomlinson at 2026-08-17T17:52:14+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
- - - - -
67ac4951 by Andrea Marchesini at 2026-08-17T17:52:15+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
- - - - -
f83930c8 by Henri Sivonen at 2026-08-17T17:52:15+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
- - - - -
6b68c8bc by Olli Pettay at 2026-08-17T17:52:16+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
- - - - -
8014b877 by Dimi at 2026-08-17T17:52:16+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
- - - - -
f4c87ed0 by Lee Salzman at 2026-08-17T17:52:17+02:00
Bug 2060000. r=gfx-reviewers,nical, a=dsmith
Differential Revision: https://phabricator.services.mozilla.com/D315942
- - - - -
a3205d37 by Emilio Cobos Álvarez at 2026-08-17T17:52:17+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
=====================================
@@ -12177,6 +12177,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(a)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(a)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(a)somewhere.com': Error: FAKE invalid manifest error/,
+ },
+ {
+ message:
+ /XPI_verifySignature Warning on '123456789.*(a)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(a)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/mullvad-browser/-/compare/d1…
--
View it on GitLab: https://gitlab.torproject.org/tpo/applications/mullvad-browser/-/compare/d1…
You're receiving this email because of your account on gitlab.torproject.org. Manage all notifications: https://gitlab.torproject.org/-/profile/notifications | Help: https://gitlab.torproject.org/help
1
0
[Git][tpo/applications/tor-browser] Pushed new tag tor-browser-140.14.0esr-15.0-1-build2
by ma1 (@ma1) 17 Aug '26
by ma1 (@ma1) 17 Aug '26
17 Aug '26
ma1 pushed new tag tor-browser-140.14.0esr-15.0-1-build2 at The Tor Project / Applications / Tor Browser
--
View it on GitLab: https://gitlab.torproject.org/tpo/applications/tor-browser/-/tree/tor-brows…
You're receiving this email because of your account on gitlab.torproject.org. Manage all notifications: https://gitlab.torproject.org/-/profile/notifications | Help: https://gitlab.torproject.org/help
1
0
[Git][tpo/applications/tor-browser][tor-browser-140.14.0esr-15.0-1] 12 commits: Bug 1978587 - Forward onEnterAnimationComplete to interested fragments r=android-reviewers,twhite
by ma1 (@ma1) 17 Aug '26
by ma1 (@ma1) 17 Aug '26
17 Aug '26
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(a)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(a)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(a)somewhere.com': Error: FAKE invalid manifest error/,
+ },
+ {
+ message:
+ /XPI_verifySignature Warning on '123456789.*(a)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(a)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/e71cf3…
--
View it on GitLab: https://gitlab.torproject.org/tpo/applications/tor-browser/-/compare/e71cf3…
You're receiving this email because of your account on gitlab.torproject.org. Manage all notifications: https://gitlab.torproject.org/-/profile/notifications | Help: https://gitlab.torproject.org/help
1
0
[Git][tpo/applications/mullvad-browser] Pushed new tag mullvad-browser-153.1.0esr-16.0-1-build2
by ma1 (@ma1) 17 Aug '26
by ma1 (@ma1) 17 Aug '26
17 Aug '26
ma1 pushed new tag mullvad-browser-153.1.0esr-16.0-1-build2 at The Tor Project / Applications / Mullvad Browser
--
View it on GitLab: https://gitlab.torproject.org/tpo/applications/mullvad-browser/-/tree/mullv…
You're receiving this email because of your account on gitlab.torproject.org. Manage all notifications: https://gitlab.torproject.org/-/profile/notifications | Help: https://gitlab.torproject.org/help
1
0
[Git][tpo/applications/mullvad-browser][mullvad-browser-153.1.0esr-16.0-1] 5 commits: Bug 1842361 - Download confirmation notification can be overlaid over other...
by ma1 (@ma1) 17 Aug '26
by ma1 (@ma1) 17 Aug '26
17 Aug '26
ma1 pushed to branch mullvad-browser-153.1.0esr-16.0-1 at The Tor Project / Applications / Mullvad Browser
Commits:
afa215f6 by giorga at 2026-08-17T17:44:13+02:00
Bug 1842361 - Download confirmation notification can be overlaid over other origins. r=android-reviewers,jdelorenzo
Differential Revision: https://phabricator.services.mozilla.com/D309062
- - - - -
3f028fb0 by Marcin Koziński at 2026-08-17T17:44:14+02:00
Bug 1978587 - Forward onEnterAnimationComplete to interested fragments r=android-reviewers,twhite
Differential Revision: https://phabricator.services.mozilla.com/D306954
- - - - -
dda99725 by Marcin Koziński at 2026-08-17T17:44:14+02:00
Bug 2049034 - Add an initial delay to download button in Fenix download dialog a=pascalc
Original Revision: https://phabricator.services.mozilla.com/D309334
Differential Revision: https://phabricator.services.mozilla.com/D310049
- - - - -
287fd32c by Jamie Nicol at 2026-08-17T17:44:15+02:00
Bug 2049810 - Allocate HardwareBuffer for screen pixels request in parent process. r=gfx-reviewers,lsalzman
Differential Revision: https://phabricator.services.mozilla.com/D308519
- - - - -
3468dc6e by owlishDeveloper at 2026-08-17T17:44:16+02:00
Bug 2055683 - IPC improvement a=pascalc
Original Revision: https://phabricator.services.mozilla.com/D314463
Differential Revision: https://phabricator.services.mozilla.com/D314798
- - - - -
24 changed files:
- gfx/layers/ipc/PUiCompositorController.ipdl
- gfx/layers/ipc/UiCompositorControllerChild.cpp
- gfx/layers/ipc/UiCompositorControllerChild.h
- gfx/layers/ipc/UiCompositorControllerParent.cpp
- gfx/layers/ipc/UiCompositorControllerParent.h
- gfx/layers/wr/WebRenderBridgeParent.cpp
- gfx/layers/wr/WebRenderBridgeParent.h
- gfx/webrender_bindings/RenderCompositor.h
- gfx/webrender_bindings/RenderCompositorOGLSWGL.cpp
- gfx/webrender_bindings/RenderCompositorOGLSWGL.h
- gfx/webrender_bindings/RendererOGL.cpp
- gfx/webrender_bindings/RendererOGL.h
- gfx/webrender_bindings/WebRenderAPI.cpp
- gfx/webrender_bindings/WebRenderAPI.h
- mobile/android/android-components/components/feature/downloads/src/main/java/mozilla/components/feature/downloads/DownloadsFeature.kt
- mobile/android/android-components/components/feature/downloads/src/test/java/mozilla/components/feature/downloads/DownloadsFeatureTest.kt
- 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/components/geckoview/GeckoViewContentChannelParent.cpp
- mobile/android/fenix/app/src/androidTest/java/org/mozilla/fenix/ui/robots/DownloadRobot.kt
- mobile/android/fenix/app/src/main/java/org/mozilla/fenix/addons/AddonPopupBaseFragment.kt
- mobile/android/fenix/app/src/main/java/org/mozilla/fenix/browser/BaseBrowserFragment.kt
- mobile/android/fenix/app/src/main/java/org/mozilla/fenix/customtabs/ExternalAppBrowserActivity.kt
- mobile/android/fenix/app/src/main/java/org/mozilla/fenix/downloads/RenameAndChangeLocationDialogFragment.kt
Changes:
=====================================
gfx/layers/ipc/PUiCompositorController.ipdl
=====================================
@@ -3,9 +3,7 @@
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
using mozilla::gfx::IntRect from "mozilla/gfx/Rect.h";
-using mozilla::gfx::IntSize from "mozilla/gfx/Point.h";
using mozilla::layers::CompositorScrollUpdate from "mozilla/layers/CompositorScrollUpdate.h";
-using mozilla::void_t from "mozilla/ipc/IPCCore.h";
include "mozilla/GfxMessageUtils.h";
include "mozilla/layers/LayersMessageUtils.h";
@@ -34,15 +32,14 @@ parent:
async MaxToolbarHeight(int32_t aHeight);
async FixedBottomOffset(int32_t aOffset);
async DefaultClearColor(uint32_t aColor);
- async RequestScreenPixels(uint64_t aRequestId, IntRect aSourceRect, IntSize aDestSize);
+ async RequestScreenPixels(uint64_t aRequestId, IntRect aSourceRect,
+ FileDescriptor aHardwareBuffer);
async EnableLayerUpdateNotifications(bool aEnable);
child:
async ToolbarAnimatorMessageFromCompositor(int32_t aMessage);
async NotifyCompositorScrollUpdate(CompositorScrollUpdate aUpdate);
- // Returns when the child side has finished using the HardwareBuffer,
- // indicating that the parent side can now release it.
- async ScreenPixels(uint64_t aRequestId, FileDescriptor? aHardwareBuffer, FileDescriptor? aAcquireFence)
- returns (void_t ok);
+ async ScreenPixels(uint64_t aRequestId, bool aSuccess,
+ FileDescriptor? aAcquireFence);
};
} // layers
=====================================
gfx/layers/ipc/UiCompositorControllerChild.cpp
=====================================
@@ -148,19 +148,36 @@ UiCompositorControllerChild::RequestScreenPixels(gfx::IntRect aSourceRect,
// We only support one request at a time. If an old request is still
// outstanding when a new request is made, just reject the old request.
- if (mScreenPixelsPromise) {
- mScreenPixelsPromise.extract().second->Reject(NS_ERROR_ABORT, __func__);
+ if (mScreenPixelsRequest) {
+ mScreenPixelsRequest.extract().mPromise->Reject(NS_ERROR_ABORT, __func__);
+ }
+
+ RefPtr<layers::AndroidHardwareBuffer> hardwareBuffer =
+ layers::AndroidHardwareBuffer::Create(aDestSize,
+ gfx::SurfaceFormat::R8G8B8A8);
+ if (!hardwareBuffer) {
+ return ScreenPixelsPromise::CreateAndReject(NS_ERROR_OUT_OF_MEMORY,
+ __func__);
+ }
+
+ UniqueFileHandle bufferFd = hardwareBuffer->SerializeToFileDescriptor();
+ if (!bufferFd) {
+ return ScreenPixelsPromise::CreateAndReject(NS_ERROR_FAILURE, __func__);
}
static uint64_t nextRequestId = 0;
const uint64_t requestId = nextRequestId++;
auto promise = MakeRefPtr<ScreenPixelsPromise::Private>(__func__);
- // Using synchronous dispatch ensures we are done using the hardware buffer
- // prior to RecvScreenPixels calling aResolver which in turn will cause the
- // hardware buffer on the parent side to be released.
- promise->UseSynchronousTaskDispatch(__func__);
- mScreenPixelsPromise.emplace(requestId, promise);
- (void)SendRequestScreenPixels(requestId, aSourceRect, aDestSize);
+ mScreenPixelsRequest.emplace(ScreenPixelsRequest{
+ .mRequestId = requestId,
+ .mHardwareBuffer = hardwareBuffer,
+ .mPromise = promise,
+ });
+ if (!SendRequestScreenPixels(requestId, aSourceRect,
+ ipc::FileDescriptor(std::move(bufferFd)))) {
+ mScreenPixelsRequest.extract().mPromise->Reject(NS_ERROR_NOT_AVAILABLE,
+ __func__);
+ }
return promise;
}
#endif
@@ -213,8 +230,8 @@ void UiCompositorControllerChild::ActorDestroy(ActorDestroyReason aWhy) {
mParent = nullptr;
#ifdef MOZ_WIDGET_ANDROID
- if (mScreenPixelsPromise) {
- mScreenPixelsPromise->second->Reject(NS_ERROR_ABORT, __func__);
+ if (mScreenPixelsRequest) {
+ mScreenPixelsRequest->mPromise->Reject(NS_ERROR_ABORT, __func__);
}
#endif
if (mProcessToken) {
@@ -258,39 +275,28 @@ UiCompositorControllerChild::RecvNotifyCompositorScrollUpdate(
}
mozilla::ipc::IPCResult UiCompositorControllerChild::RecvScreenPixels(
- uint64_t aRequestId, Maybe<ipc::FileDescriptor>&& aHardwareBuffer,
- Maybe<ipc::FileDescriptor>&& aAcquireFence,
- ScreenPixelsResolver&& aResolver) {
+ uint64_t aRequestId, bool aSuccess,
+ Maybe<ipc::FileDescriptor>&& aAcquireFence) {
#if defined(MOZ_WIDGET_ANDROID)
- if (!mScreenPixelsPromise || mScreenPixelsPromise->first != aRequestId) {
+ if (!mScreenPixelsRequest || mScreenPixelsRequest->mRequestId != aRequestId) {
// Response is for an outdated request whose promise will have already been
// rejected. Just ignore it.
return IPC_OK();
}
- RefPtr<layers::AndroidHardwareBuffer> hardwareBuffer;
- if (aHardwareBuffer) {
- hardwareBuffer =
- layers::AndroidHardwareBuffer::DeserializeFromFileDescriptor(
- aHardwareBuffer->TakePlatformHandle());
+ auto request = mScreenPixelsRequest.extract();
+ if (!aSuccess) {
+ request.mPromise->Reject(NS_ERROR_FAILURE, __func__);
+ return IPC_OK();
}
- if (hardwareBuffer && aAcquireFence) {
- hardwareBuffer->SetAcquireFence(aAcquireFence->TakePlatformHandle());
+
+ if (aAcquireFence) {
+ request.mHardwareBuffer->SetAcquireFence(
+ aAcquireFence->TakePlatformHandle());
}
- // Note this is resolved synchronously, ensuring we have finished using the
- // hardware buffer as soon as this call returns (and importantly before the
- // aResolver call below).
- mScreenPixelsPromise.extract().second->Resolve(std::move(hardwareBuffer),
- __func__);
+ request.mPromise->Resolve(std::move(request.mHardwareBuffer), __func__);
#endif // defined(MOZ_WIDGET_ANDROID)
- // Notify the parent side that it can drop its reference to the hardware
- // buffer. In theory this could be done as soon as we have called
- // DeserializeFromFileDescriptor(). However, on certain Exynos devices we have
- // seen that releasing the original hardware buffer frees the underlying
- // resource even if a reference obtained via (de)serialization remains alive.
- // See bug 2017901.
- aResolver(void_t{});
return IPC_OK();
}
=====================================
gfx/layers/ipc/UiCompositorControllerChild.h
=====================================
@@ -84,9 +84,8 @@ class UiCompositorControllerChild final
mozilla::ipc::IPCResult RecvNotifyCompositorScrollUpdate(
const CompositorScrollUpdate& aUpdate);
mozilla::ipc::IPCResult RecvScreenPixels(
- uint64_t aRequestId, Maybe<ipc::FileDescriptor>&& aHardwareBuffer,
- Maybe<ipc::FileDescriptor>&& aAcquireFence,
- ScreenPixelsResolver&& aResolver);
+ uint64_t aRequestId, bool aSuccess,
+ Maybe<ipc::FileDescriptor>&& aAcquireFence);
private:
explicit UiCompositorControllerChild(const uint64_t& aProcessToken,
@@ -118,8 +117,12 @@ class UiCompositorControllerChild final
// RecvScreenPixels() altogether. Unfortunately, however, we cannot chain to a
// promise returned from an IPDL function on the Android UI thread, as the
// thread does not support direct task dispatch.
- Maybe<std::pair<uint64_t, RefPtr<ScreenPixelsPromise::Private>>>
- mScreenPixelsPromise;
+ struct ScreenPixelsRequest {
+ uint64_t mRequestId;
+ RefPtr<layers::AndroidHardwareBuffer> mHardwareBuffer;
+ RefPtr<ScreenPixelsPromise::Private> mPromise;
+ };
+ Maybe<ScreenPixelsRequest> mScreenPixelsRequest;
#endif
// Should only be set when compositor is in process.
=====================================
gfx/layers/ipc/UiCompositorControllerParent.cpp
=====================================
@@ -139,39 +139,39 @@ mozilla::ipc::IPCResult UiCompositorControllerParent::RecvDefaultClearColor(
}
mozilla::ipc::IPCResult UiCompositorControllerParent::RecvRequestScreenPixels(
- uint64_t aRequestId, gfx::IntRect aSourceRect, gfx::IntSize aDestSize) {
+ uint64_t aRequestId, gfx::IntRect aSourceRect,
+ ipc::FileDescriptor&& aHardwareBuffer) {
#if defined(MOZ_WIDGET_ANDROID)
+ RefPtr<AndroidHardwareBuffer> hardwareBuffer =
+ AndroidHardwareBuffer::DeserializeFromFileDescriptor(
+ aHardwareBuffer.TakePlatformHandle());
+ if (!hardwareBuffer) {
+ (void)SendScreenPixels(aRequestId, false, Nothing());
+ return IPC_OK();
+ }
+
LayerTreeState* state =
CompositorBridgeParent::GetLayerTreeState(mRootLayerTreeId);
if (state && state->mWrBridge) {
- state->mWrBridge->RequestScreenPixels(aSourceRect, aDestSize)
+ state->mWrBridge->RequestScreenPixels(aSourceRect, hardwareBuffer)
->Then(
GetCurrentSerialEventTarget(), __func__,
- [target = RefPtr{this},
- aRequestId](RefPtr<AndroidHardwareBuffer> aHardwareBuffer) {
- UniqueFileHandle bufferFd =
- aHardwareBuffer->SerializeToFileDescriptor();
+ [target = RefPtr{this}, aRequestId,
+ hardwareBuffer = std::move(hardwareBuffer)](Ok) {
UniqueFileHandle fenceFd =
- aHardwareBuffer->GetAndResetAcquireFence();
- target
- ->SendScreenPixels(
- aRequestId,
- aHardwareBuffer
- ? Some(ipc::FileDescriptor(std::move(bufferFd)))
- : Nothing(),
- fenceFd ? Some(ipc::FileDescriptor(std::move(fenceFd)))
- : Nothing())
- // Ensure the hardware buffer remains alive until child side
- // has finished using it.
- ->Then(GetCurrentSerialEventTarget(), __func__,
- [aHardwareBuffer](
- ScreenPixelsPromise::ResolveOrRejectValue&&) {});
+ hardwareBuffer->GetAndResetAcquireFence();
+ (void)target->SendScreenPixels(
+ aRequestId, true,
+ fenceFd ? Some(ipc::FileDescriptor(std::move(fenceFd)))
+ : Nothing());
},
[target = RefPtr{this}, aRequestId](nsresult aError) {
- (void)target->SendScreenPixels(aRequestId, Nothing(), Nothing());
+ (void)target->SendScreenPixels(aRequestId, false, Nothing());
});
state->mWrBridge->ScheduleForcedGenerateFrame(wr::RenderReasons::OTHER);
+ } else {
+ (void)SendScreenPixels(aRequestId, false, Nothing());
}
#endif // defined(MOZ_WIDGET_ANDROID)
=====================================
gfx/layers/ipc/UiCompositorControllerParent.h
=====================================
@@ -41,9 +41,9 @@ class UiCompositorControllerParent final
mozilla::ipc::IPCResult RecvMaxToolbarHeight(const int32_t& aHeight);
mozilla::ipc::IPCResult RecvFixedBottomOffset(const int32_t& aOffset);
mozilla::ipc::IPCResult RecvDefaultClearColor(const uint32_t& aColor);
- mozilla::ipc::IPCResult RecvRequestScreenPixels(uint64_t aRequestId,
- gfx::IntRect aSourceRect,
- gfx::IntSize aDestSize);
+ mozilla::ipc::IPCResult RecvRequestScreenPixels(
+ uint64_t aRequestId, gfx::IntRect aSourceRect,
+ ipc::FileDescriptor&& aHardwareBuffer);
mozilla::ipc::IPCResult RecvEnableLayerUpdateNotifications(
const bool& aEnable);
void ActorDestroy(ActorDestroyReason aWhy) override;
=====================================
gfx/layers/wr/WebRenderBridgeParent.cpp
=====================================
@@ -1945,8 +1945,8 @@ void WebRenderBridgeParent::UpdateBoolParameters() {
#if defined(MOZ_WIDGET_ANDROID)
RefPtr<WebRenderBridgeParent::ScreenPixelsPromise>
-WebRenderBridgeParent::RequestScreenPixels(gfx::IntRect aSourceRect,
- gfx::IntSize aDestSize) {
+WebRenderBridgeParent::RequestScreenPixels(
+ gfx::IntRect aSourceRect, RefPtr<AndroidHardwareBuffer> aHardwareBuffer) {
if (mDestroyed) {
return ScreenPixelsPromise::CreateAndReject(NS_ERROR_ABORT, __func__);
}
@@ -1962,7 +1962,7 @@ WebRenderBridgeParent::RequestScreenPixels(gfx::IntRect aSourceRect,
}
mScreenPixelsRequest.emplace(ScreenPixelsRequest{
.mSourceRect = aSourceRect,
- .mDestSize = aDestSize,
+ .mHardwareBuffer = std::move(aHardwareBuffer),
.mPromise = new ScreenPixelsPromise::Private(__func__),
});
return mScreenPixelsRequest->mPromise;
@@ -1982,7 +1982,9 @@ void WebRenderBridgeParent::MaybeCaptureScreenPixels() {
MOZ_ASSERT(cbp && !cbp->IsPaused());
# endif
- mLateInit->mApi->RequestScreenPixels(request.mSourceRect, request.mDestSize)
+ mLateInit->mApi
+ ->RequestScreenPixels(request.mSourceRect,
+ std::move(request.mHardwareBuffer))
->ChainTo(request.mPromise.forget(), __func__);
}
#endif
=====================================
gfx/layers/wr/WebRenderBridgeParent.h
=====================================
@@ -325,13 +325,13 @@ class WebRenderBridgeParent final : public PWebRenderBridgeParent,
void BeginRecording(const TimeStamp& aRecordingStart);
#if defined(MOZ_WIDGET_ANDROID)
- using ScreenPixelsPromise =
- MozPromise<RefPtr<layers::AndroidHardwareBuffer>, nsresult, true>;
+ using ScreenPixelsPromise = MozPromise<Ok, nsresult, true>;
/**
* Request a screengrab for android
*/
- RefPtr<ScreenPixelsPromise> RequestScreenPixels(gfx::IntRect aSourceRect,
- gfx::IntSize aDestSize);
+ RefPtr<ScreenPixelsPromise> RequestScreenPixels(
+ gfx::IntRect aSourceRect,
+ RefPtr<layers::AndroidHardwareBuffer> aHardwareBuffer);
#endif
/**
@@ -539,7 +539,7 @@ class WebRenderBridgeParent final : public PWebRenderBridgeParent,
#if defined(MOZ_WIDGET_ANDROID)
struct ScreenPixelsRequest {
gfx::IntRect mSourceRect;
- gfx::IntSize mDestSize;
+ RefPtr<layers::AndroidHardwareBuffer> mHardwareBuffer;
RefPtr<ScreenPixelsPromise::Private> mPromise;
};
Maybe<ScreenPixelsRequest> mScreenPixelsRequest;
=====================================
gfx/webrender_bindings/RenderCompositor.h
=====================================
@@ -235,7 +235,7 @@ class RenderCompositor {
#ifdef MOZ_WIDGET_ANDROID
virtual bool MaybeCaptureScreenPixels(
const gfx::IntRect& aSourceRect,
- RefPtr<layers::AndroidHardwareBuffer> aHardwareBuffer) {
+ layers::AndroidHardwareBuffer* aHardwareBuffer) {
return false;
}
#endif
=====================================
gfx/webrender_bindings/RenderCompositorOGLSWGL.cpp
=====================================
@@ -315,7 +315,7 @@ bool RenderCompositorOGLSWGL::MaybeReadback(
#ifdef MOZ_WIDGET_ANDROID
bool RenderCompositorOGLSWGL::MaybeCaptureScreenPixels(
const gfx::IntRect& aSourceRect,
- RefPtr<layers::AndroidHardwareBuffer> aHardwareBuffer) {
+ layers::AndroidHardwareBuffer* aHardwareBuffer) {
auto* const gl = GetGLContext();
gl::ScopedBindFramebuffer scopedBind(gl);
=====================================
gfx/webrender_bindings/RenderCompositorOGLSWGL.h
=====================================
@@ -59,7 +59,7 @@ class RenderCompositorOGLSWGL : public RenderCompositorLayersSWGL {
#ifdef MOZ_WIDGET_ANDROID
bool MaybeCaptureScreenPixels(
const gfx::IntRect& aSourceRect,
- RefPtr<layers::AndroidHardwareBuffer> aHardwareBuffer) override;
+ layers::AndroidHardwareBuffer* aHardwareBuffer) override;
#endif
private:
=====================================
gfx/webrender_bindings/RendererOGL.cpp
=====================================
@@ -469,7 +469,13 @@ Maybe<layers::FrameRecording> RendererOGL::EndRecording() {
#ifdef MOZ_WIDGET_ANDROID
RefPtr<RendererOGL::ScreenPixelsPromise> RendererOGL::RequestScreenPixels(
- gfx::IntRect aSourceRect, gfx::IntSize aDestSize) {
+ gfx::IntRect aSourceRect,
+ RefPtr<layers::AndroidHardwareBuffer> aHardwareBuffer) {
+ if (!aHardwareBuffer) {
+ return ScreenPixelsPromise::CreateAndReject(NS_ERROR_ILLEGAL_VALUE,
+ __func__);
+ }
+
// If a new request is made we no longer care about the result of the previous
// one, so just reject it if it exists.
if (mPendingScreenPixelsRequest) {
@@ -478,7 +484,7 @@ RefPtr<RendererOGL::ScreenPixelsPromise> RendererOGL::RequestScreenPixels(
}
mPendingScreenPixelsRequest.emplace(ScreenPixelsRequest{
.mSourceRect = aSourceRect,
- .mDestSize = aDestSize,
+ .mHardwareBuffer = std::move(aHardwareBuffer),
.mPromise = new ScreenPixelsPromise::Private(__func__),
});
return mPendingScreenPixelsRequest->mPromise;
@@ -491,19 +497,16 @@ void RendererOGL::MaybeCaptureScreenPixels() {
auto request = mPendingScreenPixelsRequest.extract();
- const RefPtr<layers::AndroidHardwareBuffer> hardwareBuffer =
- layers::AndroidHardwareBuffer::Create(request.mDestSize,
- gfx::SurfaceFormat::R8G8B8A8);
-
if (mCompositor->MaybeCaptureScreenPixels(request.mSourceRect,
- hardwareBuffer)) {
- request.mPromise->Resolve(hardwareBuffer, __func__);
+ request.mHardwareBuffer)) {
+ request.mPromise->Resolve(Ok{}, __func__);
return;
}
auto* const gle = gl::GLContextEGL::Cast(gl());
const auto& egl = gle->mEgl;
- gl::ScopedEGLImageForAndroidHardwareBuffer eglImage(gle, hardwareBuffer);
+ gl::ScopedEGLImageForAndroidHardwareBuffer eglImage(gle,
+ request.mHardwareBuffer);
gl::ScopedBindFramebuffer scopedBind(gl());
gl::ScopedRenderbuffer rb(gl());
gl()->fBindRenderbuffer(LOCAL_GL_RENDERBUFFER, rb);
@@ -517,7 +520,7 @@ void RendererOGL::MaybeCaptureScreenPixels() {
request.mSourceRect.x,
mCompositor->GetBufferSize().height - request.mSourceRect.y,
request.mSourceRect.width, -request.mSourceRect.height);
- const auto destRect = gfx::IntRect({}, hardwareBuffer->mSize);
+ const auto destRect = gfx::IntRect({}, request.mHardwareBuffer->mSize);
gl()->BindReadFB(0);
gl()->BindDrawFB(fb.FB());
gl()->fBlitFramebuffer(srcRect.x, srcRect.y, srcRect.XMost(), srcRect.YMost(),
@@ -529,12 +532,12 @@ void RendererOGL::MaybeCaptureScreenPixels() {
egl->fCreateSync(LOCAL_EGL_SYNC_NATIVE_FENCE_ANDROID, nullptr)) {
auto fence = UniqueFileHandle(egl->fDupNativeFenceFDANDROID(sync));
if (fence) {
- hardwareBuffer->SetAcquireFence(std::move(fence));
+ request.mHardwareBuffer->SetAcquireFence(std::move(fence));
}
egl->fDestroySync(sync);
}
- request.mPromise->Resolve(hardwareBuffer, __func__);
+ request.mPromise->Resolve(Ok{}, __func__);
}
#endif
=====================================
gfx/webrender_bindings/RendererOGL.h
=====================================
@@ -93,12 +93,12 @@ class RendererOGL {
Maybe<layers::FrameRecording> EndRecording();
#ifdef MOZ_WIDGET_ANDROID
- using ScreenPixelsPromise =
- MozPromise<RefPtr<layers::AndroidHardwareBuffer>, nsresult, true>;
+ using ScreenPixelsPromise = MozPromise<Ok, nsresult, true>;
// Captures the pixels for the next rendered frame. Returns a promise that
// resolves once the pixels are captured.
- RefPtr<ScreenPixelsPromise> RequestScreenPixels(gfx::IntRect aSourceRect,
- gfx::IntSize aDestSize);
+ RefPtr<ScreenPixelsPromise> RequestScreenPixels(
+ gfx::IntRect aSourceRect,
+ RefPtr<layers::AndroidHardwareBuffer> aHardwareBuffer);
#endif
/// This can be called on the render thread only.
@@ -174,7 +174,7 @@ class RendererOGL {
#ifdef MOZ_WIDGET_ANDROID
struct ScreenPixelsRequest {
gfx::IntRect mSourceRect;
- gfx::IntSize mDestSize;
+ RefPtr<layers::AndroidHardwareBuffer> mHardwareBuffer;
RefPtr<ScreenPixelsPromise::Private> mPromise;
};
Maybe<ScreenPixelsRequest> mPendingScreenPixelsRequest;
=====================================
gfx/webrender_bindings/WebRenderAPI.cpp
=====================================
@@ -962,12 +962,17 @@ RefPtr<WebRenderAPI::EndRecordingPromise> WebRenderAPI::EndRecording() {
#ifdef MOZ_WIDGET_ANDROID
RefPtr<WebRenderAPI::ScreenPixelsPromise> WebRenderAPI::RequestScreenPixels(
- gfx::IntRect aSourceRect, gfx::IntSize aDestSize) {
+ gfx::IntRect aSourceRect,
+ RefPtr<layers::AndroidHardwareBuffer> aHardwareBuffer) {
class ScreenshotEvent final : public RendererEvent {
public:
- explicit ScreenshotEvent(gfx::IntRect aSourceRect, gfx::IntSize aDestSize,
- RefPtr<ScreenPixelsPromise::Private> aPromise)
- : mSourceRect(aSourceRect), mDestSize(aDestSize), mPromise(aPromise) {
+ explicit ScreenshotEvent(
+ gfx::IntRect aSourceRect,
+ RefPtr<layers::AndroidHardwareBuffer> aHardwareBuffer,
+ RefPtr<ScreenPixelsPromise::Private> aPromise)
+ : mSourceRect(aSourceRect),
+ mHardwareBuffer(std::move(aHardwareBuffer)),
+ mPromise(aPromise) {
MOZ_COUNT_CTOR(ScreenshotEvent);
}
@@ -977,8 +982,9 @@ RefPtr<WebRenderAPI::ScreenPixelsPromise> WebRenderAPI::RequestScreenPixels(
RendererOGL* const renderer = aRenderThread.GetRenderer(aWindowId);
if (!renderer) {
mPromise->Reject(NS_ERROR_FAILURE, __func__);
+ return;
}
- renderer->RequestScreenPixels(mSourceRect, mDestSize)
+ renderer->RequestScreenPixels(mSourceRect, std::move(mHardwareBuffer))
->ChainTo(mPromise.forget(), __func__);
}
@@ -986,12 +992,13 @@ RefPtr<WebRenderAPI::ScreenPixelsPromise> WebRenderAPI::RequestScreenPixels(
private:
const gfx::IntRect mSourceRect;
- const gfx::IntSize mDestSize;
+ RefPtr<layers::AndroidHardwareBuffer> mHardwareBuffer;
RefPtr<ScreenPixelsPromise::Private> mPromise;
};
auto promise = MakeRefPtr<ScreenPixelsPromise::Private>(__func__);
- auto event = MakeUnique<ScreenshotEvent>(aSourceRect, aDestSize, promise);
+ auto event = MakeUnique<ScreenshotEvent>(aSourceRect,
+ std::move(aHardwareBuffer), promise);
RenderThread::Get()->PostEvent(mId, std::move(event));
return promise;
=====================================
gfx/webrender_bindings/WebRenderAPI.h
=====================================
@@ -322,13 +322,13 @@ class WebRenderAPI final {
RefPtr<EndRecordingPromise> EndRecording();
#ifdef MOZ_WIDGET_ANDROID
- using ScreenPixelsPromise =
- MozPromise<RefPtr<layers::AndroidHardwareBuffer>, nsresult, true>;
+ using ScreenPixelsPromise = MozPromise<Ok, nsresult, true>;
// Queues a task to the render thread to capture screen pixels for the next
// rendered frame. Returns a promise that resolves once the pixels are
// captured.
- RefPtr<ScreenPixelsPromise> RequestScreenPixels(gfx::IntRect aSourceRect,
- gfx::IntSize aDestSize);
+ RefPtr<ScreenPixelsPromise> RequestScreenPixels(
+ gfx::IntRect aSourceRect,
+ RefPtr<layers::AndroidHardwareBuffer> aHardwareBuffer);
#endif
layers::RemoteTextureInfoList* GetPendingRemoteTextureInfoList();
=====================================
mobile/android/android-components/components/feature/downloads/src/main/java/mozilla/components/feature/downloads/DownloadsFeature.kt
=====================================
@@ -123,6 +123,8 @@ value class OpenFileCallback(val value: () -> Unit)
* manager is provided, a dialog will be shown before every download.
* @property promptsStyling styling properties for the dialog.
* @property onDownloadStartedListener a callback invoked when a download is started.
+ * @property dismissCustomFirstPartyDownloadDialog A callback invoked when the custom first party
+ * download dialog should be dismissed.
* @property shouldForwardToThirdParties Indicates if downloads should be forward to third party apps,
* if there are multiple apps a chooser dialog will shown.
* @property customFirstPartyDownloadDialog An optional delegate for showing a dialog for a download
@@ -146,6 +148,7 @@ class DownloadsFeature(
private val fragmentManager: FragmentManager? = null,
private val promptsStyling: PromptsStyling? = null,
private val onDownloadStartedListener: ((String) -> Unit) = {},
+ private val dismissCustomFirstPartyDownloadDialog: () -> Unit = {},
private val shouldForwardToThirdParties: () -> Boolean = { false },
private val customFirstPartyDownloadDialog: (
(
@@ -556,6 +559,7 @@ class DownloadsFeature(
internal fun dismissAllDownloadDialogs() {
findPreviousDownloadDialogFragment()?.dismiss()
findPreviousAppDownloaderDialogFragment()?.dismiss()
+ dismissCustomFirstPartyDownloadDialog.invoke()
}
private val ActivityInfo.identifier: String get() = packageName + name
=====================================
mobile/android/android-components/components/feature/downloads/src/test/java/mozilla/components/feature/downloads/DownloadsFeatureTest.kt
=====================================
@@ -1460,6 +1460,64 @@ class DownloadsFeatureTest {
verify(cancelDownloadRequestUseCase).invoke(anyString(), anyString())
}
+ @Test
+ fun `GIVEN a custom download dialog is used WHEN dismissAllDownloadDialogs is called THEN the dialog is dismissed`() = runTest(testDispatcher) {
+ val dismissCustomDialog = mock<() -> Unit>()
+ val feature = DownloadsFeature(
+ testContext,
+ store,
+ useCases = DownloadsUseCases(store, mock()),
+ downloadFileUtils = FakeDownloadFileUtils(),
+ downloadManager = mock(),
+ mainDispatcher = testDispatcher,
+ dismissCustomFirstPartyDownloadDialog = dismissCustomDialog,
+ )
+
+ feature.dismissAllDownloadDialogs()
+
+ verify(dismissCustomDialog).invoke()
+ }
+
+ @Test
+ fun `GIVEN a custom download dialog is used WHEN navigating to another website THEN the dialog is dismissed`() = runTest(testDispatcher) {
+ val dismissCustomDialog = mock<() -> Unit>()
+ val downloadsUseCases = spy(DownloadsUseCases(store, mock()))
+ val cancelDownloadRequestUseCase = mock<CancelDownloadRequestUseCase>()
+ val download = DownloadState(url = "https://www.mozilla.org", sessionId = "test-tab")
+ store.dispatch(ContentAction.UpdateDownloadAction("test-tab", download = download))
+
+ doReturn(cancelDownloadRequestUseCase).`when`(downloadsUseCases).cancelDownloadRequest
+
+ val feature = spy(
+ DownloadsFeature(
+ testContext,
+ store,
+ useCases = downloadsUseCases,
+ downloadFileUtils = FakeDownloadFileUtils(),
+ downloadManager = mock(),
+ mainDispatcher = testDispatcher,
+ dismissCustomFirstPartyDownloadDialog = dismissCustomDialog,
+ ),
+ )
+
+ doReturn(true).`when`(feature).processDownload(any(), any())
+
+ feature.start()
+ testDispatcher.scheduler.advanceUntilIdle()
+
+ store.dispatch(ContentAction.UpdateDownloadAction("test-tab", download = download))
+ testDispatcher.scheduler.advanceUntilIdle()
+
+ grantPermissions()
+
+ val tab = createTab("https://www.firefox.com")
+ store.dispatch(TabListAction.AddTabAction(tab, select = true))
+ testDispatcher.scheduler.advanceUntilIdle()
+
+ verify(feature).dismissAllDownloadDialogs()
+ verify(dismissCustomDialog).invoke()
+ }
+
@Test
fun `ResolveInfo to DownloaderApps`() = runTest(testDispatcher) {
val spyContext = spy(testContext)
=====================================
mobile/android/android-components/components/feature/sitepermissions/src/main/java/mozilla/components/feature/sitepermissions/SitePermissionsDialogFragment.kt
=====================================
@@ -32,6 +32,7 @@ import mozilla.components.support.base.log.logger.Logger
import mozilla.components.support.ktx.android.content.appName
import mozilla.components.support.ktx.kotlin.ifNullOrEmpty
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"
@@ -51,7 +52,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")
@@ -134,6 +137,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/components/geckoview/GeckoViewContentChannelParent.cpp
=====================================
@@ -159,6 +159,11 @@ bool GeckoViewContentChannelParent::Init(
nsCOMPtr<nsIURI> uri = ipc::DeserializeURI(aArgs.uri());
+ if (!uri || !uri->SchemeIs("content")) {
+ rv = NS_ERROR_UNKNOWN_PROTOCOL;
+ return false;
+ }
+
nsAutoCString remoteType;
rv = GetRemoteType(remoteType);
if (MOZ_UNLIKELY(NS_FAILED(rv))) {
=====================================
mobile/android/fenix/app/src/androidTest/java/org/mozilla/fenix/ui/robots/DownloadRobot.kt
=====================================
@@ -29,6 +29,7 @@ import androidx.test.espresso.intent.matcher.IntentMatchers
import androidx.test.uiautomator.By
import androidx.test.uiautomator.UiSelector
import androidx.test.uiautomator.Until
+import mozilla.components.support.ktx.util.PromptAbuserDetector
import org.hamcrest.CoreMatchers.allOf
import org.mozilla.fenix.R
import org.mozilla.fenix.compose.snackbar.SNACKBAR_TEST_TAG
@@ -257,7 +258,9 @@ class DownloadRobot(private val composeTestRule: ComposeTestRule) {
class Transition(private val composeTestRule: ComposeTestRule) {
fun clickDownload(composeTestRule: ComposeTestRule, interact: DownloadRobot.() -> Unit): Transition {
Log.i(TAG, "clickDownload: Trying to click the \"Download\" download prompt button")
+ PromptAbuserDetector.validationsEnabled = false
composeTestRule.downloadButton().performClick()
+ PromptAbuserDetector.validationsEnabled = true
Log.i(TAG, "clickDownload: Clicked the \"Download\" download prompt button")
DownloadRobot(composeTestRule).interact()
=====================================
mobile/android/fenix/app/src/main/java/org/mozilla/fenix/addons/AddonPopupBaseFragment.kt
=====================================
@@ -161,6 +161,10 @@ abstract class AddonPopupBaseFragment :
onNeedToRequestPermissions = { permissions ->
requestPermissions(permissions, REQUEST_CODE_DOWNLOAD_PERMISSIONS)
},
+ dismissCustomFirstPartyDownloadDialog = {
+ dismissRenameDialog()
+ downloadDialog?.dismiss()
+ },
customFirstPartyDownloadDialog = { currentDownloadState, _, positiveAction, negativeAction, _ ->
run {
if (canShowDownloadDialog()) {
@@ -394,6 +398,13 @@ abstract class AddonPopupBaseFragment :
return downloadDialog == null && !isRenameFragmentShowing
}
+ private fun dismissRenameDialog() {
+ val renameDialog = childFragmentManager.findFragmentByTag(
+ RenameAndChangeLocationDialogFragment.RENAME_AND_CHANGE_LOCATION_DIALOG_TAG,
+ ) as? RenameAndChangeLocationDialogFragment
+ renameDialog?.dismissAllowingStateLoss()
+ }
+
/**
* Forwards activity results to the [ActivityResultHandler] features.
*/
=====================================
mobile/android/fenix/app/src/main/java/org/mozilla/fenix/browser/BaseBrowserFragment.kt
=====================================
@@ -742,6 +742,10 @@ abstract class BaseBrowserFragment :
onNeedToRequestPermissions = { permissions ->
requestPermissions(permissions, REQUEST_CODE_DOWNLOAD_PERMISSIONS)
},
+ dismissCustomFirstPartyDownloadDialog = {
+ dismissRenameDialog()
+ dismissDownloadDialogs()
+ },
customFirstPartyDownloadDialog = {
currentDownloadState,
fileNameIfAlreadyDownloaded,
=====================================
mobile/android/fenix/app/src/main/java/org/mozilla/fenix/customtabs/ExternalAppBrowserActivity.kt
=====================================
@@ -11,6 +11,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
@@ -92,5 +93,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)
+ }
}
}
=====================================
mobile/android/fenix/app/src/main/java/org/mozilla/fenix/downloads/RenameAndChangeLocationDialogFragment.kt
=====================================
@@ -20,6 +20,8 @@ import androidx.fragment.app.DialogFragment
import com.google.android.material.dialog.MaterialAlertDialogBuilder
import mozilla.components.concept.base.crash.Breadcrumb
import mozilla.components.support.base.log.logger.Logger
+import mozilla.components.support.ktx.util.PromptAbuserDetector
+import mozilla.components.support.utils.OnEnterAnimationCompleteListener
import org.mozilla.fenix.R
import org.mozilla.fenix.ext.components
import org.mozilla.fenix.ext.requireComponents
@@ -38,10 +40,12 @@ import org.mozilla.fenix.theme.FirefoxTheme
*
* The callback [onConfirmSave] is invoked with the final file name and directory path.
*/
-class RenameAndChangeLocationDialogFragment : DialogFragment() {
+class RenameAndChangeLocationDialogFragment : DialogFragment(), OnEnterAnimationCompleteListener {
private val logger = Logger("RenameAndChangeLocationDialogFragment")
private val safeArguments get() = requireNotNull(arguments)
+ private val promptAbuserDetector = PromptAbuserDetector(TIME_SHOWN_OFFSET_MILLIS)
+
internal val fileName: String
get() = safeArguments.getString(KEY_FILE_NAME, "")
@@ -75,6 +79,15 @@ class RenameAndChangeLocationDialogFragment : DialogFragment() {
}
}
+ override fun onResume() {
+ super.onResume()
+ promptAbuserDetector.start()
+ }
+
+ override fun onEnterAnimationComplete() {
+ promptAbuserDetector.start()
+ }
+
override fun onCancel(dialog: DialogInterface) {
super.onCancel(dialog)
onCancel()
@@ -99,6 +112,8 @@ class RenameAndChangeLocationDialogFragment : DialogFragment() {
val composeView = createComposeView()
+ promptAbuserDetector.start()
+
return MaterialAlertDialogBuilder(requireContext())
.setView(composeView)
.create()
@@ -144,11 +159,15 @@ class RenameAndChangeLocationDialogFragment : DialogFragment() {
directoryLauncher.launch(null)
},
onConfirm = {
- onConfirmSave(
- dialogState.fileName,
- dialogState.directoryPath,
- )
- dismiss()
+ if (promptAbuserDetector.areDialogsBeingAbused()) {
+ promptAbuserDetector.updateJSDialogAbusedState()
+ } else {
+ onConfirmSave(
+ dialogState.fileName,
+ dialogState.directoryPath,
+ )
+ dismiss()
+ }
},
onCancel = {
onCancel()
@@ -182,6 +201,7 @@ class RenameAndChangeLocationDialogFragment : DialogFragment() {
private const val KEY_DIRECTORY_PATH = "directory_path"
private const val KEY_CONTENT_SIZE = "content_size"
const val RENAME_AND_CHANGE_LOCATION_DIALOG_TAG = "RENAME_AND_CHANGE_LOCATION_DIALOG_TAG"
+ private const val TIME_SHOWN_OFFSET_MILLIS = 500
/**
* Creates a new instance of [RenameAndChangeLocationDialogFragment].
@@ -203,3 +223,14 @@ class RenameAndChangeLocationDialogFragment : DialogFragment() {
}
}
}
+
+/**
+ * Starts (or restarts) the time-based check without increasing the "click count".
+ *
+ * Makes it safe to call from multiple/successive lifecycle methods, without running into the risk
+ * of triggering the more restrictive count-based protection on the 1st click (or even before it).
+ */
+private fun PromptAbuserDetector.start() {
+ resetJSAlertAbuseState()
+ updateJSDialogAbusedState()
+}
View it on GitLab: https://gitlab.torproject.org/tpo/applications/mullvad-browser/-/compare/61…
--
View it on GitLab: https://gitlab.torproject.org/tpo/applications/mullvad-browser/-/compare/61…
You're receiving this email because of your account on gitlab.torproject.org. Manage all notifications: https://gitlab.torproject.org/-/profile/notifications | Help: https://gitlab.torproject.org/help
1
0
[Git][tpo/applications/tor-browser] Pushed new tag tor-browser-153.1.0esr-16.0-1-build2
by ma1 (@ma1) 17 Aug '26
by ma1 (@ma1) 17 Aug '26
17 Aug '26
ma1 pushed new tag tor-browser-153.1.0esr-16.0-1-build2 at The Tor Project / Applications / Tor Browser
--
View it on GitLab: https://gitlab.torproject.org/tpo/applications/tor-browser/-/tree/tor-brows…
You're receiving this email because of your account on gitlab.torproject.org. Manage all notifications: https://gitlab.torproject.org/-/profile/notifications | Help: https://gitlab.torproject.org/help
1
0
[Git][tpo/applications/tor-browser][tor-browser-153.1.0esr-16.0-1] 5 commits: Bug 1842361 - Download confirmation notification can be overlaid over other...
by ma1 (@ma1) 17 Aug '26
by ma1 (@ma1) 17 Aug '26
17 Aug '26
ma1 pushed to branch tor-browser-153.1.0esr-16.0-1 at The Tor Project / Applications / Tor Browser
Commits:
bb66338d by giorga at 2026-08-16T00:41:44+02:00
Bug 1842361 - Download confirmation notification can be overlaid over other origins. r=android-reviewers,jdelorenzo
Differential Revision: https://phabricator.services.mozilla.com/D309062
- - - - -
7655bbd2 by Marcin Koziński at 2026-08-16T00:41:44+02:00
Bug 1978587 - Forward onEnterAnimationComplete to interested fragments r=android-reviewers,twhite
Differential Revision: https://phabricator.services.mozilla.com/D306954
- - - - -
d13ffd02 by Marcin Koziński at 2026-08-16T00:56:58+02:00
Bug 2049034 - Add an initial delay to download button in Fenix download dialog a=pascalc
Original Revision: https://phabricator.services.mozilla.com/D309334
Differential Revision: https://phabricator.services.mozilla.com/D310049
- - - - -
4e27f4b0 by Jamie Nicol at 2026-08-17T08:17:03+02:00
Bug 2049810 - Allocate HardwareBuffer for screen pixels request in parent process. r=gfx-reviewers,lsalzman
Differential Revision: https://phabricator.services.mozilla.com/D308519
- - - - -
a6ed37e7 by owlishDeveloper at 2026-08-17T10:29:07+02:00
Bug 2055683 - IPC improvement a=pascalc
Original Revision: https://phabricator.services.mozilla.com/D314463
Differential Revision: https://phabricator.services.mozilla.com/D314798
- - - - -
24 changed files:
- gfx/layers/ipc/PUiCompositorController.ipdl
- gfx/layers/ipc/UiCompositorControllerChild.cpp
- gfx/layers/ipc/UiCompositorControllerChild.h
- gfx/layers/ipc/UiCompositorControllerParent.cpp
- gfx/layers/ipc/UiCompositorControllerParent.h
- gfx/layers/wr/WebRenderBridgeParent.cpp
- gfx/layers/wr/WebRenderBridgeParent.h
- gfx/webrender_bindings/RenderCompositor.h
- gfx/webrender_bindings/RenderCompositorOGLSWGL.cpp
- gfx/webrender_bindings/RenderCompositorOGLSWGL.h
- gfx/webrender_bindings/RendererOGL.cpp
- gfx/webrender_bindings/RendererOGL.h
- gfx/webrender_bindings/WebRenderAPI.cpp
- gfx/webrender_bindings/WebRenderAPI.h
- mobile/android/android-components/components/feature/downloads/src/main/java/mozilla/components/feature/downloads/DownloadsFeature.kt
- mobile/android/android-components/components/feature/downloads/src/test/java/mozilla/components/feature/downloads/DownloadsFeatureTest.kt
- 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/components/geckoview/GeckoViewContentChannelParent.cpp
- mobile/android/fenix/app/src/androidTest/java/org/mozilla/fenix/ui/robots/DownloadRobot.kt
- mobile/android/fenix/app/src/main/java/org/mozilla/fenix/addons/AddonPopupBaseFragment.kt
- mobile/android/fenix/app/src/main/java/org/mozilla/fenix/browser/BaseBrowserFragment.kt
- mobile/android/fenix/app/src/main/java/org/mozilla/fenix/customtabs/ExternalAppBrowserActivity.kt
- mobile/android/fenix/app/src/main/java/org/mozilla/fenix/downloads/RenameAndChangeLocationDialogFragment.kt
Changes:
=====================================
gfx/layers/ipc/PUiCompositorController.ipdl
=====================================
@@ -3,9 +3,7 @@
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
using mozilla::gfx::IntRect from "mozilla/gfx/Rect.h";
-using mozilla::gfx::IntSize from "mozilla/gfx/Point.h";
using mozilla::layers::CompositorScrollUpdate from "mozilla/layers/CompositorScrollUpdate.h";
-using mozilla::void_t from "mozilla/ipc/IPCCore.h";
include "mozilla/GfxMessageUtils.h";
include "mozilla/layers/LayersMessageUtils.h";
@@ -34,15 +32,14 @@ parent:
async MaxToolbarHeight(int32_t aHeight);
async FixedBottomOffset(int32_t aOffset);
async DefaultClearColor(uint32_t aColor);
- async RequestScreenPixels(uint64_t aRequestId, IntRect aSourceRect, IntSize aDestSize);
+ async RequestScreenPixels(uint64_t aRequestId, IntRect aSourceRect,
+ FileDescriptor aHardwareBuffer);
async EnableLayerUpdateNotifications(bool aEnable);
child:
async ToolbarAnimatorMessageFromCompositor(int32_t aMessage);
async NotifyCompositorScrollUpdate(CompositorScrollUpdate aUpdate);
- // Returns when the child side has finished using the HardwareBuffer,
- // indicating that the parent side can now release it.
- async ScreenPixels(uint64_t aRequestId, FileDescriptor? aHardwareBuffer, FileDescriptor? aAcquireFence)
- returns (void_t ok);
+ async ScreenPixels(uint64_t aRequestId, bool aSuccess,
+ FileDescriptor? aAcquireFence);
};
} // layers
=====================================
gfx/layers/ipc/UiCompositorControllerChild.cpp
=====================================
@@ -148,19 +148,36 @@ UiCompositorControllerChild::RequestScreenPixels(gfx::IntRect aSourceRect,
// We only support one request at a time. If an old request is still
// outstanding when a new request is made, just reject the old request.
- if (mScreenPixelsPromise) {
- mScreenPixelsPromise.extract().second->Reject(NS_ERROR_ABORT, __func__);
+ if (mScreenPixelsRequest) {
+ mScreenPixelsRequest.extract().mPromise->Reject(NS_ERROR_ABORT, __func__);
+ }
+
+ RefPtr<layers::AndroidHardwareBuffer> hardwareBuffer =
+ layers::AndroidHardwareBuffer::Create(aDestSize,
+ gfx::SurfaceFormat::R8G8B8A8);
+ if (!hardwareBuffer) {
+ return ScreenPixelsPromise::CreateAndReject(NS_ERROR_OUT_OF_MEMORY,
+ __func__);
+ }
+
+ UniqueFileHandle bufferFd = hardwareBuffer->SerializeToFileDescriptor();
+ if (!bufferFd) {
+ return ScreenPixelsPromise::CreateAndReject(NS_ERROR_FAILURE, __func__);
}
static uint64_t nextRequestId = 0;
const uint64_t requestId = nextRequestId++;
auto promise = MakeRefPtr<ScreenPixelsPromise::Private>(__func__);
- // Using synchronous dispatch ensures we are done using the hardware buffer
- // prior to RecvScreenPixels calling aResolver which in turn will cause the
- // hardware buffer on the parent side to be released.
- promise->UseSynchronousTaskDispatch(__func__);
- mScreenPixelsPromise.emplace(requestId, promise);
- (void)SendRequestScreenPixels(requestId, aSourceRect, aDestSize);
+ mScreenPixelsRequest.emplace(ScreenPixelsRequest{
+ .mRequestId = requestId,
+ .mHardwareBuffer = hardwareBuffer,
+ .mPromise = promise,
+ });
+ if (!SendRequestScreenPixels(requestId, aSourceRect,
+ ipc::FileDescriptor(std::move(bufferFd)))) {
+ mScreenPixelsRequest.extract().mPromise->Reject(NS_ERROR_NOT_AVAILABLE,
+ __func__);
+ }
return promise;
}
#endif
@@ -213,8 +230,8 @@ void UiCompositorControllerChild::ActorDestroy(ActorDestroyReason aWhy) {
mParent = nullptr;
#ifdef MOZ_WIDGET_ANDROID
- if (mScreenPixelsPromise) {
- mScreenPixelsPromise->second->Reject(NS_ERROR_ABORT, __func__);
+ if (mScreenPixelsRequest) {
+ mScreenPixelsRequest->mPromise->Reject(NS_ERROR_ABORT, __func__);
}
#endif
if (mProcessToken) {
@@ -258,39 +275,28 @@ UiCompositorControllerChild::RecvNotifyCompositorScrollUpdate(
}
mozilla::ipc::IPCResult UiCompositorControllerChild::RecvScreenPixels(
- uint64_t aRequestId, Maybe<ipc::FileDescriptor>&& aHardwareBuffer,
- Maybe<ipc::FileDescriptor>&& aAcquireFence,
- ScreenPixelsResolver&& aResolver) {
+ uint64_t aRequestId, bool aSuccess,
+ Maybe<ipc::FileDescriptor>&& aAcquireFence) {
#if defined(MOZ_WIDGET_ANDROID)
- if (!mScreenPixelsPromise || mScreenPixelsPromise->first != aRequestId) {
+ if (!mScreenPixelsRequest || mScreenPixelsRequest->mRequestId != aRequestId) {
// Response is for an outdated request whose promise will have already been
// rejected. Just ignore it.
return IPC_OK();
}
- RefPtr<layers::AndroidHardwareBuffer> hardwareBuffer;
- if (aHardwareBuffer) {
- hardwareBuffer =
- layers::AndroidHardwareBuffer::DeserializeFromFileDescriptor(
- aHardwareBuffer->TakePlatformHandle());
+ auto request = mScreenPixelsRequest.extract();
+ if (!aSuccess) {
+ request.mPromise->Reject(NS_ERROR_FAILURE, __func__);
+ return IPC_OK();
}
- if (hardwareBuffer && aAcquireFence) {
- hardwareBuffer->SetAcquireFence(aAcquireFence->TakePlatformHandle());
+
+ if (aAcquireFence) {
+ request.mHardwareBuffer->SetAcquireFence(
+ aAcquireFence->TakePlatformHandle());
}
- // Note this is resolved synchronously, ensuring we have finished using the
- // hardware buffer as soon as this call returns (and importantly before the
- // aResolver call below).
- mScreenPixelsPromise.extract().second->Resolve(std::move(hardwareBuffer),
- __func__);
+ request.mPromise->Resolve(std::move(request.mHardwareBuffer), __func__);
#endif // defined(MOZ_WIDGET_ANDROID)
- // Notify the parent side that it can drop its reference to the hardware
- // buffer. In theory this could be done as soon as we have called
- // DeserializeFromFileDescriptor(). However, on certain Exynos devices we have
- // seen that releasing the original hardware buffer frees the underlying
- // resource even if a reference obtained via (de)serialization remains alive.
- // See bug 2017901.
- aResolver(void_t{});
return IPC_OK();
}
=====================================
gfx/layers/ipc/UiCompositorControllerChild.h
=====================================
@@ -84,9 +84,8 @@ class UiCompositorControllerChild final
mozilla::ipc::IPCResult RecvNotifyCompositorScrollUpdate(
const CompositorScrollUpdate& aUpdate);
mozilla::ipc::IPCResult RecvScreenPixels(
- uint64_t aRequestId, Maybe<ipc::FileDescriptor>&& aHardwareBuffer,
- Maybe<ipc::FileDescriptor>&& aAcquireFence,
- ScreenPixelsResolver&& aResolver);
+ uint64_t aRequestId, bool aSuccess,
+ Maybe<ipc::FileDescriptor>&& aAcquireFence);
private:
explicit UiCompositorControllerChild(const uint64_t& aProcessToken,
@@ -118,8 +117,12 @@ class UiCompositorControllerChild final
// RecvScreenPixels() altogether. Unfortunately, however, we cannot chain to a
// promise returned from an IPDL function on the Android UI thread, as the
// thread does not support direct task dispatch.
- Maybe<std::pair<uint64_t, RefPtr<ScreenPixelsPromise::Private>>>
- mScreenPixelsPromise;
+ struct ScreenPixelsRequest {
+ uint64_t mRequestId;
+ RefPtr<layers::AndroidHardwareBuffer> mHardwareBuffer;
+ RefPtr<ScreenPixelsPromise::Private> mPromise;
+ };
+ Maybe<ScreenPixelsRequest> mScreenPixelsRequest;
#endif
// Should only be set when compositor is in process.
=====================================
gfx/layers/ipc/UiCompositorControllerParent.cpp
=====================================
@@ -139,39 +139,39 @@ mozilla::ipc::IPCResult UiCompositorControllerParent::RecvDefaultClearColor(
}
mozilla::ipc::IPCResult UiCompositorControllerParent::RecvRequestScreenPixels(
- uint64_t aRequestId, gfx::IntRect aSourceRect, gfx::IntSize aDestSize) {
+ uint64_t aRequestId, gfx::IntRect aSourceRect,
+ ipc::FileDescriptor&& aHardwareBuffer) {
#if defined(MOZ_WIDGET_ANDROID)
+ RefPtr<AndroidHardwareBuffer> hardwareBuffer =
+ AndroidHardwareBuffer::DeserializeFromFileDescriptor(
+ aHardwareBuffer.TakePlatformHandle());
+ if (!hardwareBuffer) {
+ (void)SendScreenPixels(aRequestId, false, Nothing());
+ return IPC_OK();
+ }
+
LayerTreeState* state =
CompositorBridgeParent::GetLayerTreeState(mRootLayerTreeId);
if (state && state->mWrBridge) {
- state->mWrBridge->RequestScreenPixels(aSourceRect, aDestSize)
+ state->mWrBridge->RequestScreenPixels(aSourceRect, hardwareBuffer)
->Then(
GetCurrentSerialEventTarget(), __func__,
- [target = RefPtr{this},
- aRequestId](RefPtr<AndroidHardwareBuffer> aHardwareBuffer) {
- UniqueFileHandle bufferFd =
- aHardwareBuffer->SerializeToFileDescriptor();
+ [target = RefPtr{this}, aRequestId,
+ hardwareBuffer = std::move(hardwareBuffer)](Ok) {
UniqueFileHandle fenceFd =
- aHardwareBuffer->GetAndResetAcquireFence();
- target
- ->SendScreenPixels(
- aRequestId,
- aHardwareBuffer
- ? Some(ipc::FileDescriptor(std::move(bufferFd)))
- : Nothing(),
- fenceFd ? Some(ipc::FileDescriptor(std::move(fenceFd)))
- : Nothing())
- // Ensure the hardware buffer remains alive until child side
- // has finished using it.
- ->Then(GetCurrentSerialEventTarget(), __func__,
- [aHardwareBuffer](
- ScreenPixelsPromise::ResolveOrRejectValue&&) {});
+ hardwareBuffer->GetAndResetAcquireFence();
+ (void)target->SendScreenPixels(
+ aRequestId, true,
+ fenceFd ? Some(ipc::FileDescriptor(std::move(fenceFd)))
+ : Nothing());
},
[target = RefPtr{this}, aRequestId](nsresult aError) {
- (void)target->SendScreenPixels(aRequestId, Nothing(), Nothing());
+ (void)target->SendScreenPixels(aRequestId, false, Nothing());
});
state->mWrBridge->ScheduleForcedGenerateFrame(wr::RenderReasons::OTHER);
+ } else {
+ (void)SendScreenPixels(aRequestId, false, Nothing());
}
#endif // defined(MOZ_WIDGET_ANDROID)
=====================================
gfx/layers/ipc/UiCompositorControllerParent.h
=====================================
@@ -41,9 +41,9 @@ class UiCompositorControllerParent final
mozilla::ipc::IPCResult RecvMaxToolbarHeight(const int32_t& aHeight);
mozilla::ipc::IPCResult RecvFixedBottomOffset(const int32_t& aOffset);
mozilla::ipc::IPCResult RecvDefaultClearColor(const uint32_t& aColor);
- mozilla::ipc::IPCResult RecvRequestScreenPixels(uint64_t aRequestId,
- gfx::IntRect aSourceRect,
- gfx::IntSize aDestSize);
+ mozilla::ipc::IPCResult RecvRequestScreenPixels(
+ uint64_t aRequestId, gfx::IntRect aSourceRect,
+ ipc::FileDescriptor&& aHardwareBuffer);
mozilla::ipc::IPCResult RecvEnableLayerUpdateNotifications(
const bool& aEnable);
void ActorDestroy(ActorDestroyReason aWhy) override;
=====================================
gfx/layers/wr/WebRenderBridgeParent.cpp
=====================================
@@ -1945,8 +1945,8 @@ void WebRenderBridgeParent::UpdateBoolParameters() {
#if defined(MOZ_WIDGET_ANDROID)
RefPtr<WebRenderBridgeParent::ScreenPixelsPromise>
-WebRenderBridgeParent::RequestScreenPixels(gfx::IntRect aSourceRect,
- gfx::IntSize aDestSize) {
+WebRenderBridgeParent::RequestScreenPixels(
+ gfx::IntRect aSourceRect, RefPtr<AndroidHardwareBuffer> aHardwareBuffer) {
if (mDestroyed) {
return ScreenPixelsPromise::CreateAndReject(NS_ERROR_ABORT, __func__);
}
@@ -1962,7 +1962,7 @@ WebRenderBridgeParent::RequestScreenPixels(gfx::IntRect aSourceRect,
}
mScreenPixelsRequest.emplace(ScreenPixelsRequest{
.mSourceRect = aSourceRect,
- .mDestSize = aDestSize,
+ .mHardwareBuffer = std::move(aHardwareBuffer),
.mPromise = new ScreenPixelsPromise::Private(__func__),
});
return mScreenPixelsRequest->mPromise;
@@ -1982,7 +1982,9 @@ void WebRenderBridgeParent::MaybeCaptureScreenPixels() {
MOZ_ASSERT(cbp && !cbp->IsPaused());
# endif
- mLateInit->mApi->RequestScreenPixels(request.mSourceRect, request.mDestSize)
+ mLateInit->mApi
+ ->RequestScreenPixels(request.mSourceRect,
+ std::move(request.mHardwareBuffer))
->ChainTo(request.mPromise.forget(), __func__);
}
#endif
=====================================
gfx/layers/wr/WebRenderBridgeParent.h
=====================================
@@ -325,13 +325,13 @@ class WebRenderBridgeParent final : public PWebRenderBridgeParent,
void BeginRecording(const TimeStamp& aRecordingStart);
#if defined(MOZ_WIDGET_ANDROID)
- using ScreenPixelsPromise =
- MozPromise<RefPtr<layers::AndroidHardwareBuffer>, nsresult, true>;
+ using ScreenPixelsPromise = MozPromise<Ok, nsresult, true>;
/**
* Request a screengrab for android
*/
- RefPtr<ScreenPixelsPromise> RequestScreenPixels(gfx::IntRect aSourceRect,
- gfx::IntSize aDestSize);
+ RefPtr<ScreenPixelsPromise> RequestScreenPixels(
+ gfx::IntRect aSourceRect,
+ RefPtr<layers::AndroidHardwareBuffer> aHardwareBuffer);
#endif
/**
@@ -539,7 +539,7 @@ class WebRenderBridgeParent final : public PWebRenderBridgeParent,
#if defined(MOZ_WIDGET_ANDROID)
struct ScreenPixelsRequest {
gfx::IntRect mSourceRect;
- gfx::IntSize mDestSize;
+ RefPtr<layers::AndroidHardwareBuffer> mHardwareBuffer;
RefPtr<ScreenPixelsPromise::Private> mPromise;
};
Maybe<ScreenPixelsRequest> mScreenPixelsRequest;
=====================================
gfx/webrender_bindings/RenderCompositor.h
=====================================
@@ -235,7 +235,7 @@ class RenderCompositor {
#ifdef MOZ_WIDGET_ANDROID
virtual bool MaybeCaptureScreenPixels(
const gfx::IntRect& aSourceRect,
- RefPtr<layers::AndroidHardwareBuffer> aHardwareBuffer) {
+ layers::AndroidHardwareBuffer* aHardwareBuffer) {
return false;
}
#endif
=====================================
gfx/webrender_bindings/RenderCompositorOGLSWGL.cpp
=====================================
@@ -315,7 +315,7 @@ bool RenderCompositorOGLSWGL::MaybeReadback(
#ifdef MOZ_WIDGET_ANDROID
bool RenderCompositorOGLSWGL::MaybeCaptureScreenPixels(
const gfx::IntRect& aSourceRect,
- RefPtr<layers::AndroidHardwareBuffer> aHardwareBuffer) {
+ layers::AndroidHardwareBuffer* aHardwareBuffer) {
auto* const gl = GetGLContext();
gl::ScopedBindFramebuffer scopedBind(gl);
=====================================
gfx/webrender_bindings/RenderCompositorOGLSWGL.h
=====================================
@@ -59,7 +59,7 @@ class RenderCompositorOGLSWGL : public RenderCompositorLayersSWGL {
#ifdef MOZ_WIDGET_ANDROID
bool MaybeCaptureScreenPixels(
const gfx::IntRect& aSourceRect,
- RefPtr<layers::AndroidHardwareBuffer> aHardwareBuffer) override;
+ layers::AndroidHardwareBuffer* aHardwareBuffer) override;
#endif
private:
=====================================
gfx/webrender_bindings/RendererOGL.cpp
=====================================
@@ -469,7 +469,13 @@ Maybe<layers::FrameRecording> RendererOGL::EndRecording() {
#ifdef MOZ_WIDGET_ANDROID
RefPtr<RendererOGL::ScreenPixelsPromise> RendererOGL::RequestScreenPixels(
- gfx::IntRect aSourceRect, gfx::IntSize aDestSize) {
+ gfx::IntRect aSourceRect,
+ RefPtr<layers::AndroidHardwareBuffer> aHardwareBuffer) {
+ if (!aHardwareBuffer) {
+ return ScreenPixelsPromise::CreateAndReject(NS_ERROR_ILLEGAL_VALUE,
+ __func__);
+ }
+
// If a new request is made we no longer care about the result of the previous
// one, so just reject it if it exists.
if (mPendingScreenPixelsRequest) {
@@ -478,7 +484,7 @@ RefPtr<RendererOGL::ScreenPixelsPromise> RendererOGL::RequestScreenPixels(
}
mPendingScreenPixelsRequest.emplace(ScreenPixelsRequest{
.mSourceRect = aSourceRect,
- .mDestSize = aDestSize,
+ .mHardwareBuffer = std::move(aHardwareBuffer),
.mPromise = new ScreenPixelsPromise::Private(__func__),
});
return mPendingScreenPixelsRequest->mPromise;
@@ -491,19 +497,16 @@ void RendererOGL::MaybeCaptureScreenPixels() {
auto request = mPendingScreenPixelsRequest.extract();
- const RefPtr<layers::AndroidHardwareBuffer> hardwareBuffer =
- layers::AndroidHardwareBuffer::Create(request.mDestSize,
- gfx::SurfaceFormat::R8G8B8A8);
-
if (mCompositor->MaybeCaptureScreenPixels(request.mSourceRect,
- hardwareBuffer)) {
- request.mPromise->Resolve(hardwareBuffer, __func__);
+ request.mHardwareBuffer)) {
+ request.mPromise->Resolve(Ok{}, __func__);
return;
}
auto* const gle = gl::GLContextEGL::Cast(gl());
const auto& egl = gle->mEgl;
- gl::ScopedEGLImageForAndroidHardwareBuffer eglImage(gle, hardwareBuffer);
+ gl::ScopedEGLImageForAndroidHardwareBuffer eglImage(gle,
+ request.mHardwareBuffer);
gl::ScopedBindFramebuffer scopedBind(gl());
gl::ScopedRenderbuffer rb(gl());
gl()->fBindRenderbuffer(LOCAL_GL_RENDERBUFFER, rb);
@@ -517,7 +520,7 @@ void RendererOGL::MaybeCaptureScreenPixels() {
request.mSourceRect.x,
mCompositor->GetBufferSize().height - request.mSourceRect.y,
request.mSourceRect.width, -request.mSourceRect.height);
- const auto destRect = gfx::IntRect({}, hardwareBuffer->mSize);
+ const auto destRect = gfx::IntRect({}, request.mHardwareBuffer->mSize);
gl()->BindReadFB(0);
gl()->BindDrawFB(fb.FB());
gl()->fBlitFramebuffer(srcRect.x, srcRect.y, srcRect.XMost(), srcRect.YMost(),
@@ -529,12 +532,12 @@ void RendererOGL::MaybeCaptureScreenPixels() {
egl->fCreateSync(LOCAL_EGL_SYNC_NATIVE_FENCE_ANDROID, nullptr)) {
auto fence = UniqueFileHandle(egl->fDupNativeFenceFDANDROID(sync));
if (fence) {
- hardwareBuffer->SetAcquireFence(std::move(fence));
+ request.mHardwareBuffer->SetAcquireFence(std::move(fence));
}
egl->fDestroySync(sync);
}
- request.mPromise->Resolve(hardwareBuffer, __func__);
+ request.mPromise->Resolve(Ok{}, __func__);
}
#endif
=====================================
gfx/webrender_bindings/RendererOGL.h
=====================================
@@ -93,12 +93,12 @@ class RendererOGL {
Maybe<layers::FrameRecording> EndRecording();
#ifdef MOZ_WIDGET_ANDROID
- using ScreenPixelsPromise =
- MozPromise<RefPtr<layers::AndroidHardwareBuffer>, nsresult, true>;
+ using ScreenPixelsPromise = MozPromise<Ok, nsresult, true>;
// Captures the pixels for the next rendered frame. Returns a promise that
// resolves once the pixels are captured.
- RefPtr<ScreenPixelsPromise> RequestScreenPixels(gfx::IntRect aSourceRect,
- gfx::IntSize aDestSize);
+ RefPtr<ScreenPixelsPromise> RequestScreenPixels(
+ gfx::IntRect aSourceRect,
+ RefPtr<layers::AndroidHardwareBuffer> aHardwareBuffer);
#endif
/// This can be called on the render thread only.
@@ -174,7 +174,7 @@ class RendererOGL {
#ifdef MOZ_WIDGET_ANDROID
struct ScreenPixelsRequest {
gfx::IntRect mSourceRect;
- gfx::IntSize mDestSize;
+ RefPtr<layers::AndroidHardwareBuffer> mHardwareBuffer;
RefPtr<ScreenPixelsPromise::Private> mPromise;
};
Maybe<ScreenPixelsRequest> mPendingScreenPixelsRequest;
=====================================
gfx/webrender_bindings/WebRenderAPI.cpp
=====================================
@@ -962,12 +962,17 @@ RefPtr<WebRenderAPI::EndRecordingPromise> WebRenderAPI::EndRecording() {
#ifdef MOZ_WIDGET_ANDROID
RefPtr<WebRenderAPI::ScreenPixelsPromise> WebRenderAPI::RequestScreenPixels(
- gfx::IntRect aSourceRect, gfx::IntSize aDestSize) {
+ gfx::IntRect aSourceRect,
+ RefPtr<layers::AndroidHardwareBuffer> aHardwareBuffer) {
class ScreenshotEvent final : public RendererEvent {
public:
- explicit ScreenshotEvent(gfx::IntRect aSourceRect, gfx::IntSize aDestSize,
- RefPtr<ScreenPixelsPromise::Private> aPromise)
- : mSourceRect(aSourceRect), mDestSize(aDestSize), mPromise(aPromise) {
+ explicit ScreenshotEvent(
+ gfx::IntRect aSourceRect,
+ RefPtr<layers::AndroidHardwareBuffer> aHardwareBuffer,
+ RefPtr<ScreenPixelsPromise::Private> aPromise)
+ : mSourceRect(aSourceRect),
+ mHardwareBuffer(std::move(aHardwareBuffer)),
+ mPromise(aPromise) {
MOZ_COUNT_CTOR(ScreenshotEvent);
}
@@ -977,8 +982,9 @@ RefPtr<WebRenderAPI::ScreenPixelsPromise> WebRenderAPI::RequestScreenPixels(
RendererOGL* const renderer = aRenderThread.GetRenderer(aWindowId);
if (!renderer) {
mPromise->Reject(NS_ERROR_FAILURE, __func__);
+ return;
}
- renderer->RequestScreenPixels(mSourceRect, mDestSize)
+ renderer->RequestScreenPixels(mSourceRect, std::move(mHardwareBuffer))
->ChainTo(mPromise.forget(), __func__);
}
@@ -986,12 +992,13 @@ RefPtr<WebRenderAPI::ScreenPixelsPromise> WebRenderAPI::RequestScreenPixels(
private:
const gfx::IntRect mSourceRect;
- const gfx::IntSize mDestSize;
+ RefPtr<layers::AndroidHardwareBuffer> mHardwareBuffer;
RefPtr<ScreenPixelsPromise::Private> mPromise;
};
auto promise = MakeRefPtr<ScreenPixelsPromise::Private>(__func__);
- auto event = MakeUnique<ScreenshotEvent>(aSourceRect, aDestSize, promise);
+ auto event = MakeUnique<ScreenshotEvent>(aSourceRect,
+ std::move(aHardwareBuffer), promise);
RenderThread::Get()->PostEvent(mId, std::move(event));
return promise;
=====================================
gfx/webrender_bindings/WebRenderAPI.h
=====================================
@@ -322,13 +322,13 @@ class WebRenderAPI final {
RefPtr<EndRecordingPromise> EndRecording();
#ifdef MOZ_WIDGET_ANDROID
- using ScreenPixelsPromise =
- MozPromise<RefPtr<layers::AndroidHardwareBuffer>, nsresult, true>;
+ using ScreenPixelsPromise = MozPromise<Ok, nsresult, true>;
// Queues a task to the render thread to capture screen pixels for the next
// rendered frame. Returns a promise that resolves once the pixels are
// captured.
- RefPtr<ScreenPixelsPromise> RequestScreenPixels(gfx::IntRect aSourceRect,
- gfx::IntSize aDestSize);
+ RefPtr<ScreenPixelsPromise> RequestScreenPixels(
+ gfx::IntRect aSourceRect,
+ RefPtr<layers::AndroidHardwareBuffer> aHardwareBuffer);
#endif
layers::RemoteTextureInfoList* GetPendingRemoteTextureInfoList();
=====================================
mobile/android/android-components/components/feature/downloads/src/main/java/mozilla/components/feature/downloads/DownloadsFeature.kt
=====================================
@@ -122,6 +122,8 @@ value class OpenFileCallback(val value: () -> Unit)
* manager is provided, a dialog will be shown before every download.
* @property promptsStyling styling properties for the dialog.
* @property onDownloadStartedListener a callback invoked when a download is started.
+ * @property dismissCustomFirstPartyDownloadDialog A callback invoked when the custom first party
+ * download dialog should be dismissed.
* @property shouldForwardToThirdParties Indicates if downloads should be forward to third party apps,
* if there are multiple apps a chooser dialog will shown.
* @property customFirstPartyDownloadDialog An optional delegate for showing a dialog for a download
@@ -145,6 +147,7 @@ class DownloadsFeature(
private val fragmentManager: FragmentManager? = null,
private val promptsStyling: PromptsStyling? = null,
private val onDownloadStartedListener: ((String) -> Unit) = {},
+ private val dismissCustomFirstPartyDownloadDialog: () -> Unit = {},
private val shouldForwardToThirdParties: () -> Boolean = { false },
private val customFirstPartyDownloadDialog: (
(
@@ -555,6 +558,7 @@ class DownloadsFeature(
internal fun dismissAllDownloadDialogs() {
findPreviousDownloadDialogFragment()?.dismiss()
findPreviousAppDownloaderDialogFragment()?.dismiss()
+ dismissCustomFirstPartyDownloadDialog.invoke()
}
private val ActivityInfo.identifier: String get() = packageName + name
=====================================
mobile/android/android-components/components/feature/downloads/src/test/java/mozilla/components/feature/downloads/DownloadsFeatureTest.kt
=====================================
@@ -1513,6 +1513,64 @@ class DownloadsFeatureTest {
verify(cancelDownloadRequestUseCase).invoke(anyString(), anyString())
}
+ @Test
+ fun `GIVEN a custom download dialog is used WHEN dismissAllDownloadDialogs is called THEN the dialog is dismissed`() = runTest(testDispatcher) {
+ val dismissCustomDialog = mock<() -> Unit>()
+ val feature = DownloadsFeature(
+ testContext,
+ store,
+ useCases = DownloadsUseCases(store, mock()),
+ downloadFileUtils = FakeDownloadFileUtils(),
+ downloadManager = mock(),
+ mainDispatcher = testDispatcher,
+ dismissCustomFirstPartyDownloadDialog = dismissCustomDialog,
+ )
+
+ feature.dismissAllDownloadDialogs()
+
+ verify(dismissCustomDialog).invoke()
+ }
+
+ @Test
+ fun `GIVEN a custom download dialog is used WHEN navigating to another website THEN the dialog is dismissed`() = runTest(testDispatcher) {
+ val dismissCustomDialog = mock<() -> Unit>()
+ val downloadsUseCases = spy(DownloadsUseCases(store, mock()))
+ val cancelDownloadRequestUseCase = mock<CancelDownloadRequestUseCase>()
+ val download = DownloadState(url = "https://www.mozilla.org", sessionId = "test-tab")
+ store.dispatch(ContentAction.UpdateDownloadAction("test-tab", download = download))
+
+ doReturn(cancelDownloadRequestUseCase).`when`(downloadsUseCases).cancelDownloadRequest
+
+ val feature = spy(
+ DownloadsFeature(
+ testContext,
+ store,
+ useCases = downloadsUseCases,
+ downloadFileUtils = FakeDownloadFileUtils(),
+ downloadManager = mock(),
+ mainDispatcher = testDispatcher,
+ dismissCustomFirstPartyDownloadDialog = dismissCustomDialog,
+ ),
+ )
+
+ doReturn(true).`when`(feature).processDownload(any(), any())
+
+ feature.start()
+ testDispatcher.scheduler.advanceUntilIdle()
+
+ store.dispatch(ContentAction.UpdateDownloadAction("test-tab", download = download))
+ testDispatcher.scheduler.advanceUntilIdle()
+
+ grantPermissions()
+
+ val tab = createTab("https://www.firefox.com")
+ store.dispatch(TabListAction.AddTabAction(tab, select = true))
+ testDispatcher.scheduler.advanceUntilIdle()
+
+ verify(feature).dismissAllDownloadDialogs()
+ verify(dismissCustomDialog).invoke()
+ }
+
@Test
fun `ResolveInfo to DownloaderApps`() = runTest(testDispatcher) {
val spyContext = spy(testContext)
=====================================
mobile/android/android-components/components/feature/sitepermissions/src/main/java/mozilla/components/feature/sitepermissions/SitePermissionsDialogFragment.kt
=====================================
@@ -32,6 +32,7 @@ import mozilla.components.support.base.log.logger.Logger
import mozilla.components.support.ktx.android.content.appName
import mozilla.components.support.ktx.kotlin.ifNullOrEmpty
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"
@@ -51,7 +52,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")
@@ -134,6 +137,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/components/geckoview/GeckoViewContentChannelParent.cpp
=====================================
@@ -159,6 +159,11 @@ bool GeckoViewContentChannelParent::Init(
nsCOMPtr<nsIURI> uri = ipc::DeserializeURI(aArgs.uri());
+ if (!uri || !uri->SchemeIs("content")) {
+ rv = NS_ERROR_UNKNOWN_PROTOCOL;
+ return false;
+ }
+
nsAutoCString remoteType;
rv = GetRemoteType(remoteType);
if (MOZ_UNLIKELY(NS_FAILED(rv))) {
=====================================
mobile/android/fenix/app/src/androidTest/java/org/mozilla/fenix/ui/robots/DownloadRobot.kt
=====================================
@@ -29,6 +29,7 @@ import androidx.test.espresso.intent.matcher.IntentMatchers
import androidx.test.uiautomator.By
import androidx.test.uiautomator.UiSelector
import androidx.test.uiautomator.Until
+import mozilla.components.support.ktx.util.PromptAbuserDetector
import org.hamcrest.CoreMatchers.allOf
import org.mozilla.fenix.R
import org.mozilla.fenix.compose.snackbar.SNACKBAR_TEST_TAG
@@ -257,7 +258,9 @@ class DownloadRobot(private val composeTestRule: ComposeTestRule) {
class Transition(private val composeTestRule: ComposeTestRule) {
fun clickDownload(composeTestRule: ComposeTestRule, interact: DownloadRobot.() -> Unit): Transition {
Log.i(TAG, "clickDownload: Trying to click the \"Download\" download prompt button")
+ PromptAbuserDetector.validationsEnabled = false
composeTestRule.downloadButton().performClick()
+ PromptAbuserDetector.validationsEnabled = true
Log.i(TAG, "clickDownload: Clicked the \"Download\" download prompt button")
DownloadRobot(composeTestRule).interact()
=====================================
mobile/android/fenix/app/src/main/java/org/mozilla/fenix/addons/AddonPopupBaseFragment.kt
=====================================
@@ -161,6 +161,10 @@ abstract class AddonPopupBaseFragment :
onNeedToRequestPermissions = { permissions ->
requestPermissions(permissions, REQUEST_CODE_DOWNLOAD_PERMISSIONS)
},
+ dismissCustomFirstPartyDownloadDialog = {
+ dismissRenameDialog()
+ downloadDialog?.dismiss()
+ },
customFirstPartyDownloadDialog = { currentDownloadState, _, positiveAction, negativeAction, _ ->
run {
if (canShowDownloadDialog()) {
@@ -394,6 +398,13 @@ abstract class AddonPopupBaseFragment :
return downloadDialog == null && !isRenameFragmentShowing
}
+ private fun dismissRenameDialog() {
+ val renameDialog = childFragmentManager.findFragmentByTag(
+ RenameAndChangeLocationDialogFragment.RENAME_AND_CHANGE_LOCATION_DIALOG_TAG,
+ ) as? RenameAndChangeLocationDialogFragment
+ renameDialog?.dismissAllowingStateLoss()
+ }
+
/**
* Forwards activity results to the [ActivityResultHandler] features.
*/
=====================================
mobile/android/fenix/app/src/main/java/org/mozilla/fenix/browser/BaseBrowserFragment.kt
=====================================
@@ -742,6 +742,10 @@ abstract class BaseBrowserFragment :
onNeedToRequestPermissions = { permissions ->
requestPermissions(permissions, REQUEST_CODE_DOWNLOAD_PERMISSIONS)
},
+ dismissCustomFirstPartyDownloadDialog = {
+ dismissRenameDialog()
+ dismissDownloadDialogs()
+ },
customFirstPartyDownloadDialog = {
currentDownloadState,
fileNameIfAlreadyDownloaded,
=====================================
mobile/android/fenix/app/src/main/java/org/mozilla/fenix/customtabs/ExternalAppBrowserActivity.kt
=====================================
@@ -11,6 +11,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
@@ -92,5 +93,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)
+ }
}
}
=====================================
mobile/android/fenix/app/src/main/java/org/mozilla/fenix/downloads/RenameAndChangeLocationDialogFragment.kt
=====================================
@@ -20,6 +20,8 @@ import androidx.fragment.app.DialogFragment
import com.google.android.material.dialog.MaterialAlertDialogBuilder
import mozilla.components.concept.base.crash.Breadcrumb
import mozilla.components.support.base.log.logger.Logger
+import mozilla.components.support.ktx.util.PromptAbuserDetector
+import mozilla.components.support.utils.OnEnterAnimationCompleteListener
import org.mozilla.fenix.R
import org.mozilla.fenix.ext.components
import org.mozilla.fenix.ext.requireComponents
@@ -38,10 +40,12 @@ import org.mozilla.fenix.theme.FirefoxTheme
*
* The callback [onConfirmSave] is invoked with the final file name and directory path.
*/
-class RenameAndChangeLocationDialogFragment : DialogFragment() {
+class RenameAndChangeLocationDialogFragment : DialogFragment(), OnEnterAnimationCompleteListener {
private val logger = Logger("RenameAndChangeLocationDialogFragment")
private val safeArguments get() = requireNotNull(arguments)
+ private val promptAbuserDetector = PromptAbuserDetector(TIME_SHOWN_OFFSET_MILLIS)
+
internal val fileName: String
get() = safeArguments.getString(KEY_FILE_NAME, "")
@@ -75,6 +79,15 @@ class RenameAndChangeLocationDialogFragment : DialogFragment() {
}
}
+ override fun onResume() {
+ super.onResume()
+ promptAbuserDetector.start()
+ }
+
+ override fun onEnterAnimationComplete() {
+ promptAbuserDetector.start()
+ }
+
override fun onCancel(dialog: DialogInterface) {
super.onCancel(dialog)
onCancel()
@@ -99,6 +112,8 @@ class RenameAndChangeLocationDialogFragment : DialogFragment() {
val composeView = createComposeView()
+ promptAbuserDetector.start()
+
return MaterialAlertDialogBuilder(requireContext())
.setView(composeView)
.create()
@@ -144,11 +159,15 @@ class RenameAndChangeLocationDialogFragment : DialogFragment() {
directoryLauncher.launch(null)
},
onConfirm = {
- onConfirmSave(
- dialogState.fileName,
- dialogState.directoryPath,
- )
- dismiss()
+ if (promptAbuserDetector.areDialogsBeingAbused()) {
+ promptAbuserDetector.updateJSDialogAbusedState()
+ } else {
+ onConfirmSave(
+ dialogState.fileName,
+ dialogState.directoryPath,
+ )
+ dismiss()
+ }
},
onCancel = {
onCancel()
@@ -182,6 +201,7 @@ class RenameAndChangeLocationDialogFragment : DialogFragment() {
private const val KEY_DIRECTORY_PATH = "directory_path"
private const val KEY_CONTENT_SIZE = "content_size"
const val RENAME_AND_CHANGE_LOCATION_DIALOG_TAG = "RENAME_AND_CHANGE_LOCATION_DIALOG_TAG"
+ private const val TIME_SHOWN_OFFSET_MILLIS = 500
/**
* Creates a new instance of [RenameAndChangeLocationDialogFragment].
@@ -203,3 +223,14 @@ class RenameAndChangeLocationDialogFragment : DialogFragment() {
}
}
}
+
+/**
+ * Starts (or restarts) the time-based check without increasing the "click count".
+ *
+ * Makes it safe to call from multiple/successive lifecycle methods, without running into the risk
+ * of triggering the more restrictive count-based protection on the 1st click (or even before it).
+ */
+private fun PromptAbuserDetector.start() {
+ resetJSAlertAbuseState()
+ updateJSDialogAbusedState()
+}
View it on GitLab: https://gitlab.torproject.org/tpo/applications/tor-browser/-/compare/9857f6…
--
View it on GitLab: https://gitlab.torproject.org/tpo/applications/tor-browser/-/compare/9857f6…
You're receiving this email because of your account on gitlab.torproject.org. Manage all notifications: https://gitlab.torproject.org/-/profile/notifications | Help: https://gitlab.torproject.org/help
1
0