lists.torproject.org
Sign In Sign Up
Manage this list Sign In Sign Up

Keyboard Shortcuts

Thread View

  • j: Next unread message
  • k: Previous unread message
  • j a: Jump to all threads
  • j l: Jump to MailingList overview

tbb-commits

Thread Start a new thread
Threads by month
  • ----- 2025 -----
  • 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
tbb-commits@lists.torproject.org

September 2025

  • 1 participants
  • 213 discussions
[Git][tpo/applications/mullvad-browser][mullvad-browser-140.2.0esr-15.0-1] 6 commits: BB 43564: Modify ./mach bootstrap for Base Browser
by brizental (@brizental) 08 Sep '25

08 Sep '25
brizental pushed to branch mullvad-browser-140.2.0esr-15.0-1 at The Tor Project / Applications / Mullvad Browser Commits: c3546be5 by Beatriz Rizental at 2025-09-08T19:32:45+02:00 BB 43564: Modify ./mach bootstrap for Base Browser - - - - - 802cceaf by Beatriz Rizental at 2025-09-08T19:32:46+02:00 fixup! BB 43564: Modify ./mach bootstrap for Base Browser EXTRA: Stop asking to configure git during bootstrap. - - - - - 856dc4ee by Beatriz Rizental at 2025-09-08T19:32:47+02:00 fixup! BB 43564: Modify ./mach bootstrap for Base Browser - - - - - a1b3a8cf by Beatriz Rizental at 2025-09-08T19:32:47+02:00 fixup! BB 41803: Add some developer tools for working on tor-browser. - - - - - 4d66d017 by Beatriz Rizental at 2025-09-08T19:32:48+02:00 fixup! BB 43564: Modify ./mach bootstrap for Base Browser - - - - - 642024c9 by Beatriz Rizental at 2025-09-08T19:32:49+02:00 fixup! BB 43564: Modify ./mach bootstrap for Base Browser - - - - - 25 changed files: - + build/moz.configure/basebrowser-resources.configure - build/moz.configure/bootstrap.configure - build/moz.configure/init.configure - moz.configure - python/mozboot/mozboot/bootstrap.py - python/mozbuild/mozbuild/action/tooltool.py - python/mozbuild/mozbuild/artifact_commands.py - python/mozbuild/mozbuild/backend/base.py - + python/mozbuild/mozbuild/tbbutils.py - python/mozbuild/mozbuild/test/python.toml - + python/mozbuild/mozbuild/test/test_tbbutils.py - − tools/base-browser/l10n/combine/tests/README - tools/base-browser/git-rebase-fixup-preprocessor → tools/base_browser/git-rebase-fixup-preprocessor - tools/base-browser/l10n/combine-translation-versions.py → tools/base_browser/l10n/combine-translation-versions.py - tools/base-browser/l10n/combine/__init__.py → tools/base_browser/l10n/combine/__init__.py - tools/base-browser/l10n/combine/combine.py → tools/base_browser/l10n/combine/combine.py - tools/base-browser/l10n/combine/tests/__init__.py → tools/base_browser/l10n/combine/tests/__init__.py - + tools/base_browser/l10n/combine/tests/python.toml - tools/base-browser/l10n/combine/tests/test_android.py → tools/base_browser/l10n/combine/tests/test_android.py - tools/base-browser/l10n/combine/tests/test_dtd.py → tools/base_browser/l10n/combine/tests/test_dtd.py - tools/base-browser/l10n/combine/tests/test_fluent.py → tools/base_browser/l10n/combine/tests/test_fluent.py - tools/base-browser/l10n/combine/tests/test_properties.py → tools/base_browser/l10n/combine/tests/test_properties.py - tools/base-browser/missing-css-variables.py → tools/base_browser/missing-css-variables.py - tools/base-browser/tb-dev → tools/base_browser/tb-dev - tools/moz.build Changes: ===================================== build/moz.configure/basebrowser-resources.configure ===================================== @@ -0,0 +1,88 @@ +# Helpers +# ------------------------------------------------- + + +@depends(build_project) +def is_desktop_build(build_project): + return build_project == "browser" + + +# Bootstrap resources +# ------------------------------------------------- + + +option( + "--with-noscript", + env="NOSCRIPT", + nargs=1, + default=None, + help="Path to noscript .xpi extension archive.", +) + + +@depends( + "--with-noscript", + mozbuild_state_path, + bootstrap_path( + "noscript", no_unpack=True, when=depends("--with-noscript")(lambda x: not x) + ), +) +@checking("for noscript") +@imports(_from="pathlib", _import="Path") +def noscript(value, mozbuild_state_path, _bootstrapped): + if value: + path = Path(value[0]) + if path.is_file() and path.suffix == ".xpi": + return value[0] + else: + die("--with-noscript must be an existing .xpi file") + + bootstrapped_location = Path(mozbuild_state_path) / "browser" + for file in bootstrapped_location.glob(f"*.xpi"): + if "noscript" in file.name: + return str(bootstrapped_location / file) + + # noscript is not required for building. + return None + + +set_config("NOSCRIPT", noscript) + + +option( + "--with-tor-browser-fonts", + env="TOR_BROWSER_FONTS", + nargs=1, + default=None, + help="Path to location of fonts directory.", +) + + +@depends( + "--with-tor-browser-fonts", + mozbuild_state_path, + bootstrap_path( + "fonts", + when=depends("--with-tor-browser-fonts")(lambda x: not x) & is_desktop_build, + ), +) +@checking("for tor-browser fonts directory") +@imports(_from="pathlib", _import="Path") +def tor_browser_fonts(value, mozbuild_state_path, _bootstrapped): + if value: + path = Path(value[0]) + # TODO: Do a more thorough check on the directory. + if path.is_dir(): + return value[0] + else: + die("--with-tor-browser-fonts must point to a real directory.") + + bootstrapped_location = Path(mozbuild_state_path) / "fonts" + if bootstrapped_location.is_dir(): + return str(bootstrapped_location) + + # tor browser fonts directory is not required for building. + return None + + +set_config("TOR_BROWSER_FONTS", tor_browser_fonts) ===================================== build/moz.configure/bootstrap.configure ===================================== @@ -4,6 +4,29 @@ # 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/. +option( + "--with-tor-browser-build-out", + env="TOR_BROWSER_BUILD_OUT", + nargs=1, + default="https://tb-build-06.torproject.org/~tb-builder/tor-browser-build/out", + help="URL pointing to a Tor Browser Build out folder, served over HTTP[S].", +) + + +@depends("--with-tor-browser-build-out") +def tor_browser_build_out(value): + if value: + return value[0] + + +option( + "--enable-tor-browser-build-only-bootstrap", + env="TBB_ONLY_BOOTSTRAP", + default=False, + help="Flag that disables bootstrapping any artifact from Mozilla's Taskcluster. Will only bootstrap artifacts from tor-browser-build.", +) + + option( env="MOZ_FETCHES_DIR", nargs=1, @@ -115,9 +138,10 @@ def bootstrap_toolchain_tasks(host): def bootstrap_path(path, **kwargs): when = kwargs.pop("when", None) allow_failure = kwargs.pop("allow_failure", None) + no_unpack = kwargs.pop("no_unpack", False) if kwargs: configure_error( - "bootstrap_path only takes `when` and `allow_failure` as a keyword argument" + "bootstrap_path only takes `when`, `allow_failure` and `no_unpack` as keyword arguments" ) @depends( @@ -129,11 +153,16 @@ def bootstrap_path(path, **kwargs): build_environment, dependable(path), dependable(allow_failure), + dependable(no_unpack), + tor_browser_build_out, + "--enable-tor-browser-build-only-bootstrap", + target, when=when, ) @imports("os") @imports("subprocess") @imports("sys") + @imports("mozbuild.tbbutils") @imports(_from="mozbuild.dirutils", _import="ensureParentDir") @imports(_from="importlib", _import="import_module") @imports(_from="shutil", _import="rmtree") @@ -148,6 +177,10 @@ def bootstrap_path(path, **kwargs): build_env, path, allow_failure, + no_unpack, + tor_browser_build_out, + tbb_only_bootstrap, + target, ): if not path: return @@ -158,6 +191,83 @@ def bootstrap_path(path, **kwargs): if path_parts[0] == "clang-tools": path_prefix = path_parts.pop(0) + # Small hack because noscript is inside the browser folder. + if path_parts[0] == "noscript": + path_prefix = "browser" + + def try_tbb_bootstrap(exists): + if not tor_browser_build_out: + return False + + # Tor browser build doesn't have artifacts for all targets supported + # by the Firefox build system. When this is empty it means we are + # building for a platform which tbb doesn't support. + if not target.tor_browser_build_alias: + return False + + artifact = mozbuild.tbbutils.get_artifact_name(path_parts[0], tasks.prefix) + if not artifact: + log.info("%s is not mapped to a tbb artifact", path_parts[0]) + return False + + artifact_path = mozbuild.tbbutils.get_artifact_path( + tor_browser_build_out, + artifact, + target, + prefix=path_prefix, + log=log.warning, + ) + if not artifact_path: + log.info("no path found in tbb/out for %s", artifact) + return False + + # We will use the name of the artifact as the index. + # + # It's usually unique to the artifact version, but each artifact follows + # a different naming convention, so we can't really get more specific here. + artifact_index = artifact_path.rsplit("/", 1)[-1] + index_file = os.path.join(toolchains_base_dir, "indices", artifact) + try: + with open(index_file) as fh: + index = fh.read().strip() + except Exception: + index = None + if index == artifact_index and exists: + log.debug("%s is up-to-date", artifact) + return True + + command = ["artifact", "toolchain", "--from-url", artifact_path] + + if no_unpack: + command.append("--no-unpack") + + # Note to rebasers: + # From here on, it's a slightly modified copy/paste + # from the end of the try_bootstrap function + log.info( + "%s bootstrapped toolchain from TBB in %s", + "Updating" if exists else "Installing", + os.path.join(toolchains_base_dir, path_prefix, artifact), + ) + os.makedirs(os.path.join(toolchains_base_dir, path_prefix), exist_ok=True) + proc = subprocess.run( + [ + sys.executable, + os.path.join(build_env.topsrcdir, "mach"), + "--log-no-times", + ] + + command, + cwd=os.path.join(toolchains_base_dir, path_prefix), + check=not allow_failure, + ) + if proc.returncode != 0 and allow_failure: + return False + ensureParentDir(index_file) + with open(index_file, "w") as fh: + fh.write(artifact_index) + + return True + def try_bootstrap(exists): if not tasks: return False @@ -280,9 +390,10 @@ def bootstrap_path(path, **kwargs): try: # With --enable-bootstrap=no-update, we don't `try_bootstrap`, except # when the toolchain can't be found. - if ( - "no-update" not in enable_bootstrap or not exists - ) and not try_bootstrap(exists): + if ("no-update" not in enable_bootstrap or not exists) and not ( + try_tbb_bootstrap(exists) + or (not tbb_only_bootstrap and try_bootstrap(exists)) + ): # If there aren't toolchain artifacts to use for this build, # don't return a path. return None ===================================== build/moz.configure/init.configure ===================================== @@ -590,6 +590,21 @@ def split_triplet(triplet, allow_wasi=False): else: toolchain = "%s-%s" % (cpu, os) + # In tor-browser-build we use slightly different terminology for + # the supported platforms. Let's prepare that OS string here. + # + # Not all possible platforms listed here are supported in tbb, + # so this value will be empty sometimes. + tor_browser_build_alias = None + if canonical_os == "Android" and canonical_kernel == "Linux": + tor_browser_build_alias = f"android" + elif canonical_os == "GNU" and canonical_kernel == "Linux": + tor_browser_build_alias = f"linux" + elif canonical_os == "OSX" and canonical_kernel == "Darwin": + tor_browser_build_alias = f"macos" + elif canonical_os == "WINNT" and canonical_kernel == "WINNT": + tor_browser_build_alias = f"windows" + return namespace( alias=triplet, cpu=CPU(canonical_cpu), @@ -604,6 +619,7 @@ def split_triplet(triplet, allow_wasi=False): toolchain=toolchain, vendor=vendor, sub_configure_alias=sub_configure_alias, + tor_browser_build_alias=tor_browser_build_alias, ) ===================================== moz.configure ===================================== @@ -229,6 +229,7 @@ check_prog("WGET", ("wget",), allow_missing=True) include("build/moz.configure/toolchain.configure", when="--enable-compile-environment") +include("build/moz.configure/basebrowser-resources.configure") include("build/moz.configure/pkg.configure") include("build/moz.configure/memory.configure", when="--enable-compile-environment") ===================================== python/mozboot/mozboot/bootstrap.py ===================================== @@ -52,21 +52,28 @@ Note on Artifact Mode: Artifact builds download prebuilt C++ components rather than building them locally. Artifact builds are faster! -Artifact builds are recommended for people working on Firefox or -Firefox for Android frontends, or the GeckoView Java API. They are unsuitable +Artifact builds are recommended for people working on Tor Browser or +Base Browser for Android frontends, or the GeckoView Java API. They are unsuitable for those working on C++ code. For more information see: https://firefox-source-docs.mozilla.org/contributing/build/artifact_builds.…. -Please choose the version of Firefox you want to build (see note above): +# Note to Base Browser developers + +This is still highly experimental. Expect bugs! + +Please choose the version of Base Browser you want to build (see note above): %s Your choice: """ APPLICATIONS = OrderedDict( [ - ("Firefox for Desktop Artifact Mode", "browser_artifact_mode"), - ("Firefox for Desktop", "browser"), - ("GeckoView/Firefox for Android Artifact Mode", "mobile_android_artifact_mode"), - ("GeckoView/Firefox for Android", "mobile_android"), + ("Base Browser for Desktop Artifact Mode", "browser_artifact_mode"), + ("Base Browser for Desktop", "browser"), + ( + "GeckoView/Base Browser for Android Artifact Mode", + "mobile_android_artifact_mode", + ), + ("GeckoView/Base Browser for Android", "mobile_android"), ("SpiderMonkey JavaScript engine", "js"), ] ) @@ -360,6 +367,8 @@ class Bootstrapper: getattr(self.instance, "ensure_%s_packages" % application)() def check_code_submission(self, checkout_root: Path): + return + if self.instance.no_interactive or which("moz-phab"): return @@ -474,8 +483,7 @@ class Bootstrapper: configure_mercurial(hg, state_dir) # Offer to configure Git, if the current checkout or repo type is Git. - elif git and checkout_type == "git": - should_configure_git = False + elif False and git and checkout_type == "git": if not self.instance.no_interactive: should_configure_git = self.instance.prompt_yesno(prompt=CONFIGURE_GIT) else: ===================================== python/mozbuild/mozbuild/action/tooltool.py ===================================== @@ -1029,14 +1029,29 @@ def unpack_file(filename): """Untar `filename`, assuming it is uncompressed or compressed with bzip2, xz, gzip, zst, or unzip a zip file. The file is assumed to contain a single directory with a name matching the base of the given filename. - Xz support is handled by shelling out to 'tar'.""" + Xz support is handled by shelling out to 'tar'. + + tor-browser#41564 - For supporting tor-browser-build artifacts that contain + multiple directories, the archive is extracted into a directory with the + same name as the base of the filename. This modification is only applied to + tar archives, because that is all that was necessary. + """ if os.path.isfile(filename) and tarfile.is_tarfile(filename): tar_file, zip_ext = os.path.splitext(filename) base_file, tar_ext = os.path.splitext(tar_file) clean_path(base_file) log.info('untarring "%s"' % filename) with TarFile.open(filename) as tar: - safe_extract(tar) + top_level_directories = set() + for name in tar.getnames(): + dir = name.split("/", 1)[0] + top_level_directories.add(dir) + if len(top_level_directories) == 1: + safe_extract(tar) + else: + safe_extract( + tar, path=os.path.join(os.path.dirname(filename), base_file) + ) elif os.path.isfile(filename) and filename.endswith(".tar.zst"): import zstandard ===================================== python/mozbuild/mozbuild/artifact_commands.py ===================================== @@ -244,6 +244,12 @@ def artifact_clear_cache(command_context, tree=None, job=None, verbose=False): nargs="+", help="Download toolchain artifact from a given task.", ) +@CommandArgument( + "--from-url", + metavar="URL", + nargs="+", + help="Download toolchain artifact from an arbitrary address.", +) @CommandArgument( "--tooltool-manifest", metavar="MANIFEST", @@ -273,6 +279,7 @@ def artifact_toolchain( skip_cache=False, from_build=(), from_task=(), + from_url=[], tooltool_manifest=None, no_unpack=False, retry=0, @@ -504,6 +511,13 @@ def artifact_toolchain( record = ArtifactRecord(task_id, name) records[record.filename] = record + if from_url: + for file in from_url: + record = DownloadRecord( + file, file.rsplit("/", 1)[-1], None, None, None, True + ) + records[record.filename] = record + for record in records.values(): command_context.log( logging.INFO, ===================================== python/mozbuild/mozbuild/backend/base.py ===================================== @@ -2,11 +2,14 @@ # 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/. +import errno import itertools +import logging import os import time from abc import ABCMeta, abstractmethod from contextlib import contextmanager +from pathlib import Path import mozpack.path as mozpath from mach.mixin.logging import LoggingMixin @@ -239,6 +242,72 @@ class BuildBackend(LoggingMixin): with open(mozpath.join(dir, ".purgecaches"), "w") as f: f.write("\n") + def _setup_tor_browser_environment(self, config): + app = config.substs["MOZ_BUILD_APP"] + + noscript_target_filename = "{73a6fe31-595d-460b-a920-fcc0f8843232}.xpi" + noscript_location = config.substs.get("NOSCRIPT") + + def _infallible_symlink(src, dst): + try: + os.symlink(src, dst) + except OSError as e: + if e.errno == errno.EEXIST: + # If the symlink already exists, remove it and try again. + os.remove(dst) + os.symlink(src, dst) + else: + return + + if app == "browser": + tbdir = Path(config.topobjdir) / "dist" / "bin" + + if config.substs.get("OS_TARGET") == "Darwin": + tbdir = next(tbdir.glob("*.app")) + paths = { + "docs": tbdir / "Contents/Resources/TorBrowser/Docs", + "exts": tbdir / "Contents/Resources/distribution/extensions", + "fonts": tbdir / "Resources/fonts", + } + else: + paths = { + "docs": tbdir / "TorBrowser/Docs", + "exts": tbdir / "distribution/extensions", + "fonts": tbdir / "fonts", + } + + fonts_location = config.substs.get("TOR_BROWSER_FONTS") + if fonts_location: + self.log( + logging.INFO, + "_setup_tor_browser_environment", + { + "fonts_location": fonts_location, + "fonts_target": str(paths["fonts"]), + }, + "Creating symlink for fonts files from {fonts_location} to {fonts_target}", + ) + + for file in Path(fonts_location).iterdir(): + target = paths["fonts"] / file.name + _infallible_symlink(file, target) + + # Set up NoScript extension + if noscript_location: + noscript_target = paths["exts"] / noscript_target_filename + self.log( + logging.INFO, + "_setup_tor_browser_environment", + { + "noscript_location": noscript_location, + "noscript_target": str(noscript_target), + }, + "Creating symlink for NoScript from {noscript_location} to {noscript_target}", + ) + + paths["exts"].mkdir(parents=True, exist_ok=True) + _infallible_symlink(noscript_location, noscript_target) + def post_build(self, config, output, jobs, verbose, status): """Called late during 'mach build' execution, after `build(...)` has finished. @@ -257,6 +326,9 @@ class BuildBackend(LoggingMixin): """ self._write_purgecaches(config) + if status == 0: + self._setup_tor_browser_environment(config) + return status @contextmanager ===================================== python/mozbuild/mozbuild/tbbutils.py ===================================== @@ -0,0 +1,103 @@ +import re +from urllib.request import Request, urlopen + + +def list_files_http(url): + try: + req = Request(url, method="GET") + with urlopen(req) as response: + if response.status != 200: + return [] + html = response.read().decode() + except Exception: + return [] + + links = [] + for href in re.findall(r'<a href="([^"]+)"', html): + if href == "../": + continue + + links.append(href) + + return links + + +TOR_BROWSER_BUILD_ARTIFACTS = [ + # Tor Browser Build-only artifacts, these artifacts are not common with Firefox. + "noscript", + "fonts", +] + +# Mapping of artifacts from taskcluster to tor-browser-build. +ARTIFACT_NAME_MAP = { + "cbindgen": "cbindgen", + # FIXME (tor-browser-build#41471): nasm is more or less ready to go, but it needs to have the + # executable in the root of the artifact folder instead of nasm/bin. + # "nasm": "nasm", + # FIXME (tor-browser-build#41421): the clang project as is, is not ready to use. It needs + # to be repackaged with a bunch of things that differ per platform. Fun stuff. + # "clang": "clang", + "node": "node", +} + + +def get_artifact_name(original_artifact_name, host): + # These are not build artifacts, they are pre-built artifacts to be added to the final build, + # therefore this check can come before the host check. + if original_artifact_name in TOR_BROWSER_BUILD_ARTIFACTS: + return original_artifact_name + + if host != "linux64": + # Tor browser build only has development artifacts for linux64 host systems. + return None + + return ARTIFACT_NAME_MAP.get(original_artifact_name) + + +def get_artifact_path(url, artifact, target, prefix="", log=lambda *args, **kwargs: {}): + if prefix: + path = prefix + else: + path = artifact + + # The `?C=M;O=D` parameters make it so links are ordered by + # the last modified date. This here to make us get the latest + # version of file in the case there are multiple and we just + # grab the first one. + files = list_files_http(f"{url}/{path}?C=M;O=D") + + if not files: + log(f"No files found in {url} for {artifact}.") + return None + + def filter_files(files, keyword): + return [file for file in files if keyword in file] + + artifact_files = [file for file in files if file.startswith(artifact)] + + if len(artifact_files) == 0: + log(f"No files found in {url} for {artifact}.") + return None + + if len(artifact_files) == 1: + return f"{url}/{path}/{artifact_files[0]}" + + files_per_os = filter_files(artifact_files, target.tor_browser_build_alias) + + # If there are files in the folder, but they don't have the OS in the name + # it probably means we can get any of them because they can be used to build + # for any OS. So let's just get the first one. + # + # Note: It could be the case that the artifact _is_ OS dependant, but there + # just are no files for the OS we are looking for. In that case, this will + # return an incorrect artifact. This should not happen often though and is + # something we cannot address until artifact names are standardized on tbb. + if len(files_per_os) == 0: + return f"{url}/{artifact}/{artifact_files[0]}" + + elif len(files_per_os) == 1: + return f"{url}/{artifact}/{files_per_os[0]}" + + matches = filter_files(files_per_os, target.cpu) + + return f"{url}/{artifact}/{matches[0]}" if matches else None ===================================== python/mozbuild/mozbuild/test/python.toml ===================================== @@ -111,6 +111,9 @@ subsuite = "mozbuild" ["test_rewrite_mozbuild.py"] +["test_tbbutils.py"] +subsuite = "base-browser" + ["test_telemetry.py"] ["test_telemetry_settings.py"] ===================================== python/mozbuild/mozbuild/test/test_tbbutils.py ===================================== @@ -0,0 +1,139 @@ +import unittest +from types import SimpleNamespace +from unittest.mock import MagicMock, patch + +import mozunit + +from mozbuild.tbbutils import get_artifact_path, list_files_http + + +class TestGetArtifactName(unittest.TestCase): + def setUp(self): + self.artifact = "artifact" + self.host = "linux64" + + @patch("mozbuild.tbbutils.TOR_BROWSER_BUILD_ARTIFACTS", new=["artifact"]) + def test_artifact_in_tbb_artifacts(self): + from mozbuild.tbbutils import get_artifact_name + + result = get_artifact_name(self.artifact, self.host) + self.assertEqual(result, self.artifact) + + @patch("mozbuild.tbbutils.ARTIFACT_NAME_MAP", new={"artifact": "tcafitra"}) + def test_host_is_not_linux64(self): + from mozbuild.tbbutils import get_artifact_name + + result = get_artifact_name(self.artifact, "linux64-aarch64") + self.assertIsNone(result) + + @patch("mozbuild.tbbutils.ARTIFACT_NAME_MAP", new={"artifact": "tcafitra"}) + def test_mapped_artifact(self): + from mozbuild.tbbutils import get_artifact_name + + result = get_artifact_name(self.artifact, self.host) + self.assertEqual(result, self.artifact[::-1]) + + +class TestGetArtifactPath(unittest.TestCase): + def setUp(self): + self.url = "http://example.com" + self.artifact = "artifact" + # This is just an example target which is valid. But it doesn't make + # any difference and could be anything for these tests. + self.target = SimpleNamespace(tor_browser_build_alias="linux", cpu="x86_64") + + @patch("mozbuild.tbbutils.list_files_http") + def test_no_files_returns_none(self, mock_list_files): + mock_list_files.return_value = [] + result = get_artifact_path(self.url, self.artifact, self.target) + self.assertIsNone(result) + + @patch("mozbuild.tbbutils.list_files_http") + def test_no_matching_files_returns_none(self, mock_list_files): + mock_list_files.return_value = ["somethingelse.zip", "yetanotherthing.zip"] + result = get_artifact_path(self.url, self.artifact, self.target) + self.assertIsNone(result) + + @patch("mozbuild.tbbutils.list_files_http") + def test_single_artifact_match(self, mock_list_files): + mock_list_files.return_value = ["artifact-1.zip"] + result = get_artifact_path(self.url, self.artifact, self.target) + self.assertEqual(result, f"{self.url}/{self.artifact}/artifact-1.zip") + + @patch("mozbuild.tbbutils.list_files_http") + def test_artifact_without_os_returns_first(self, mock_list_files): + mock_list_files.return_value = ["artifact-1.zip", "artifact-2.zip"] + result = get_artifact_path(self.url, self.artifact, self.target) + self.assertTrue(result.startswith(f"{self.url}/{self.artifact}/")) + self.assertIn("artifact-", result) + + @patch("mozbuild.tbbutils.list_files_http") + def test_artifact_with_os_match(self, mock_list_files): + mock_list_files.return_value = [ + "artifact-windows.zip", + "artifact-linux.zip", + ] + result = get_artifact_path(self.url, self.artifact, self.target) + self.assertEqual(result, f"{self.url}/{self.artifact}/artifact-linux.zip") + + @patch("mozbuild.tbbutils.list_files_http") + def test_artifact_with_cpu_match(self, mock_list_files): + mock_list_files.return_value = [ + "artifact-linux-arm.zip", + "artifact-linux-x86_64.zip", + ] + result = get_artifact_path(self.url, self.artifact, self.target) + self.assertEqual( + result, f"{self.url}/{self.artifact}/artifact-linux-x86_64.zip" + ) + + @patch("mozbuild.tbbutils.list_files_http") + def test_artifact_with_prefix(self, mock_list_files): + mock_list_files.return_value = ["artifact-1.zip"] + + prefix = "prefix" + result = get_artifact_path(self.url, self.artifact, self.target, prefix=prefix) + self.assertEqual(result, f"{self.url}/{prefix}/artifact-1.zip") + mock_list_files.assert_called_with(f"{self.url}/{prefix}?C=M;O=D") + + +class TestListFilesHttp(unittest.TestCase): + def setUp(self): + self.url = "http://example.com" + + @patch("mozbuild.tbbutils.urlopen") + def test_non_200_status_returns_empty(self, mock_urlopen): + mock_resp = MagicMock() + mock_resp.status = 404 + mock_resp.read.return_value = b"" + mock_urlopen.return_value.__enter__.return_value = mock_resp + + result = list_files_http(self.url) + self.assertEqual(result, []) + + @patch("mozbuild.tbbutils.urlopen") + def test_exception_returns_empty(self, mock_urlopen): + mock_urlopen.side_effect = Exception("network error") + result = list_files_http(self.url) + self.assertEqual(result, []) + + @patch("mozbuild.tbbutils.urlopen") + def test_regular_links(self, mock_urlopen): + html = b""" + <html><body> + <a href="../">Parent</a> + <a href="file1.zip">file1</a> + <a href="file2.zip">file2</a> + </body></html> + """ + mock_resp = MagicMock() + mock_resp.status = 200 + mock_resp.read.return_value = html + mock_urlopen.return_value.__enter__.return_value = mock_resp + + result = list_files_http(self.url) + self.assertEqual(result, ["file1.zip", "file2.zip"]) + + +if __name__ == "__main__": + mozunit.main() ===================================== tools/base-browser/l10n/combine/tests/README deleted ===================================== @@ -1,2 +0,0 @@ -python tests to be run with pytest. -Requires the compare-locales package. ===================================== tools/base-browser/git-rebase-fixup-preprocessor → tools/base_browser/git-rebase-fixup-preprocessor ===================================== ===================================== tools/base-browser/l10n/combine-translation-versions.py → tools/base_browser/l10n/combine-translation-versions.py ===================================== ===================================== tools/base-browser/l10n/combine/__init__.py → tools/base_browser/l10n/combine/__init__.py ===================================== ===================================== tools/base-browser/l10n/combine/combine.py → tools/base_browser/l10n/combine/combine.py ===================================== ===================================== tools/base-browser/l10n/combine/tests/__init__.py → tools/base_browser/l10n/combine/tests/__init__.py ===================================== ===================================== tools/base_browser/l10n/combine/tests/python.toml ===================================== @@ -0,0 +1,10 @@ +[DEFAULT] +subsuite = "base-browser" + +["test_android.py"] + +["test_dtd.py"] + +["test_fluent.py"] + +["test_properties.py"] ===================================== tools/base-browser/l10n/combine/tests/test_android.py → tools/base_browser/l10n/combine/tests/test_android.py ===================================== @@ -1,6 +1,7 @@ import textwrap -from combine import combine_files +import mozunit +from base_browser.l10n.combine import combine_files def wrap_in_xml(content): @@ -413,3 +414,7 @@ def test_alternatives(): <string name="string_4_alt">Other string</string> """, ) + + +if __name__ == "__main__": + mozunit.main() ===================================== tools/base-browser/l10n/combine/tests/test_dtd.py → tools/base_browser/l10n/combine/tests/test_dtd.py ===================================== @@ -1,6 +1,7 @@ import textwrap -from combine import combine_files +import mozunit +from base_browser.l10n.combine import combine_files def assert_result(new_content, old_content, expect): @@ -411,3 +412,7 @@ def test_alternatives(): <!ENTITY string.4.alt "Other string"> """, ) + + +if __name__ == "__main__": + mozunit.main() ===================================== tools/base-browser/l10n/combine/tests/test_fluent.py → tools/base_browser/l10n/combine/tests/test_fluent.py ===================================== @@ -1,6 +1,7 @@ import textwrap -from combine import combine_files +import mozunit +from base_browser.l10n.combine import combine_files def assert_result(new_content, old_content, expect): @@ -475,3 +476,7 @@ def test_alternatives(): -string-4-alt = Other string """, ) + + +if __name__ == "__main__": + mozunit.main() ===================================== tools/base-browser/l10n/combine/tests/test_properties.py → tools/base_browser/l10n/combine/tests/test_properties.py ===================================== @@ -1,6 +1,7 @@ import textwrap -from combine import combine_files +import mozunit +from base_browser.l10n.combine import combine_files def assert_result(new_content, old_content, expect): @@ -408,3 +409,7 @@ def test_alternatives(): string.4.alt = Other string """, ) + + +if __name__ == "__main__": + mozunit.main() ===================================== tools/base-browser/missing-css-variables.py → tools/base_browser/missing-css-variables.py ===================================== ===================================== tools/base-browser/tb-dev → tools/base_browser/tb-dev ===================================== ===================================== tools/moz.build ===================================== @@ -71,6 +71,7 @@ with Files("tryselect/docs/**"): SCHEDULES.exclusive = ["docs"] PYTHON_UNITTEST_MANIFESTS += [ + "base_browser/l10n/combine/tests/python.toml", "fuzzing/smoke/python.toml", "lint/test/python.toml", "tryselect/test/python.toml", View it on GitLab: https://gitlab.torproject.org/tpo/applications/mullvad-browser/-/compare/a5… -- View it on GitLab: https://gitlab.torproject.org/tpo/applications/mullvad-browser/-/compare/a5… You're receiving this email because of your account on gitlab.torproject.org.
1 0
0 0
[Git][tpo/applications/tor-browser][base-browser-140.2.0esr-15.0-1] 6 commits: BB 43564: Modify ./mach bootstrap for Base Browser
by brizental (@brizental) 08 Sep '25

08 Sep '25
brizental pushed to branch base-browser-140.2.0esr-15.0-1 at The Tor Project / Applications / Tor Browser Commits: 85b4d8d5 by Beatriz Rizental at 2025-09-08T19:22:50+02:00 BB 43564: Modify ./mach bootstrap for Base Browser - - - - - 02a18b4d by Beatriz Rizental at 2025-09-08T19:22:58+02:00 fixup! BB 43564: Modify ./mach bootstrap for Base Browser EXTRA: Stop asking to configure git during bootstrap. - - - - - 4937c1b6 by Beatriz Rizental at 2025-09-08T19:23:06+02:00 fixup! BB 43564: Modify ./mach bootstrap for Base Browser - - - - - 98a01dbd by Beatriz Rizental at 2025-09-08T19:23:32+02:00 fixup! BB 41803: Add some developer tools for working on tor-browser. - - - - - 10ce4ee0 by Beatriz Rizental at 2025-09-08T19:28:44+02:00 fixup! BB 43564: Modify ./mach bootstrap for Base Browser - - - - - e841a083 by Beatriz Rizental at 2025-09-08T19:28:49+02:00 fixup! BB 43564: Modify ./mach bootstrap for Base Browser - - - - - 25 changed files: - + build/moz.configure/basebrowser-resources.configure - build/moz.configure/bootstrap.configure - build/moz.configure/init.configure - moz.configure - python/mozboot/mozboot/bootstrap.py - python/mozbuild/mozbuild/action/tooltool.py - python/mozbuild/mozbuild/artifact_commands.py - python/mozbuild/mozbuild/backend/base.py - + python/mozbuild/mozbuild/tbbutils.py - python/mozbuild/mozbuild/test/python.toml - + python/mozbuild/mozbuild/test/test_tbbutils.py - − tools/base-browser/l10n/combine/tests/README - tools/base-browser/git-rebase-fixup-preprocessor → tools/base_browser/git-rebase-fixup-preprocessor - tools/base-browser/l10n/combine-translation-versions.py → tools/base_browser/l10n/combine-translation-versions.py - tools/base-browser/l10n/combine/__init__.py → tools/base_browser/l10n/combine/__init__.py - tools/base-browser/l10n/combine/combine.py → tools/base_browser/l10n/combine/combine.py - tools/base-browser/l10n/combine/tests/__init__.py → tools/base_browser/l10n/combine/tests/__init__.py - + tools/base_browser/l10n/combine/tests/python.toml - tools/base-browser/l10n/combine/tests/test_android.py → tools/base_browser/l10n/combine/tests/test_android.py - tools/base-browser/l10n/combine/tests/test_dtd.py → tools/base_browser/l10n/combine/tests/test_dtd.py - tools/base-browser/l10n/combine/tests/test_fluent.py → tools/base_browser/l10n/combine/tests/test_fluent.py - tools/base-browser/l10n/combine/tests/test_properties.py → tools/base_browser/l10n/combine/tests/test_properties.py - tools/base-browser/missing-css-variables.py → tools/base_browser/missing-css-variables.py - tools/base-browser/tb-dev → tools/base_browser/tb-dev - tools/moz.build Changes: ===================================== build/moz.configure/basebrowser-resources.configure ===================================== @@ -0,0 +1,88 @@ +# Helpers +# ------------------------------------------------- + + +@depends(build_project) +def is_desktop_build(build_project): + return build_project == "browser" + + +# Bootstrap resources +# ------------------------------------------------- + + +option( + "--with-noscript", + env="NOSCRIPT", + nargs=1, + default=None, + help="Path to noscript .xpi extension archive.", +) + + +@depends( + "--with-noscript", + mozbuild_state_path, + bootstrap_path( + "noscript", no_unpack=True, when=depends("--with-noscript")(lambda x: not x) + ), +) +@checking("for noscript") +@imports(_from="pathlib", _import="Path") +def noscript(value, mozbuild_state_path, _bootstrapped): + if value: + path = Path(value[0]) + if path.is_file() and path.suffix == ".xpi": + return value[0] + else: + die("--with-noscript must be an existing .xpi file") + + bootstrapped_location = Path(mozbuild_state_path) / "browser" + for file in bootstrapped_location.glob(f"*.xpi"): + if "noscript" in file.name: + return str(bootstrapped_location / file) + + # noscript is not required for building. + return None + + +set_config("NOSCRIPT", noscript) + + +option( + "--with-tor-browser-fonts", + env="TOR_BROWSER_FONTS", + nargs=1, + default=None, + help="Path to location of fonts directory.", +) + + +@depends( + "--with-tor-browser-fonts", + mozbuild_state_path, + bootstrap_path( + "fonts", + when=depends("--with-tor-browser-fonts")(lambda x: not x) & is_desktop_build, + ), +) +@checking("for tor-browser fonts directory") +@imports(_from="pathlib", _import="Path") +def tor_browser_fonts(value, mozbuild_state_path, _bootstrapped): + if value: + path = Path(value[0]) + # TODO: Do a more thorough check on the directory. + if path.is_dir(): + return value[0] + else: + die("--with-tor-browser-fonts must point to a real directory.") + + bootstrapped_location = Path(mozbuild_state_path) / "fonts" + if bootstrapped_location.is_dir(): + return str(bootstrapped_location) + + # tor browser fonts directory is not required for building. + return None + + +set_config("TOR_BROWSER_FONTS", tor_browser_fonts) ===================================== build/moz.configure/bootstrap.configure ===================================== @@ -4,6 +4,29 @@ # 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/. +option( + "--with-tor-browser-build-out", + env="TOR_BROWSER_BUILD_OUT", + nargs=1, + default="https://tb-build-06.torproject.org/~tb-builder/tor-browser-build/out", + help="URL pointing to a Tor Browser Build out folder, served over HTTP[S].", +) + + +@depends("--with-tor-browser-build-out") +def tor_browser_build_out(value): + if value: + return value[0] + + +option( + "--enable-tor-browser-build-only-bootstrap", + env="TBB_ONLY_BOOTSTRAP", + default=False, + help="Flag that disables bootstrapping any artifact from Mozilla's Taskcluster. Will only bootstrap artifacts from tor-browser-build.", +) + + option( env="MOZ_FETCHES_DIR", nargs=1, @@ -115,9 +138,10 @@ def bootstrap_toolchain_tasks(host): def bootstrap_path(path, **kwargs): when = kwargs.pop("when", None) allow_failure = kwargs.pop("allow_failure", None) + no_unpack = kwargs.pop("no_unpack", False) if kwargs: configure_error( - "bootstrap_path only takes `when` and `allow_failure` as a keyword argument" + "bootstrap_path only takes `when`, `allow_failure` and `no_unpack` as keyword arguments" ) @depends( @@ -129,11 +153,16 @@ def bootstrap_path(path, **kwargs): build_environment, dependable(path), dependable(allow_failure), + dependable(no_unpack), + tor_browser_build_out, + "--enable-tor-browser-build-only-bootstrap", + target, when=when, ) @imports("os") @imports("subprocess") @imports("sys") + @imports("mozbuild.tbbutils") @imports(_from="mozbuild.dirutils", _import="ensureParentDir") @imports(_from="importlib", _import="import_module") @imports(_from="shutil", _import="rmtree") @@ -148,6 +177,10 @@ def bootstrap_path(path, **kwargs): build_env, path, allow_failure, + no_unpack, + tor_browser_build_out, + tbb_only_bootstrap, + target, ): if not path: return @@ -158,6 +191,83 @@ def bootstrap_path(path, **kwargs): if path_parts[0] == "clang-tools": path_prefix = path_parts.pop(0) + # Small hack because noscript is inside the browser folder. + if path_parts[0] == "noscript": + path_prefix = "browser" + + def try_tbb_bootstrap(exists): + if not tor_browser_build_out: + return False + + # Tor browser build doesn't have artifacts for all targets supported + # by the Firefox build system. When this is empty it means we are + # building for a platform which tbb doesn't support. + if not target.tor_browser_build_alias: + return False + + artifact = mozbuild.tbbutils.get_artifact_name(path_parts[0], tasks.prefix) + if not artifact: + log.info("%s is not mapped to a tbb artifact", path_parts[0]) + return False + + artifact_path = mozbuild.tbbutils.get_artifact_path( + tor_browser_build_out, + artifact, + target, + prefix=path_prefix, + log=log.warning, + ) + if not artifact_path: + log.info("no path found in tbb/out for %s", artifact) + return False + + # We will use the name of the artifact as the index. + # + # It's usually unique to the artifact version, but each artifact follows + # a different naming convention, so we can't really get more specific here. + artifact_index = artifact_path.rsplit("/", 1)[-1] + index_file = os.path.join(toolchains_base_dir, "indices", artifact) + try: + with open(index_file) as fh: + index = fh.read().strip() + except Exception: + index = None + if index == artifact_index and exists: + log.debug("%s is up-to-date", artifact) + return True + + command = ["artifact", "toolchain", "--from-url", artifact_path] + + if no_unpack: + command.append("--no-unpack") + + # Note to rebasers: + # From here on, it's a slightly modified copy/paste + # from the end of the try_bootstrap function + log.info( + "%s bootstrapped toolchain from TBB in %s", + "Updating" if exists else "Installing", + os.path.join(toolchains_base_dir, path_prefix, artifact), + ) + os.makedirs(os.path.join(toolchains_base_dir, path_prefix), exist_ok=True) + proc = subprocess.run( + [ + sys.executable, + os.path.join(build_env.topsrcdir, "mach"), + "--log-no-times", + ] + + command, + cwd=os.path.join(toolchains_base_dir, path_prefix), + check=not allow_failure, + ) + if proc.returncode != 0 and allow_failure: + return False + ensureParentDir(index_file) + with open(index_file, "w") as fh: + fh.write(artifact_index) + + return True + def try_bootstrap(exists): if not tasks: return False @@ -280,9 +390,10 @@ def bootstrap_path(path, **kwargs): try: # With --enable-bootstrap=no-update, we don't `try_bootstrap`, except # when the toolchain can't be found. - if ( - "no-update" not in enable_bootstrap or not exists - ) and not try_bootstrap(exists): + if ("no-update" not in enable_bootstrap or not exists) and not ( + try_tbb_bootstrap(exists) + or (not tbb_only_bootstrap and try_bootstrap(exists)) + ): # If there aren't toolchain artifacts to use for this build, # don't return a path. return None ===================================== build/moz.configure/init.configure ===================================== @@ -590,6 +590,21 @@ def split_triplet(triplet, allow_wasi=False): else: toolchain = "%s-%s" % (cpu, os) + # In tor-browser-build we use slightly different terminology for + # the supported platforms. Let's prepare that OS string here. + # + # Not all possible platforms listed here are supported in tbb, + # so this value will be empty sometimes. + tor_browser_build_alias = None + if canonical_os == "Android" and canonical_kernel == "Linux": + tor_browser_build_alias = f"android" + elif canonical_os == "GNU" and canonical_kernel == "Linux": + tor_browser_build_alias = f"linux" + elif canonical_os == "OSX" and canonical_kernel == "Darwin": + tor_browser_build_alias = f"macos" + elif canonical_os == "WINNT" and canonical_kernel == "WINNT": + tor_browser_build_alias = f"windows" + return namespace( alias=triplet, cpu=CPU(canonical_cpu), @@ -604,6 +619,7 @@ def split_triplet(triplet, allow_wasi=False): toolchain=toolchain, vendor=vendor, sub_configure_alias=sub_configure_alias, + tor_browser_build_alias=tor_browser_build_alias, ) ===================================== moz.configure ===================================== @@ -229,6 +229,7 @@ check_prog("WGET", ("wget",), allow_missing=True) include("build/moz.configure/toolchain.configure", when="--enable-compile-environment") +include("build/moz.configure/basebrowser-resources.configure") include("build/moz.configure/pkg.configure") include("build/moz.configure/memory.configure", when="--enable-compile-environment") ===================================== python/mozboot/mozboot/bootstrap.py ===================================== @@ -52,21 +52,28 @@ Note on Artifact Mode: Artifact builds download prebuilt C++ components rather than building them locally. Artifact builds are faster! -Artifact builds are recommended for people working on Firefox or -Firefox for Android frontends, or the GeckoView Java API. They are unsuitable +Artifact builds are recommended for people working on Tor Browser or +Base Browser for Android frontends, or the GeckoView Java API. They are unsuitable for those working on C++ code. For more information see: https://firefox-source-docs.mozilla.org/contributing/build/artifact_builds.…. -Please choose the version of Firefox you want to build (see note above): +# Note to Base Browser developers + +This is still highly experimental. Expect bugs! + +Please choose the version of Base Browser you want to build (see note above): %s Your choice: """ APPLICATIONS = OrderedDict( [ - ("Firefox for Desktop Artifact Mode", "browser_artifact_mode"), - ("Firefox for Desktop", "browser"), - ("GeckoView/Firefox for Android Artifact Mode", "mobile_android_artifact_mode"), - ("GeckoView/Firefox for Android", "mobile_android"), + ("Base Browser for Desktop Artifact Mode", "browser_artifact_mode"), + ("Base Browser for Desktop", "browser"), + ( + "GeckoView/Base Browser for Android Artifact Mode", + "mobile_android_artifact_mode", + ), + ("GeckoView/Base Browser for Android", "mobile_android"), ("SpiderMonkey JavaScript engine", "js"), ] ) @@ -360,6 +367,8 @@ class Bootstrapper: getattr(self.instance, "ensure_%s_packages" % application)() def check_code_submission(self, checkout_root: Path): + return + if self.instance.no_interactive or which("moz-phab"): return @@ -474,8 +483,7 @@ class Bootstrapper: configure_mercurial(hg, state_dir) # Offer to configure Git, if the current checkout or repo type is Git. - elif git and checkout_type == "git": - should_configure_git = False + elif False and git and checkout_type == "git": if not self.instance.no_interactive: should_configure_git = self.instance.prompt_yesno(prompt=CONFIGURE_GIT) else: ===================================== python/mozbuild/mozbuild/action/tooltool.py ===================================== @@ -1029,14 +1029,29 @@ def unpack_file(filename): """Untar `filename`, assuming it is uncompressed or compressed with bzip2, xz, gzip, zst, or unzip a zip file. The file is assumed to contain a single directory with a name matching the base of the given filename. - Xz support is handled by shelling out to 'tar'.""" + Xz support is handled by shelling out to 'tar'. + + tor-browser#41564 - For supporting tor-browser-build artifacts that contain + multiple directories, the archive is extracted into a directory with the + same name as the base of the filename. This modification is only applied to + tar archives, because that is all that was necessary. + """ if os.path.isfile(filename) and tarfile.is_tarfile(filename): tar_file, zip_ext = os.path.splitext(filename) base_file, tar_ext = os.path.splitext(tar_file) clean_path(base_file) log.info('untarring "%s"' % filename) with TarFile.open(filename) as tar: - safe_extract(tar) + top_level_directories = set() + for name in tar.getnames(): + dir = name.split("/", 1)[0] + top_level_directories.add(dir) + if len(top_level_directories) == 1: + safe_extract(tar) + else: + safe_extract( + tar, path=os.path.join(os.path.dirname(filename), base_file) + ) elif os.path.isfile(filename) and filename.endswith(".tar.zst"): import zstandard ===================================== python/mozbuild/mozbuild/artifact_commands.py ===================================== @@ -244,6 +244,12 @@ def artifact_clear_cache(command_context, tree=None, job=None, verbose=False): nargs="+", help="Download toolchain artifact from a given task.", ) +@CommandArgument( + "--from-url", + metavar="URL", + nargs="+", + help="Download toolchain artifact from an arbitrary address.", +) @CommandArgument( "--tooltool-manifest", metavar="MANIFEST", @@ -273,6 +279,7 @@ def artifact_toolchain( skip_cache=False, from_build=(), from_task=(), + from_url=[], tooltool_manifest=None, no_unpack=False, retry=0, @@ -504,6 +511,13 @@ def artifact_toolchain( record = ArtifactRecord(task_id, name) records[record.filename] = record + if from_url: + for file in from_url: + record = DownloadRecord( + file, file.rsplit("/", 1)[-1], None, None, None, True + ) + records[record.filename] = record + for record in records.values(): command_context.log( logging.INFO, ===================================== python/mozbuild/mozbuild/backend/base.py ===================================== @@ -2,11 +2,14 @@ # 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/. +import errno import itertools +import logging import os import time from abc import ABCMeta, abstractmethod from contextlib import contextmanager +from pathlib import Path import mozpack.path as mozpath from mach.mixin.logging import LoggingMixin @@ -239,6 +242,72 @@ class BuildBackend(LoggingMixin): with open(mozpath.join(dir, ".purgecaches"), "w") as f: f.write("\n") + def _setup_tor_browser_environment(self, config): + app = config.substs["MOZ_BUILD_APP"] + + noscript_target_filename = "{73a6fe31-595d-460b-a920-fcc0f8843232}.xpi" + noscript_location = config.substs.get("NOSCRIPT") + + def _infallible_symlink(src, dst): + try: + os.symlink(src, dst) + except OSError as e: + if e.errno == errno.EEXIST: + # If the symlink already exists, remove it and try again. + os.remove(dst) + os.symlink(src, dst) + else: + return + + if app == "browser": + tbdir = Path(config.topobjdir) / "dist" / "bin" + + if config.substs.get("OS_TARGET") == "Darwin": + tbdir = next(tbdir.glob("*.app")) + paths = { + "docs": tbdir / "Contents/Resources/TorBrowser/Docs", + "exts": tbdir / "Contents/Resources/distribution/extensions", + "fonts": tbdir / "Resources/fonts", + } + else: + paths = { + "docs": tbdir / "TorBrowser/Docs", + "exts": tbdir / "distribution/extensions", + "fonts": tbdir / "fonts", + } + + fonts_location = config.substs.get("TOR_BROWSER_FONTS") + if fonts_location: + self.log( + logging.INFO, + "_setup_tor_browser_environment", + { + "fonts_location": fonts_location, + "fonts_target": str(paths["fonts"]), + }, + "Creating symlink for fonts files from {fonts_location} to {fonts_target}", + ) + + for file in Path(fonts_location).iterdir(): + target = paths["fonts"] / file.name + _infallible_symlink(file, target) + + # Set up NoScript extension + if noscript_location: + noscript_target = paths["exts"] / noscript_target_filename + self.log( + logging.INFO, + "_setup_tor_browser_environment", + { + "noscript_location": noscript_location, + "noscript_target": str(noscript_target), + }, + "Creating symlink for NoScript from {noscript_location} to {noscript_target}", + ) + + paths["exts"].mkdir(parents=True, exist_ok=True) + _infallible_symlink(noscript_location, noscript_target) + def post_build(self, config, output, jobs, verbose, status): """Called late during 'mach build' execution, after `build(...)` has finished. @@ -257,6 +326,9 @@ class BuildBackend(LoggingMixin): """ self._write_purgecaches(config) + if status == 0: + self._setup_tor_browser_environment(config) + return status @contextmanager ===================================== python/mozbuild/mozbuild/tbbutils.py ===================================== @@ -0,0 +1,103 @@ +import re +from urllib.request import Request, urlopen + + +def list_files_http(url): + try: + req = Request(url, method="GET") + with urlopen(req) as response: + if response.status != 200: + return [] + html = response.read().decode() + except Exception: + return [] + + links = [] + for href in re.findall(r'<a href="([^"]+)"', html): + if href == "../": + continue + + links.append(href) + + return links + + +TOR_BROWSER_BUILD_ARTIFACTS = [ + # Tor Browser Build-only artifacts, these artifacts are not common with Firefox. + "noscript", + "fonts", +] + +# Mapping of artifacts from taskcluster to tor-browser-build. +ARTIFACT_NAME_MAP = { + "cbindgen": "cbindgen", + # FIXME (tor-browser-build#41471): nasm is more or less ready to go, but it needs to have the + # executable in the root of the artifact folder instead of nasm/bin. + # "nasm": "nasm", + # FIXME (tor-browser-build#41421): the clang project as is, is not ready to use. It needs + # to be repackaged with a bunch of things that differ per platform. Fun stuff. + # "clang": "clang", + "node": "node", +} + + +def get_artifact_name(original_artifact_name, host): + # These are not build artifacts, they are pre-built artifacts to be added to the final build, + # therefore this check can come before the host check. + if original_artifact_name in TOR_BROWSER_BUILD_ARTIFACTS: + return original_artifact_name + + if host != "linux64": + # Tor browser build only has development artifacts for linux64 host systems. + return None + + return ARTIFACT_NAME_MAP.get(original_artifact_name) + + +def get_artifact_path(url, artifact, target, prefix="", log=lambda *args, **kwargs: {}): + if prefix: + path = prefix + else: + path = artifact + + # The `?C=M;O=D` parameters make it so links are ordered by + # the last modified date. This here to make us get the latest + # version of file in the case there are multiple and we just + # grab the first one. + files = list_files_http(f"{url}/{path}?C=M;O=D") + + if not files: + log(f"No files found in {url} for {artifact}.") + return None + + def filter_files(files, keyword): + return [file for file in files if keyword in file] + + artifact_files = [file for file in files if file.startswith(artifact)] + + if len(artifact_files) == 0: + log(f"No files found in {url} for {artifact}.") + return None + + if len(artifact_files) == 1: + return f"{url}/{path}/{artifact_files[0]}" + + files_per_os = filter_files(artifact_files, target.tor_browser_build_alias) + + # If there are files in the folder, but they don't have the OS in the name + # it probably means we can get any of them because they can be used to build + # for any OS. So let's just get the first one. + # + # Note: It could be the case that the artifact _is_ OS dependant, but there + # just are no files for the OS we are looking for. In that case, this will + # return an incorrect artifact. This should not happen often though and is + # something we cannot address until artifact names are standardized on tbb. + if len(files_per_os) == 0: + return f"{url}/{artifact}/{artifact_files[0]}" + + elif len(files_per_os) == 1: + return f"{url}/{artifact}/{files_per_os[0]}" + + matches = filter_files(files_per_os, target.cpu) + + return f"{url}/{artifact}/{matches[0]}" if matches else None ===================================== python/mozbuild/mozbuild/test/python.toml ===================================== @@ -111,6 +111,9 @@ subsuite = "mozbuild" ["test_rewrite_mozbuild.py"] +["test_tbbutils.py"] +subsuite = "base-browser" + ["test_telemetry.py"] ["test_telemetry_settings.py"] ===================================== python/mozbuild/mozbuild/test/test_tbbutils.py ===================================== @@ -0,0 +1,139 @@ +import unittest +from types import SimpleNamespace +from unittest.mock import MagicMock, patch + +import mozunit + +from mozbuild.tbbutils import get_artifact_path, list_files_http + + +class TestGetArtifactName(unittest.TestCase): + def setUp(self): + self.artifact = "artifact" + self.host = "linux64" + + @patch("mozbuild.tbbutils.TOR_BROWSER_BUILD_ARTIFACTS", new=["artifact"]) + def test_artifact_in_tbb_artifacts(self): + from mozbuild.tbbutils import get_artifact_name + + result = get_artifact_name(self.artifact, self.host) + self.assertEqual(result, self.artifact) + + @patch("mozbuild.tbbutils.ARTIFACT_NAME_MAP", new={"artifact": "tcafitra"}) + def test_host_is_not_linux64(self): + from mozbuild.tbbutils import get_artifact_name + + result = get_artifact_name(self.artifact, "linux64-aarch64") + self.assertIsNone(result) + + @patch("mozbuild.tbbutils.ARTIFACT_NAME_MAP", new={"artifact": "tcafitra"}) + def test_mapped_artifact(self): + from mozbuild.tbbutils import get_artifact_name + + result = get_artifact_name(self.artifact, self.host) + self.assertEqual(result, self.artifact[::-1]) + + +class TestGetArtifactPath(unittest.TestCase): + def setUp(self): + self.url = "http://example.com" + self.artifact = "artifact" + # This is just an example target which is valid. But it doesn't make + # any difference and could be anything for these tests. + self.target = SimpleNamespace(tor_browser_build_alias="linux", cpu="x86_64") + + @patch("mozbuild.tbbutils.list_files_http") + def test_no_files_returns_none(self, mock_list_files): + mock_list_files.return_value = [] + result = get_artifact_path(self.url, self.artifact, self.target) + self.assertIsNone(result) + + @patch("mozbuild.tbbutils.list_files_http") + def test_no_matching_files_returns_none(self, mock_list_files): + mock_list_files.return_value = ["somethingelse.zip", "yetanotherthing.zip"] + result = get_artifact_path(self.url, self.artifact, self.target) + self.assertIsNone(result) + + @patch("mozbuild.tbbutils.list_files_http") + def test_single_artifact_match(self, mock_list_files): + mock_list_files.return_value = ["artifact-1.zip"] + result = get_artifact_path(self.url, self.artifact, self.target) + self.assertEqual(result, f"{self.url}/{self.artifact}/artifact-1.zip") + + @patch("mozbuild.tbbutils.list_files_http") + def test_artifact_without_os_returns_first(self, mock_list_files): + mock_list_files.return_value = ["artifact-1.zip", "artifact-2.zip"] + result = get_artifact_path(self.url, self.artifact, self.target) + self.assertTrue(result.startswith(f"{self.url}/{self.artifact}/")) + self.assertIn("artifact-", result) + + @patch("mozbuild.tbbutils.list_files_http") + def test_artifact_with_os_match(self, mock_list_files): + mock_list_files.return_value = [ + "artifact-windows.zip", + "artifact-linux.zip", + ] + result = get_artifact_path(self.url, self.artifact, self.target) + self.assertEqual(result, f"{self.url}/{self.artifact}/artifact-linux.zip") + + @patch("mozbuild.tbbutils.list_files_http") + def test_artifact_with_cpu_match(self, mock_list_files): + mock_list_files.return_value = [ + "artifact-linux-arm.zip", + "artifact-linux-x86_64.zip", + ] + result = get_artifact_path(self.url, self.artifact, self.target) + self.assertEqual( + result, f"{self.url}/{self.artifact}/artifact-linux-x86_64.zip" + ) + + @patch("mozbuild.tbbutils.list_files_http") + def test_artifact_with_prefix(self, mock_list_files): + mock_list_files.return_value = ["artifact-1.zip"] + + prefix = "prefix" + result = get_artifact_path(self.url, self.artifact, self.target, prefix=prefix) + self.assertEqual(result, f"{self.url}/{prefix}/artifact-1.zip") + mock_list_files.assert_called_with(f"{self.url}/{prefix}?C=M;O=D") + + +class TestListFilesHttp(unittest.TestCase): + def setUp(self): + self.url = "http://example.com" + + @patch("mozbuild.tbbutils.urlopen") + def test_non_200_status_returns_empty(self, mock_urlopen): + mock_resp = MagicMock() + mock_resp.status = 404 + mock_resp.read.return_value = b"" + mock_urlopen.return_value.__enter__.return_value = mock_resp + + result = list_files_http(self.url) + self.assertEqual(result, []) + + @patch("mozbuild.tbbutils.urlopen") + def test_exception_returns_empty(self, mock_urlopen): + mock_urlopen.side_effect = Exception("network error") + result = list_files_http(self.url) + self.assertEqual(result, []) + + @patch("mozbuild.tbbutils.urlopen") + def test_regular_links(self, mock_urlopen): + html = b""" + <html><body> + <a href="../">Parent</a> + <a href="file1.zip">file1</a> + <a href="file2.zip">file2</a> + </body></html> + """ + mock_resp = MagicMock() + mock_resp.status = 200 + mock_resp.read.return_value = html + mock_urlopen.return_value.__enter__.return_value = mock_resp + + result = list_files_http(self.url) + self.assertEqual(result, ["file1.zip", "file2.zip"]) + + +if __name__ == "__main__": + mozunit.main() ===================================== tools/base-browser/l10n/combine/tests/README deleted ===================================== @@ -1,2 +0,0 @@ -python tests to be run with pytest. -Requires the compare-locales package. ===================================== tools/base-browser/git-rebase-fixup-preprocessor → tools/base_browser/git-rebase-fixup-preprocessor ===================================== ===================================== tools/base-browser/l10n/combine-translation-versions.py → tools/base_browser/l10n/combine-translation-versions.py ===================================== ===================================== tools/base-browser/l10n/combine/__init__.py → tools/base_browser/l10n/combine/__init__.py ===================================== ===================================== tools/base-browser/l10n/combine/combine.py → tools/base_browser/l10n/combine/combine.py ===================================== ===================================== tools/base-browser/l10n/combine/tests/__init__.py → tools/base_browser/l10n/combine/tests/__init__.py ===================================== ===================================== tools/base_browser/l10n/combine/tests/python.toml ===================================== @@ -0,0 +1,10 @@ +[DEFAULT] +subsuite = "base-browser" + +["test_android.py"] + +["test_dtd.py"] + +["test_fluent.py"] + +["test_properties.py"] ===================================== tools/base-browser/l10n/combine/tests/test_android.py → tools/base_browser/l10n/combine/tests/test_android.py ===================================== @@ -1,6 +1,7 @@ import textwrap -from combine import combine_files +import mozunit +from base_browser.l10n.combine import combine_files def wrap_in_xml(content): @@ -413,3 +414,7 @@ def test_alternatives(): <string name="string_4_alt">Other string</string> """, ) + + +if __name__ == "__main__": + mozunit.main() ===================================== tools/base-browser/l10n/combine/tests/test_dtd.py → tools/base_browser/l10n/combine/tests/test_dtd.py ===================================== @@ -1,6 +1,7 @@ import textwrap -from combine import combine_files +import mozunit +from base_browser.l10n.combine import combine_files def assert_result(new_content, old_content, expect): @@ -411,3 +412,7 @@ def test_alternatives(): <!ENTITY string.4.alt "Other string"> """, ) + + +if __name__ == "__main__": + mozunit.main() ===================================== tools/base-browser/l10n/combine/tests/test_fluent.py → tools/base_browser/l10n/combine/tests/test_fluent.py ===================================== @@ -1,6 +1,7 @@ import textwrap -from combine import combine_files +import mozunit +from base_browser.l10n.combine import combine_files def assert_result(new_content, old_content, expect): @@ -475,3 +476,7 @@ def test_alternatives(): -string-4-alt = Other string """, ) + + +if __name__ == "__main__": + mozunit.main() ===================================== tools/base-browser/l10n/combine/tests/test_properties.py → tools/base_browser/l10n/combine/tests/test_properties.py ===================================== @@ -1,6 +1,7 @@ import textwrap -from combine import combine_files +import mozunit +from base_browser.l10n.combine import combine_files def assert_result(new_content, old_content, expect): @@ -408,3 +409,7 @@ def test_alternatives(): string.4.alt = Other string """, ) + + +if __name__ == "__main__": + mozunit.main() ===================================== tools/base-browser/missing-css-variables.py → tools/base_browser/missing-css-variables.py ===================================== ===================================== tools/base-browser/tb-dev → tools/base_browser/tb-dev ===================================== ===================================== tools/moz.build ===================================== @@ -71,6 +71,7 @@ with Files("tryselect/docs/**"): SCHEDULES.exclusive = ["docs"] PYTHON_UNITTEST_MANIFESTS += [ + "base_browser/l10n/combine/tests/python.toml", "fuzzing/smoke/python.toml", "lint/test/python.toml", "tryselect/test/python.toml", View it on GitLab: https://gitlab.torproject.org/tpo/applications/tor-browser/-/compare/53201c… -- View it on GitLab: https://gitlab.torproject.org/tpo/applications/tor-browser/-/compare/53201c… You're receiving this email because of your account on gitlab.torproject.org.
1 0
0 0
[Git][tpo/applications/tor-browser][tor-browser-140.2.0esr-15.0-1] fixup! BB 43564: Modify ./mach bootstrap for Base Browser
by brizental (@brizental) 08 Sep '25

08 Sep '25
brizental pushed to branch tor-browser-140.2.0esr-15.0-1 at The Tor Project / Applications / Tor Browser Commits: e3afce24 by Beatriz Rizental at 2025-09-08T18:43:45+02:00 fixup! BB 43564: Modify ./mach bootstrap for Base Browser - - - - - 3 changed files: - build/moz.configure/bootstrap.configure - python/mozbuild/mozbuild/tbbutils.py - python/mozbuild/mozbuild/test/test_tbbutils.py Changes: ===================================== build/moz.configure/bootstrap.configure ===================================== @@ -211,7 +211,11 @@ def bootstrap_path(path, **kwargs): return False artifact_path = mozbuild.tbbutils.get_artifact_path( - tor_browser_build_out, artifact, target, prefix=path_prefix + tor_browser_build_out, + artifact, + target, + prefix=path_prefix, + log=log.warning, ) if not artifact_path: log.info("no path found in tbb/out for %s", artifact) ===================================== python/mozbuild/mozbuild/tbbutils.py ===================================== @@ -58,7 +58,7 @@ def get_artifact_name(original_artifact_name, host): return ARTIFACT_NAME_MAP.get(original_artifact_name) -def get_artifact_path(url, artifact, target, prefix=""): +def get_artifact_path(url, artifact, target, prefix="", log=lambda *args, **kwargs: {}): if prefix: path = prefix else: @@ -71,6 +71,7 @@ def get_artifact_path(url, artifact, target, prefix=""): files = list_files_http(f"{url}/{path}?C=M;O=D") if not files: + log(f"No files found in {url} for {artifact}.") return None def filter_files(files, keyword): @@ -78,6 +79,10 @@ def get_artifact_path(url, artifact, target, prefix=""): artifact_files = [file for file in files if file.startswith(artifact)] + if len(artifact_files) == 0: + log(f"No files found in {url} for {artifact}.") + return None + if len(artifact_files) == 1: return f"{url}/{path}/{artifact_files[0]}" ===================================== python/mozbuild/mozbuild/test/test_tbbutils.py ===================================== @@ -48,6 +48,12 @@ class TestGetArtifactPath(unittest.TestCase): result = get_artifact_path(self.url, self.artifact, self.target) self.assertIsNone(result) + @patch("mozbuild.tbbutils.list_files_http") + def test_no_matching_files_returns_none(self, mock_list_files): + mock_list_files.return_value = ["somethingelse.zip", "yetanotherthing.zip"] + result = get_artifact_path(self.url, self.artifact, self.target) + self.assertIsNone(result) + @patch("mozbuild.tbbutils.list_files_http") def test_single_artifact_match(self, mock_list_files): mock_list_files.return_value = ["artifact-1.zip"] View it on GitLab: https://gitlab.torproject.org/tpo/applications/tor-browser/-/commit/e3afce2… -- View it on GitLab: https://gitlab.torproject.org/tpo/applications/tor-browser/-/commit/e3afce2… You're receiving this email because of your account on gitlab.torproject.org.
1 0
0 0
[Git][tpo/applications/mullvad-browser][mullvad-browser-140.2.0esr-15.0-1] fixup! MB 419: Mullvad Browser migration procedures.
by Pier Angelo Vendrame (@pierov) 08 Sep '25

08 Sep '25
Pier Angelo Vendrame pushed to branch mullvad-browser-140.2.0esr-15.0-1 at The Tor Project / Applications / Mullvad Browser Commits: a5709ed4 by Pier Angelo Vendrame at 2025-09-08T16:07:32+02:00 fixup! MB 419: Mullvad Browser migration procedures. BB 43770: Follow upstream's BrowserGlue simplifications. - - - - - 2 changed files: - browser/components/BrowserGlue.sys.mjs - browser/components/ProfileDataUpgrader.sys.mjs Changes: ===================================== browser/components/BrowserGlue.sys.mjs ===================================== @@ -430,9 +430,7 @@ BrowserGlue.prototype = { // handle any UI migration this._migrateUI(); lazy.ProfileDataUpgrader.upgradeBB(this._isNewProfile); - - // Mullvad Browser-specific version of _migrateUI. - this._migrateUIMB(); + lazy.ProfileDataUpgrader.upgradeMB(this._isNewProfile); if (!Services.prefs.prefHasUserValue(PREF_PDFJS_ISDEFAULT_CACHE_STATE)) { lazy.PdfJs.checkIsDefault(this._isNewProfile); @@ -1654,58 +1652,6 @@ BrowserGlue.prototype = { } }, - // Use this method for any MB migration that can be run just before showing - // the UI. - // Anything that critically needs to be migrated earlier should not use this. - async _migrateUIMB() { - // Version 1: Mullvad Browser 14.5a6: Clear home page update url preference - // (mullvad-browser#411). - // Version 2: Mullvad Browser 15.0a2: Remove legacy search addons - // (tor-browser#43111). - const MB_MIGRATION_VERSION = 2; - const MIGRATION_PREF = "mullvadbrowser.migration.version"; - - // If we decide to force updating users to pass through any version - // following 14.5, we can remove this check, and check only whether - // MIGRATION_PREF has a user value, like Mozilla does. - if (this._isNewProfile) { - // Do not migrate fresh profiles - Services.prefs.setIntPref(MIGRATION_PREF, MB_MIGRATION_VERSION); - return; - } else if (this._isNewProfile === undefined) { - // If this happens, check if upstream updated their function and do not - // set this member anymore! - console.error("_migrateUIMB: this._isNewProfile is undefined."); - } - - const currentVersion = Services.prefs.getIntPref(MIGRATION_PREF, 0); - - if (currentVersion < 1) { - Services.prefs.clearUserPref("mullvadbrowser.post_update.url"); - } - const dropAddons = async list => { - for (const id of list) { - try { - const engine = await lazy.AddonManager.getAddonByID(id); - await engine?.uninstall(); - } catch {} - } - }; - if (currentVersion < 2) { - await dropAddons([ - "brave(a)search.mozilla.org", - "ddg(a)search.mozilla.org", - "ddg-html(a)search.mozilla.org", - "metager(a)search.mozilla.org", - "mojeek(a)search.mozilla.org", - "mullvad-leta(a)search.mozilla.org", - "startpage(a)search.mozilla.org", - ]); - } - - Services.prefs.setIntPref(MIGRATION_PREF, MB_MIGRATION_VERSION); - }, - async _showUpgradeDialog() { const data = await lazy.OnboardingMessageProvider.getUpgradeMessage(); const { gBrowser } = lazy.BrowserWindowTracker.getTopWindow(); ===================================== browser/components/ProfileDataUpgrader.sys.mjs ===================================== @@ -974,4 +974,50 @@ export let ProfileDataUpgrader = { } Services.prefs.setIntPref(MIGRATION_PREF, MIGRATION_VERSION); }, + + async upgradeMB(isNewProfile) { + // Version 1: Mullvad Browser 14.5a6: Clear home page update url preference + // (mullvad-browser#411). + // Version 2: Mullvad Browser 15.0a2: Remove legacy search addons + // (tor-browser#43111). + const MB_MIGRATION_VERSION = 2; + const MIGRATION_PREF = "mullvadbrowser.migration.version"; + + if (isNewProfile) { + // Do not migrate fresh profiles + Services.prefs.setIntPref(MIGRATION_PREF, MB_MIGRATION_VERSION); + return; + } else if (isNewProfile === undefined) { + // If this happens, check if upstream updated their function and do not + // set this member anymore! + console.error("upgradeTB: isNewProfile is undefined."); + } + + const currentVersion = Services.prefs.getIntPref(MIGRATION_PREF, 0); + + if (currentVersion < 1) { + Services.prefs.clearUserPref("mullvadbrowser.post_update.url"); + } + const dropAddons = async list => { + for (const id of list) { + try { + const engine = await lazy.AddonManager.getAddonByID(id); + await engine?.uninstall(); + } catch {} + } + }; + if (currentVersion < 2) { + await dropAddons([ + "brave(a)search.mozilla.org", + "ddg(a)search.mozilla.org", + "ddg-html(a)search.mozilla.org", + "metager(a)search.mozilla.org", + "mojeek(a)search.mozilla.org", + "mullvad-leta(a)search.mozilla.org", + "startpage(a)search.mozilla.org", + ]); + } + + Services.prefs.setIntPref(MIGRATION_PREF, MB_MIGRATION_VERSION); + }, }; View it on GitLab: https://gitlab.torproject.org/tpo/applications/mullvad-browser/-/commit/a57… -- View it on GitLab: https://gitlab.torproject.org/tpo/applications/mullvad-browser/-/commit/a57… You're receiving this email because of your account on gitlab.torproject.org.
1 0
0 0
[Git][tpo/applications/tor-browser][tor-browser-140.2.0esr-15.0-1] fixup! TB 43006: Disable RFP for Font Visibility on Android
by morgan (@morgan) 08 Sep '25

08 Sep '25
morgan pushed to branch tor-browser-140.2.0esr-15.0-1 at The Tor Project / Applications / Tor Browser Commits: dc42afa4 by Pier Angelo Vendrame at 2025-09-08T13:51:04+00:00 fixup! TB 43006: Disable RFP for Font Visibility on Android TB 43943: Refactor fontvis exclusion on Android. Rather than updating Document, we can use nsRFPService::HandleExeptionalRFPTargets. - - - - - 2 changed files: - dom/base/Document.cpp - toolkit/components/resistfingerprinting/nsRFPService.cpp Changes: ===================================== dom/base/Document.cpp ===================================== @@ -17308,12 +17308,6 @@ bool Document::RecomputeResistFingerprinting(bool aForceRefreshRTPCallerType) { } bool Document::ShouldResistFingerprinting(RFPTarget aTarget) const { -#ifdef ANDROID - if (aTarget == RFPTarget::FontVisibilityBaseSystem || - aTarget == RFPTarget::FontVisibilityLangPack) { - return false; - } -#endif return mShouldResistFingerprinting && nsRFPService::IsRFPEnabledFor(this->IsInPrivateBrowsing(), aTarget, mOverriddenFingerprintingSettings); ===================================== toolkit/components/resistfingerprinting/nsRFPService.cpp ===================================== @@ -310,6 +310,13 @@ Maybe<bool> nsRFPService::HandleExeptionalRFPTargets( } #endif +#ifdef ANDROID + if (aTarget == RFPTarget::FontVisibilityBaseSystem || + aTarget == RFPTarget::FontVisibilityLangPack) { + return Some(false); + } +#endif + return Nothing(); } View it on GitLab: https://gitlab.torproject.org/tpo/applications/tor-browser/-/commit/dc42afa… -- View it on GitLab: https://gitlab.torproject.org/tpo/applications/tor-browser/-/commit/dc42afa… You're receiving this email because of your account on gitlab.torproject.org.
1 0
0 0
[Git][tpo/applications/mullvad-browser][mullvad-browser-140.2.0esr-15.0-1] fixup! Firefox preference overrides.
by Pier Angelo Vendrame (@pierov) 08 Sep '25

08 Sep '25
Pier Angelo Vendrame pushed to branch mullvad-browser-140.2.0esr-15.0-1 at The Tor Project / Applications / Mullvad Browser Commits: c735c7ed by Pier Angelo Vendrame at 2025-09-08T15:51:22+02:00 fixup! Firefox preference overrides. BB 43950: Disable HEVC. HEVC support can be used for fingerprinting, as it's hardware-dependent on some systems, or depends on distribution support/installed packages for Linux. - - - - - 1 changed file: - browser/app/profile/001-base-profile.js Changes: ===================================== browser/app/profile/001-base-profile.js ===================================== @@ -483,6 +483,9 @@ pref("gfx.offscreencanvas.enabled", false); pref("dom.disable_window_move_resize", true); // Set video VP9 to 0 for everyone (bug 22548) pref("media.benchmark.vp9.threshold", 0); +// tor-browser#43950: Disable HEVC, as it will reveal lacking hardware support, +// or differences in installed codec for Linux systems. +pref("media.hevc.enabled", false); pref("privacy.resistFingerprinting.block_mozAddonManager", true); // Bug 26114 pref("dom.webmidi.enabled", false); // Bug 41398: Disable Web MIDI API // tor-browser#42043: Stop reporting device IDs (and spoof their number without View it on GitLab: https://gitlab.torproject.org/tpo/applications/mullvad-browser/-/commit/c73… -- View it on GitLab: https://gitlab.torproject.org/tpo/applications/mullvad-browser/-/commit/c73… You're receiving this email because of your account on gitlab.torproject.org.
1 0
0 0
[Git][tpo/applications/tor-browser][base-browser-140.2.0esr-15.0-1] fixup! Firefox preference overrides.
by Pier Angelo Vendrame (@pierov) 08 Sep '25

08 Sep '25
Pier Angelo Vendrame pushed to branch base-browser-140.2.0esr-15.0-1 at The Tor Project / Applications / Tor Browser Commits: 53201cbe by Pier Angelo Vendrame at 2025-09-08T15:49:55+02:00 fixup! Firefox preference overrides. BB 43950: Disable HEVC. HEVC support can be used for fingerprinting, as it's hardware-dependent on some systems, or depends on distribution support/installed packages for Linux. - - - - - 1 changed file: - browser/app/profile/001-base-profile.js Changes: ===================================== browser/app/profile/001-base-profile.js ===================================== @@ -483,6 +483,9 @@ pref("gfx.offscreencanvas.enabled", false); pref("dom.disable_window_move_resize", true); // Set video VP9 to 0 for everyone (bug 22548) pref("media.benchmark.vp9.threshold", 0); +// tor-browser#43950: Disable HEVC, as it will reveal lacking hardware support, +// or differences in installed codec for Linux systems. +pref("media.hevc.enabled", false); pref("privacy.resistFingerprinting.block_mozAddonManager", true); // Bug 26114 pref("dom.webmidi.enabled", false); // Bug 41398: Disable Web MIDI API // tor-browser#42043: Stop reporting device IDs (and spoof their number without View it on GitLab: https://gitlab.torproject.org/tpo/applications/tor-browser/-/commit/53201cb… -- View it on GitLab: https://gitlab.torproject.org/tpo/applications/tor-browser/-/commit/53201cb… You're receiving this email because of your account on gitlab.torproject.org.
1 0
0 0
[Git][tpo/applications/tor-browser][tor-browser-140.2.0esr-15.0-1] fixup! Firefox preference overrides.
by Pier Angelo Vendrame (@pierov) 08 Sep '25

08 Sep '25
Pier Angelo Vendrame pushed to branch tor-browser-140.2.0esr-15.0-1 at The Tor Project / Applications / Tor Browser Commits: c02477fa by Pier Angelo Vendrame at 2025-09-08T13:44:13+00:00 fixup! Firefox preference overrides. BB 43950: Disable HEVC. HEVC support can be used for fingerprinting, as it's hardware-dependent on some systems, or depends on distribution support/installed packages for Linux. - - - - - 1 changed file: - browser/app/profile/001-base-profile.js Changes: ===================================== browser/app/profile/001-base-profile.js ===================================== @@ -483,6 +483,9 @@ pref("gfx.offscreencanvas.enabled", false); pref("dom.disable_window_move_resize", true); // Set video VP9 to 0 for everyone (bug 22548) pref("media.benchmark.vp9.threshold", 0); +// tor-browser#43950: Disable HEVC, as it will reveal lacking hardware support, +// or differences in installed codec for Linux systems. +pref("media.hevc.enabled", false); pref("privacy.resistFingerprinting.block_mozAddonManager", true); // Bug 26114 pref("dom.webmidi.enabled", false); // Bug 41398: Disable Web MIDI API // tor-browser#42043: Stop reporting device IDs (and spoof their number without View it on GitLab: https://gitlab.torproject.org/tpo/applications/tor-browser/-/commit/c02477f… -- View it on GitLab: https://gitlab.torproject.org/tpo/applications/tor-browser/-/commit/c02477f… You're receiving this email because of your account on gitlab.torproject.org.
1 0
0 0
[Git][tpo/applications/mullvad-browser][mullvad-browser-140.2.0esr-15.0-1] 3 commits: fixup! BB 42019: Empty browser's clipboard on browser shutdown
by Pier Angelo Vendrame (@pierov) 08 Sep '25

08 Sep '25
Pier Angelo Vendrame pushed to branch mullvad-browser-140.2.0esr-15.0-1 at The Tor Project / Applications / Mullvad Browser Commits: 4c83f2db by Pier Angelo Vendrame at 2025-09-08T12:32:21+02:00 fixup! BB 42019: Empty browser's clipboard on browser shutdown BB 43770: Follow upstream's BrowserGlue simplifications. ClipboardPrivacy to its own file and initialize it from the manifest, rather than from BrowserGlue. - - - - - 4b7d805c by Pier Angelo Vendrame at 2025-09-08T12:32:32+02:00 fixup! BB 40925: Implemented the Security Level component BB 43770: Follow upstream's BrowserGlue simplifications. Initialize the security level notification from the manifest. Also, since it was the only occurrence of the file path, move it to the moz-src:// scheme. - - - - - 7a6cc7a1 by Pier Angelo Vendrame at 2025-09-08T12:35:10+02:00 fixup! BB 42027: Base Browser migration procedures. BB 43770: Follow upstream's BrowserGlue simplifications. - - - - - 5 changed files: - browser/components/BrowserComponents.manifest - browser/components/BrowserGlue.sys.mjs - browser/components/ProfileDataUpgrader.sys.mjs - + browser/modules/ClipboardPrivacy.sys.mjs - browser/modules/moz.build Changes: ===================================== browser/components/BrowserComponents.manifest ===================================== @@ -50,6 +50,8 @@ category browser-first-window-ready resource://gre/modules/CaptchaDetectionPingU category browser-first-window-ready resource://gre/modules/SandboxUtils.sys.mjs SandboxUtils.maybeWarnAboutMissingUserNamespaces #endif #endif +category browser-first-window-ready moz-src:///browser/modules/ClipboardPrivacy.sys.mjs ClipboardPrivacy.init +category browser-first-window-ready moz-src:///browser/modules/SecurityLevelRestartNotification.sys.mjs SecurityLevelRestartNotification.ready category browser-idle-startup resource:///modules/PlacesUIUtils.sys.mjs PlacesUIUtils.unblockToolbars category browser-idle-startup resource:///modules/BuiltInThemes.sys.mjs BuiltInThemes.ensureBuiltInThemes ===================================== browser/components/BrowserGlue.sys.mjs ===================================== @@ -12,7 +12,6 @@ ChromeUtils.defineESModuleGetters(lazy, { AWToolbarButton: "resource:///modules/aboutwelcome/AWToolbarUtils.sys.mjs", ASRouter: "resource:///modules/asrouter/ASRouter.sys.mjs", AddonManager: "resource://gre/modules/AddonManager.sys.mjs", - AsyncShutdown: "resource://gre/modules/AsyncShutdown.sys.mjs", BackupService: "resource:///modules/backup/BackupService.sys.mjs", BrowserSearchTelemetry: "moz-src:///browser/components/search/BrowserSearchTelemetry.sys.mjs", @@ -61,8 +60,6 @@ ChromeUtils.defineESModuleGetters(lazy, { ScreenshotsUtils: "resource:///modules/ScreenshotsUtils.sys.mjs", SearchSERPTelemetry: "moz-src:///browser/components/search/SearchSERPTelemetry.sys.mjs", - SecurityLevelRestartNotification: - "resource:///modules/SecurityLevelRestartNotification.sys.mjs", SessionStartup: "resource:///modules/sessionstore/SessionStartup.sys.mjs", SessionStore: "resource:///modules/sessionstore/SessionStore.sys.mjs", ShortcutUtils: "resource://gre/modules/ShortcutUtils.sys.mjs", @@ -106,170 +103,6 @@ if (AppConstants.ENABLE_WEBDRIVER) { const PREF_PDFJS_ISDEFAULT_CACHE_STATE = "pdfjs.enabledCache.state"; -// Empty clipboard content from private windows on exit -// (tor-browser#42154) -const ClipboardPrivacy = { - _lastClipboardHash: null, - _globalActivation: false, - _isPrivateClipboard: false, - _hasher: null, - _shuttingDown: false, - _log: null, - - _createTransferable() { - const trans = Cc["@mozilla.org/widget/transferable;1"].createInstance( - Ci.nsITransferable - ); - trans.init(null); - return trans; - }, - _computeClipboardHash() { - const flavors = ["text/x-moz-url", "text/plain"]; - if ( - !Services.clipboard.hasDataMatchingFlavors( - flavors, - Ci.nsIClipboard.kGlobalClipboard - ) - ) { - return null; - } - const trans = this._createTransferable(); - flavors.forEach(trans.addDataFlavor); - try { - Services.clipboard.getData(trans, Ci.nsIClipboard.kGlobalClipboard); - const clipboardContent = {}; - trans.getAnyTransferData({}, clipboardContent); - const { data } = clipboardContent.value.QueryInterface( - Ci.nsISupportsString - ); - const bytes = new TextEncoder().encode(data); - const hasher = (this._hasher ||= Cc[ - "@mozilla.org/security/hash;1" - ].createInstance(Ci.nsICryptoHash)); - hasher.init(hasher.SHA256); - hasher.update(bytes, bytes.length); - return hasher.finish(true); - } catch (e) {} - return null; - }, - - startup() { - this._log = console.createInstance({ - prefix: "ClipboardPrivacy", - }); - this._lastClipboardHash = this._computeClipboardHash(); - - // Here we track changes in active window / application, - // by filtering focus events and window closures. - const handleActivation = (win, activation) => { - if (activation) { - if (!this._globalActivation) { - // focus changed within this window, bail out. - return; - } - this._globalActivation = false; - } else if (!Services.focus.activeWindow) { - // focus is leaving this window: - // let's track whether it remains within the browser. - lazy.setTimeout(() => { - this._globalActivation = !Services.focus.activeWindow; - }, 100); - } - - const checkClipboardContent = () => { - const clipboardHash = this._computeClipboardHash(); - if (clipboardHash !== this._lastClipboardHash) { - this._isPrivateClipboard = - !activation && - (lazy.PrivateBrowsingUtils.permanentPrivateBrowsing || - lazy.PrivateBrowsingUtils.isWindowPrivate(win)); - this._lastClipboardHash = clipboardHash; - this._log.debug( - `Clipboard changed: private ${this._isPrivateClipboard}, hash ${clipboardHash}.` - ); - } - }; - - if (win.closed) { - checkClipboardContent(); - } else { - // defer clipboard access on DOM events to work-around tor-browser#42306 - lazy.setTimeout(checkClipboardContent, 0); - } - }; - const focusListener = e => - e.isTrusted && handleActivation(e.currentTarget, e.type === "focusin"); - const initWindow = win => { - for (const e of ["focusin", "focusout"]) { - win.addEventListener(e, focusListener); - } - }; - for (const w of Services.ww.getWindowEnumerator()) { - initWindow(w); - } - Services.ww.registerNotification((win, event) => { - switch (event) { - case "domwindowopened": - initWindow(win); - break; - case "domwindowclosed": - handleActivation(win, false); - if ( - this._isPrivateClipboard && - lazy.PrivateBrowsingUtils.isWindowPrivate(win) && - (this._shuttingDown || - !Array.from(Services.ww.getWindowEnumerator()).find( - w => - lazy.PrivateBrowsingUtils.isWindowPrivate(w) && - // We need to filter out the HIDDEN WebExtensions window, - // which might be private as well but is not UI-relevant. - !w.location.href.startsWith("chrome://extensions/") - )) - ) { - // no more private windows, empty private content if needed - this.emptyPrivate(); - } - } - }); - - lazy.AsyncShutdown.quitApplicationGranted.addBlocker( - "ClipboardPrivacy: removing private data", - () => { - this._shuttingDown = true; - this.emptyPrivate(); - } - ); - }, - emptyPrivate() { - if ( - this._isPrivateClipboard && - !Services.prefs.getBoolPref( - "browser.privatebrowsing.preserveClipboard", - false - ) && - this._lastClipboardHash === this._computeClipboardHash() - ) { - // nsIClipboard.emptyClipboard() does nothing in Wayland: - // we'll set an empty string as a work-around. - const trans = this._createTransferable(); - const flavor = "text/plain"; - trans.addDataFlavor(flavor); - const emptyString = Cc["@mozilla.org/supports-string;1"].createInstance( - Ci.nsISupportsString - ); - emptyString.data = ""; - trans.setTransferData(flavor, emptyString); - const { clipboard } = Services, - { kGlobalClipboard } = clipboard; - clipboard.setData(trans, null, kGlobalClipboard); - clipboard.emptyClipboard(kGlobalClipboard); - this._lastClipboardHash = null; - this._isPrivateClipboard = false; - this._log.info("Private clipboard emptied."); - } - }, -}; - ChromeUtils.defineLazyGetter( lazy, "WeaveService", @@ -596,9 +429,7 @@ BrowserGlue.prototype = { // handle any UI migration this._migrateUI(); - - // Base Browser-specific version of _migrateUI. - this._migrateUIBB(); + lazy.ProfileDataUpgrader.upgradeBB(this._isNewProfile); // Mullvad Browser-specific version of _migrateUI. this._migrateUIMB(); @@ -987,10 +818,6 @@ BrowserGlue.prototype = { lazy.WeaveService.init(); } - lazy.SecurityLevelRestartNotification.ready(); - - ClipboardPrivacy.startup(); - lazy.BrowserUtils.callModulesFromCategory( { categoryName: "browser-first-window-ready", @@ -1827,83 +1654,6 @@ BrowserGlue.prototype = { } }, - _migrateUIBB() { - // Version 1: 13.0a3. Reset layout.css.prefers-color-scheme.content-override - // for tor-browser#41739. - // Version 2: 14.0a5: Reset the privacy tracking headers preferences since - // the UI is hidden. tor-browser#42777. - // Also, do not set - // dom.security.https_only_mode_send_http_background_request in - // the security level anymore (tor-browser#42149). - // Also, reset security.xfocsp.errorReporting.automatic since we - // hid its neterror checkbox. tor-browser#42653. - // Version 3: 14.0a7: Reset general.smoothScroll. tor-browser#42070. - // Version 4: 15.0a2: Drop ML components. tor-browser#44045. - const MIGRATION_VERSION = 4; - const MIGRATION_PREF = "basebrowser.migration.version"; - if (this._isNewProfile) { - // Do not migrate fresh profiles - Services.prefs.setIntPref(MIGRATION_PREF, MIGRATION_VERSION); - return; - } else if (this._isNewProfile === undefined) { - // If this happens, check if upstream updated their function and do not - // set this member anymore! - console.error("_migrateUIBB: this._isNewProfile is undefined."); - } - // We do not care whether this is a new or old profile, since in version 1 - // we just quickly clear a user preference, which should not do anything to - // new profiles. - // Shall we ever raise the version number and have a watershed, we can add - // a check easily (any version > 0 will be an old profile). - const currentVersion = Services.prefs.getIntPref(MIGRATION_PREF, 0); - if (currentVersion < 1) { - Services.prefs.clearUserPref( - "layout.css.prefers-color-scheme.content-override" - ); - } - if (currentVersion < 2) { - for (const prefName of [ - "privacy.globalprivacycontrol.enabled", - "privacy.donottrackheader.enabled", - // Telemetry preference for if the user changed the value. - "privacy.globalprivacycontrol.was_ever_enabled", - // The next two preferences have no corresponding UI, but are related. - "privacy.globalprivacycontrol.functionality.enabled", - "privacy.globalprivacycontrol.pbmode.enabled", - "dom.security.https_only_mode_send_http_background_request", - "security.xfocsp.errorReporting.automatic", - ]) { - Services.prefs.clearUserPref(prefName); - } - } - if (currentVersion < 3) { - Services.prefs.clearUserPref("general.smoothScroll"); - } - if (currentVersion < 4) { - for (const prefName of [ - "browser.translations.enable", - "browser.ml.enable", - "browser.ml.chat.enabled", - "browser.ml.linkPreview.enabled", - "browser.tabs.groups.smart.enabled", - "browser.tabs.groups.smart.userEnabled", - "extensions.ml.enabled", - "pdfjs.enableAltText", - "pdfjs.enableAltTextForEnglish", - "pdfjs.enableGuessAltText", - "pdfjs.enableAltTextModelDownload", - "browser.urlbar.quicksuggest.mlEnabled", - "places.semanticHistory.featureGate", - ]) { - // Preferences are locked. Do not want user values to linger in the - // user's profile and become active if these preferences become unlocked - // in the future. - Services.prefs.clearUserPref(prefName); - } - } - Services.prefs.setIntPref(MIGRATION_PREF, MIGRATION_VERSION); - }, - // Use this method for any MB migration that can be run just before showing // the UI. // Anything that critically needs to be migrated earlier should not use this. ===================================== browser/components/ProfileDataUpgrader.sys.mjs ===================================== @@ -900,4 +900,78 @@ export let ProfileDataUpgrader = { // Update the migration version. Services.prefs.setIntPref("browser.migration.version", newVersion); }, + + upgradeBB(isNewProfile) { + // Version 1: 13.0a3. Reset layout.css.prefers-color-scheme.content-override + // for tor-browser#41739. + // Version 2: 14.0a5: Reset the privacy tracking headers preferences since + // the UI is hidden. tor-browser#42777. + // Also, do not set + // dom.security.https_only_mode_send_http_background_request in + // the security level anymore (tor-browser#42149). + // Also, reset security.xfocsp.errorReporting.automatic since we + // hid its neterror checkbox. tor-browser#42653. + // Version 3: 14.0a7: Reset general.smoothScroll. tor-browser#42070. + // Version 4: 15.0a2: Drop ML components. tor-browser#44045. + const MIGRATION_VERSION = 4; + const MIGRATION_PREF = "basebrowser.migration.version"; + + if (isNewProfile) { + // Do not migrate fresh profiles + Services.prefs.setIntPref(MIGRATION_PREF, MIGRATION_VERSION); + return; + } else if (isNewProfile === undefined) { + // If this happens, check if upstream updated their function and do not + // set this member anymore! + console.error("upgradeBB: isNewProfile is undefined."); + } + + const currentVersion = Services.prefs.getIntPref(MIGRATION_PREF, 0); + if (currentVersion < 1) { + Services.prefs.clearUserPref( + "layout.css.prefers-color-scheme.content-override" + ); + } + if (currentVersion < 2) { + for (const prefName of [ + "privacy.globalprivacycontrol.enabled", + "privacy.donottrackheader.enabled", + // Telemetry preference for if the user changed the value. + "privacy.globalprivacycontrol.was_ever_enabled", + // The next two preferences have no corresponding UI, but are related. + "privacy.globalprivacycontrol.functionality.enabled", + "privacy.globalprivacycontrol.pbmode.enabled", + "dom.security.https_only_mode_send_http_background_request", + "security.xfocsp.errorReporting.automatic", + ]) { + Services.prefs.clearUserPref(prefName); + } + } + if (currentVersion < 3) { + Services.prefs.clearUserPref("general.smoothScroll"); + } + if (currentVersion < 4) { + for (const prefName of [ + "browser.translations.enable", + "browser.ml.enable", + "browser.ml.chat.enabled", + "browser.ml.linkPreview.enabled", + "browser.tabs.groups.smart.enabled", + "browser.tabs.groups.smart.userEnabled", + "extensions.ml.enabled", + "pdfjs.enableAltText", + "pdfjs.enableAltTextForEnglish", + "pdfjs.enableGuessAltText", + "pdfjs.enableAltTextModelDownload", + "browser.urlbar.quicksuggest.mlEnabled", + "places.semanticHistory.featureGate", + ]) { + // Preferences are locked. Do not want user values to linger in the + // user's profile and become active if these preferences become unlocked + // in the future. + Services.prefs.clearUserPref(prefName); + } + } + Services.prefs.setIntPref(MIGRATION_PREF, MIGRATION_VERSION); + }, }; ===================================== browser/modules/ClipboardPrivacy.sys.mjs ===================================== @@ -0,0 +1,178 @@ +/* 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/. */ + +const lazy = {}; + +ChromeUtils.defineESModuleGetters(lazy, { + AsyncShutdown: "resource://gre/modules/AsyncShutdown.sys.mjs", + PrivateBrowsingUtils: "resource://gre/modules/PrivateBrowsingUtils.sys.mjs", + setTimeout: "resource://gre/modules/Timer.sys.mjs", +}); + +/** + * Empty clipboard content from private windows on exit. + * + * See tor-browser#42154. + */ +export const ClipboardPrivacy = { + _lastClipboardHash: null, + _globalActivation: false, + _isPrivateClipboard: false, + _hasher: null, + _shuttingDown: false, + _log: null, + + _createTransferable() { + const trans = Cc["@mozilla.org/widget/transferable;1"].createInstance( + Ci.nsITransferable + ); + trans.init(null); + return trans; + }, + _computeClipboardHash() { + const flavors = ["text/x-moz-url", "text/plain"]; + if ( + !Services.clipboard.hasDataMatchingFlavors( + flavors, + Ci.nsIClipboard.kGlobalClipboard + ) + ) { + return null; + } + const trans = this._createTransferable(); + flavors.forEach(trans.addDataFlavor); + try { + Services.clipboard.getData(trans, Ci.nsIClipboard.kGlobalClipboard); + const clipboardContent = {}; + trans.getAnyTransferData({}, clipboardContent); + const { data } = clipboardContent.value.QueryInterface( + Ci.nsISupportsString + ); + const bytes = new TextEncoder().encode(data); + const hasher = (this._hasher ||= Cc[ + "@mozilla.org/security/hash;1" + ].createInstance(Ci.nsICryptoHash)); + hasher.init(hasher.SHA256); + hasher.update(bytes, bytes.length); + return hasher.finish(true); + } catch (e) {} + return null; + }, + + init() { + this._log = console.createInstance({ + prefix: "ClipboardPrivacy", + }); + this._lastClipboardHash = this._computeClipboardHash(); + + // Here we track changes in active window / application, + // by filtering focus events and window closures. + const handleActivation = (win, activation) => { + if (activation) { + if (!this._globalActivation) { + // focus changed within this window, bail out. + return; + } + this._globalActivation = false; + } else if (!Services.focus.activeWindow) { + // focus is leaving this window: + // let's track whether it remains within the browser. + lazy.setTimeout(() => { + this._globalActivation = !Services.focus.activeWindow; + }, 100); + } + + const checkClipboardContent = () => { + const clipboardHash = this._computeClipboardHash(); + if (clipboardHash !== this._lastClipboardHash) { + this._isPrivateClipboard = + !activation && + (lazy.PrivateBrowsingUtils.permanentPrivateBrowsing || + lazy.PrivateBrowsingUtils.isWindowPrivate(win)); + this._lastClipboardHash = clipboardHash; + this._log.debug( + `Clipboard changed: private ${this._isPrivateClipboard}, hash ${clipboardHash}.` + ); + } + }; + + if (win.closed) { + checkClipboardContent(); + } else { + // defer clipboard access on DOM events to work-around tor-browser#42306 + lazy.setTimeout(checkClipboardContent, 0); + } + }; + const focusListener = e => + e.isTrusted && handleActivation(e.currentTarget, e.type === "focusin"); + const initWindow = win => { + for (const e of ["focusin", "focusout"]) { + win.addEventListener(e, focusListener); + } + }; + for (const w of Services.ww.getWindowEnumerator()) { + initWindow(w); + } + Services.ww.registerNotification((win, event) => { + switch (event) { + case "domwindowopened": + initWindow(win); + break; + case "domwindowclosed": + handleActivation(win, false); + if ( + this._isPrivateClipboard && + lazy.PrivateBrowsingUtils.isWindowPrivate(win) && + (this._shuttingDown || + !Array.from(Services.ww.getWindowEnumerator()).find( + w => + lazy.PrivateBrowsingUtils.isWindowPrivate(w) && + // We need to filter out the HIDDEN WebExtensions window, + // which might be private as well but is not UI-relevant. + !w.location.href.startsWith("chrome://extensions/") + )) + ) { + // no more private windows, empty private content if needed + this.emptyPrivate(); + } + } + }); + + lazy.AsyncShutdown.quitApplicationGranted.addBlocker( + "ClipboardPrivacy: removing private data", + () => { + this._shuttingDown = true; + this.emptyPrivate(); + } + ); + }, + emptyPrivate() { + if ( + this._isPrivateClipboard && + !Services.prefs.getBoolPref( + "browser.privatebrowsing.preserveClipboard", + false + ) && + this._lastClipboardHash === this._computeClipboardHash() + ) { + // nsIClipboard.emptyClipboard() does nothing in Wayland: + // we'll set an empty string as a work-around. + const trans = this._createTransferable(); + const flavor = "text/plain"; + trans.addDataFlavor(flavor); + const emptyString = Cc["@mozilla.org/supports-string;1"].createInstance( + Ci.nsISupportsString + ); + emptyString.data = ""; + trans.setTransferData(flavor, emptyString); + const { clipboard } = Services, + { kGlobalClipboard } = clipboard; + clipboard.setData(trans, null, kGlobalClipboard); + clipboard.emptyClipboard(kGlobalClipboard); + this._lastClipboardHash = null; + this._isPrivateClipboard = false; + this._log.info("Private clipboard emptied."); + } + }, +}; ===================================== browser/modules/moz.build ===================================== @@ -136,7 +136,6 @@ EXTRA_JS_MODULES += [ "PopupBlockerObserver.sys.mjs", "ProcessHangMonitor.sys.mjs", "Sanitizer.sys.mjs", - "SecurityLevelRestartNotification.sys.mjs", "SelectionChangedMenulist.sys.mjs", "SharingUtils.sys.mjs", "SiteDataManager.sys.mjs", @@ -149,6 +148,8 @@ EXTRA_JS_MODULES += [ MOZ_SRC_FILES += [ "ContextId.sys.mjs", + "ClipboardPrivacy.sys.mjs", + "SecurityLevelRestartNotification.sys.mjs", ] if CONFIG["MOZ_WIDGET_TOOLKIT"] == "windows": View it on GitLab: https://gitlab.torproject.org/tpo/applications/mullvad-browser/-/compare/b8… -- View it on GitLab: https://gitlab.torproject.org/tpo/applications/mullvad-browser/-/compare/b8… You're receiving this email because of your account on gitlab.torproject.org.
1 0
0 0
[Git][tpo/applications/tor-browser][base-browser-140.2.0esr-15.0-1] 3 commits: fixup! BB 42019: Empty browser's clipboard on browser shutdown
by Pier Angelo Vendrame (@pierov) 08 Sep '25

08 Sep '25
Pier Angelo Vendrame pushed to branch base-browser-140.2.0esr-15.0-1 at The Tor Project / Applications / Tor Browser Commits: 24214f3e by Pier Angelo Vendrame at 2025-09-08T12:12:36+02:00 fixup! BB 42019: Empty browser's clipboard on browser shutdown BB 43770: Follow upstream's BrowserGlue simplifications. ClipboardPrivacy to its own file and initialize it from the manifest, rather than from BrowserGlue. - - - - - 92770da8 by Pier Angelo Vendrame at 2025-09-08T12:16:00+02:00 fixup! BB 40925: Implemented the Security Level component BB 43770: Follow upstream's BrowserGlue simplifications. Initialize the security level notification from the manifest. Also, since it was the only occurrence of the file path, move it to the moz-src:// scheme. - - - - - 88bb3bc2 by Pier Angelo Vendrame at 2025-09-08T12:25:21+02:00 fixup! BB 42027: Base Browser migration procedures. BB 43770: Follow upstream's BrowserGlue simplifications. - - - - - 5 changed files: - browser/components/BrowserComponents.manifest - browser/components/BrowserGlue.sys.mjs - browser/components/ProfileDataUpgrader.sys.mjs - + browser/modules/ClipboardPrivacy.sys.mjs - browser/modules/moz.build Changes: ===================================== browser/components/BrowserComponents.manifest ===================================== @@ -50,6 +50,8 @@ category browser-first-window-ready resource://gre/modules/CaptchaDetectionPingU category browser-first-window-ready resource://gre/modules/SandboxUtils.sys.mjs SandboxUtils.maybeWarnAboutMissingUserNamespaces #endif #endif +category browser-first-window-ready moz-src:///browser/modules/ClipboardPrivacy.sys.mjs ClipboardPrivacy.init +category browser-first-window-ready moz-src:///browser/modules/SecurityLevelRestartNotification.sys.mjs SecurityLevelRestartNotification.ready category browser-idle-startup resource:///modules/PlacesUIUtils.sys.mjs PlacesUIUtils.unblockToolbars category browser-idle-startup resource:///modules/BuiltInThemes.sys.mjs BuiltInThemes.ensureBuiltInThemes ===================================== browser/components/BrowserGlue.sys.mjs ===================================== @@ -12,7 +12,6 @@ ChromeUtils.defineESModuleGetters(lazy, { AWToolbarButton: "resource:///modules/aboutwelcome/AWToolbarUtils.sys.mjs", ASRouter: "resource:///modules/asrouter/ASRouter.sys.mjs", AddonManager: "resource://gre/modules/AddonManager.sys.mjs", - AsyncShutdown: "resource://gre/modules/AsyncShutdown.sys.mjs", BackupService: "resource:///modules/backup/BackupService.sys.mjs", BrowserSearchTelemetry: "moz-src:///browser/components/search/BrowserSearchTelemetry.sys.mjs", @@ -61,8 +60,6 @@ ChromeUtils.defineESModuleGetters(lazy, { ScreenshotsUtils: "resource:///modules/ScreenshotsUtils.sys.mjs", SearchSERPTelemetry: "moz-src:///browser/components/search/SearchSERPTelemetry.sys.mjs", - SecurityLevelRestartNotification: - "resource:///modules/SecurityLevelRestartNotification.sys.mjs", SessionStartup: "resource:///modules/sessionstore/SessionStartup.sys.mjs", SessionStore: "resource:///modules/sessionstore/SessionStore.sys.mjs", ShortcutUtils: "resource://gre/modules/ShortcutUtils.sys.mjs", @@ -106,170 +103,6 @@ if (AppConstants.ENABLE_WEBDRIVER) { const PREF_PDFJS_ISDEFAULT_CACHE_STATE = "pdfjs.enabledCache.state"; -// Empty clipboard content from private windows on exit -// (tor-browser#42154) -const ClipboardPrivacy = { - _lastClipboardHash: null, - _globalActivation: false, - _isPrivateClipboard: false, - _hasher: null, - _shuttingDown: false, - _log: null, - - _createTransferable() { - const trans = Cc["@mozilla.org/widget/transferable;1"].createInstance( - Ci.nsITransferable - ); - trans.init(null); - return trans; - }, - _computeClipboardHash() { - const flavors = ["text/x-moz-url", "text/plain"]; - if ( - !Services.clipboard.hasDataMatchingFlavors( - flavors, - Ci.nsIClipboard.kGlobalClipboard - ) - ) { - return null; - } - const trans = this._createTransferable(); - flavors.forEach(trans.addDataFlavor); - try { - Services.clipboard.getData(trans, Ci.nsIClipboard.kGlobalClipboard); - const clipboardContent = {}; - trans.getAnyTransferData({}, clipboardContent); - const { data } = clipboardContent.value.QueryInterface( - Ci.nsISupportsString - ); - const bytes = new TextEncoder().encode(data); - const hasher = (this._hasher ||= Cc[ - "@mozilla.org/security/hash;1" - ].createInstance(Ci.nsICryptoHash)); - hasher.init(hasher.SHA256); - hasher.update(bytes, bytes.length); - return hasher.finish(true); - } catch (e) {} - return null; - }, - - startup() { - this._log = console.createInstance({ - prefix: "ClipboardPrivacy", - }); - this._lastClipboardHash = this._computeClipboardHash(); - - // Here we track changes in active window / application, - // by filtering focus events and window closures. - const handleActivation = (win, activation) => { - if (activation) { - if (!this._globalActivation) { - // focus changed within this window, bail out. - return; - } - this._globalActivation = false; - } else if (!Services.focus.activeWindow) { - // focus is leaving this window: - // let's track whether it remains within the browser. - lazy.setTimeout(() => { - this._globalActivation = !Services.focus.activeWindow; - }, 100); - } - - const checkClipboardContent = () => { - const clipboardHash = this._computeClipboardHash(); - if (clipboardHash !== this._lastClipboardHash) { - this._isPrivateClipboard = - !activation && - (lazy.PrivateBrowsingUtils.permanentPrivateBrowsing || - lazy.PrivateBrowsingUtils.isWindowPrivate(win)); - this._lastClipboardHash = clipboardHash; - this._log.debug( - `Clipboard changed: private ${this._isPrivateClipboard}, hash ${clipboardHash}.` - ); - } - }; - - if (win.closed) { - checkClipboardContent(); - } else { - // defer clipboard access on DOM events to work-around tor-browser#42306 - lazy.setTimeout(checkClipboardContent, 0); - } - }; - const focusListener = e => - e.isTrusted && handleActivation(e.currentTarget, e.type === "focusin"); - const initWindow = win => { - for (const e of ["focusin", "focusout"]) { - win.addEventListener(e, focusListener); - } - }; - for (const w of Services.ww.getWindowEnumerator()) { - initWindow(w); - } - Services.ww.registerNotification((win, event) => { - switch (event) { - case "domwindowopened": - initWindow(win); - break; - case "domwindowclosed": - handleActivation(win, false); - if ( - this._isPrivateClipboard && - lazy.PrivateBrowsingUtils.isWindowPrivate(win) && - (this._shuttingDown || - !Array.from(Services.ww.getWindowEnumerator()).find( - w => - lazy.PrivateBrowsingUtils.isWindowPrivate(w) && - // We need to filter out the HIDDEN WebExtensions window, - // which might be private as well but is not UI-relevant. - !w.location.href.startsWith("chrome://extensions/") - )) - ) { - // no more private windows, empty private content if needed - this.emptyPrivate(); - } - } - }); - - lazy.AsyncShutdown.quitApplicationGranted.addBlocker( - "ClipboardPrivacy: removing private data", - () => { - this._shuttingDown = true; - this.emptyPrivate(); - } - ); - }, - emptyPrivate() { - if ( - this._isPrivateClipboard && - !Services.prefs.getBoolPref( - "browser.privatebrowsing.preserveClipboard", - false - ) && - this._lastClipboardHash === this._computeClipboardHash() - ) { - // nsIClipboard.emptyClipboard() does nothing in Wayland: - // we'll set an empty string as a work-around. - const trans = this._createTransferable(); - const flavor = "text/plain"; - trans.addDataFlavor(flavor); - const emptyString = Cc["@mozilla.org/supports-string;1"].createInstance( - Ci.nsISupportsString - ); - emptyString.data = ""; - trans.setTransferData(flavor, emptyString); - const { clipboard } = Services, - { kGlobalClipboard } = clipboard; - clipboard.setData(trans, null, kGlobalClipboard); - clipboard.emptyClipboard(kGlobalClipboard); - this._lastClipboardHash = null; - this._isPrivateClipboard = false; - this._log.info("Private clipboard emptied."); - } - }, -}; - ChromeUtils.defineLazyGetter( lazy, "WeaveService", @@ -596,9 +429,7 @@ BrowserGlue.prototype = { // handle any UI migration this._migrateUI(); - - // Base Browser-specific version of _migrateUI. - this._migrateUIBB(); + lazy.ProfileDataUpgrader.upgradeBB(this._isNewProfile); if (!Services.prefs.prefHasUserValue(PREF_PDFJS_ISDEFAULT_CACHE_STATE)) { lazy.PdfJs.checkIsDefault(this._isNewProfile); @@ -984,10 +815,6 @@ BrowserGlue.prototype = { lazy.WeaveService.init(); } - lazy.SecurityLevelRestartNotification.ready(); - - ClipboardPrivacy.startup(); - lazy.BrowserUtils.callModulesFromCategory( { categoryName: "browser-first-window-ready", @@ -1823,83 +1650,6 @@ BrowserGlue.prototype = { } }, - _migrateUIBB() { - // Version 1: 13.0a3. Reset layout.css.prefers-color-scheme.content-override - // for tor-browser#41739. - // Version 2: 14.0a5: Reset the privacy tracking headers preferences since - // the UI is hidden. tor-browser#42777. - // Also, do not set - // dom.security.https_only_mode_send_http_background_request in - // the security level anymore (tor-browser#42149). - // Also, reset security.xfocsp.errorReporting.automatic since we - // hid its neterror checkbox. tor-browser#42653. - // Version 3: 14.0a7: Reset general.smoothScroll. tor-browser#42070. - // Version 4: 15.0a2: Drop ML components. tor-browser#44045. - const MIGRATION_VERSION = 4; - const MIGRATION_PREF = "basebrowser.migration.version"; - if (this._isNewProfile) { - // Do not migrate fresh profiles - Services.prefs.setIntPref(MIGRATION_PREF, MIGRATION_VERSION); - return; - } else if (this._isNewProfile === undefined) { - // If this happens, check if upstream updated their function and do not - // set this member anymore! - console.error("_migrateUIBB: this._isNewProfile is undefined."); - } - // We do not care whether this is a new or old profile, since in version 1 - // we just quickly clear a user preference, which should not do anything to - // new profiles. - // Shall we ever raise the version number and have a watershed, we can add - // a check easily (any version > 0 will be an old profile). - const currentVersion = Services.prefs.getIntPref(MIGRATION_PREF, 0); - if (currentVersion < 1) { - Services.prefs.clearUserPref( - "layout.css.prefers-color-scheme.content-override" - ); - } - if (currentVersion < 2) { - for (const prefName of [ - "privacy.globalprivacycontrol.enabled", - "privacy.donottrackheader.enabled", - // Telemetry preference for if the user changed the value. - "privacy.globalprivacycontrol.was_ever_enabled", - // The next two preferences have no corresponding UI, but are related. - "privacy.globalprivacycontrol.functionality.enabled", - "privacy.globalprivacycontrol.pbmode.enabled", - "dom.security.https_only_mode_send_http_background_request", - "security.xfocsp.errorReporting.automatic", - ]) { - Services.prefs.clearUserPref(prefName); - } - } - if (currentVersion < 3) { - Services.prefs.clearUserPref("general.smoothScroll"); - } - if (currentVersion < 4) { - for (const prefName of [ - "browser.translations.enable", - "browser.ml.enable", - "browser.ml.chat.enabled", - "browser.ml.linkPreview.enabled", - "browser.tabs.groups.smart.enabled", - "browser.tabs.groups.smart.userEnabled", - "extensions.ml.enabled", - "pdfjs.enableAltText", - "pdfjs.enableAltTextForEnglish", - "pdfjs.enableGuessAltText", - "pdfjs.enableAltTextModelDownload", - "browser.urlbar.quicksuggest.mlEnabled", - "places.semanticHistory.featureGate", - ]) { - // Preferences are locked. Do not want user values to linger in the - // user's profile and become active if these preferences become unlocked - // in the future. - Services.prefs.clearUserPref(prefName); - } - } - Services.prefs.setIntPref(MIGRATION_PREF, MIGRATION_VERSION); - }, - async _showUpgradeDialog() { const data = await lazy.OnboardingMessageProvider.getUpgradeMessage(); const { gBrowser } = lazy.BrowserWindowTracker.getTopWindow(); ===================================== browser/components/ProfileDataUpgrader.sys.mjs ===================================== @@ -900,4 +900,78 @@ export let ProfileDataUpgrader = { // Update the migration version. Services.prefs.setIntPref("browser.migration.version", newVersion); }, + + upgradeBB(isNewProfile) { + // Version 1: 13.0a3. Reset layout.css.prefers-color-scheme.content-override + // for tor-browser#41739. + // Version 2: 14.0a5: Reset the privacy tracking headers preferences since + // the UI is hidden. tor-browser#42777. + // Also, do not set + // dom.security.https_only_mode_send_http_background_request in + // the security level anymore (tor-browser#42149). + // Also, reset security.xfocsp.errorReporting.automatic since we + // hid its neterror checkbox. tor-browser#42653. + // Version 3: 14.0a7: Reset general.smoothScroll. tor-browser#42070. + // Version 4: 15.0a2: Drop ML components. tor-browser#44045. + const MIGRATION_VERSION = 4; + const MIGRATION_PREF = "basebrowser.migration.version"; + + if (isNewProfile) { + // Do not migrate fresh profiles + Services.prefs.setIntPref(MIGRATION_PREF, MIGRATION_VERSION); + return; + } else if (isNewProfile === undefined) { + // If this happens, check if upstream updated their function and do not + // set this member anymore! + console.error("upgradeBB: isNewProfile is undefined."); + } + + const currentVersion = Services.prefs.getIntPref(MIGRATION_PREF, 0); + if (currentVersion < 1) { + Services.prefs.clearUserPref( + "layout.css.prefers-color-scheme.content-override" + ); + } + if (currentVersion < 2) { + for (const prefName of [ + "privacy.globalprivacycontrol.enabled", + "privacy.donottrackheader.enabled", + // Telemetry preference for if the user changed the value. + "privacy.globalprivacycontrol.was_ever_enabled", + // The next two preferences have no corresponding UI, but are related. + "privacy.globalprivacycontrol.functionality.enabled", + "privacy.globalprivacycontrol.pbmode.enabled", + "dom.security.https_only_mode_send_http_background_request", + "security.xfocsp.errorReporting.automatic", + ]) { + Services.prefs.clearUserPref(prefName); + } + } + if (currentVersion < 3) { + Services.prefs.clearUserPref("general.smoothScroll"); + } + if (currentVersion < 4) { + for (const prefName of [ + "browser.translations.enable", + "browser.ml.enable", + "browser.ml.chat.enabled", + "browser.ml.linkPreview.enabled", + "browser.tabs.groups.smart.enabled", + "browser.tabs.groups.smart.userEnabled", + "extensions.ml.enabled", + "pdfjs.enableAltText", + "pdfjs.enableAltTextForEnglish", + "pdfjs.enableGuessAltText", + "pdfjs.enableAltTextModelDownload", + "browser.urlbar.quicksuggest.mlEnabled", + "places.semanticHistory.featureGate", + ]) { + // Preferences are locked. Do not want user values to linger in the + // user's profile and become active if these preferences become unlocked + // in the future. + Services.prefs.clearUserPref(prefName); + } + } + Services.prefs.setIntPref(MIGRATION_PREF, MIGRATION_VERSION); + }, }; ===================================== browser/modules/ClipboardPrivacy.sys.mjs ===================================== @@ -0,0 +1,178 @@ +/* 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/. */ + +const lazy = {}; + +ChromeUtils.defineESModuleGetters(lazy, { + AsyncShutdown: "resource://gre/modules/AsyncShutdown.sys.mjs", + PrivateBrowsingUtils: "resource://gre/modules/PrivateBrowsingUtils.sys.mjs", + setTimeout: "resource://gre/modules/Timer.sys.mjs", +}); + +/** + * Empty clipboard content from private windows on exit. + * + * See tor-browser#42154. + */ +export const ClipboardPrivacy = { + _lastClipboardHash: null, + _globalActivation: false, + _isPrivateClipboard: false, + _hasher: null, + _shuttingDown: false, + _log: null, + + _createTransferable() { + const trans = Cc["@mozilla.org/widget/transferable;1"].createInstance( + Ci.nsITransferable + ); + trans.init(null); + return trans; + }, + _computeClipboardHash() { + const flavors = ["text/x-moz-url", "text/plain"]; + if ( + !Services.clipboard.hasDataMatchingFlavors( + flavors, + Ci.nsIClipboard.kGlobalClipboard + ) + ) { + return null; + } + const trans = this._createTransferable(); + flavors.forEach(trans.addDataFlavor); + try { + Services.clipboard.getData(trans, Ci.nsIClipboard.kGlobalClipboard); + const clipboardContent = {}; + trans.getAnyTransferData({}, clipboardContent); + const { data } = clipboardContent.value.QueryInterface( + Ci.nsISupportsString + ); + const bytes = new TextEncoder().encode(data); + const hasher = (this._hasher ||= Cc[ + "@mozilla.org/security/hash;1" + ].createInstance(Ci.nsICryptoHash)); + hasher.init(hasher.SHA256); + hasher.update(bytes, bytes.length); + return hasher.finish(true); + } catch (e) {} + return null; + }, + + init() { + this._log = console.createInstance({ + prefix: "ClipboardPrivacy", + }); + this._lastClipboardHash = this._computeClipboardHash(); + + // Here we track changes in active window / application, + // by filtering focus events and window closures. + const handleActivation = (win, activation) => { + if (activation) { + if (!this._globalActivation) { + // focus changed within this window, bail out. + return; + } + this._globalActivation = false; + } else if (!Services.focus.activeWindow) { + // focus is leaving this window: + // let's track whether it remains within the browser. + lazy.setTimeout(() => { + this._globalActivation = !Services.focus.activeWindow; + }, 100); + } + + const checkClipboardContent = () => { + const clipboardHash = this._computeClipboardHash(); + if (clipboardHash !== this._lastClipboardHash) { + this._isPrivateClipboard = + !activation && + (lazy.PrivateBrowsingUtils.permanentPrivateBrowsing || + lazy.PrivateBrowsingUtils.isWindowPrivate(win)); + this._lastClipboardHash = clipboardHash; + this._log.debug( + `Clipboard changed: private ${this._isPrivateClipboard}, hash ${clipboardHash}.` + ); + } + }; + + if (win.closed) { + checkClipboardContent(); + } else { + // defer clipboard access on DOM events to work-around tor-browser#42306 + lazy.setTimeout(checkClipboardContent, 0); + } + }; + const focusListener = e => + e.isTrusted && handleActivation(e.currentTarget, e.type === "focusin"); + const initWindow = win => { + for (const e of ["focusin", "focusout"]) { + win.addEventListener(e, focusListener); + } + }; + for (const w of Services.ww.getWindowEnumerator()) { + initWindow(w); + } + Services.ww.registerNotification((win, event) => { + switch (event) { + case "domwindowopened": + initWindow(win); + break; + case "domwindowclosed": + handleActivation(win, false); + if ( + this._isPrivateClipboard && + lazy.PrivateBrowsingUtils.isWindowPrivate(win) && + (this._shuttingDown || + !Array.from(Services.ww.getWindowEnumerator()).find( + w => + lazy.PrivateBrowsingUtils.isWindowPrivate(w) && + // We need to filter out the HIDDEN WebExtensions window, + // which might be private as well but is not UI-relevant. + !w.location.href.startsWith("chrome://extensions/") + )) + ) { + // no more private windows, empty private content if needed + this.emptyPrivate(); + } + } + }); + + lazy.AsyncShutdown.quitApplicationGranted.addBlocker( + "ClipboardPrivacy: removing private data", + () => { + this._shuttingDown = true; + this.emptyPrivate(); + } + ); + }, + emptyPrivate() { + if ( + this._isPrivateClipboard && + !Services.prefs.getBoolPref( + "browser.privatebrowsing.preserveClipboard", + false + ) && + this._lastClipboardHash === this._computeClipboardHash() + ) { + // nsIClipboard.emptyClipboard() does nothing in Wayland: + // we'll set an empty string as a work-around. + const trans = this._createTransferable(); + const flavor = "text/plain"; + trans.addDataFlavor(flavor); + const emptyString = Cc["@mozilla.org/supports-string;1"].createInstance( + Ci.nsISupportsString + ); + emptyString.data = ""; + trans.setTransferData(flavor, emptyString); + const { clipboard } = Services, + { kGlobalClipboard } = clipboard; + clipboard.setData(trans, null, kGlobalClipboard); + clipboard.emptyClipboard(kGlobalClipboard); + this._lastClipboardHash = null; + this._isPrivateClipboard = false; + this._log.info("Private clipboard emptied."); + } + }, +}; ===================================== browser/modules/moz.build ===================================== @@ -136,7 +136,6 @@ EXTRA_JS_MODULES += [ "PopupBlockerObserver.sys.mjs", "ProcessHangMonitor.sys.mjs", "Sanitizer.sys.mjs", - "SecurityLevelRestartNotification.sys.mjs", "SelectionChangedMenulist.sys.mjs", "SharingUtils.sys.mjs", "SiteDataManager.sys.mjs", @@ -149,6 +148,8 @@ EXTRA_JS_MODULES += [ MOZ_SRC_FILES += [ "ContextId.sys.mjs", + "ClipboardPrivacy.sys.mjs", + "SecurityLevelRestartNotification.sys.mjs", ] if CONFIG["MOZ_WIDGET_TOOLKIT"] == "windows": View it on GitLab: https://gitlab.torproject.org/tpo/applications/tor-browser/-/compare/a1886d… -- View it on GitLab: https://gitlab.torproject.org/tpo/applications/tor-browser/-/compare/a1886d… You're receiving this email because of your account on gitlab.torproject.org.
1 0
0 0
  • ← Newer
  • 1
  • ...
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • Older →

HyperKitty Powered by HyperKitty version 1.3.12.